diff --git a/.ci_files/anims_ofw.txt b/.ci_files/anims_ofw.txt new file mode 100644 index 000000000..a6c7ca694 --- /dev/null +++ b/.ci_files/anims_ofw.txt @@ -0,0 +1,128 @@ +Filetype: Flipper Animation Manifest +Version: 1 + +Name: L1_Waves_128x50 +Min butthurt: 0 +Max butthurt: 5 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Laptop_128x51 +Min butthurt: 0 +Max butthurt: 7 +Min level: 1 +Max level: 1 +Weight: 3 + +Name: L1_Sleep_128x64 +Min butthurt: 0 +Max butthurt: 10 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Recording_128x51 +Min butthurt: 0 +Max butthurt: 8 +Min level: 1 +Max level: 1 +Weight: 3 + +Name: L1_Furippa1_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 1 +Max level: 1 +Weight: 3 + +Name: L2_Furippa2_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L3_Furippa3_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 3 +Max level: 3 +Weight: 3 + +Name: L1_Read_books_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 1 +Max level: 1 +Weight: 3 + +Name: L2_Hacking_pc_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L1_Cry_128x64 +Min butthurt: 8 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Boxing_128x64 +Min butthurt: 10 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Mad_fist_128x64 +Min butthurt: 9 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Mods_128x64 +Min butthurt: 0 +Max butthurt: 9 +Min level: 1 +Max level: 3 +Weight: 5 + +Name: L1_Painting_128x64 +Min butthurt: 0 +Max butthurt: 7 +Min level: 1 +Max level: 3 +Weight: 4 + +Name: L3_Hijack_radio_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 3 +Max level: 3 +Weight: 3 + +Name: L3_Lab_research_128x54 +Min butthurt: 0 +Max butthurt: 10 +Min level: 3 +Max level: 3 +Weight: 3 + +Name: L2_Soldering_128x64 +Min butthurt: 0 +Max butthurt: 10 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L1_Leaving_sad_128x64 +Min butthurt: 14 +Max butthurt: 14 +Min level: 1 +Max level: 3 +Weight: 3 diff --git a/.drone.yml b/.drone.yml index 5b14639d4..d46354045 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,6 +1,6 @@ kind: pipeline type: docker -name: "Build firmware" +name: "Release firmware" steps: - name: "Update submodules" @@ -11,19 +11,38 @@ steps: - git submodule foreach git config --local gc.auto 0 - git log -1 --format='%H' - - name: "Build default fw" + - name: "Build firmware" image: hfdj/fztools pull: never commands: - export DIST_SUFFIX=${DRONE_TAG} - - export WORKFLOW_BRANCH_OR_TAG=dev-cfw + - export WORKFLOW_BRANCH_OR_TAG=release-cfw - ./fbt COMPACT=1 DEBUG=0 updater_package - mkdir artifacts-default - mv dist/f7-C/* artifacts-default/ - ls -laS artifacts-default - ls -laS artifacts-default/f7-update-${DRONE_TAG} - sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md - - echo '# [Install via Web Updater](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=dev-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md + - echo '# [Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md + environment: + FBT_TOOLS_CUSTOM_LINK: + from_secret: fbt_link + + - name: "Build no anims FW" + image: hfdj/fztools + pull: never + commands: + - rm -f assets/dolphin/external/manifest.txt + - cp .ci_files/anims_ofw.txt assets/dolphin/external/manifest.txt + - export DIST_SUFFIX=${DRONE_TAG}n + - export WORKFLOW_BRANCH_OR_TAG=no-custom-anims + - ./fbt COMPACT=1 DEBUG=0 updater_package + - mkdir artifacts-ofw-anims + - mv dist/f7-C/* artifacts-ofw-anims/ + - ls -laS artifacts-ofw-anims + - ls -laS artifacts-ofw-anims/f7-update-${DRONE_TAG}n + - echo '' >> CHANGELOG.md + - echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md environment: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link @@ -31,12 +50,19 @@ steps: - name: "Bundle self-update packages" image: kramos/alpine-zip commands: + - cp artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.tgz . - cp artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz . + - zip -r artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.zip artifacts-ofw-anims/f7-update-${DRONE_TAG}n - zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG} + - tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts debug + - rm -rf artifacts-ofw-anims/f7-update-${DRONE_TAG} - rm -rf artifacts-default/f7-update-${DRONE_TAG} + - ls -laS artifacts-ofw-anims - ls -laS artifacts-default + - mv artifacts-default/ ${DRONE_TAG} + - ls -laS ${DRONE_TAG} - - name: "Upload to updates server" + - name: "Upload default to updates srv" image: appleboy/drone-scp settings: host: @@ -48,8 +74,29 @@ steps: port: from_secret: dep_port target: - from_secret: dep_target - source: flipper-z-f7-update-${DRONE_TAG}.tgz + from_secret: dep_target_new + source: + - ${DRONE_TAG}/*.tgz + - ${DRONE_TAG}/*.zip + - ${DRONE_TAG}/*.json + - ${DRONE_TAG}/*.elf + - ${DRONE_TAG}/*.dfu + - ${DRONE_TAG}/*.bin + + - name: "Upload no-anims to updates srv" + image: appleboy/drone-scp + settings: + host: + from_secret: dep_host + username: + from_secret: dep_user + password: + from_secret: dep_passwd + port: + from_secret: dep_port + target: + from_secret: dep_target_noanim + source: flipper-z-f7-update-${DRONE_TAG}n.tgz - name: "Do Github release" image: ddplugins/github-release @@ -61,8 +108,9 @@ steps: api_key: from_secret: github_apikey files: - - artifacts-default/*.tgz - - artifacts-default/*.zip + - ${DRONE_TAG}/*.tgz + - ${DRONE_TAG}/*.zip + - artifacts-ofw-anims/*.tgz title: ${DRONE_TAG} note: CHANGELOG.md checksum: @@ -70,6 +118,17 @@ steps: - sha1 - crc32 + - name: "Trigger update server reindex" + image: hfdj/fztools + pull: never + environment: + UPD_KEY: + from_secret: git_update_serv_token + UPD_URL: + from_secret: git_update_server_url + commands: + - curl -X POST -F 'key='$UPD_KEY'' $UPD_URL + - name: "Send files to telegram" image: appleboy/drone-telegram settings: @@ -87,9 +146,18 @@ steps: [-Github-](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/${DRONE_TAG}) - [-Install via Web Updater-](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=dev-cfw&version=${DRONE_TAG})" + [-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md) + + + [-Download latest extra apps pack-](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps) + + + [-Version without custom animations - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n) + + + [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=release-cfw&version=${DRONE_TAG})" document: - - artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz + - ${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz - name: "Send discord notification" image: appleboy/drone-discord @@ -107,7 +175,16 @@ steps: [[Github]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/${DRONE_TAG}) - [-Install via Web Updater-](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=dev-cfw&version=${DRONE_TAG})" + [-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md) + + + [-Download latest extra apps pack-](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps) + + + [-Version without custom animations - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n) + + + [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=release-cfw&version=${DRONE_TAG})" trigger: event: @@ -115,3 +192,121 @@ trigger: node: typ: haupt + +--- +kind: pipeline +type: docker +name: "Dev build" + +steps: + - name: "Update submodules" + image: alpine/git + commands: + - git submodule sync + - git -c protocol.version=2 submodule update --init --force --recursive + - git submodule foreach git config --local gc.auto 0 + - git log -1 --format='%H' + + - name: "Build dev FW" + image: hfdj/fztools + pull: never + commands: + - export DIST_SUFFIX=${DRONE_BUILD_NUMBER} + - export WORKFLOW_BRANCH_OR_TAG=dev-cfw + - ./fbt COMPACT=1 DEBUG=0 updater_package + - mkdir artifacts-default + - mv dist/f7-C/* artifacts-default/ + - ls -laS artifacts-default + - ls -laS artifacts-default/f7-update-${DRONE_BUILD_NUMBER} + environment: + FBT_TOOLS_CUSTOM_LINK: + from_secret: fbt_link + + - name: "Bundle self-update packages" + image: kramos/alpine-zip + commands: + - cp artifacts-default/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz . + - rm -rf artifacts-default/f7-update-${DRONE_BUILD_NUMBER} + - ls -laS artifacts-default + - mv artifacts-default/ dev + - ls -laS dev + + - name: "Clean dev folder" + image: appleboy/drone-ssh + settings: + host: + from_secret: dep_host + username: + from_secret: dep_user + password: + from_secret: dep_passwd + port: + from_secret: dep_port + command_timeout: 30s + script: + - cd web/unleashedflip.com/public_html/fw/dev + - rm -f ./* + + - name: "Upload default to updates srv" + image: appleboy/drone-scp + settings: + host: + from_secret: dep_host + username: + from_secret: dep_user + password: + from_secret: dep_passwd + port: + from_secret: dep_port + target: + from_secret: dep_target_new + source: + - dev/*.tgz + - dev/*.zip + - dev/*.json + - dev/*.elf + - dev/*.dfu + - dev/*.bin + + - name: "Trigger update server reindex" + image: hfdj/fztools + pull: never + environment: + UPD_KEY: + from_secret: git_update_serv_token + UPD_URL: + from_secret: git_update_server_url + commands: + - curl -X POST -F 'key='$UPD_KEY'' $UPD_URL + + - name: "Send files to telegram" + image: appleboy/drone-telegram + settings: + token: + from_secret: tgtoken + to: + from_secret: tgid_dev + format: markdown + message: "Unleashed firmware dev build successful! + + + Build: {{build.number}} + + SHA: {{commit.sha}} + + + Commit: {{commit.message}} + + + [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER})" + document: + - dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz + +trigger: + branch: + - dev + event: + - push + +node: + typ: haupt diff --git a/.gitignore b/.gitignore index 8f4ab0e1f..813505b50 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,5 @@ openocd.log # PVS Studio temporary files .PVS-Studio/ PVS-Studio.log + +.gdbinit diff --git a/.gitmodules b/.gitmodules index ba7644981..a97e0933a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,12 +22,12 @@ [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 [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git +[submodule "applications/plugins/dap_link/lib/free-dap"] + path = applications/plugins/dap_link/lib/free-dap + url = https://github.com/ataradov/free-dap.git diff --git a/.pvsconfig b/.pvsconfig index d17eaa5a0..5f1ffb7cb 100644 --- a/.pvsconfig +++ b/.pvsconfig @@ -5,6 +5,7 @@ //-V:BPTREE_DEF2:779,1086,557,773,512 //-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685 //-V:ALGO_DEF:1048,747,1044 +//-V:TUPLE_DEF2:524,590,1001,760 # Non-severe malloc/null pointer deref warnings //-V::522:2,3 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..5c46d3979 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -38,6 +38,7 @@ "postAttachCommands": [ // "compare-sections", "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", // "source debug/FreeRTOS/FreeRTOS.py", // "svd_load debug/STM32WB55_CM4.svd" ] @@ -59,6 +60,7 @@ "set confirm off", "set mem inaccessible-by-default off", "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", // "compare-sections", ] // "showDevDebugOutput": "raw", @@ -76,9 +78,30 @@ "rtos": "FreeRTOS", "postAttachCommands": [ "source debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", ] // "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", + "fap-set-debug-elf-root build/latest/.extapps", + ], + // "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/.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/CHANGELOG.md b/CHANGELOG.md index 75d414b30..39d7c1e71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,42 @@ ### New changes -* Add 433Mhz Security+ 1.0/2.0 in add manually in subghz -* Update Minesweeper (https://github.com/panki27/minesweeper) ------ -* SubGHz: Fix DTM Neo (keeloq) encoder -* fbt: fix flash usb without resources -* OFW: DesktopSettings: reset submenu before running dialog +* Plugins: SubGHz Bruteforcer -> Add support for Ansonic 12bit protocol (FM238) +* Plugins: Fix DTMF Dolphin -> Add forgotten scene and menu item +* Plugins: Update DTMF Dolphin [(by litui)](https://github.com/litui/dtmf_dolphin) +* Plugins: Update TOTP [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) +* Plugins: iButton Fuzzer and RFID Fuzzer improvements +* Plugins: i2c tools fix name display +* Plugins: Add 3 new plugins BlackJack, Solitaire [(by teeebor)](https://github.com/teeebor/flipper_games) and HEX Viewer [(by QtRoS)](https://github.com/QtRoS/flipperzero-firmware) +* Plugins -> PR: Wifi marauder BT menus option (by @rf-bandit | PR #164) +* Plugins -> PR: Update i2c tools (New UI) (by @NaejEL | PR #171) +* Plugins -> PR: Fix htu21d falsely reading temp as humidity (by @GottZ | PR #175) +* SubGHz -> PR: GUI Fix - Allow setting RSSI trigger to beggining (by @TQMatvey | PR #180) +* SubGHz: Remove not widely used frequency from hopper +* SubGHz: Fix starline encoder +* SubGHz: Frequency Analyzer -> Save last trigger level +* SubGHz: Speedup subghz launch from favourites +* SubGHz: Add new freqs and modulation to user config +* Infrared: Update universal remote assets (by @Amec0e) +* CI/CD: Improvements, dev builds (can be found in this telegram channel -> https://t.me/kotnehleb) +* Power -> PR: Show battery percentile while charging (by @TQMatvey | PR #178) +* Docs -> PR: Some updates (by @lucemans | PR #169 and #170) +* CLI -> PR: Update cli_commands.c To add `src` / `source` command for people exploring cli (by @PharoahCoder | PR #176) +* OFW: Fix U2F HID vulnerability +* OFW: Core: thread allocation shortcut +* OFW: WS: add protocol GT-WT02 +* OFW: SubGhz: add protocol "Ansonic" +* OFW: SubGhz: add protocol Nice_Flo 20bit -#### [🎲 Download extra apps pack](https://download-directory.github.io/?url=https://github.com/UberGuidoZ/Flipper/tree/main/Applications/Unleashed) +#### [🎲 Download latest extra apps pack](https://download-directory.github.io/?url=https://github.com/xMasterX/unleashed-extra-pack/tree/main/apps) [-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md) [-> Download qFlipper 1.2.1 (allows .tgz installation) (official link)](https://update.flipperzero.one/builds/qFlipper/1.2.1/) ## Please support development of the project +* Boosty: https://boosty.to/mmxdev +* destream (100 EUR min): https://destream.net/live/MMX/donate +* USDT(TRC20): `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs` +* BCH: `qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3` * ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a` * BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9` * DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv` @@ -23,10 +47,3 @@ Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for iOS mobile app / qFlipper Update using qFlipper (1.2.0+) is now possible with `.tgz` update package! Also you can use Web Updater or self-update package. - - -## Support unleashed (Not RM) so unleashed team can develop new features -* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a` -* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9` -* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv` -* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9` diff --git a/RM11240405-0.71.2-f50f05b.tgz b/RM11240405-0.71.2-f50f05b.tgz new file mode 100644 index 000000000..3921339a6 Binary files /dev/null and b/RM11240405-0.71.2-f50f05b.tgz differ diff --git a/RM11240405-0.71.2-f50f05b.zip b/RM11240405-0.71.2-f50f05b.zip new file mode 100644 index 000000000..0c656f4d8 Binary files /dev/null and b/RM11240405-0.71.2-f50f05b.zip differ diff --git a/ReadMe.md b/ReadMe.md index 4b294d876..1e9fd9c78 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,51 +1,52 @@ -

+# Flipper Zero FW [ROGUEMASTER] +This firmware is a fork of [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware) and the main Flipper Devices FW! I will try to keep active development and updates from both in this build along with any other projects that can be found to be useful to the community. I try to keep this FW build the most cutting edge with updates from both and updates from active community projects. -[Join THE Flipper Uncensored Discord](https://discord.gg/gF2bBUzAFe) & [support us if you like what you see](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md)! πŸ˜„πŸš€πŸ’Έ

-
+This software is for experimental purposes only and is not meant for any illegal activity/purposes. We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law. -#### Thank you to all the supporters; this firmware is a fork of [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware) & the main Flipper Devices FW! I will try to keep active development and updates from both in this build along with any other projects that can be found to be useful to the community. I try to keep this FW build the most cutting edge with updates from both and updates from active community projects. All features and projects pulled are listed in expandable sections below. Please do [support us](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md) and/or [xMasterX](https://github.com/DarkFlippers/unleashed-firmware)! Everyone gives much of their uncompensated free time to ensure the success of the Flipper Zero! +## [Support Us!](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md) +Everyone gives much of their uncompensated free time to ensure the success of the Flipper Zero! +Thank you to all the supporters! -
fzCUSTOM
+- [Support us if you like what you see](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/SUPPORT.md)! πŸ˜„πŸš€πŸ’Έ +- Donations: BTC: `3MPQbKmGRCstg4FjnadfHa3woCT94JkR2a` +- Donations: ETH: `0xC32Ea488DBeCF95992A5C81BD411e56Bd418BC5f` +- [Join THE Flipper Uncensored Discord](https://discord.gg/gF2bBUzAFe) -Latest Updates: +## Latest Updates -- To avoid Application errors and duplicates, delete /ext/apps before doing the RM firmware update - Known Issues: `Chess` -- Last Synced/Checked [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware), changes in [changelog](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/CHANGELOG.md): `2022-10-13 17:56 GMT` -- Last Synced/Checked [OFW](https://github.com/flipperdevices/flipperzero-firmware), changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2022-10-13 17:56 GMT` -- Updated [TOTP (By akopachov)](https://github.com/akopachov/flipperzero-firmware/tree/totp_plugin) -- Assets: Includes New Dolphin Animations: [Blaster (By Sasquach)] -- Assets: Includes New Dolphin Animations: [Flipper City (By Svaarich)] -- Updated Random Deed Function To Include New Options from OFW -- Updated Assets: Dolphin Animations: [Rogue Master Custom Firmware (By Sasquach)] & [Narut0 (By Sasquach)] -- OFW: Dolphin score points update **(WARNING!!! Dolphin Level will be reset after installing of this release!!!!)** -- Updated view for desktop to exclude icons on the left and show minimal battery [By Talking-Sasquach](https://github.com/skizzophrenic/Talking-Sasquach/tree/main/Battery%20Only%20Top%20Status%20Bar) -- Added: [Black Jack (By teeebor)](https://github.com/teeebor/flipper_games) (Thank you Haseo for PR) -- Added: [Music Beeper (By OFW)](With Changes By qqMajiKpp/Haseo) -- Chip 8 Removed, Tama Fix and Tanks Fixed [By Haseo](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/362) +- Last Synced/Checked [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware), changes in [changelog](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/CHANGELOG.md): `2022-11-23 21:01 EST` +- Last Synced/Checked [OFW](https://github.com/flipperdevices/flipperzero-firmware), changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2022-11-23 21:01 EST` +- Added: [Zero Tracker (By DrZZlo13)](https://github.com/DrZlo13/flipper-zero-music-tracker) +- Updated: [DHT Temp Monitor (By quen0n)](https://github.com/quen0n/FipperZero-DHT-Monitor) `Req: DHT11/DHT22(AM2302)/AM2301` +- Updated: [Sub-GHz Bruteforcer (By Ganapati & xMasterX)](https://github.com/derskythe/flipperzero-subbrute/tree/master) +- Updated: [Wii EC Analyser (By csBlueChip)](https://github.com/csBlueChip/FlipperZero_WiiEC) +- Updated: [Authenticator/TOTP (By akopachov)](https://github.com/akopachov/flipper-zero_authenticator) +- Updated: [Temperature Sensor (By Mywk)](https://github.com/Mywk/FlipperTemperatureSensor) `Req: HTU2XD, SHT2X, SI702X, SI700X, SI701X or AM2320` +- Updated: [Zero Tracker (By DrZZlo13)](https://github.com/DrZlo13/flipper-zero-music-tracker) +- Added: [Password Generator (By anakod)](https://github.com/anakod/flipper_passgen) +- Updated: [Timelapse (By theageoflove)](https://github.com/theageoflove/flipperzero-zeitraffer) +- Added: [NRF24 Scanner (By vad7)](https://github.com/vad7/nrf24scan) -
- TO DO / REMOVED
+## Install from Release +FLASH STOCK FIRST BEFORE UPDATING TO CUSTOM FIRMWARE! +- To avoid Application errors, delete /ext/apps before doing the RM firmware update +- To install new FW, extract the [latest release zip file](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/releases) to a folder, put the folder in the update folder on your SD card, and run the update file inside the folder using the Archive app (down from flipper desktop). If you were previously unleashed, you need to update your extend_range.txt file.`UPDATE IGNORE FLAG TO TRUE TO UNLEASH YOUR FLIPPER!!` On any update, you may need to update this file and your unirf map file, so keep backups. πŸ˜„ -- [Keynote BT plugin: long press on OK to switch between Space and Retur… #1729 (By coded-with-claws)] -- GPIO: Feature to read EEPROM of SFP Modules using I2C [(By marcusju)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/198) -- Settings: Rename from App [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/259) -- Lost To Faps: Settings: Favorite Game by holding UP on Desktop [Thanks to gotnull](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/57) -- Lost To Faps: Settings: Hold Down for Games Menu [(Thanks to ESurge)](https://github.com/ESurge/flipperzero-firmware-wPlugins) -- Moved SubGHz Remote to FAP App (Not Working, So Reverted To Internal) -- Moved GPIO to FAP App (Not Working, So Reverted To Internal) -- Moved Infrared to FAP App (Not Working, So Reverted To Internal) -- [IΒ²C-Scanner #1431 (By GitChris3004)](https://github.com/flipperdevices/flipperzero-firmware/pull/1431) +## Build +```bash +$ git clone --recursive https://github.com/RogueMaster/flipperzero-firmware-wPlugins.git +$ cd flipperzero-firmware-wPlugins/ +$ ./fbt updater_package -
+# If building FAPS: +$ ./fbt fap_dist - - - -

This software is for experimental purposes only and is not meant for any illegal activity/purposes. We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law.

-
FLASH STOCK FIRST BEFORE UPDATING TO CUSTOM FIRMWARE +# If building image assets: +$ ./fbt resources icons dolphin_ext +``` -
- HOW TOs
+## How To - [HERE IS A GUIDE FOR INSTALL (BY PINGYWON)](https://flipper.pingywon.com/) - [HERE IS A NOOB GUIDE TO FLASH AND UNLOCK (BY interestingsoup)](https://interestingsoup.com/n00b-guide-flashing-flipper-zero-to-rougemaster/) @@ -59,62 +60,43 @@ - [How to add extra SubGHz frequencies](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/documentation/SubGHzSettings.md) - [πŸ’Ž Extra plugins precompiled for Unleashed](https://github.com/UberGuidoZ/Flipper/tree/main/Applications/Unleashed) - [Configure Sub-GHz Remote App](https://github.com/RogueMaster/flipperzero-firmware/blob/420/documentation/SubGHzRemotePlugin.md) -
+- [GAMES ONLY MODE PASSWORD: UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT IN CLOCK](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md) +- [FLIPPER PROJECT WISH LIST](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/RoadMap.md) +- [SAMPLE EDUCATIONAL PROJECTS πŸ˜„](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/RoadMap.md) +- Contact me on [Discord](https://discord.gg/gF2bBUzAFe) if you want a renamed Flipper DFU. I can do custom names too! +- [`FUN Links HERE` Collection for your Flipper SD](https://github.com/RogueMaster/awesome-flipperzero-withModules) -- To avoid Application errors, delete /ext/apps before doing the RM firmware update -- To install new FW, extract the [latest release zip file](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/releases) to a folder, put the folder in the update folder on your SD card, and run the update file inside the folder using the Archive app (down from flipper desktop). If you were previously unleashed, you need to update your extend_range.txt file. **UPDATE IGNORE FLAG TO TRUE TO UNLEASH YOUR FLIPPER!!** On any update, you may need to update this file and your unirf map file, so keep backups. πŸ˜„ - - [

GAMES ONLY MODE PASSWORD: UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT IN CLOCK

](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md) -
[FLIPPER PROJECT WISH LIST](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/RoadMap.md) - [SAMPLE EDUCATIONAL PROJECTS πŸ˜„](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/RoadMap.md) -
[`FUN Links HERE` Collection for your Flipper SD](https://github.com/RogueMaster/awesome-flipperzero-withModules) -
-

DONATIONS ACCEPTED πŸ˜„πŸš€πŸ’Έ

-BTC: 3MPQbKmGRCstg4FjnadfHa3woCT94JkR2a
-ETH: 0xC32Ea488DBeCF95992A5C81BD411e56Bd418BC5f -

- -Contact me on [Discord](https://discord.gg/gF2bBUzAFe) if you want a renamed Flipper DFU. I can do custom names too! -

[Join my Flipper Uncensored Discord](https://discord.gg/gF2bBUzAFe)

-
- You should clone with
+## All Changes/Features - ```shell -$ git clone --recursive https://github.com/RogueMaster/flipperzero-firmware-wPlugins.git -$ cd flipperzero-firmware-wPlugins/ -$ ./fbt resources icons -$ ./fbt updater_package - -# If building FAPS: -$ ./fbt plugin_dist -``` -
-
- -
- All Changes/Features
- +- Animations: File `/ext/dolphin/manifest.txt` no longer will get overwritten. Any automatic building of animations is disabled [here](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/fbe9175e0c828a54e651ee11f64f10f21e36a907). - Animations: Hold Center to change Flipper idle animation. [Thanks to Zycenios](https://github.com/flipperdevices/flipperzero-firmware/commit/111786ef40e50a40d2e510595672b569d9b97bba) With changes by RogueMaster. - Animations: iButton and RFID P0kem0n images [Thanks to Panzer00Z](https://github.com/Panzer00Z/flipperzero-firmware/) - Animations: Idle animations will show all animations regardless of level and butthurt [Thanks to qqMajiKpp] +- Animations: Painting speech bubbles updated from FalsePhilosopher - Animations: RM FW Update image [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/257) - Animations: SubGHZ Scanning image with Pikachu [Thanks to Panzer00Z](https://github.com/Panzer00Z/flipperzero-firmware/blob/3a548ea9bb181c9348d8afb427890c411456134e/assets/icons/SubGhz/Scanning_123x52.png) +- Animations: Trimmed out the Flipper animations. `/ext/dolphin` folder on your Flipper should now be managed by you! [Copy this folder (RM Select)](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin-RMselect) or [this folder (RM minimal)](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin-minimal) if you don't want to do the work but want more animations. +- [Archive: File Browser Ordering (By Dig03)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/389) +- [Archive: Browser: Context menu to show file content (By askoriy)](https://github.com/DarkFlippers/unleashed-firmware/pull/139) - Archive: FAPs are now launchable from Archive [By RogueMaster], thanks xMasterX for the suggestion -- Assets: Includes a NFC Level 50 Gan0n Amiibo +- Assets: Includes a NFC Level 50 Z3lda Amiibo - Assets: Includes a NFC Rick Roll link - Assets: Includes New Dolphin Animations: [Listed Here](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/assets/resources/dolphin) - Assets: Includes sample Music Player tunes - Assets: Includes sample SubGHz assets for Crosswalk, Handicap Doors, Sextoys, Tesla Charge Port, and Unitree Go1 Robot Dog - Assets: Includes Sonic Screw Driver sound for the Wav Player - Assets: Running DolphinRestorer.fap on new install will auto-level to Level 7. +- [BadUSB: BadUSB as FAP #396 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/396) - BadUSB: Added ignore DUCKY_LANG cmd to retain compatibility with existing scripts [(Thanks to v1nc)](https://github.com/v1nc/flipperzero-firmware) -- BadUSB: Assets for Kiosk Evasion and Wifi Stealer +- BadUSB: Assets for Kiosk Evasion (By nocomp) and Wifi Stealer (By 7h30th3r0n3) - BadUSB: [Dummy decoy/bad usb keyboard layout #1525 (By dummy-decoy)](https://github.com/flipperdevices/flipperzero-firmware/pull/1525) - BadUSB: nb-NO Added norwegian keyboard layout [(By jd-raymaker)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/357) - BadUSB: show script errors on screen [(By CromFr)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/200) - BadUSB: sk-SK maping keybord for BadUsb [(By jaroslavmraz)](https://github.com/flipperdevices/flipperzero-firmware/pull/1619) -- Clock.fap, iButton.fap and U2F.fap loader apps and available as Favorites [Thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/336) +- Clock: Clock.fap loader apps and available as Favorites [Thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/336) - Desktop: Hidden top bar [Thanks to ESurge](https://github.com/ESurge/) +- Desktop: Exclude icons on the left and show minimal battery [Thanks to skizzophrenic/Talking-Sasquach](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/360) - Development free space thanks to removal of unused debug tools and [thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/46/files) for removal of first start assets. - Dolphin: Assigned profile pic for levels 1-10 (Happy Lvl 1 Dolphin), 11-15 (Happy Lvl 2 Dolphin), 16-18 (Happy Lvl 3 Dolphin), 19-21 (Kid G0ku), 22-24 (Adult G0ku), 25-27 (SSJ G0ku) and 28-30 (SSJ3 G0ku) - Dolphin: Expanded max level from 3 to 30 using [Roll20](https://roll20.net/compendium/dnd5e/Monsters#h-Experience%20Points), Increased max deed XP per action type from 15 to 45 exp daily & updated animation manifest for max level 30 for all animations (By RogueMaster) @@ -125,29 +107,37 @@ $ ./fbt plugin_dist - Dolphin: Passport: Show EXP [(By Dabolus)](https://github.com/Dabolus/flipperzero-firmware-rpg/) - Dolphin: Changed daily MAX to 198 on all 7 Deed Types. Random Deed Selection used for MAX +3 EXP daily gain. - Dolphin: Plugin Achivement +3 EXP for a total of up to 700 EXP daily. Plugin Achivements are: -- - Dice First Roll for d20+ = sides on dice (i.e. Nat 20 on d20) -- - Dice First Roll for d20+ = sides on dice - 1 (+1 EXP) (i.e. 19 on d20) -- - Games Only Mode from Lock Menu -- - Getting 2048 in 2048 -- - Mouse Jacker Ducky Run -- - NRFSniffer Found Address -- - Stopwatch @ Alert -- - Tetris, Snake, or Flappy Bird Score For EXP -- - Zombiez for every 20 Zombie kills + - Dice First Roll for d20+ = sides on dice (i.e. Nat 20 on d20) + - Dice First Roll for d20+ = sides on dice - 1 (+1 EXP) (i.e. 19 on d20) + - Games Only Mode from Lock Menu + - Getting 2048 in 2048 + - Mouse Jacker Ducky Run + - NRFSniffer Found Address + - Stopwatch @ Alert + - Tetris, Snake, or Flappy Bird Score For EXP + - Zombiez for every 20 Zombie kills - Dolphin: SD dolphin manifest updated to weight animations differently +- GPIO: Feature to read EEPROM of SFP Modules using I2C [(By marcusju)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/198) +- GPIO: [IΒ²C-Scanner #1431 (By GitChris3004)](https://github.com/flipperdevices/flipperzero-firmware/pull/1431) +- iButton: [Fixed issue when loading iButton keys or U2F token from Archive app #382 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/382) +- iButton: iButton.fap loader apps and available as Favorites [Thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/336) - Icon Decode/Encode [(Thanks to PixlEmly)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/55/files) -- IR: Universal AC, Audio, Fans & Projectors from [Unleashed/Eng1n33r](https://github.com/DarkFlippers/unleashed-firmware) +- [Infrared: Infrared AS FAP #398 (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/398) (With Thanks to ESurge) [Restored Infrared and RFID CLI #405 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/405) +- Infrared: Universal AC, Audio, Fans & Projectors from [Unleashed/Eng1n33r](https://github.com/DarkFlippers/unleashed-firmware) - Plugins: 2048, Arkanoid, Snake, and Tetris show score. Thanks to [whoamins](https://github.com/flipperdevices/flipperzero-firmware/commit/7feda832ede1ba8468eff2ca055fef3ddbdc16ac) and [DevMilanIan](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/188) With position changes by RogueMaster. Also all + Tic Tac Toe updated by [Unleashed/Eng1n33r](https://github.com/DarkFlippers/unleashed-firmware) for stability. - Plugins: Icon for Clock [Thanks to Redlink](https://github.com/redlink2/flipperzero-firmware/tree/menuChanges) +- [RFID: LFRFID AS FAP #397 (By ROgueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/397) (With Thanks to ESurge) [Restored Infrared and RFID CLI #405 (By ESurge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/405) - Settings: "Lock W PIN + Off" add to UP menu [(By RogueMaster)] - Settings: Actual PIN Lock [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/desktop/desktop.c) - Settings: Auto-Lock Options Added: 10s+15s+90s [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c) - Settings: Battery Meter on Desktop [Thanks to McAzzaMan](https://github.com/McAzzaMan/flipperzero-firmware/tree/BatteryPercentageView) - Settings: Custom name with this compile: CUSTOM_FLIPPER_NAME=name ./fbt updater_package [By Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware) - Settings: Desktop => [Games Only Mode (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md) -- - [UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT FROM CLOCK](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md)<== FULL LIST OF GAMES ONLY CONTROLS +- - [UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT FROM CLOCK](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/GAMES_ONLY.md) (FULL LIST OF GAMES ONLY CONTROLS) - Settings: LCD Timeout Options Added: 10s+90s+2min+5min+10min [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/notification/notification_settings_app.c) +- Settings: Rename from App [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/409) - Settings: Rename from SD `dolphin/name.txt` [(Thanks to E_Surge)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/259) +- Settings: Power: Fix for settings reload every second [(By lokiuox)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/364) - Settings: Scan names will have timestamp instead of random name assigned for [NFC](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/lib/toolbox/random_name.c) and [SubGHz](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/subghz/scenes/subghz_scene_read_raw.c) (By RogueMaster) - Settings: Storage Info: [SD info: Add dynamic units and free % #1634 (By non-bin)](https://github.com/flipperdevices/flipperzero-firmware/pull/1634) - Settings: Updated Dummy Mode mode to have access to 2048, Dice, Snake, Tetris & Zombiez [(By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/bf964fffdd2c1d730623673987a6de32a3f7c92f) @@ -160,83 +150,105 @@ $ ./fbt plugin_dist - SubGHz: [Add settings to subghz read functionality to allow setting RSSI threshold (raw only) (By PolymerPrints)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/184) - SubGHz: Extended ranges enabled through flag in /ext/subghz/assets/extend_range.txt [from tkerrby](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/116) - SubGHz: Moved setting_user file to setting_user.txt! This makes it changable from IOS app. (By RogueMaster) -- SubGHz: New frequency analyzer [(By ClusterM)](https://github.com/flipperdevices/flipperzero-firmware/pull/1501) [feedback mode (by darmiel)](https://github.com/darmiel/flipper-playlist/tree/feat/stealth-frequency-analyzer) [Quiet Mode (by Himura2la)](https://github.com/ClusterM/flipperzero-firmware/pull/1) +- SubGHz: New frequency analyzer [(By ClusterM)](https://github.com/flipperdevices/flipperzero-firmware/pull/1501) [feedback mode (by darmiel)](https://github.com/darmiel/flipper-playlist/tree/feat/stealth-frequency-analyzer) [Quiet Mode (by Himura2la)](https://github.com/ClusterM/flipperzero-firmware/pull/1) [New frequency analyzer #1557 (By ClusterM)](https://github.com/flipperdevices/flipperzero-firmware/pull/1557) - SubGHz: Protocols An-Motors, BFT Mitto, Came Atomo, FAAC SLH (Spa), HCS101, Keeloq, Keeloq Common, Nice Flor S, SecPlus v1+v2 and Star Line updates from [Eng1n33r](https://github.com/DarkFlippers/unleashed-firmware) - SubGHz: Unlock from SD flag from [(cloudbreakdaniel)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/97db0dc91ee3dff812b4dec0618e3f198de14405). Update `subghz/assets/extend_range.txt` with [this file](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/assets/resources/subghz/assets/extend_range.txt) on SD. **UPDATE IGNORE FLAG TO TRUE TO UNLEASH YOUR FLIPPER!!** +- U2F: U2F.fap loader apps and available as Favorites [Thanks to ESurge](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/336) -
- - -
- Open PRs Checked Out & Not Merged In Main
- -- [NFC: Display UL PWD_AUTH payload / ntag-pwd-capture #1471 (Thanks to GMMan)](https://github.com/flipperdevices/flipperzero-firmware/pull/1471) -- [New frequency analyzer #1557 (By ClusterM)](https://github.com/flipperdevices/flipperzero-firmware/pull/1557) -- [Automatic shutdown on idle #1647 (By SHxKenzuto)](https://github.com/flipperdevices/flipperzero-firmware/pull/1647) -- [Decode RAW recordings #1667 (By qistoph)](https://github.com/flipperdevices/flipperzero-firmware/pull/1667) - -
- -
- -
- GAMES
+## Games +- [15 (By x27)](https://github.com/x27/flipperzero-game15) - [2048 (By OlegSchwann)](https://github.com/OlegSchwann/flipperzero-firmware/tree/hackaton/game_2048/applications/game-2048) [(Score By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/186) - [Arkanoid (By gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins) [(Score By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/188) -- [Black Jack (By teeebor)](https://github.com/teeebor/flipper_games) +- [BlackJack (By teeebor)](https://github.com/teeebor/flipper_games) - [Chess (By Okalachev)](https://github.com/okalachev/flipperzero-firmware/tree/chess) Crashes 1st load if FW <~750KB or every load on larger FW `Broken?` -- [Chip8 Emulator (By mega8bit)](https://github.com/mega8bit/flipperzero-firmware) Updated by ESurge. Add SD folder `chip8`, [Get GAMES HERE](https://johnearnest.github.io/chip8Archive/) (Needs Controls Programmed) `HIDDEN because its broken` -- [Doom (By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/) - [Dice Roller Including SEX/WAR/8BALL/WEED DICE (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/dice/dice.c) -- [Flappy Bird (By DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird) +- [Doom (By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/) +- [Flappy Bird (By DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird) [Flappy: Border hitboxes, bigger Pilars (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/114) [Increase pilars line width to improve visibility (By ahumeniy)](https://github.com/DarkFlippers/unleashed-firmware/pull/140) - [Game of Life (Updated to work by tgxn) (By itsyourbedtime)](https://github.com/tgxn/flipperzero-firmware/blob/dev/applications/game_of_life/game_of_life.c) +- [Heap Defence (By xMasterX)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/commit/fc776446de9fdd553b221c02668b925b689378d8) [(original by wquinoa & Vedmein)](https://github.com/Vedmein/flipperzero-firmware/tree/hd/svisto-perdelki) - [Mandelbrot Set (By Possibly-Matt)](https://github.com/Possibly-Matt/flipperzero-firmware-wPlugins) - [Minesweeper (By panki27)](https://github.com/panki27/minesweeper) - [Monty Hall (By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/203) -- Snake [OFW] +- [Scorched Tanks (By jasniec)](https://github.com/jasniec/flipper-scorched-tanks-game) +- [Snake (By OlegSchwann)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/829)(With updates from DrZlo13, xMasterX, QtRoS and RogueMaster) [Snake Score Saving (By JuanJakobo)](https://github.com/flipperdevices/flipperzero-firmware/pull/1922) [Turns anywhere (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/125) [Food Spawns Anywwhere (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/130) +- [Solitaire (By teeebor)](https://github.com/teeebor/flipper_games) +- [T-Rex (By gelin)](https://github.com/gelin/t-rex-runner) WIP - [TAMA P1 (By GMMan)](https://github.com/GMMan/flipperzero-firmware/tree/tama-p1) requires [this rom](https://tinyurl.com/tamap1) IN `tama_p1` on SD as `rom.bin` to make it work. - [Tanks (By Alexgr13)](https://github.com/alexgr13/flipperzero-firmware/tree/fork/dev/applications/tanks-game) - [Tetris (By jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game) - [Tic Tac Toe (By gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins) - [Video Poker (By PixlEmly)](https://github.com/PixlEmly/flipperzero-firmware-testing/blob/420/applications/VideoPoker/poker.c) - [Zombiez (Reworked By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/240) [(Original By Dooskington)](https://github.com/Dooskington/flipperzero-zombiez) -
-
- PLUGINS
+## Plugins -- [Bluetooth Remote (By Cutch)[OFW]](https://github.com/flipperdevices/flipperzero-firmware/pull/1330) +- [Air Mouse (By ginkage)](https://github.com/ginkage/FlippAirMouse/) +- [Authenticator/TOTP (By akopachov)](https://github.com/akopachov/flipper-zero_authenticator) +- [Bad Apple (By GMMan)](https://github.com/GMMan/flipperzero-badapple) `video.bin NEEDED, Please LMK if you have it` +- [Barcode Generator (By McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) +- [Bluetooth Remote (By Cutch)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1330) - [BPM Tapper (By panki27)](https://github.com/panki27/bpm-tapper) - [Calculator (By n-o-T-I-n-s-a-n-e)](https://github.com/n-o-T-I-n-s-a-n-e) - [Ceasar Cipher (By panki27)](https://github.com/panki27/caesar-cipher) - [Clock/Stopwatch (By CompaqDisc, Stopwatch & Sound Alert By RogueMaster)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61) [12/24HR (By non-bin)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/254) [Refactoring (By GMMan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/256) -- [DSTIKE Deauther (By SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module/tree/FlipperZero-Module-v2/FlipperZeroModule/FlipperZero-ESP8266-Deauth-App)) `Req: ESP8266` -- [Dolphin Backup (By nminaylov)](https://github.com/flipperdevices/flipperzero-firmware/pull/1384) Modified by RogueMaster +- [Counter (By Krulknul)](https://github.com/Krulknul/dolphin-counter) +- [DAP Link (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1897) +- [Deauther PWNDTOOLS V2.6.0 (By HEX0DAYS)](https://github.com/HEX0DAYS/FlipperZero-PWNDTOOLS) `Req: ESP8266` [Original](https://github.com/SpacehuhnTech/esp8266_deauther) +- [DHT Temp Monitor (By quen0n)](https://github.com/quen0n/FipperZero-DHT-Monitor) `Req: DHT11/DHT22(AM2302)/AM2301` +- [Distance Sensor (By Sanqui)](https://github.com/Sanqui/flipperzero-firmware/tree/hc_sr04)) `Req: HC-SR04` Ported/Modified by xMasterX +- [Dolphin Backup (By nminaylov)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1384) Modified by RogueMaster - [Dolphin Restorer (By nminaylov)](https://github.com/flipperdevices/flipperzero-firmware/pull/1384) Cloned by RogueMaster +- [DSTIKE Deauther (By SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module/tree/FlipperZero-Module-v2/FlipperZeroModule/FlipperZero-ESP8266-Deauth-App) `Req: ESP8266` - [DTMF Dolphin (By litui)](https://github.com/litui/dtmf_dolphin) +- [Flashlight (By xMasterX)](https://github.com/xMasterX/flipper-flashlight) +- [GPS (By ezod)](https://github.com/ezod/flipperzero-gps) `Req: NMEA 0183` +- [HEX Viewer (By QtRoS)](https://github.com/QtRoS/flipperzero-firmware) +- [iButton Fuzzer (By xMasterX)](https://github.com/DarkFlippers/unleashed-firmware) +- [i2c Tools (By NaejEL)](https://github.com/NaejEL/flipperzero-i2ctools) +- [Lightmeter (By oleksiikutuzov)](https://github.com/oleksiikutuzov/flipperzero-lightmeter) `Req: BH1750` +- [IFTTT Virtual Button (By Ferrazzi)](https://github.com/Ferrazzi/FlipperZero_IFTTT_Virtual_Button) `Req: ESP8266 w/ IFTTT FW Flashed` - [Metronome (By panki27)](https://github.com/panki27/Metronome) +- [Morse Code (By wh00hw)](https://github.com/DarkFlippers/unleashed-firmware/pull/144) - [Mouse Jacker (By mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/mousejacker) from nocomp/Frog/UberGuidoZ) `Req: NRF24` - [Mouse Jiggler (By Jacob-Tate)](https://github.com/Jacob-Tate/flipperzero-firmware/blob/dev/applications/mouse_jiggler/mouse_jiggler.c) (Original By MuddleBox) - [Multi Converter (By theisolinearchip)](https://github.com/theisolinearchip) -- Music Beeper [OFW] (Music Player With Changes By qqMajiKpp/Haseo) -- Music Player [OFW] +- [Music Beeper (By DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/pull/1189) (With Changes By qqMajiKpp/Haseo) +- [Music Player (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1189) +- [NFC Magic (By gornekich)](https://github.com/flipperdevices/flipperzero-firmware/pull/1966) - [NRF Sniff (By mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/nrfsniff) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/nrfsniff) from nocomp/Frog/UberGuidoZ) `Req: NRF24` +- [NRF24 Scanner (By vad7)](https://github.com/vad7/nrf24scan) +- [Ocarina (By invalidna-me)](https://github.com/invalidna-me/flipperzero-ocarina) [Here are the LOTZ Songs](https://www.zeldadungeon.net/wiki/Ocarina_of_Time_Songs) - [Paint (By n-o-T-I-n-s-a-n-e)](https://github.com/n-o-T-I-n-s-a-n-e) +- [Password Generator (By anakod)](https://github.com/anakod/flipper_passgen) - [PicoPass Reader (By Bettse)](https://github.com/flipperdevices/flipperzero-firmware/pull/1366) +- [Pomodoro Timer (By sbrin)](https://github.com/sbrin/flipperzero_pomodoro) - [RFID Fuzzer (By Ganapati)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/245) [Changes by Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware) - [RF Remix (By ESurge)](https://github.com/ESurge/flipperzero-firmware-unirfremix) [(Original By jimilinuxguy)](https://github.com/jimilinuxguy/flipperzero-universal-rf-remote/tree/028d615c83f059bb2c905530ddb3d4efbd3cbcae/applications/jukebox) [(More protocols thanks to darmiel & xMasterX)](https://github.com/darmiel/flipper-playlist/blob/feat/unirf-protocols/applications/unirfremix/unirfremix_app.c) +- [SAM (By Unknown)][Original?](https://github.com/ctoth/SAM) - [Sentry Safe (By H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/sentry_safe) from [UberGuidoZ](https://github.com/UberGuidoZ/)) +- [Signal Generator (By nminaylov)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1793) - [Spectrum Analyzer (By jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) [Updates (for testing) Thanks to theY4Kman](https://github.com/theY4Kman/flipperzero-firmware) -- [Sub-GHz Bruteforcer (By Ganapati & xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/57) +- [Sub-GHz Bruteforcer (By Ganapati & xMasterX)](https://github.com/derskythe/flipperzero-subbrute/tree/master) - [Sub-GHz Playlist (By darmiel)](https://github.com/darmiel/flipper-playlist) -- [TOTP (By akopachov)](https://github.com/akopachov/flipperzero-firmware/tree/totp_plugin) -- [UPC-A Generator (By McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) +- [Temperature Sensor (By Mywk)](https://github.com/Mywk/FlipperTemperatureSensor) `Req: HTU2XD, SHT2X, SI702X, SI700X, SI701X or AM2320` +- [Temperature Sensor (By xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/commit/9c4612e571db42f5e6123a3f159e01337453a6af) `Req: AM2320` +- [Timelapse (By theageoflove)](https://github.com/theageoflove/flipperzero-zeitraffer) +- [Tuning Fork (By besya)](https://github.com/besya/flipperzero-tuning-fork) +- [UART Echo (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/831) +- [USB HID Autofire (By pbek)](https://github.com/pbek/usb_hid_autofire) - [USB Keyboard (By huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard) -- [WAV Player (By Zlo)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) Updated by Atmanos & RogueMaster To Work -- [WiFi (Deauther) (By Timmotools)](https://github.com/Timmotools/flipperzero_esp8266_deautherv2) -- [WiFi (Marauder) (By 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) `REQUIRES ESP32 WITH MARAUDER FLASHED` +- [WAV Player (By DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) Updated by Atmanos & RogueMaster To Work +- [WiFi (Deauther) V2 (By Timmotools)](https://github.com/Timmotools/flipperzero_esp8266_deautherv2) `Req: ESP8266` +- [WiFi (Marauder) (By 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) `Req: ESP32 WITH MARAUDER FLASHED` - [WiFi Scanner v.0.4 (By SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module-ESP8266) `Req: ESP8266 or ESP32` -
+- [Wii EC Analyser (By csBlueChip)](https://github.com/csBlueChip/FlipperZero_WiiEC) +- [Zero Tracker (By DrZZlo13)](https://github.com/DrZlo13/flipper-zero-music-tracker) -
+## Open PRs Checked Out & Not Merged In Main + +- [Automatic shutdown on idle #1647 (By SHxKenzuto)](https://github.com/flipperdevices/flipperzero-firmware/pull/1647) +- [Decode RAW recordings #1667 (By qistoph)](https://github.com/flipperdevices/flipperzero-firmware/pull/1667) +- [NFC - Machine Readable Travel Documents #1866 (By qistoph)](https://github.com/flipperdevices/flipperzero-firmware/pull/1866) +- [nfc: NTAG password auto capture (and other password-related changes) #1843 (By GMMan)](https://github.com/flipperdevices/flipperzero-firmware/pull/1843) +- [Snake Plugin: Store game state on close and restore it on restart, show highscore #1922 (By JuanJakobo)](https://github.com/flipperdevices/flipperzero-firmware/pull/1922) diff --git a/RoadMap.md b/RoadMap.md index 0cf95331d..8c9fcea8e 100644 --- a/RoadMap.md +++ b/RoadMap.md @@ -6,9 +6,9 @@ - I tried to organize them in terms of difficulty. ## WISH LIST ITEMS: +- Battery Type moved to Power Settings - `Notepad` APP to allow taking quick notes and saving to SD. - - Also can possibly open/edit .md,.txt,.fmf and other text friendly formats -- `UART` Move UART Echo app from Debug to GPIO section. - `Write URL to NFC` APP to allow creating URL NFC tags from only the flipper - - Also can possibly support larger URLs than the ones in samples (due to length limits on NFC types) - - Also can possibly create other types of tags, like WIFI configurations diff --git a/SConstruct b/SConstruct index 74fa5667b..474175c14 100644 --- a/SConstruct +++ b/SConstruct @@ -7,6 +7,7 @@ # construction of certain targets behind command-line options. import os +from fbt.util import path_as_posix DefaultEnvironment(tools=[]) @@ -33,10 +34,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=[ @@ -47,6 +44,7 @@ distenv = coreenv.Clone( "jflash", ], ENV=os.environ, + UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}", ) firmware_env = distenv.AddFwProject( @@ -144,23 +142,28 @@ distenv.Default(basic_dist) dist_dir = distenv.GetProjetDirName() fap_dist = [ distenv.Install( - f"#/dist/{dist_dir}/apps/debug_elf", - firmware_env["FW_EXTAPPS"]["debug"].values(), + distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"), + list( + app_artifact.debug + for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() + ), ), - *( - distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1]) - for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() + distenv.Install( + f"#/dist/{dist_dir}/apps", + "#/assets/resources/apps", ), ] -Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values()) +Depends( + fap_dist, + list( + app_artifact.validator + for app_artifact in firmware_env["FW_EXTAPPS"].applications.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"], plugin_resources_dist) +distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) # Target for bundling core2 package for qFlipper @@ -198,6 +201,7 @@ firmware_debug = distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), ) distenv.Depends(firmware_debug, firmware_flash) @@ -207,6 +211,7 @@ distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBREMOTE="${BLACKMAGIC_ADDR}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), ) # Debug alien elf @@ -215,7 +220,7 @@ distenv.PhonyTarget( "${GDBPYCOM}", GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", - GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', + GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', ) distenv.PhonyTarget( @@ -235,14 +240,14 @@ distenv.PhonyTarget( # Linter distenv.PhonyTarget( "lint", - "${PYTHON3} scripts/lint.py check ${LINT_SOURCES}", - LINT_SOURCES=firmware_env["LINT_SOURCES"], + "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}", + LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], ) distenv.PhonyTarget( "format", - "${PYTHON3} scripts/lint.py format ${LINT_SOURCES}", - LINT_SOURCES=firmware_env["LINT_SOURCES"], + "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}", + LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], ) # PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests @@ -282,7 +287,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 @@ -291,6 +296,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/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 000000000..037474aa3 Binary files /dev/null and b/applications/debug/file_browser_test/icons/badusb_10px.png differ diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index 701e5325a..16996ba8c 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -220,11 +220,7 @@ static UartEchoApp* uart_echo_app_alloc() { furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); - app->worker_thread = furi_thread_alloc(); - furi_thread_set_name(app->worker_thread, "UsbUartWorker"); - furi_thread_set_stack_size(app->worker_thread, 1024); - furi_thread_set_context(app->worker_thread, app); - furi_thread_set_callback(app->worker_thread, uart_echo_worker); + app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app); furi_thread_start(app->worker_thread); return app; diff --git a/applications/debug/unit_tests/infrared/infrared_test.c b/applications/debug/unit_tests/infrared/infrared_test.c index 8879c8fc8..2bcb95da8 100644 --- a/applications/debug/unit_tests/infrared/infrared_test.c +++ b/applications/debug/unit_tests/infrared/infrared_test.c @@ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) { infrared_test_run_decoder(InfraredProtocolRC5, 5); infrared_test_run_decoder(InfraredProtocolSamsung32, 1); infrared_test_run_decoder(InfraredProtocolSIRC, 3); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); } MU_TEST(infrared_test_decoder_nec) { @@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) { infrared_test_run_encoder(InfraredProtocolRC6, 1); } +MU_TEST(infrared_test_decoder_kaseikyo) { + infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 2); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 3); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 4); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 5); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 6); +} + MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1); infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1); @@ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1); infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1); infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1); + infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1); } MU_TEST_SUITE(infrared_test) { @@ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) { MU_RUN_TEST(infrared_test_decoder_nec); MU_RUN_TEST(infrared_test_decoder_samsung32); MU_RUN_TEST(infrared_test_decoder_necext1); + MU_RUN_TEST(infrared_test_decoder_kaseikyo); MU_RUN_TEST(infrared_test_decoder_mixed); MU_RUN_TEST(infrared_test_encoder_decoder_all); } diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index f149508b0..454c11c0f 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -16,6 +18,8 @@ #define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/") #define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" #define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" +#define NFC_TEST_DICT_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc") +#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_dev_test.nfc") static const char* nfc_test_file_type = "Flipper NFC test"; static const uint32_t nfc_test_file_version = 1; @@ -220,11 +224,272 @@ MU_TEST(mf_classic_dict_test) { furi_string_free(temp_str); } +MU_TEST(mf_classic_dict_load_test) { + Storage* storage = furi_record_open(RECORD_STORAGE); + mu_assert(storage != NULL, "storage != NULL assert failed\r\n"); + + // Delete unit test dict file if exists + if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) { + mu_assert( + storage_simply_remove(storage, NFC_TEST_DICT_PATH), + "remove == true assert failed\r\n"); + } + + // Create unit test dict file + Stream* file_stream = file_stream_alloc(storage); + mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n"); + mu_assert( + file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS), + "file_stream_open == true assert failed\r\n"); + + // Write unit test dict file + char key_str[] = "a0a1a2a3a4a5"; + mu_assert( + stream_write_cstring(file_stream, key_str) == strlen(key_str), + "write == true assert failed\r\n"); + // Close unit test dict file + mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); + + // Load unit test dict file + MfClassicDict* instance = NULL; + instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest); + mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n"); + uint32_t total_keys = mf_classic_dict_get_total_keys(instance); + mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n"); + + // Read key + uint64_t key_ref = 0xa0a1a2a3a4a5; + uint64_t key_dut = 0; + FuriString* temp_str = furi_string_alloc(); + mu_assert( + mf_classic_dict_get_next_key_str(instance, temp_str), + "get_next_key_str == true assert failed\r\n"); + mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n"); + mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); + mu_assert( + mf_classic_dict_get_next_key(instance, &key_dut), + "get_next_key == true assert failed\r\n"); + mu_assert(key_dut == key_ref, "invalid key loaded\r\n"); + furi_string_free(temp_str); + mf_classic_dict_free(instance); + + // Check that MfClassicDict added new line to the end of the file + mu_assert( + file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING), + "file_stream_open == true assert failed\r\n"); + mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n"); + uint8_t last_char = 0; + mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n"); + mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n"); + mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); + + // Delete unit test dict file + mu_assert( + storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n"); + stream_free(file_stream); + furi_record_close(RECORD_STORAGE); +} + +MU_TEST(nfca_file_test) { + NfcDevice* nfc = nfc_device_alloc(); + mu_assert(nfc != NULL, "nfc_device_data != NULL assert failed\r\n"); + nfc->format = NfcDeviceSaveFormatUid; + + // Fill the UID, sak, ATQA and type + uint8_t uid[7] = {0x04, 0x01, 0x23, 0x45, 0x67, 0x89, 0x00}; + memcpy(nfc->dev_data.nfc_data.uid, uid, 7); + nfc->dev_data.nfc_data.uid_len = 7; + + nfc->dev_data.nfc_data.sak = 0x08; + nfc->dev_data.nfc_data.atqa[0] = 0x00; + nfc->dev_data.nfc_data.atqa[1] = 0x04; + nfc->dev_data.nfc_data.type = FuriHalNfcTypeA; + + // Save the NFC device data to the file + mu_assert( + nfc_device_save(nfc, NFC_TEST_NFC_DEV_PATH), "nfc_device_save == true assert failed\r\n"); + nfc_device_free(nfc); + + // Load the NFC device data from the file + NfcDevice* nfc_validate = nfc_device_alloc(); + mu_assert( + nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, true), + "nfc_device_load == true assert failed\r\n"); + + // Check the UID, sak, ATQA and type + mu_assert(memcmp(nfc_validate->dev_data.nfc_data.uid, uid, 7) == 0, "uid assert failed\r\n"); + mu_assert(nfc_validate->dev_data.nfc_data.sak == 0x08, "sak == 0x08 assert failed\r\n"); + mu_assert( + nfc_validate->dev_data.nfc_data.atqa[0] == 0x00, "atqa[0] == 0x00 assert failed\r\n"); + mu_assert( + nfc_validate->dev_data.nfc_data.atqa[1] == 0x04, "atqa[1] == 0x04 assert failed\r\n"); + mu_assert( + nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA, + "type == FuriHalNfcTypeA assert failed\r\n"); + nfc_device_free(nfc_validate); +} + +static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { + NfcDevice* nfc_dev = nfc_device_alloc(); + mu_assert(nfc_dev != NULL, "nfc_device_data != NULL assert failed\r\n"); + nfc_dev->format = NfcDeviceSaveFormatMifareClassic; + + // Create a test file + nfc_generate_mf_classic(&nfc_dev->dev_data, uid_len, type); + + // Get the uid from generated MFC + uint8_t uid[7] = {0}; + memcpy(uid, nfc_dev->dev_data.nfc_data.uid, uid_len); + uint8_t sak = nfc_dev->dev_data.nfc_data.sak; + uint8_t atqa[2] = {}; + memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2); + + MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data; + // Check the manufacturer block (should be uid[uid_len] + 0xFF[rest]) + uint8_t manufacturer_block[16] = {0}; + memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16); + mu_assert( + memcmp(manufacturer_block, uid, uid_len) == 0, + "manufacturer_block uid doesn't match the file\r\n"); + for(uint8_t i = uid_len; i < 16; i++) { + mu_assert( + manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n"); + } + + // Reference sector trailers (should be 0xFF[6] + 0xFF + 0x07 + 0x80 + 0x69 + 0xFF[6]) + uint8_t sector_trailer[16] = { + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x07, + 0x80, + 0x69, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF}; + // Reference block data + uint8_t block_data[16] = {}; + memset(block_data, 0xff, sizeof(block_data)); + uint16_t total_blocks = mf_classic_get_total_block_num(type); + for(size_t i = 1; i < total_blocks; i++) { + if(mf_classic_is_sector_trailer(i)) { + mu_assert( + memcmp(mf_data->block[i].value, sector_trailer, 16) == 0, + "Failed sector trailer compare"); + } else { + mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare"); + } + } + // Save the NFC device data to the file + mu_assert( + nfc_device_save(nfc_dev, NFC_TEST_NFC_DEV_PATH), + "nfc_device_save == true assert failed\r\n"); + // Verify that key cache is saved + FuriString* key_cache_name = furi_string_alloc(); + furi_string_set_str(key_cache_name, "/ext/nfc/cache/"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(key_cache_name, "%02X", uid[i]); + } + furi_string_cat_printf(key_cache_name, ".keys"); + mu_assert( + storage_common_stat(nfc_dev->storage, furi_string_get_cstr(key_cache_name), NULL) == + FSE_OK, + "Key cache file save failed"); + nfc_device_free(nfc_dev); + + // Load the NFC device data from the file + NfcDevice* nfc_validate = nfc_device_alloc(); + mu_assert(nfc_validate, "Nfc device alloc assert"); + mu_assert( + nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, false), + "nfc_device_load == true assert failed\r\n"); + + // Check the UID, sak, ATQA and type + mu_assert( + memcmp(nfc_validate->dev_data.nfc_data.uid, uid, uid_len) == 0, + "uid compare assert failed\r\n"); + mu_assert(nfc_validate->dev_data.nfc_data.sak == sak, "sak compare assert failed\r\n"); + mu_assert( + memcmp(nfc_validate->dev_data.nfc_data.atqa, atqa, 2) == 0, + "atqa compare assert failed\r\n"); + mu_assert( + nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA, + "type == FuriHalNfcTypeA assert failed\r\n"); + + // Check the manufacturer block + mu_assert( + memcmp(nfc_validate->dev_data.mf_classic_data.block[0].value, manufacturer_block, 16) == 0, + "manufacturer_block assert failed\r\n"); + // Check other blocks + for(size_t i = 1; i < total_blocks; i++) { + if(mf_classic_is_sector_trailer(i)) { + mu_assert( + memcmp(mf_data->block[i].value, sector_trailer, 16) == 0, + "Failed sector trailer compare"); + } else { + mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare"); + } + } + nfc_device_free(nfc_validate); + + // Check saved key cache + NfcDevice* nfc_keys = nfc_device_alloc(); + mu_assert(nfc_validate, "Nfc device alloc assert"); + nfc_keys->dev_data.nfc_data.uid_len = uid_len; + memcpy(nfc_keys->dev_data.nfc_data.uid, uid, uid_len); + mu_assert(nfc_device_load_key_cache(nfc_keys), "Failed to load key cache"); + uint8_t total_sec = mf_classic_get_total_sectors_num(type); + uint8_t default_key[6] = {}; + memset(default_key, 0xff, 6); + for(size_t i = 0; i < total_sec; i++) { + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(&nfc_keys->dev_data.mf_classic_data, i); + mu_assert(memcmp(sec_tr->key_a, default_key, 6) == 0, "Failed key compare"); + mu_assert(memcmp(sec_tr->key_b, default_key, 6) == 0, "Failed key compare"); + } + + // Delete key cache file + mu_assert( + storage_common_remove(nfc_keys->storage, furi_string_get_cstr(key_cache_name)) == FSE_OK, + "Failed to remove key cache file"); + furi_string_free(key_cache_name); + nfc_device_free(nfc_keys); +} + +MU_TEST(mf_classic_1k_4b_file_test) { + mf_classic_generator_test(4, MfClassicType1k); +} + +MU_TEST(mf_classic_4k_4b_file_test) { + mf_classic_generator_test(4, MfClassicType4k); +} + +MU_TEST(mf_classic_1k_7b_file_test) { + mf_classic_generator_test(7, MfClassicType1k); +} + +MU_TEST(mf_classic_4k_7b_file_test) { + mf_classic_generator_test(7, MfClassicType4k); +} + MU_TEST_SUITE(nfc) { nfc_test_alloc(); + MU_RUN_TEST(nfca_file_test); + MU_RUN_TEST(mf_classic_1k_4b_file_test); + MU_RUN_TEST(mf_classic_4k_4b_file_test); + MU_RUN_TEST(mf_classic_1k_7b_file_test); + MU_RUN_TEST(mf_classic_4k_7b_file_test); MU_RUN_TEST(nfc_digital_signal_test); MU_RUN_TEST(mf_classic_dict_test); + MU_RUN_TEST(mf_classic_dict_load_test); nfc_test_free(); } diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c index 7c1c669ff..115009701 100644 --- a/applications/debug/unit_tests/storage/storage_test.c +++ b/applications/debug/unit_tests/storage/storage_test.c @@ -43,11 +43,8 @@ MU_TEST(storage_file_open_lock) { File* file = storage_file_alloc(storage); // file_locker thread start - FuriThread* locker_thread = furi_thread_alloc(); - furi_thread_set_name(locker_thread, "StorageFileLocker"); - furi_thread_set_stack_size(locker_thread, 2048); - furi_thread_set_context(locker_thread, semaphore); - furi_thread_set_callback(locker_thread, storage_file_locker); + FuriThread* locker_thread = + furi_thread_alloc_ex("StorageFileLocker", 2048, storage_file_locker, semaphore); furi_thread_start(locker_thread); // wait for file lock @@ -133,11 +130,8 @@ MU_TEST(storage_dir_open_lock) { File* file = storage_file_alloc(storage); // file_locker thread start - FuriThread* locker_thread = furi_thread_alloc(); - furi_thread_set_name(locker_thread, "StorageDirLocker"); - furi_thread_set_stack_size(locker_thread, 2048); - furi_thread_set_context(locker_thread, semaphore); - furi_thread_set_callback(locker_thread, storage_dir_locker); + FuriThread* locker_thread = + furi_thread_alloc_ex("StorageDirLocker", 2048, storage_dir_locker, semaphore); furi_thread_start(locker_thread); // wait for dir lock diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 210d3770f..3052448b7 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #define TAG "SubGhz TEST" @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 233 +#define TEST_RANDOM_COUNT_PARSE 244 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -43,6 +43,8 @@ static void subghz_test_init(void) { environment_handler, CAME_ATOMO_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment_handler, NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry( + environment_handler, (void*)&subghz_protocol_registry); receiver_handler = subghz_receiver_alloc_init(environment_handler); subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); @@ -413,11 +415,11 @@ MU_TEST(subghz_decoder_honeywell_wdb_test) { "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); } -MU_TEST(subghz_decoder_magellen_test) { +MU_TEST(subghz_decoder_magellan_test) { mu_assert( subghz_decoder_test( - EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME), - "Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); + EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n"); } MU_TEST(subghz_decoder_intertechno_v3_test) { @@ -435,11 +437,11 @@ MU_TEST(subghz_decoder_clemsa_test) { "Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); } -MU_TEST(subghz_decoder_oregon2_test) { +MU_TEST(subghz_decoder_ansonic_test) { mu_assert( subghz_decoder_test( - EXT_PATH("unit_tests/subghz/oregon2_raw.sub"), SUBGHZ_PROTOCOL_OREGON2_NAME), - "Test decoder " SUBGHZ_PROTOCOL_OREGON2_NAME " error\r\n"); + EXT_PATH("unit_tests/subghz/ansonic_raw.sub"), SUBGHZ_PROTOCOL_ANSONIC_NAME), + "Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); } //test encoders @@ -545,10 +547,10 @@ MU_TEST(subghz_encoder_honeywell_wdb_test) { "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); } -MU_TEST(subghz_encoder_magellen_test) { +MU_TEST(subghz_encoder_magellan_test) { mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")), - "Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n"); + subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")), + "Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n"); } MU_TEST(subghz_encoder_intertechno_v3_test) { @@ -563,6 +565,12 @@ MU_TEST(subghz_encoder_clemsa_test) { "Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); } +MU_TEST(subghz_encoder_ansonic_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/ansonic.sub")), + "Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -600,10 +608,10 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_doitrand_test); MU_RUN_TEST(subghz_decoder_phoenix_v2_test); MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); - MU_RUN_TEST(subghz_decoder_magellen_test); + MU_RUN_TEST(subghz_decoder_magellan_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_clemsa_test); - MU_RUN_TEST(subghz_decoder_oregon2_test); + MU_RUN_TEST(subghz_decoder_ansonic_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -622,9 +630,10 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_doitrand_test); MU_RUN_TEST(subghz_encoder_phoenix_v2_test); MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); - MU_RUN_TEST(subghz_encoder_magellen_test); + MU_RUN_TEST(subghz_encoder_magellan_test); MU_RUN_TEST(subghz_encoder_intertechno_v3_test); MU_RUN_TEST(subghz_encoder_clemsa_test); + MU_RUN_TEST(subghz_encoder_ansonic_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/applications/examples/example_images/ReadMe.md b/applications/examples/example_images/ReadMe.md new file mode 100644 index 000000000..d884a0a97 --- /dev/null +++ b/applications/examples/example_images/ReadMe.md @@ -0,0 +1,24 @@ +# Application icons +To use icons, do the following: +* add a line to the application manifest: `fap_icon_assets="folder"`, where `folder` points to the folder where your icons are located +* add `#include "application_id_icons.h"` to the application code, where `application_id` is the appid from the manifest +* every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension + +## Example +We have an application with the following manifest: +``` +App( + appid="example_images", + ... + fap_icon_assets="images", +) +``` + +So the icons are in the `images` folder and will be available in the generated `example_images_icons.h` file. + +The example code is located in `example_images_main.c` and contains the following line: +``` +#include "example_images_icons.h" +``` + +Image `dolphin_71x25.png` is available as `I_dolphin_71x25`. diff --git a/applications/main/application.fam b/applications/main/application.fam index 94aff522e..ef4933630 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -4,20 +4,25 @@ App( apptype=FlipperAppType.METAPACKAGE, provides=[ "clock_loader", - "gpio", + # "gpio", + "gpio_loader", # "ibutton", "ibutton_loader", - "infrared", + # "infrared", + "infrared_loader", "lfrfid", + # "lfrfid_loader", "nfc", "subghz", - "bad_usb", + #"bad_usb", + "bad_usb_loader", # "u2f", "u2f_loader", "fap_loader", "archive", # "Clock", - "SubGHz_Remote", + #"SubGHz_Remote", + "SubGHz_Remote_loader", # "Spectrum_Analyzer", ], ) diff --git a/applications/main/archive/application.fam b/applications/main/archive/application.fam index f0a980ab0..309cee8d5 100644 --- a/applications/main/archive/application.fam +++ b/applications/main/archive/application.fam @@ -5,7 +5,7 @@ App( entry_point="archive_app", cdefines=["APP_ARCHIVE"], requires=["gui"], - stack_size=4 * 1024, + stack_size=6 * 1024, icon="A_FileManager_14", order=0, ) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 01ba4a994..4155c0afb 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -64,7 +64,13 @@ static void archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path)); } else { with_view_model( - browser->view, ArchiveBrowserViewModel * model, { model->list_loading = false; }, true); + browser->view, + ArchiveBrowserViewModel * model, + { + files_array_sort(model->files); + model->list_loading = false; + }, + true); } } diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 1bf51a2b0..5c06c1bda 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -32,6 +32,11 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder if(is_folder) { file->type = ArchiveFileTypeFolder; } else { + char tmp_extension[MAX_EXT_LEN]; + path_extract_extension(file->path, tmp_extension, MAX_EXT_LEN); + if((strcmp(tmp_extension, ".txt") == 0) || (strcmp(tmp_extension, ".md") == 0)) { + file->is_text_file = true; + } file->type = ArchiveFileTypeUnknown; } } diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index d7b25a64b..db624f5b5 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include "toolbox/path.h" @@ -29,6 +31,7 @@ typedef struct { FuriString* custom_name; bool fav; bool is_app; + bool is_text_file; } ArchiveFile_t; static void ArchiveFile_t_init(ArchiveFile_t* obj) { @@ -38,6 +41,7 @@ static void ArchiveFile_t_init(ArchiveFile_t* obj) { obj->custom_name = furi_string_alloc(); obj->fav = false; obj->is_app = false; + obj->is_text_file = false; } static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { @@ -52,6 +56,7 @@ static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) obj->custom_name = furi_string_alloc_set(src->custom_name); obj->fav = src->fav; obj->is_app = src->is_app; + obj->is_text_file = src->is_text_file; } static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { @@ -66,6 +71,7 @@ static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { furi_string_set(obj->custom_name, src->custom_name); obj->fav = src->fav; obj->is_app = src->is_app; + obj->is_text_file = src->is_text_file; } static void ArchiveFile_t_clear(ArchiveFile_t* obj) { @@ -77,13 +83,26 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) { furi_string_free(obj->custom_name); } -ARRAY_DEF( - files_array, - ArchiveFile_t, - (INIT(API_2(ArchiveFile_t_init)), - SET(API_6(ArchiveFile_t_set)), - INIT_SET(API_6(ArchiveFile_t_init_set)), - CLEAR(API_2(ArchiveFile_t_clear)))) +static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) { + if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) { + return -1; + } + + return furi_string_cmp(a->path, b->path); +} + +#define M_OPL_ArchiveFile_t() \ + (INIT(API_2(ArchiveFile_t_init)), \ + SET(API_6(ArchiveFile_t_set)), \ + INIT_SET(API_6(ArchiveFile_t_init_set)), \ + CLEAR(API_2(ArchiveFile_t_clear)), \ + CMP(API_6(ArchiveFile_t_cmp)), \ + SWAP(M_SWAP_DEFAULT), \ + EQUAL(API_6(M_EQUAL_DEFAULT))) + +ARRAY_DEF(files_array, ArchiveFile_t) + +ALGO_DEF(files_array, ARRAY_OPLIST(files_array, M_OPL_ArchiveFile_t())) void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app); bool archive_get_items(void* context, const char* path); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index dbf221990..ea7548750 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -45,10 +45,55 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec if(param != NULL) { param++; } - status = loader_start(loader, flipper_app_name[selected->type], param); + + if(strcmp(flipper_app_name[selected->type], "U2F") == 0) { + char* tmpType = "/ext/apps/Main/U2F.fapΒ―"; + char* result = + malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1); + + strcpy(result, tmpType); + strcat(result, furi_string_get_cstr(selected->path)); + status = loader_start(loader, "Applications", result); + } else { + status = loader_start(loader, flipper_app_name[selected->type], param); + } } else { - status = loader_start( - loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path)); + if(strcmp(flipper_app_name[selected->type], "iButton") == 0) { + char* tmpType = "/ext/apps/Main/ibutton.fapΒ―"; + char* result = + malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1); + + strcpy(result, tmpType); + strcat(result, furi_string_get_cstr(selected->path)); + status = loader_start(loader, "Applications", result); + } else if(strcmp(flipper_app_name[selected->type], "Bad USB") == 0) { + char* tmpType = "/ext/apps/Main/bad_usb.fapΒ―"; + char* result = + malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1); + + strcpy(result, tmpType); + strcat(result, furi_string_get_cstr(selected->path)); + status = loader_start(loader, "Applications", result); + // } else if(strcmp(flipper_app_name[selected->type], "125 kHz RFID") == 0) { + // char* tmpType = "/ext/apps/Main/lfrfid.fapΒ―"; + // char* result = + // malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1); + + // strcpy(result, tmpType); + // strcat(result, furi_string_get_cstr(selected->path)); + // status = loader_start(loader, "Applications", result); + } else if(strcmp(flipper_app_name[selected->type], "Infrared") == 0) { + char* tmpType = "/ext/apps/Main/infrared.fapΒ―"; + char* result = + malloc(strlen(tmpType) + strlen(furi_string_get_cstr(selected->path)) + 1); + + strcpy(result, tmpType); + strcat(result, furi_string_get_cstr(selected->path)); + status = loader_start(loader, "Applications", result); + } else { + status = loader_start( + loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path)); + } } if(status != LoaderStatusOk) { @@ -150,6 +195,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneInfo); consumed = true; break; + case ArchiveBrowserEventFileMenuShow: + archive_show_file_menu(browser, false); + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_DEFAULT); + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneShow); + consumed = true; + break; case ArchiveBrowserEventFileMenuDelete: if(archive_get_tab(browser) != ArchiveTabFavorites) { scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); diff --git a/applications/main/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h index 6dda7a20d..d16c6f1a6 100644 --- a/applications/main/archive/scenes/archive_scene_config.h +++ b/applications/main/archive/scenes/archive_scene_config.h @@ -2,3 +2,4 @@ ADD_SCENE(archive, browser, Browser) ADD_SCENE(archive, rename, Rename) ADD_SCENE(archive, delete, Delete) ADD_SCENE(archive, info, Info) +ADD_SCENE(archive, show, Show) diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c index 6c7a90cb4..c2f2ddad5 100644 --- a/applications/main/archive/scenes/archive_scene_delete.c +++ b/applications/main/archive/scenes/archive_scene_delete.c @@ -48,11 +48,16 @@ bool archive_scene_delete_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeRight) { + // Show loading popup on delete + view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewStack); + archive_show_loading_popup(app, true); + if(selected->is_app) { archive_app_delete_file(browser, name); } else { archive_delete_file(browser, "%s", name); } + archive_show_loading_popup(app, false); archive_show_file_menu(browser, false); return scene_manager_previous_scene(app->scene_manager); } else if(event.event == GuiButtonTypeLeft) { diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index 5512b0619..346383162 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -64,6 +64,7 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { ArchiveFile_t* file = archive_get_current_file(archive->browser); FuriString* path_dst; + path_dst = furi_string_alloc(); if(file->type == ArchiveFileTypeFolder) { diff --git a/applications/main/archive/scenes/archive_scene_show.c b/applications/main/archive/scenes/archive_scene_show.c new file mode 100644 index 000000000..416c10ded --- /dev/null +++ b/applications/main/archive/scenes/archive_scene_show.c @@ -0,0 +1,147 @@ +#include "../archive_i.h" +#include "../helpers/archive_browser.h" +#include + +#define TAG "Archive" + +#define SHOW_MAX_FILE_SIZE 5000 + +void archive_scene_show_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +static bool text_show_read_lines(File* file, FuriString* str_result) { + //furi_string_reset(str_result); + uint8_t buffer[SHOW_MAX_FILE_SIZE]; + + uint16_t read_count = storage_file_read(file, buffer, SHOW_MAX_FILE_SIZE); + if(storage_file_get_error(file) != FSE_OK) { + return false; + } + + for(uint16_t i = 0; i < read_count; i++) { + furi_string_push_back(str_result, buffer[i]); + } + + return true; +} + +void archive_scene_show_on_enter(void* context) { + furi_assert(context); + ArchiveApp* instance = context; + + FuriString* filename; + filename = furi_string_alloc(); + + FuriString* buffer; + buffer = furi_string_alloc(); + + ArchiveFile_t* current = archive_get_current_file(instance->browser); + Storage* fs_api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(fs_api); + + FileInfo fileinfo; + FS_Error error = storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo); + if(error == FSE_OK) { + if((fileinfo.size < SHOW_MAX_FILE_SIZE) && (fileinfo.size > 2)) { + bool ok = storage_file_open( + file, furi_string_get_cstr(current->path), FSAM_READ, FSOM_OPEN_EXISTING); + if(ok) { + if(!text_show_read_lines(file, buffer)) { + goto text_file_read_err; + } + if(!furi_string_size(buffer)) { + goto text_file_read_err; + } + + storage_file_seek(file, 0, true); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(buffer)); + + } else { + text_file_read_err: + widget_add_text_box_element( + instance->widget, + 0, + 0, + 128, + 64, + AlignLeft, + AlignCenter, + "\e#Error:\nStorage file open error\e#", + false); + } + storage_file_close(file); + } else if(fileinfo.size < 2) { + widget_add_text_box_element( + instance->widget, + 0, + 0, + 128, + 64, + AlignLeft, + AlignCenter, + "\e#Error:\nFile is too small\e#", + false); + } else { + widget_add_text_box_element( + instance->widget, + 0, + 0, + 128, + 64, + AlignLeft, + AlignCenter, + "\e#Error:\nFile is too large to show\e#", + false); + } + } else { + widget_add_text_box_element( + instance->widget, + 0, + 0, + 128, + 64, + AlignLeft, + AlignCenter, + "\e#Error:\nFile system error\e#", + false); + } + path_extract_filename(current->path, filename, false); + + // This one to return and cursor select this file + path_extract_filename_no_ext(furi_string_get_cstr(current->path), filename); + strlcpy(instance->text_store, furi_string_get_cstr(filename), MAX_NAME_LEN); + + furi_string_free(buffer); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + furi_string_free(filename); + + view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget); +} + +bool archive_scene_show_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_next_scene(app->scene_manager, ArchiveAppSceneBrowser); + return true; + } + return false; +} + +void archive_scene_show_on_exit(void* context) { + furi_assert(context); + ArchiveApp* app = (ArchiveApp*)context; + + widget_reset(app->widget); +} diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index cf25e7c89..1dc87319b 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -51,6 +51,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { FuriString* item_run = furi_string_alloc_set("Run In App"); FuriString* item_pin = furi_string_alloc_set("Pin"); FuriString* item_info = furi_string_alloc_set("Info"); + FuriString* item_show = furi_string_alloc_set("Show"); FuriString* item_rename = furi_string_alloc_set("Rename"); FuriString* item_delete = furi_string_alloc_set("Delete"); @@ -79,6 +80,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_info, ArchiveBrowserEventFileMenuInfo); + if(selected->is_text_file) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_show, + ArchiveBrowserEventFileMenuShow); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), item_rename, @@ -100,6 +107,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_pin, ArchiveBrowserEventFileMenuPin); + if(selected->type <= ArchiveFileTypeBadUsb) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_show, + ArchiveBrowserEventFileMenuShow); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), item_rename, @@ -114,6 +127,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_info, ArchiveBrowserEventFileMenuInfo); + if(selected->type <= ArchiveFileTypeBadUsb) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_show, + ArchiveBrowserEventFileMenuShow); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), item_pin, @@ -136,6 +155,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_info, ArchiveBrowserEventFileMenuInfo); + if(selected->type <= ArchiveFileTypeBadUsb) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_show, + ArchiveBrowserEventFileMenuShow); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), item_rename, @@ -149,6 +174,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { furi_string_free(item_run); furi_string_free(item_pin); furi_string_free(item_info); + furi_string_free(item_show); furi_string_free(item_rename); furi_string_free(item_delete); } /*else { @@ -160,9 +186,9 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { canvas_set_color(canvas, ColorWhite); uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu) * line_height); - canvas_draw_box(canvas, 71, 11, 57, calc_height + 4); + canvas_draw_box(canvas, 71, 1, 57, calc_height + 4); canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_frame(canvas, 70, 12, 58, calc_height + 4); + elements_slightly_rounded_frame(canvas, 70, 2, 58, calc_height + 4); /*FURI_LOG_D( TAG, @@ -172,10 +198,10 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { model->menu_idx);*/ for(size_t i = 0; i < size_menu; i++) { ArchiveContextMenuItem_t* current = menu_array_get(model->context_menu, i); - canvas_draw_str(canvas, 82, 21 + i * line_height, furi_string_get_cstr(current->text)); + canvas_draw_str(canvas, 82, 11 + i * line_height, furi_string_get_cstr(current->text)); } - canvas_draw_icon(canvas, 74, 14 + model->menu_idx * line_height, &I_ButtonRight_4x7); + canvas_draw_icon(canvas, 74, 4 + model->menu_idx * line_height, &I_ButtonRight_4x7); } static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) { diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 67b50cb02..7d01e5387 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -40,6 +40,7 @@ typedef enum { ArchiveBrowserEventFileMenuRename, ArchiveBrowserEventFileMenuDelete, ArchiveBrowserEventFileMenuInfo, + ArchiveBrowserEventFileMenuShow, ArchiveBrowserEventFileMenuClose, ArchiveBrowserEventEnterDir, diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_usb/application.fam index 4da34f0de..d068bc8a2 100644 --- a/applications/main/bad_usb/application.fam +++ b/applications/main/bad_usb/application.fam @@ -1,7 +1,7 @@ App( appid="bad_usb", name="Bad USB", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.EXTERNAL, entry_point="bad_usb_app", cdefines=["APP_BAD_USB"], requires=[ @@ -9,6 +9,10 @@ App( "dialogs", ], stack_size=2 * 1024, - icon="A_BadUsb_14", + # icon="A_BadUsb_14", order=70, + fap_category="Main", + fap_icon="badusb_10px.png", + fap_icon_assets="images", + fap_libs=["assets"], ) diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index e950c0198..eda67eae5 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -5,6 +5,7 @@ #include "bad_usb_script.h" #include +#include #include #include #include diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 93b4026f5..e03b0f17d 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -86,7 +86,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}, @@ -243,12 +243,8 @@ static int32_t const char* line_tmp = furi_string_get_cstr(line); bool state = false; - for(uint32_t i = 0; i < line_len; i++) { - if((line_tmp[i] != ' ') && (line_tmp[i] != '\t') && (line_tmp[i] != '\n')) { - line_tmp = &line_tmp[i]; - break; // Skip spaces and tabs - } - if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines + if(line_len == 0) { + return SCRIPT_STATE_NEXT_LINE; // Skip empty lines } FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); @@ -347,10 +343,6 @@ static int32_t furi_hal_hid_kb_release(key); return (0); } - if(error != NULL) { - strncpy(error, "Unknown error", error_len); - } - return SCRIPT_STATE_ERROR; } static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { @@ -463,19 +455,20 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil bad_usb->st.line_cur++; bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1); bad_usb->buf_start = i + 1; + furi_string_trim(bad_usb->line); delay_val = ducky_parse_line( bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error)); - - if(delay_val < 0) { + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val < 0) { bad_usb->st.error_line = bad_usb->st.line_cur; if(delay_val == SCRIPT_STATE_NEXT_LINE) { snprintf( bad_usb->st.error, sizeof(bad_usb->st.error), "Forbidden empty line"); FURI_LOG_E( - WORKER_TAG, "Forbidden empty line at line %lu", bad_usb->st.line_cur); + WORKER_TAG, "Forbidden empty line at line %u", bad_usb->st.line_cur); } else { - FURI_LOG_E( - WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur); + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur); } return SCRIPT_STATE_ERROR; } else { @@ -541,12 +534,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; @@ -573,6 +570,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( @@ -650,7 +672,7 @@ static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { BadUsbScript* bad_usb_script_open(FuriString* file_path) { furi_assert(file_path); - BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); + BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); //-V773 bad_usb->file_path = furi_string_alloc(); furi_string_set(bad_usb->file_path, file_path); bad_usb_script_set_default_keyboard_layout(bad_usb); @@ -658,12 +680,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) { bad_usb->st.state = BadUsbStateInit; bad_usb->st.error[0] = '\0'; - bad_usb->thread = furi_thread_alloc(); - furi_thread_set_name(bad_usb->thread, "BadUsbWorker"); - furi_thread_set_stack_size(bad_usb->thread, 2048); - furi_thread_set_context(bad_usb->thread, bad_usb); - furi_thread_set_callback(bad_usb->thread, bad_usb_worker); - + bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb); furi_thread_start(bad_usb->thread); return bad_usb; } diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h index c8b3a1653..1e4d98fe7 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/badusb_10px.png b/applications/main/bad_usb/badusb_10px.png new file mode 100644 index 000000000..037474aa3 Binary files /dev/null and b/applications/main/bad_usb/badusb_10px.png differ diff --git a/applications/main/bad_usb/images/ActiveConnection_50x64.png b/applications/main/bad_usb/images/ActiveConnection_50x64.png new file mode 100644 index 000000000..1d7686ddd Binary files /dev/null and b/applications/main/bad_usb/images/ActiveConnection_50x64.png differ diff --git a/applications/main/bad_usb/images/Clock_18x18.png b/applications/main/bad_usb/images/Clock_18x18.png new file mode 100644 index 000000000..ab06d008e Binary files /dev/null and b/applications/main/bad_usb/images/Clock_18x18.png differ diff --git a/applications/main/bad_usb/images/Error_18x18.png b/applications/main/bad_usb/images/Error_18x18.png new file mode 100644 index 000000000..16a5a74d9 Binary files /dev/null and b/applications/main/bad_usb/images/Error_18x18.png differ diff --git a/applications/main/bad_usb/images/EviSmile1_18x21.png b/applications/main/bad_usb/images/EviSmile1_18x21.png new file mode 100644 index 000000000..987af3258 Binary files /dev/null and b/applications/main/bad_usb/images/EviSmile1_18x21.png differ diff --git a/applications/main/bad_usb/images/EviSmile2_18x21.png b/applications/main/bad_usb/images/EviSmile2_18x21.png new file mode 100644 index 000000000..7e28c9f01 Binary files /dev/null and b/applications/main/bad_usb/images/EviSmile2_18x21.png differ diff --git a/applications/main/bad_usb/images/EviWaiting1_18x21.png b/applications/main/bad_usb/images/EviWaiting1_18x21.png new file mode 100644 index 000000000..d39d21733 Binary files /dev/null and b/applications/main/bad_usb/images/EviWaiting1_18x21.png differ diff --git a/applications/main/bad_usb/images/EviWaiting2_18x21.png b/applications/main/bad_usb/images/EviWaiting2_18x21.png new file mode 100644 index 000000000..15ca088fd Binary files /dev/null and b/applications/main/bad_usb/images/EviWaiting2_18x21.png differ diff --git a/applications/main/bad_usb/images/Percent_10x14.png b/applications/main/bad_usb/images/Percent_10x14.png new file mode 100644 index 000000000..677911fd4 Binary files /dev/null and b/applications/main/bad_usb/images/Percent_10x14.png differ diff --git a/applications/main/bad_usb/images/SDQuestion_35x43.png b/applications/main/bad_usb/images/SDQuestion_35x43.png new file mode 100644 index 000000000..9b9c9a58e Binary files /dev/null and b/applications/main/bad_usb/images/SDQuestion_35x43.png differ diff --git a/applications/main/bad_usb/images/Smile_18x18.png b/applications/main/bad_usb/images/Smile_18x18.png new file mode 100644 index 000000000..d2aae0dc3 Binary files /dev/null and b/applications/main/bad_usb/images/Smile_18x18.png differ diff --git a/applications/main/bad_usb/images/UsbTree_48x22.png b/applications/main/bad_usb/images/UsbTree_48x22.png new file mode 100644 index 000000000..cc41b5b9a Binary files /dev/null and b/applications/main/bad_usb/images/UsbTree_48x22.png differ diff --git a/applications/main/bad_usb/images/badusb_10px.png b/applications/main/bad_usb/images/badusb_10px.png new file mode 100644 index 000000000..037474aa3 Binary files /dev/null and b/applications/main/bad_usb/images/badusb_10px.png differ diff --git a/applications/main/bad_usb/images/keyboard_10px.png b/applications/main/bad_usb/images/keyboard_10px.png new file mode 100644 index 000000000..74a10e6db Binary files /dev/null and b/applications/main/bad_usb/images/keyboard_10px.png differ diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index a090afc62..d2c30b99e 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -2,6 +2,7 @@ #include "../bad_usb_script.h" #include #include +#include #define MAX_NAME_LEN 64 @@ -44,10 +45,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 24, &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) || @@ -60,6 +64,11 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB"); + } else if(model->state.state == BadUsbStateWillRun) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); } else if(model->state.state == BadUsbStateFileError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); diff --git a/applications/main/bad_usb_loader/application.fam b/applications/main/bad_usb_loader/application.fam new file mode 100644 index 000000000..d3bc81cf0 --- /dev/null +++ b/applications/main/bad_usb_loader/application.fam @@ -0,0 +1,14 @@ +App( + appid="bad_usb_loader", + name="Bad USB", + apptype=FlipperAppType.APP, + entry_point="bad_usb_loader_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=int(2 * 1024), + icon="A_BadUsb_14", + order=70, + link="/ext/apps/Main/bad_usb.fap", +) diff --git a/applications/main/bad_usb_loader/bad_usb_loader_app.c b/applications/main/bad_usb_loader/bad_usb_loader_app.c new file mode 100644 index 000000000..cede52b55 --- /dev/null +++ b/applications/main/bad_usb_loader/bad_usb_loader_app.c @@ -0,0 +1,9 @@ +#include + +#define TAG "bad_usb_loader_app" + +int32_t bad_usb_loader_app(void* p) { + UNUSED(p); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/clock_app/ClockIcon.png b/applications/main/clock/ClockIcon.png similarity index 100% rename from applications/plugins/clock_app/ClockIcon.png rename to applications/main/clock/ClockIcon.png diff --git a/applications/main/clock/application.fam b/applications/main/clock/application.fam new file mode 100644 index 000000000..a5486947a --- /dev/null +++ b/applications/main/clock/application.fam @@ -0,0 +1,14 @@ +App( + appid="Clock", + name="Clock", + apptype=FlipperAppType.EXTERNAL, + entry_point="clock_app", + cdefines=["APP_CLOCK"], + requires=["gui"], + # icon="A_Clock_14", + stack_size=2 * 1024, + order=9, + fap_icon="ClockIcon.png", + fap_category="Main", + fap_icon_assets="icons", +) \ No newline at end of file diff --git a/applications/plugins/clock_app/clock_app.c b/applications/main/clock/clock_app.c similarity index 87% rename from applications/plugins/clock_app/clock_app.c rename to applications/main/clock/clock_app.c index f923f7c96..ee445db71 100644 --- a/applications/plugins/clock_app/clock_app.c +++ b/applications/main/clock/clock_app.c @@ -7,6 +7,7 @@ #include #include #include "applications/settings/desktop_settings/desktop_settings_app.h" +#include "Clock_icons.h" #define TAG "Clock" @@ -200,6 +201,43 @@ const NotificationSequence clock_alert_startStop = { NULL, }; +const NotificationMessage message_red_127 = { + .type = NotificationMessageTypeLedRed, + .data.led.value = 0x7F, +}; + +const NotificationMessage message_green_127 = { + .type = NotificationMessageTypeLedGreen, + .data.led.value = 0x7F, +}; + +const NotificationMessage message_blue_127 = { + .type = NotificationMessageTypeLedBlue, + .data.led.value = 0x7F, +}; + +const NotificationSequence sequence_rainbow = { + &message_red_255, &message_green_0, &message_blue_0, + &message_delay_250, &message_red_255, &message_green_127, + &message_blue_0, &message_delay_250, &message_red_255, + &message_green_255, &message_blue_0, &message_delay_250, + &message_red_127, &message_green_255, &message_blue_0, + &message_delay_250, &message_red_0, &message_green_255, + &message_blue_0, &message_delay_250, &message_red_0, + &message_green_255, &message_blue_127, &message_delay_250, + &message_red_0, &message_green_255, &message_blue_255, + &message_delay_250, &message_red_0, &message_green_127, + &message_blue_255, &message_delay_250, &message_red_0, + &message_green_0, &message_blue_255, &message_delay_250, + &message_red_127, &message_green_0, &message_blue_255, + &message_delay_250, &message_red_255, &message_green_0, + &message_blue_255, &message_delay_250, &message_red_255, + &message_green_0, &message_blue_127, &message_delay_250, + &message_red_127, &message_green_127, &message_blue_127, + &message_delay_250, &message_red_255, &message_green_255, + &message_blue_255, &message_delay_250, NULL, +}; + static void desktop_view_main_dumbmode_changed(DesktopSettings* settings) { settings->is_dumbmode = !settings->is_dumbmode; DESKTOP_SETTINGS_SAVE(settings); @@ -260,8 +298,20 @@ static void clock_render_callback(Canvas* const canvas, void* ctx) { int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) : timer_stopped_seconds; snprintf(strings[2], 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60); + int32_t elapsed_secs_img = (elapsed_secs % 60) % 5; + int32_t elapsed_secs_img2 = (elapsed_secs % 60) % 4; + // int32_t elapsed_secs_img3 = (elapsed_secs % 60) % 4; + static const Icon* const count_anim[5] = { + &I_HappyFlipper_128x64, &I_G0ku, &I_g0ku_1, &I_g0ku_2, &I_g0ku_3}; + static const Icon* const count_anim2[4] = { + &I_EviWaiting1_18x21, &I_EviWaiting2_18x21, &I_EviSmile1_18x21, &I_EviSmile2_18x21}; + static const Icon* const count_anim3[4] = { + &I_frame_01, &I_frame_02, &I_frame_03, &I_frame_02}; + canvas_draw_icon(canvas, -5, 15, count_anim[elapsed_secs_img]); + canvas_draw_icon(canvas, 90, 0, count_anim2[elapsed_secs_img2]); + canvas_draw_icon(canvas, 110, 5, count_anim3[elapsed_secs_img2]); canvas_draw_str_aligned( - canvas, 64, 40, AlignCenter, AlignTop, strings[2]); // DRAW TIMER + canvas, 64, 32, AlignCenter, AlignTop, strings[2]); // DRAW TIMER } canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignCenter, strings[1]); // DRAW TIME canvas_set_font(canvas, FontBatteryPercent); @@ -294,7 +344,9 @@ static void clock_render_callback(Canvas* const canvas, void* ctx) { elements_button_right(canvas, "S:ByMin"); } } - if(state->w_test) canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8); + if(state->w_test && state->desktop_settings->is_dumbmode) { + canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8); + } } static void clock_state_init(ClockState* const state) { @@ -393,6 +445,7 @@ int32_t clock_app(void* p) { plugin_state->codeSequence++; if(plugin_state->codeSequence == 8) { desktop_view_main_dumbmode_changed(plugin_state->desktop_settings); + plugin_state->w_test = true; // OH HEY NOW LETS GAIN EXP & MORE FUN } } else { plugin_state->codeSequence = 0; @@ -472,6 +525,8 @@ int32_t clock_app(void* p) { if(plugin_state->codeSequence < (uint32_t)-1) processing = false; } break; + default: + break; } if(plugin_state->codeSequence == 10) { plugin_state->codeSequence = 0; @@ -481,9 +536,10 @@ int32_t clock_app(void* p) { if(plugin_state->songSelect == 1 || plugin_state->songSelect == 2 || plugin_state->songSelect == 3) { notification_message(notification, &sequence_success); + notification_message(notification, &sequence_rainbow); + notification_message(notification, &sequence_rainbow); } plugin_state->militaryTime = true; // 24 HR TIME FOR THIS - plugin_state->w_test = true; // OH HEY NOW LETS GAIN EXP & MORE FUN DOLPHIN_DEED(getRandomDeed()); } } else if(event.input.type == InputTypeLong) { @@ -525,6 +581,8 @@ int32_t clock_app(void* p) { } if(plugin_state->timerSecs == plugin_state->alert_time + 2) { notification_message(notification, &clock_alert_pr3); + notification_message(notification, &sequence_rainbow); + notification_message(notification, &sequence_rainbow); } } else if(plugin_state->songSelect == 2) { if(plugin_state->timerSecs == plugin_state->alert_time) { @@ -543,6 +601,8 @@ int32_t clock_app(void* p) { } if(plugin_state->timerSecs == plugin_state->alert_time + 2) { notification_message(notification, &clock_alert_mario3); + notification_message(notification, &sequence_rainbow); + notification_message(notification, &sequence_rainbow); } } else { if(plugin_state->timerSecs == plugin_state->alert_time) { diff --git a/applications/main/clock/icons/EviSmile1_18x21.png b/applications/main/clock/icons/EviSmile1_18x21.png new file mode 100644 index 000000000..987af3258 Binary files /dev/null and b/applications/main/clock/icons/EviSmile1_18x21.png differ diff --git a/applications/main/clock/icons/EviSmile2_18x21.png b/applications/main/clock/icons/EviSmile2_18x21.png new file mode 100644 index 000000000..7e28c9f01 Binary files /dev/null and b/applications/main/clock/icons/EviSmile2_18x21.png differ diff --git a/applications/main/clock/icons/EviWaiting1_18x21.png b/applications/main/clock/icons/EviWaiting1_18x21.png new file mode 100644 index 000000000..d39d21733 Binary files /dev/null and b/applications/main/clock/icons/EviWaiting1_18x21.png differ diff --git a/applications/main/clock/icons/EviWaiting2_18x21.png b/applications/main/clock/icons/EviWaiting2_18x21.png new file mode 100644 index 000000000..15ca088fd Binary files /dev/null and b/applications/main/clock/icons/EviWaiting2_18x21.png differ diff --git a/applications/main/clock/icons/G0ku.png b/applications/main/clock/icons/G0ku.png new file mode 100644 index 000000000..84389c4f4 Binary files /dev/null and b/applications/main/clock/icons/G0ku.png differ diff --git a/applications/main/clock/icons/GameMode_11x8.png b/applications/main/clock/icons/GameMode_11x8.png new file mode 100644 index 000000000..49f2e25bf Binary files /dev/null and b/applications/main/clock/icons/GameMode_11x8.png differ diff --git a/applications/main/clock/icons/HappyFlipper_128x64.png b/applications/main/clock/icons/HappyFlipper_128x64.png new file mode 100644 index 000000000..d95412f3f Binary files /dev/null and b/applications/main/clock/icons/HappyFlipper_128x64.png differ diff --git a/applications/main/clock/icons/frame_01.png b/applications/main/clock/icons/frame_01.png new file mode 100644 index 000000000..0cd187053 Binary files /dev/null and b/applications/main/clock/icons/frame_01.png differ diff --git a/applications/main/clock/icons/frame_02.png b/applications/main/clock/icons/frame_02.png new file mode 100644 index 000000000..3c37cdb8b Binary files /dev/null and b/applications/main/clock/icons/frame_02.png differ diff --git a/applications/main/clock/icons/frame_03.png b/applications/main/clock/icons/frame_03.png new file mode 100644 index 000000000..a111ce163 Binary files /dev/null and b/applications/main/clock/icons/frame_03.png differ diff --git a/applications/main/clock/icons/g0ku_1.png b/applications/main/clock/icons/g0ku_1.png new file mode 100644 index 000000000..900d5113f Binary files /dev/null and b/applications/main/clock/icons/g0ku_1.png differ diff --git a/applications/main/clock/icons/g0ku_2.png b/applications/main/clock/icons/g0ku_2.png new file mode 100644 index 000000000..202353bb9 Binary files /dev/null and b/applications/main/clock/icons/g0ku_2.png differ diff --git a/applications/main/clock/icons/g0ku_3.png b/applications/main/clock/icons/g0ku_3.png new file mode 100644 index 000000000..517c1653f Binary files /dev/null and b/applications/main/clock/icons/g0ku_3.png differ diff --git a/applications/main/fap_loader/application.fam b/applications/main/fap_loader/application.fam index bd0403e0e..f83f58e68 100644 --- a/applications/main/fap_loader/application.fam +++ b/applications/main/fap_loader/application.fam @@ -3,11 +3,12 @@ App( name="Applications", apptype=FlipperAppType.APP, entry_point="fap_loader_app", + cdefines=["APP_FAP_LOADER"], requires=[ "gui", "storage", ], stack_size=int(1.5 * 1024), icon="A_Plugins_14", - order=90, + order=9, ) diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 6c909aeeb..71d10341f 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 @@ -16,6 +17,7 @@ struct FapLoader { DialogsApp* dialogs; Gui* gui; FuriString* fap_path; + FuriString* fap_args; ViewDispatcher* view_dispatcher; Loading* loading; }; @@ -101,16 +103,28 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { } FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); - FURI_LOG_I(TAG, "FAP Loader is staring app"); + FURI_LOG_I(TAG, "FAP Loader is starting app"); - FuriThread* thread = flipper_application_spawn(loader->app, NULL); - furi_thread_start(thread); - furi_thread_join(thread); + if(strcmp(furi_string_get_cstr(loader->fap_args), "false") == 0) { + FuriThread* thread = flipper_application_spawn(loader->app, NULL); + furi_thread_start(thread); + furi_thread_join(thread); - show_error = false; - int ret = furi_thread_get_return_code(thread); + show_error = false; + int ret = furi_thread_get_return_code(thread); - FURI_LOG_I(TAG, "FAP app returned: %i", ret); + FURI_LOG_I(TAG, "FAP app returned: %i", ret); + } else { + FuriThread* thread = flipper_application_spawn( + loader->app, (void*)furi_string_get_cstr(loader->fap_args)); + furi_thread_start(thread); + furi_thread_join(thread); + + show_error = false; + int ret = furi_thread_get_return_code(thread); + + FURI_LOG_I(TAG, "FAP app returned: %i", ret); + } } while(0); if(show_error) { @@ -154,8 +168,28 @@ static bool fap_loader_select_app(FapLoader* loader) { } static FapLoader* fap_loader_alloc(const char* path) { - FapLoader* loader = malloc(sizeof(FapLoader)); - loader->fap_path = furi_string_alloc_set(path); + FapLoader* loader = malloc(sizeof(FapLoader)); //-V773 + + char* tmp = malloc(strlen(path) + 1); + strcpy(tmp, path); + char* new_path; + + new_path = strtok(tmp, "Β―"); + + if(new_path) { + loader->fap_path = furi_string_alloc_set(new_path); + } else { + loader->fap_path = furi_string_alloc_set(path); + } + + new_path = strtok(NULL, "Β―"); + + if(new_path) { + loader->fap_args = furi_string_alloc_set(new_path); + } else { + loader->fap_args = furi_string_alloc_set("false"); + } + loader->storage = furi_record_open(RECORD_STORAGE); loader->dialogs = furi_record_open(RECORD_DIALOGS); loader->gui = furi_record_open(RECORD_GUI); @@ -172,6 +206,7 @@ static void fap_loader_free(FapLoader* loader) { loading_free(loader->loading); view_dispatcher_free(loader->view_dispatcher); furi_string_free(loader->fap_path); + furi_string_free(loader->fap_args); furi_record_close(RECORD_GUI); furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_STORAGE); diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index 1d7deb6f1..8c6fa3897 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -1,13 +1,15 @@ App( appid="gpio", name="GPIO", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.EXTERNAL, entry_point="gpio_app", cdefines=["APP_GPIO"], requires=["gui"], stack_size=1 * 1024, - icon="A_GPIO_14", + # icon="A_GPIO_14", order=50, - # fap_icon="gpioIcon.png", - # fap_category="Main", + fap_icon="gpioIcon.png", + fap_category="Main", + fap_icon_assets="images", + fap_libs=["assets"], ) diff --git a/applications/main/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c index b8afdc8ea..19e53f453 100644 --- a/applications/main/gpio/gpio_app.c +++ b/applications/main/gpio/gpio_app.c @@ -51,6 +51,16 @@ GpioApp* gpio_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); + app->gpio_i2c_scanner = gpio_i2c_scanner_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + GpioAppViewI2CScanner, + gpio_i2c_scanner_get_view(app->gpio_i2c_scanner)); + + app->gpio_i2c_sfp = gpio_i2c_sfp_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewI2CSfp, gpio_i2c_sfp_get_view(app->gpio_i2c_sfp)); + app->widget = widget_alloc(); view_dispatcher_add_view( app->view_dispatcher, GpioAppViewUsbUartCloseRpc, widget_get_view(app->widget)); @@ -75,6 +85,8 @@ void gpio_app_free(GpioApp* app) { // Views view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewI2CScanner); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewI2CSfp); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); @@ -82,6 +94,8 @@ void gpio_app_free(GpioApp* app) { widget_free(app->widget); gpio_test_free(app->gpio_test); gpio_usb_uart_free(app->gpio_usb_uart); + gpio_i2c_scanner_free(app->gpio_i2c_scanner); + gpio_i2c_sfp_free(app->gpio_i2c_sfp); // View dispatcher view_dispatcher_free(app->view_dispatcher); diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index fa91e8f79..05a3fcccd 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -15,6 +15,9 @@ #include #include "views/gpio_test.h" #include "views/gpio_usb_uart.h" +#include "views/gpio_i2c_scanner.h" +#include "views/gpio_i2c_sfp.h" +#include struct GpioApp { Gui* gui; @@ -24,9 +27,12 @@ struct GpioApp { Widget* widget; VariableItemList* var_item_list; + VariableItem* var_item_flow; GpioTest* gpio_test; GpioUsbUart* gpio_usb_uart; UsbUartBridge* usb_uart_bridge; + GpioI2CScanner* gpio_i2c_scanner; + GpioI2CSfp* gpio_i2c_sfp; }; typedef enum { @@ -35,4 +41,6 @@ typedef enum { GpioAppViewUsbUart, GpioAppViewUsbUartCfg, GpioAppViewUsbUartCloseRpc, + GpioAppViewI2CScanner, + GpioAppViewI2CSfp } GpioAppView; diff --git a/applications/main/gpio/gpio_custom_event.h b/applications/main/gpio/gpio_custom_event.h index 2bf3e5a8b..02e24f753 100644 --- a/applications/main/gpio/gpio_custom_event.h +++ b/applications/main/gpio/gpio_custom_event.h @@ -5,6 +5,8 @@ typedef enum { GpioStartEventOtgOn, GpioStartEventManualControl, GpioStartEventUsbUart, + GpioStartEventI2CScanner, + GpioStartEventI2CSfp, GpioCustomEventErrorBack, diff --git a/applications/main/gpio/gpio_i2c_scanner_control.c b/applications/main/gpio/gpio_i2c_scanner_control.c new file mode 100644 index 000000000..bf3f1090d --- /dev/null +++ b/applications/main/gpio/gpio_i2c_scanner_control.c @@ -0,0 +1,23 @@ +#include "gpio_i2c_scanner_control.h" +#include + +void gpio_i2c_scanner_run_once(I2CScannerState* i2c_scanner_state) { + //Reset the number of items for rewriting the array + i2c_scanner_state->items = 0; + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + + uint32_t response_timeout_ticks = furi_ms_to_ticks(5.f); + + //Addresses 0 to 7 are reserved and won't be scanned + for(int i = FIRST_NON_RESERVED_I2C_ADDRESS; i <= HIGHEST_I2C_ADDRESS; i++) { + if(furi_hal_i2c_is_device_ready( + &furi_hal_i2c_handle_external, + i << 1, + response_timeout_ticks)) { //Bitshift of 1 bit to convert 7-Bit Address into 8-Bit Address + i2c_scanner_state->responding_address[i2c_scanner_state->items] = i; + i2c_scanner_state->items++; + } + } + + furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} diff --git a/applications/main/gpio/gpio_i2c_scanner_control.h b/applications/main/gpio/gpio_i2c_scanner_control.h new file mode 100644 index 000000000..011f8aa36 --- /dev/null +++ b/applications/main/gpio/gpio_i2c_scanner_control.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include + +#define FIRST_NON_RESERVED_I2C_ADDRESS 8 +#define HIGHEST_I2C_ADDRESS 127 +#define AVAILABLE_NONRESVERED_I2C_ADDRESSES 120 + +typedef struct { + uint8_t items; + uint8_t responding_address[AVAILABLE_NONRESVERED_I2C_ADDRESSES]; +} I2CScannerState; + +/** Scans the I2C-Bus (SDA: Pin 15, SCL: Pin 16) for available 7-Bit slave addresses. Saves the number of detected slaves and their addresses. + * + * @param i2c_scanner_state State including the detected addresses and the number of addresses saved. + */ +void gpio_i2c_scanner_run_once(I2CScannerState* st); diff --git a/applications/main/gpio/gpio_i2c_sfp_control.c b/applications/main/gpio/gpio_i2c_sfp_control.c new file mode 100644 index 000000000..d14ba1b8e --- /dev/null +++ b/applications/main/gpio/gpio_i2c_sfp_control.c @@ -0,0 +1,334 @@ +#include "gpio_i2c_sfp_control.h" +#include +#include + +// This is map mapping the connector type to the appropriate name. (see SFF-8024 Rev. 4.9, Table 4-3) +const char* sfp_connector_map[256] = { + "Unknown or unspecified", + "SC", + "Fibre Channel Style 1", + "Fibre Channel Style 2", + "BNC/TNC", + "Fibre Channel Coax", + "Fiber Jack", + "LC", + "MT-RJ", + "MU", + "SG", + "Optical Pigtail", + "MP0 1x12", + "MP 2x16", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "HSSDC II", + "Copper pigtail", + "RJ45", + "No seperable connector", + "MXC 2x16", + "CS optical connector", + "SN (prev. Mini CS)", + "MPO 2x12", + "MPO 1x16", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved"}; + +void str_part(uint8_t* data, char* buffer, int start, int len) { + int i; + for(i = start; i < start + len; i++) { + buffer[i - start] = data[i]; + } + buffer[i - start] = '\0'; +} + +/* +void print_part(uint8_t *data, char *ptype, char *prefix, int start, int len){ + printf("%s: ", prefix); + for (int i=start; ivendor, 20, 16); + str_part(sfp_data, i2c_sfp_state->rev, 56, 4); + str_part(sfp_data, i2c_sfp_state->pn, 40, 16); + str_part(sfp_data, i2c_sfp_state->sn, 68, 16); + str_part(sfp_data, i2c_sfp_state->dc, 84, 6); + + //Look up connector in table and copy to struct. + strcpy(i2c_sfp_state->connector, sfp_connector_map[sfp_data[2]]); + i2c_sfp_state->bitrate = sfp_data[12] * 100; + i2c_sfp_state->wavelength = sfp_data[60] * 256 + sfp_data[61]; + i2c_sfp_state->sm_reach = sfp_data[14]; + i2c_sfp_state->mm_reach_om3 = sfp_data[19] * 10; + } + furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} diff --git a/applications/main/gpio/gpio_i2c_sfp_control.h b/applications/main/gpio/gpio_i2c_sfp_control.h new file mode 100644 index 000000000..7a9f4bcbe --- /dev/null +++ b/applications/main/gpio/gpio_i2c_sfp_control.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include + +#define FIRST_NON_RESERVED_I2C_ADDRESS 8 +#define HIGHEST_I2C_ADDRESS 127 +#define AVAILABLE_NONRESVERED_I2C_ADDRESSES 120 +#define SFP_I2C_ADDRESS 0x50 + +typedef struct { + char vendor[32]; + char oui[32]; + char rev[32]; + char pn[32]; + char sn[32]; + char dc[32]; + uint8_t type; + char connector[32]; + int wavelength; + int sm_reach; + int mm_reach_om3; + int bitrate; +} I2CSfpState; + +/** Reads data from a connected SFP on I2C-Bus (SDA: Pin 15, SCL: Pin 16). Saves data from SFP. + * + * @param i2c_sfp_state Data collected from SFP. + */ +void gpio_i2c_sfp_run_once(I2CSfpState* st); diff --git a/applications/main/gpio/images/ActiveConnection_50x64.png b/applications/main/gpio/images/ActiveConnection_50x64.png new file mode 100644 index 000000000..1d7686ddd Binary files /dev/null and b/applications/main/gpio/images/ActiveConnection_50x64.png differ diff --git a/applications/main/gpio/images/ArrowDownEmpty_14x15.png b/applications/main/gpio/images/ArrowDownEmpty_14x15.png new file mode 100644 index 000000000..8c6d54f9c Binary files /dev/null and b/applications/main/gpio/images/ArrowDownEmpty_14x15.png differ diff --git a/applications/main/gpio/images/ArrowDownFilled_14x15.png b/applications/main/gpio/images/ArrowDownFilled_14x15.png new file mode 100644 index 000000000..6cef0f4a7 Binary files /dev/null and b/applications/main/gpio/images/ArrowDownFilled_14x15.png differ diff --git a/applications/main/gpio/images/ArrowUpEmpty_14x15.png b/applications/main/gpio/images/ArrowUpEmpty_14x15.png new file mode 100644 index 000000000..261c6d89e Binary files /dev/null and b/applications/main/gpio/images/ArrowUpEmpty_14x15.png differ diff --git a/applications/main/gpio/images/ArrowUpFilled_14x15.png b/applications/main/gpio/images/ArrowUpFilled_14x15.png new file mode 100644 index 000000000..fa35eb2f8 Binary files /dev/null and b/applications/main/gpio/images/ArrowUpFilled_14x15.png differ diff --git a/applications/main/gpio/scenes/gpio_scene_config.h b/applications/main/gpio/scenes/gpio_scene_config.h index 3406e42d3..faca22d8d 100644 --- a/applications/main/gpio/scenes/gpio_scene_config.h +++ b/applications/main/gpio/scenes/gpio_scene_config.h @@ -3,3 +3,5 @@ ADD_SCENE(gpio, test, Test) ADD_SCENE(gpio, usb_uart, UsbUart) ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) +ADD_SCENE(gpio, i2c_scanner, I2CScanner) +ADD_SCENE(gpio, i2c_sfp, I2CSfp) diff --git a/applications/main/gpio/scenes/gpio_scene_i2c_scanner.c b/applications/main/gpio/scenes/gpio_scene_i2c_scanner.c new file mode 100644 index 000000000..7b310debc --- /dev/null +++ b/applications/main/gpio/scenes/gpio_scene_i2c_scanner.c @@ -0,0 +1,36 @@ +#include "../gpio_app_i.h" +#include + +static I2CScannerState* i2c_scanner_state; + +void gpio_scene_i2c_scanner_ok_callback(InputType type, void* context) { + furi_assert(context); + GpioApp* app = context; + + if(type == InputTypeRelease) { + notification_message(app->notifications, &sequence_set_green_255); + gpio_i2c_scanner_run_once(i2c_scanner_state); + notification_message(app->notifications, &sequence_reset_green); + gpio_i2c_scanner_update_state(app->gpio_i2c_scanner, i2c_scanner_state); + } +} + +void gpio_scene_i2c_scanner_on_enter(void* context) { + GpioApp* app = context; + i2c_scanner_state = malloc(sizeof(I2CScannerState)); + + gpio_i2c_scanner_set_ok_callback( + app->gpio_i2c_scanner, gpio_scene_i2c_scanner_ok_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewI2CScanner); +} + +bool gpio_scene_i2c_scanner_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void gpio_scene_i2c_scanner_on_exit(void* context) { + UNUSED(context); + free(i2c_scanner_state); +} diff --git a/applications/main/gpio/scenes/gpio_scene_i2c_sfp.c b/applications/main/gpio/scenes/gpio_scene_i2c_sfp.c new file mode 100644 index 000000000..49d0e8dd2 --- /dev/null +++ b/applications/main/gpio/scenes/gpio_scene_i2c_sfp.c @@ -0,0 +1,35 @@ +#include "../gpio_app_i.h" +#include + +static I2CSfpState* i2c_sfp_state; + +void gpio_scene_i2c_sfp_ok_callback(InputType type, void* context) { + furi_assert(context); + GpioApp* app = context; + + if(type == InputTypeRelease) { + notification_message(app->notifications, &sequence_set_green_255); + gpio_i2c_sfp_run_once(i2c_sfp_state); + notification_message(app->notifications, &sequence_reset_green); + gpio_i2c_sfp_update_state(app->gpio_i2c_sfp, i2c_sfp_state); + } +} + +void gpio_scene_i2c_sfp_on_enter(void* context) { + GpioApp* app = context; + i2c_sfp_state = malloc(sizeof(I2CSfpState)); + + gpio_i2c_sfp_set_ok_callback(app->gpio_i2c_sfp, gpio_scene_i2c_sfp_ok_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewI2CSfp); +} + +bool gpio_scene_i2c_sfp_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void gpio_scene_i2c_sfp_on_exit(void* context) { + UNUSED(context); + free(i2c_sfp_state); +} diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index 729922949..08b77238f 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -1,12 +1,13 @@ #include "../gpio_app_i.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" -#include enum GpioItem { GpioItemUsbUart, GpioItemTest, GpioItemOtg, + GpioItemI2CScanner, + GpioItemI2CSfp, }; enum GpioOtg { @@ -27,6 +28,10 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualControl); } else if(index == GpioItemUsbUart) { view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventUsbUart); + } else if(index == GpioItemI2CScanner) { + view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventI2CScanner); + } else if(index == GpioItemI2CSfp) { + view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventI2CSfp); } } @@ -68,6 +73,9 @@ void gpio_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]); } + variable_item_list_add(var_item_list, "I2C-Scanner", 0, NULL, NULL); + variable_item_list_add(var_item_list, "I2C-SFP", 0, NULL, NULL); + variable_item_list_set_selected_item( var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart)); @@ -86,10 +94,15 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == GpioStartEventManualControl) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest); scene_manager_next_scene(app->scene_manager, GpioSceneTest); + } else if(event.event == GpioStartEventI2CScanner) { + scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemI2CScanner); + scene_manager_next_scene(app->scene_manager, GpioSceneI2CScanner); + } else if(event.event == GpioStartEventI2CSfp) { + scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemI2CSfp); + scene_manager_next_scene(app->scene_manager, GpioSceneI2CSfp); } else if(event.event == GpioStartEventUsbUart) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); if(!furi_hal_usb_is_locked()) { - DOLPHIN_DEED(DolphinDeedGpioUartBridge); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); } else { scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index 653a306aa..c114d79a2 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -13,7 +13,7 @@ static UsbUartConfig* cfg_set; static const char* vcp_ch[] = {"0 (CLI)", "1"}; static const char* uart_ch[] = {"13,14", "15,16"}; -static const char* flow_pins[] = {"None", "2,3", "6,7"}; +static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; static const char* baudrate_mode[] = {"Host"}; static const uint32_t baudrate_list[] = { 2400, @@ -33,6 +33,24 @@ bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { return false; } +void line_ensure_flow_invariant(GpioApp* app) { + // GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is + // selected. This function enforces that invariant by resetting flow_pins + // to None if it is configured to 16,15 when LPUART is selected. + + uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; + VariableItem* item = app->var_item_flow; + variable_item_set_values_count(item, available_flow_pins); + + if(cfg_set->flow_pins >= available_flow_pins) { + cfg_set->flow_pins = 0; + usb_uart_set_config(app->usb_uart_bridge, cfg_set); + + variable_item_set_current_value_index(item, cfg_set->flow_pins); + variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); + } +} + static void line_vcp_cb(VariableItem* item) { GpioApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -54,6 +72,7 @@ static void line_port_cb(VariableItem* item) { else if(index == 1) cfg_set->uart_ch = FuriHalUartIdLPUART1; usb_uart_set_config(app->usb_uart_bridge, cfg_set); + line_ensure_flow_invariant(app); } static void line_flow_cb(VariableItem* item) { @@ -116,9 +135,12 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) { variable_item_set_current_value_index(item, cfg_set->uart_ch); variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]); - item = variable_item_list_add(var_item_list, "RTS/DTR Pins", 3, line_flow_cb, app); + item = variable_item_list_add( + var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app); variable_item_set_current_value_index(item, cfg_set->flow_pins); variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); + app->var_item_flow = item; + line_ensure_flow_invariant(app); variable_item_list_set_selected_item( var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 6e0bce736..927eedb07 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -14,6 +14,7 @@ static const GpioPin* flow_pins[][2] = { {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 {&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7 + {&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15 }; typedef enum { @@ -158,11 +159,8 @@ static int32_t usb_uart_worker(void* context) { usb_uart->tx_sem = furi_semaphore_alloc(1, 1); usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - usb_uart->tx_thread = furi_thread_alloc(); - furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker"); - furi_thread_set_stack_size(usb_uart->tx_thread, 512); - furi_thread_set_context(usb_uart->tx_thread, usb_uart); - furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); + usb_uart->tx_thread = + furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart); usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); @@ -183,7 +181,7 @@ static int32_t usb_uart_worker(void* context) { while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); + furi_check(!(events & FuriFlagError)); if(events & WorkerEvtStop) break; if(events & WorkerEvtRxDone) { size_t len = furi_stream_buffer_receive( @@ -287,7 +285,7 @@ static int32_t usb_uart_tx_thread(void* context) { while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); + furi_check(!(events & FuriFlagError)); if(events & WorkerEvtTxStop) break; if(events & WorkerEvtCdcRx) { furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk); @@ -337,11 +335,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) { memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); - usb_uart->thread = furi_thread_alloc(); - furi_thread_set_name(usb_uart->thread, "UsbUartWorker"); - furi_thread_set_stack_size(usb_uart->thread, 1024); - furi_thread_set_context(usb_uart->thread, usb_uart); - furi_thread_set_callback(usb_uart->thread, usb_uart_worker); + usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart); furi_thread_start(usb_uart->thread); return usb_uart; diff --git a/applications/main/gpio/views/gpio_i2c_scanner.c b/applications/main/gpio/views/gpio_i2c_scanner.c new file mode 100644 index 000000000..0f00639d6 --- /dev/null +++ b/applications/main/gpio/views/gpio_i2c_scanner.c @@ -0,0 +1,139 @@ +#include +#include "gpio_i2c_scanner.h" +#include "../gpio_item.h" + +#include + +struct GpioI2CScanner { + View* view; + GpioI2CScannerOkCallback callback; + void* context; +}; + +typedef struct { + uint8_t items; + uint8_t responding_address[AVAILABLE_NONRESVERED_I2C_ADDRESSES]; +} GpioI2CScannerModel; + +static bool gpio_i2c_scanner_process_ok(GpioI2CScanner* gpio_i2c_scanner, InputEvent* event); + +static void gpio_i2c_scanner_draw_callback(Canvas* canvas, void* _model) { + GpioI2CScannerModel* model = _model; + + char temp_str[25]; + elements_button_center(canvas, "Start scan"); + canvas_draw_line(canvas, 2, 10, 125, 10); + canvas_draw_line(canvas, 2, 52, 125, 52); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 9, "I2C-Scanner"); + canvas_draw_str(canvas, 3, 25, "SDA:"); + canvas_draw_str(canvas, 3, 42, "SCL:"); + + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, 25, "Slaves: %u", model->items); + canvas_draw_str_aligned(canvas, 126, 8, AlignRight, AlignBottom, temp_str); + + canvas_draw_str(canvas, 29, 25, "Pin 15"); + canvas_draw_str(canvas, 29, 42, "Pin 16"); + + canvas_set_font(canvas, FontSecondary); + + char temp_str2[6]; + if(model->items > 0) { + snprintf(temp_str, 25, "Addr: "); + for(int i = 0; i < model->items; i++) { + snprintf(temp_str2, 6, "0x%x ", model->responding_address[i]); + strcat(temp_str, temp_str2); + + if(i == 1 || model->items == 1) { //Draw a maximum of two addresses in the first line + canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, temp_str); + temp_str[0] = '\0'; + } else if( + i == 4 || (model->items - 1 == i && + i < 6)) { //Draw a maximum of three addresses in the second line + canvas_draw_str_aligned(canvas, 127, 36, AlignRight, AlignBottom, temp_str); + temp_str[0] = '\0'; + } else if(i == 7 || model->items - 1 == i) { //Draw a maximum of three addresses in the third line + canvas_draw_str_aligned(canvas, 127, 48, AlignRight, AlignBottom, temp_str); + break; + } + } + } +} + +static bool gpio_i2c_scanner_input_callback(InputEvent* event, void* context) { + furi_assert(context); + GpioI2CScanner* gpio_i2c_scanner = context; + bool consumed = false; + + if(event->key == InputKeyOk) { + consumed = gpio_i2c_scanner_process_ok(gpio_i2c_scanner, event); + } + + return consumed; +} + +static bool gpio_i2c_scanner_process_ok(GpioI2CScanner* gpio_i2c_scanner, InputEvent* event) { + bool consumed = false; + gpio_i2c_scanner->callback(event->type, gpio_i2c_scanner->context); + + return consumed; +} + +GpioI2CScanner* gpio_i2c_scanner_alloc() { + GpioI2CScanner* gpio_i2c_scanner = malloc(sizeof(GpioI2CScanner)); + + gpio_i2c_scanner->view = view_alloc(); + view_allocate_model(gpio_i2c_scanner->view, ViewModelTypeLocking, sizeof(GpioI2CScannerModel)); + view_set_context(gpio_i2c_scanner->view, gpio_i2c_scanner); + view_set_draw_callback(gpio_i2c_scanner->view, gpio_i2c_scanner_draw_callback); + view_set_input_callback(gpio_i2c_scanner->view, gpio_i2c_scanner_input_callback); + + return gpio_i2c_scanner; +} + +void gpio_i2c_scanner_free(GpioI2CScanner* gpio_i2c_scanner) { + furi_assert(gpio_i2c_scanner); + view_free(gpio_i2c_scanner->view); + free(gpio_i2c_scanner); +} + +View* gpio_i2c_scanner_get_view(GpioI2CScanner* gpio_i2c_scanner) { + furi_assert(gpio_i2c_scanner); + return gpio_i2c_scanner->view; +} + +void gpio_i2c_scanner_set_ok_callback( + GpioI2CScanner* gpio_i2c_scanner, + GpioI2CScannerOkCallback callback, + void* context) { + furi_assert(gpio_i2c_scanner); + furi_assert(callback); + with_view_model( + gpio_i2c_scanner->view, + GpioI2CScannerModel * model, + { + UNUSED(model); + gpio_i2c_scanner->callback = callback; + gpio_i2c_scanner->context = context; + }, + false); +} + +void gpio_i2c_scanner_update_state(GpioI2CScanner* instance, I2CScannerState* st) { + furi_assert(instance); + furi_assert(st); + + with_view_model( + instance->view, + GpioI2CScannerModel * model, + { + model->items = st->items; + + for(int i = 0; i < model->items; i++) { + model->responding_address[i] = st->responding_address[i]; + } + }, + true); +} diff --git a/applications/main/gpio/views/gpio_i2c_scanner.h b/applications/main/gpio/views/gpio_i2c_scanner.h new file mode 100644 index 000000000..52ebf3de3 --- /dev/null +++ b/applications/main/gpio/views/gpio_i2c_scanner.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "../gpio_i2c_scanner_control.h" + +typedef struct GpioI2CScanner GpioI2CScanner; +typedef void (*GpioI2CScannerOkCallback)(InputType type, void* context); + +GpioI2CScanner* gpio_i2c_scanner_alloc(); + +void gpio_i2c_scanner_free(GpioI2CScanner* gpio_i2c_scanner); + +View* gpio_i2c_scanner_get_view(GpioI2CScanner* gpio_i2c_scanner); + +void gpio_i2c_scanner_set_ok_callback( + GpioI2CScanner* gpio_i2c_scanner, + GpioI2CScannerOkCallback callback, + void* context); + +void gpio_i2c_scanner_update_state(GpioI2CScanner* instance, I2CScannerState* st); diff --git a/applications/main/gpio/views/gpio_i2c_sfp.c b/applications/main/gpio/views/gpio_i2c_sfp.c new file mode 100644 index 000000000..85492e783 --- /dev/null +++ b/applications/main/gpio/views/gpio_i2c_sfp.c @@ -0,0 +1,149 @@ +#include +#include "gpio_i2c_sfp.h" +#include "../gpio_item.h" + +#include + +struct GpioI2CSfp { + View* view; + GpioI2CSfpOkCallback callback; + void* context; +}; + +typedef struct { + char vendor[32]; + char oui[32]; + char rev[32]; + char pn[32]; + char sn[32]; + char dc[32]; + uint8_t type; + char connector[32]; + int wavelength; + int sm_reach; + int mm_reach_om3; + int bitrate; +} GpioI2CSfpModel; + +static bool gpio_i2c_sfp_process_ok(GpioI2CSfp* gpio_i2c_sfp, InputEvent* event); + +static void gpio_i2c_sfp_draw_callback(Canvas* canvas, void* _model) { + GpioI2CSfpModel* model = _model; + + // Temp String for formatting output + char temp_str[280]; + + canvas_set_font(canvas, FontSecondary); + elements_button_center(canvas, "Read"); + canvas_draw_str(canvas, 2, 63, "P15 SCL"); + canvas_draw_str(canvas, 92, 63, "P16 SDA"); + + snprintf(temp_str, 280, "Vendor: %s", model->vendor); + canvas_draw_str(canvas, 2, 9, temp_str); + + snprintf(temp_str, 280, "PN: %s", model->pn); + canvas_draw_str(canvas, 2, 19, temp_str); + + snprintf(temp_str, 280, "SN: %s", model->sn); + canvas_draw_str(canvas, 2, 29, temp_str); + + snprintf(temp_str, 280, "REV: %s", model->rev); + canvas_draw_str(canvas, 2, 39, temp_str); + + snprintf(temp_str, 280, "CON: %s", model->connector); + canvas_draw_str(canvas, 50, 39, temp_str); + + //Print Wavelength of Module + snprintf(temp_str, 280, "%u nm", model->wavelength); + canvas_draw_str(canvas, 2, 49, temp_str); + + // These values will be zero if not applicable.. + if(model->sm_reach != 0) { + snprintf(temp_str, 280, "%u km (SM)", model->sm_reach); + canvas_draw_str(canvas, 50, 49, temp_str); + } + if(model->mm_reach_om3 != 0) { + snprintf(temp_str, 280, "%u m (MM OM3)", model->mm_reach_om3); + canvas_draw_str(canvas, 50, 49, temp_str); + } +} + +static bool gpio_i2c_sfp_input_callback(InputEvent* event, void* context) { + furi_assert(context); + GpioI2CSfp* gpio_i2c_sfp = context; + bool consumed = false; + + if(event->key == InputKeyOk) { + consumed = gpio_i2c_sfp_process_ok(gpio_i2c_sfp, event); + } + + return consumed; +} + +static bool gpio_i2c_sfp_process_ok(GpioI2CSfp* gpio_i2c_sfp, InputEvent* event) { + bool consumed = false; + gpio_i2c_sfp->callback(event->type, gpio_i2c_sfp->context); + + return consumed; +} + +GpioI2CSfp* gpio_i2c_sfp_alloc() { + GpioI2CSfp* gpio_i2c_sfp = malloc(sizeof(GpioI2CSfp)); + + gpio_i2c_sfp->view = view_alloc(); + view_allocate_model(gpio_i2c_sfp->view, ViewModelTypeLocking, sizeof(GpioI2CSfpModel)); + view_set_context(gpio_i2c_sfp->view, gpio_i2c_sfp); + view_set_draw_callback(gpio_i2c_sfp->view, gpio_i2c_sfp_draw_callback); + view_set_input_callback(gpio_i2c_sfp->view, gpio_i2c_sfp_input_callback); + + return gpio_i2c_sfp; +} + +void gpio_i2c_sfp_free(GpioI2CSfp* gpio_i2c_sfp) { + furi_assert(gpio_i2c_sfp); + view_free(gpio_i2c_sfp->view); + free(gpio_i2c_sfp); +} + +View* gpio_i2c_sfp_get_view(GpioI2CSfp* gpio_i2c_sfp) { + furi_assert(gpio_i2c_sfp); + return gpio_i2c_sfp->view; +} + +void gpio_i2c_sfp_set_ok_callback( + GpioI2CSfp* gpio_i2c_sfp, + GpioI2CSfpOkCallback callback, + void* context) { + furi_assert(gpio_i2c_sfp); + furi_assert(callback); + with_view_model( + gpio_i2c_sfp->view, + GpioI2CSfpModel * model, + { + UNUSED(model); + gpio_i2c_sfp->callback = callback; + gpio_i2c_sfp->context = context; + }, + false); +} + +void gpio_i2c_sfp_update_state(GpioI2CSfp* instance, I2CSfpState* st) { + furi_assert(instance); + furi_assert(st); + + with_view_model( + instance->view, + GpioI2CSfpModel * model, + { + // Insert values into model... + strcpy(model->vendor, st->vendor); + strcpy(model->pn, st->pn); + strcpy(model->sn, st->sn); + strcpy(model->rev, st->rev); + strcpy(model->connector, st->connector); + model->wavelength = st->wavelength; + model->sm_reach = st->sm_reach; + model->mm_reach_om3 = st->mm_reach_om3; + }, + true); +} diff --git a/applications/main/gpio/views/gpio_i2c_sfp.h b/applications/main/gpio/views/gpio_i2c_sfp.h new file mode 100644 index 000000000..c57c143a4 --- /dev/null +++ b/applications/main/gpio/views/gpio_i2c_sfp.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "../gpio_i2c_sfp_control.h" + +typedef struct GpioI2CSfp GpioI2CSfp; +typedef void (*GpioI2CSfpOkCallback)(InputType type, void* context); + +GpioI2CSfp* gpio_i2c_sfp_alloc(); + +void gpio_i2c_sfp_free(GpioI2CSfp* gpio_i2c_sfp); + +View* gpio_i2c_sfp_get_view(GpioI2CSfp* gpio_i2c_sfp); + +void gpio_i2c_sfp_set_ok_callback( + GpioI2CSfp* gpio_i2c_sfp, + GpioI2CSfpOkCallback callback, + void* context); + +void gpio_i2c_sfp_update_state(GpioI2CSfp* instance, I2CSfpState* st); diff --git a/applications/main/gpio_loader/application.fam b/applications/main/gpio_loader/application.fam new file mode 100644 index 000000000..08a5b7967 --- /dev/null +++ b/applications/main/gpio_loader/application.fam @@ -0,0 +1,14 @@ +App( + appid="gpio_loader", + name="GPIO", + apptype=FlipperAppType.APP, + entry_point="gpio_loader_app", + requires=[ + "gui", + "storage", + ], + stack_size=int(1.5 * 1024), + icon="A_GPIO_14", + order=50, + link="/ext/apps/Main/gpio.fap", +) diff --git a/applications/main/gpio_loader/gpio_loader_app.c b/applications/main/gpio_loader/gpio_loader_app.c new file mode 100644 index 000000000..ba7fb4e31 --- /dev/null +++ b/applications/main/gpio_loader/gpio_loader_app.c @@ -0,0 +1,9 @@ +#include + +#define TAG "gpio_loader_app" + +int32_t gpio_loader_app(void* p) { + UNUSED(p); + + return 0; +} \ No newline at end of file diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 1265fb788..fc3333ae7 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -13,4 +13,6 @@ App( order=60, fap_icon="iBIcon.png", fap_category="Main", + fap_icon_assets="images", + fap_libs=["assets"], ) diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 887fb3e75..2762d68e1 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -1,10 +1,10 @@ #include "ibutton.h" -#include "assets_icons.h" #include "ibutton_i.h" #include "ibutton/scenes/ibutton_scene.h" #include #include #include +#include #define TAG "iButtonApp" @@ -337,11 +337,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/ibutton_i.h b/applications/main/ibutton/ibutton_i.h index a9515195e..f79ae0d81 100644 --- a/applications/main/ibutton/ibutton_i.h +++ b/applications/main/ibutton/ibutton_i.h @@ -4,6 +4,7 @@ #include #include +#include "ibutton_icons.h" #include #include #include diff --git a/applications/main/ibutton/images/DolphinMafia_115x62.png b/applications/main/ibutton/images/DolphinMafia_115x62.png new file mode 100644 index 000000000..53dffb4fa Binary files /dev/null and b/applications/main/ibutton/images/DolphinMafia_115x62.png differ diff --git a/applications/main/ibutton/images/DolphinNice_96x59.png b/applications/main/ibutton/images/DolphinNice_96x59.png new file mode 100644 index 000000000..b111196c7 Binary files /dev/null and b/applications/main/ibutton/images/DolphinNice_96x59.png differ diff --git a/applications/main/ibutton/images/DolphinReadingSuccess_59x63.png b/applications/main/ibutton/images/DolphinReadingSuccess_59x63.png new file mode 100644 index 000000000..46f559f65 Binary files /dev/null and b/applications/main/ibutton/images/DolphinReadingSuccess_59x63.png differ diff --git a/applications/main/ibutton/images/DolphinWait_61x59.png b/applications/main/ibutton/images/DolphinWait_61x59.png new file mode 100644 index 000000000..bb0c38400 Binary files /dev/null and b/applications/main/ibutton/images/DolphinWait_61x59.png differ diff --git a/applications/main/ibutton/images/iButtonDolphinVerySuccess_108x52.png b/applications/main/ibutton/images/iButtonDolphinVerySuccess_108x52.png new file mode 100644 index 000000000..2b4bec7c6 Binary files /dev/null and b/applications/main/ibutton/images/iButtonDolphinVerySuccess_108x52.png differ diff --git a/applications/main/ibutton/images/iButtonKey_49x44.png b/applications/main/ibutton/images/iButtonKey_49x44.png new file mode 100644 index 000000000..db895ec52 Binary files /dev/null and b/applications/main/ibutton/images/iButtonKey_49x44.png differ diff --git a/applications/main/ibutton/images/ibutt_10px.png b/applications/main/ibutton/images/ibutt_10px.png new file mode 100644 index 000000000..2fdaf123a Binary files /dev/null and b/applications/main/ibutton/images/ibutt_10px.png differ 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_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c index 8b7d9dfc0..9ff165e4a 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -39,10 +39,5 @@ void ibutton_scene_delete_success_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - - popup_disable_timeout(popup); - popup_set_context(popup, NULL); - popup_set_callback(popup, NULL); + popup_reset(popup); } 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..8b16d2929 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); @@ -41,10 +39,5 @@ void ibutton_scene_save_success_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - - popup_disable_timeout(popup); - popup_set_context(popup, NULL); - popup_set_callback(popup, NULL); + popup_reset(popup); } 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/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c index 3acb1dea0..17cd53d08 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c @@ -43,10 +43,5 @@ void ibutton_scene_write_success_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->popup; - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - - popup_disable_timeout(popup); - popup_set_context(popup, NULL); - popup_set_callback(popup, NULL); + popup_reset(popup); } diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 897afdeb8..405224d80 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -1,25 +1,19 @@ App( appid="infrared", name="Infrared", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.EXTERNAL, entry_point="infrared_app", cdefines=["APP_INFRARED"], requires=[ "gui", "dialogs", ], - provides=["infrared_start"], - icon="A_Infrared_14", + # provides=["infrared_start"], + # icon="A_Infrared_14", stack_size=3 * 1024, order=40, - # fap_icon="irIcon.png", - # fap_category="Main", -) - -App( - appid="infrared_start", - apptype=FlipperAppType.STARTUP, - entry_point="infrared_on_system_start", - requires=["infrared"], - order=20, + fap_category="Main", + fap_icon="ir_10px.png", + fap_icon_assets="images", + fap_libs=["assets"], ) diff --git a/applications/main/infrared/images/DolphinMafia_115x62.png b/applications/main/infrared/images/DolphinMafia_115x62.png new file mode 100644 index 000000000..53dffb4fa Binary files /dev/null and b/applications/main/infrared/images/DolphinMafia_115x62.png differ diff --git a/applications/main/infrared/images/DolphinNice_96x59.png b/applications/main/infrared/images/DolphinNice_96x59.png new file mode 100644 index 000000000..b111196c7 Binary files /dev/null and b/applications/main/infrared/images/DolphinNice_96x59.png differ diff --git a/applications/main/infrared/images/DolphinReadingSuccess_59x63.png b/applications/main/infrared/images/DolphinReadingSuccess_59x63.png new file mode 100644 index 000000000..46f559f65 Binary files /dev/null and b/applications/main/infrared/images/DolphinReadingSuccess_59x63.png differ diff --git a/applications/main/infrared/images/Down_25x27.png b/applications/main/infrared/images/Down_25x27.png new file mode 100644 index 000000000..c13097778 Binary files /dev/null and b/applications/main/infrared/images/Down_25x27.png differ diff --git a/applications/main/infrared/images/Down_hvr_25x27.png b/applications/main/infrared/images/Down_hvr_25x27.png new file mode 100644 index 000000000..76d181924 Binary files /dev/null and b/applications/main/infrared/images/Down_hvr_25x27.png differ diff --git a/applications/main/infrared/images/InfraredArrowDown_4x8.png b/applications/main/infrared/images/InfraredArrowDown_4x8.png new file mode 100644 index 000000000..2ac7bcdbe Binary files /dev/null and b/applications/main/infrared/images/InfraredArrowDown_4x8.png differ diff --git a/applications/main/infrared/images/InfraredArrowUp_4x8.png b/applications/main/infrared/images/InfraredArrowUp_4x8.png new file mode 100644 index 000000000..4c9a16b3f Binary files /dev/null and b/applications/main/infrared/images/InfraredArrowUp_4x8.png differ diff --git a/applications/main/infrared/images/InfraredLearnShort_128x31.png b/applications/main/infrared/images/InfraredLearnShort_128x31.png new file mode 100644 index 000000000..783ad0877 Binary files /dev/null and b/applications/main/infrared/images/InfraredLearnShort_128x31.png differ diff --git a/applications/main/infrared/images/Mode_25x27.png b/applications/main/infrared/images/Mode_25x27.png new file mode 100644 index 000000000..381ba8296 Binary files /dev/null and b/applications/main/infrared/images/Mode_25x27.png differ diff --git a/applications/main/infrared/images/Mode_hvr_25x27.png b/applications/main/infrared/images/Mode_hvr_25x27.png new file mode 100644 index 000000000..64f459f55 Binary files /dev/null and b/applications/main/infrared/images/Mode_hvr_25x27.png differ diff --git a/applications/main/infrared/images/Mute_25x27.png b/applications/main/infrared/images/Mute_25x27.png new file mode 100644 index 000000000..d8812dd4f Binary files /dev/null and b/applications/main/infrared/images/Mute_25x27.png differ diff --git a/applications/main/infrared/images/Mute_hvr_25x27.png b/applications/main/infrared/images/Mute_hvr_25x27.png new file mode 100644 index 000000000..155bd9004 Binary files /dev/null and b/applications/main/infrared/images/Mute_hvr_25x27.png differ diff --git a/applications/main/infrared/images/Pin_back_arrow_10x8.png b/applications/main/infrared/images/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/applications/main/infrared/images/Pin_back_arrow_10x8.png differ diff --git a/applications/main/infrared/images/Power_25x27.png b/applications/main/infrared/images/Power_25x27.png new file mode 100644 index 000000000..5ae493fbe Binary files /dev/null and b/applications/main/infrared/images/Power_25x27.png differ diff --git a/applications/main/infrared/images/Power_hvr_25x27.png b/applications/main/infrared/images/Power_hvr_25x27.png new file mode 100644 index 000000000..9425072c0 Binary files /dev/null and b/applications/main/infrared/images/Power_hvr_25x27.png differ diff --git a/applications/main/infrared/images/RFIDDolphinSend_97x61.png b/applications/main/infrared/images/RFIDDolphinSend_97x61.png new file mode 100644 index 000000000..343b9f734 Binary files /dev/null and b/applications/main/infrared/images/RFIDDolphinSend_97x61.png differ diff --git a/applications/main/infrared/images/Rotate_25x27.png b/applications/main/infrared/images/Rotate_25x27.png new file mode 100644 index 000000000..648634a09 Binary files /dev/null and b/applications/main/infrared/images/Rotate_25x27.png differ diff --git a/applications/main/infrared/images/Rotate_hvr_25x27.png b/applications/main/infrared/images/Rotate_hvr_25x27.png new file mode 100644 index 000000000..a2b5cf93d Binary files /dev/null and b/applications/main/infrared/images/Rotate_hvr_25x27.png differ diff --git a/applications/main/infrared/images/SDQuestion_35x43.png b/applications/main/infrared/images/SDQuestion_35x43.png new file mode 100644 index 000000000..9b9c9a58e Binary files /dev/null and b/applications/main/infrared/images/SDQuestion_35x43.png differ diff --git a/applications/main/infrared/images/Swing_25x27.png b/applications/main/infrared/images/Swing_25x27.png new file mode 100644 index 000000000..38a6c9040 Binary files /dev/null and b/applications/main/infrared/images/Swing_25x27.png differ diff --git a/applications/main/infrared/images/Swing_hvr_25x27.png b/applications/main/infrared/images/Swing_hvr_25x27.png new file mode 100644 index 000000000..6e65b4e2e Binary files /dev/null and b/applications/main/infrared/images/Swing_hvr_25x27.png differ diff --git a/applications/main/infrared/images/Timer_25x27.png b/applications/main/infrared/images/Timer_25x27.png new file mode 100644 index 000000000..2f1853a34 Binary files /dev/null and b/applications/main/infrared/images/Timer_25x27.png differ diff --git a/applications/main/infrared/images/Timer_hvr_25x27.png b/applications/main/infrared/images/Timer_hvr_25x27.png new file mode 100644 index 000000000..d4dffa544 Binary files /dev/null and b/applications/main/infrared/images/Timer_hvr_25x27.png differ diff --git a/applications/main/infrared/images/Up_25x27.png b/applications/main/infrared/images/Up_25x27.png new file mode 100644 index 000000000..b81a02e8a Binary files /dev/null and b/applications/main/infrared/images/Up_25x27.png differ diff --git a/applications/main/infrared/images/Up_hvr_25x27.png b/applications/main/infrared/images/Up_hvr_25x27.png new file mode 100644 index 000000000..cf71e5965 Binary files /dev/null and b/applications/main/infrared/images/Up_hvr_25x27.png differ diff --git a/applications/main/infrared/images/Vol_down_25x27.png b/applications/main/infrared/images/Vol_down_25x27.png new file mode 100644 index 000000000..d7ae44558 Binary files /dev/null and b/applications/main/infrared/images/Vol_down_25x27.png differ diff --git a/applications/main/infrared/images/Vol_down_hvr_25x27.png b/applications/main/infrared/images/Vol_down_hvr_25x27.png new file mode 100644 index 000000000..c556a037a Binary files /dev/null and b/applications/main/infrared/images/Vol_down_hvr_25x27.png differ diff --git a/applications/main/infrared/images/Vol_up_25x27.png b/applications/main/infrared/images/Vol_up_25x27.png new file mode 100644 index 000000000..c4d9e87a0 Binary files /dev/null and b/applications/main/infrared/images/Vol_up_25x27.png differ diff --git a/applications/main/infrared/images/Vol_up_hvr_25x27.png b/applications/main/infrared/images/Vol_up_hvr_25x27.png new file mode 100644 index 000000000..90c2df47d Binary files /dev/null and b/applications/main/infrared/images/Vol_up_hvr_25x27.png differ diff --git a/applications/main/infrared/images/ir_10px.png b/applications/main/infrared/images/ir_10px.png new file mode 100644 index 000000000..22c986180 Binary files /dev/null and b/applications/main/infrared/images/ir_10px.png differ diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c deleted file mode 100644 index 5ec57c757..000000000 --- a/applications/main/infrared/infrared_cli.c +++ /dev/null @@ -1,366 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "infrared_signal.h" - -#define INFRARED_CLI_BUF_SIZE 10 - -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 const struct { - const char* cmd; - void (*process_function)(Cli* cli, FuriString* args); -} infrared_cli_commands[] = { - {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, - {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, - {.cmd = "decode", .process_function = infrared_cli_process_decode}, -}; - -static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { - furi_assert(received_signal); - char buf[100]; - size_t buf_cnt; - Cli* cli = (Cli*)context; - - if(infrared_worker_signal_is_decoded(received_signal)) { - const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); - buf_cnt = snprintf( - buf, - sizeof(buf), - "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), - message->address, - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command, - message->repeat ? " R" : ""); - cli_write(cli, (uint8_t*)buf, buf_cnt); - } else { - const uint32_t* timings; - size_t timings_cnt; - infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); - - buf_cnt = snprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); - cli_write(cli, (uint8_t*)buf, buf_cnt); - for(size_t i = 0; i < timings_cnt; ++i) { - buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]); - cli_write(cli, (uint8_t*)buf, buf_cnt); - } - buf_cnt = snprintf(buf, sizeof(buf), "\r\n"); - cli_write(cli, (uint8_t*)buf, buf_cnt); - } -} - -static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { - UNUSED(cli); - UNUSED(args); - InfraredWorker* worker = infrared_worker_alloc(); - infrared_worker_rx_start(worker); - infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); - - printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - - infrared_worker_rx_stop(worker); - infrared_worker_free(worker); -} - -static void infrared_cli_print_usage(void) { - printf("Usage:\r\n"); - printf("\tir rx\r\n"); - printf("\tir tx
\r\n"); - printf("\t and
are hex-formatted\r\n"); - printf("\tAvailable protocols:"); - for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) { - printf(" %s", infrared_get_protocol_name((InfraredProtocol)i)); - } - printf("\r\n"); - printf("\tRaw format:\r\n"); - printf("\tir tx RAW F: DC: ...\r\n"); - printf( - "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", - INFRARED_MIN_FREQUENCY, - INFRARED_MAX_FREQUENCY); - printf("\tir decode []\r\n"); -} - -static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { - char protocol_name[32]; - InfraredMessage message; - int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message.address, &message.command); - - if(parsed != 3) { - return false; - } - - message.protocol = infrared_get_protocol_by_name(protocol_name); - message.repeat = false; - infrared_signal_set_message(signal, &message); - return infrared_signal_is_valid(signal); -} - -static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { - char frequency_str[INFRARED_CLI_BUF_SIZE]; - char duty_cycle_str[INFRARED_CLI_BUF_SIZE]; - int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); - - if(parsed != 2) { - return false; - } - - uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); - uint32_t frequency = atoi(frequency_str); - float duty_cycle = (float)atoi(duty_cycle_str) / 100; - - str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE; - - size_t timings_size = 0; - while(1) { - while(*str == ' ') { - ++str; - } - - char timing_str[INFRARED_CLI_BUF_SIZE]; - if(sscanf(str, "%9s", timing_str) != 1) { - break; - } - - str += strlen(timing_str); - uint32_t timing = atoi(timing_str); - - if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) { - break; - } - - timings[timings_size] = timing; - ++timings_size; - } - - infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); - free(timings); - - return infrared_signal_is_valid(signal); -} - -static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args) { - UNUSED(cli); - const char* str = furi_string_get_cstr(args); - InfraredSignal* signal = infrared_signal_alloc(); - - bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal); - if(success) { - infrared_signal_transmit(signal); - } else { - printf("Wrong arguments.\r\n"); - infrared_cli_print_usage(); - } - - infrared_signal_free(signal); -} - -static bool - infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) { - bool ret = infrared_signal_save(signal, file, name); - if(!ret) { - printf("Failed to save signal: \"%s\"\r\n", name); - } - return ret; -} - -static bool infrared_cli_decode_raw_signal( - InfraredRawSignal* raw_signal, - InfraredDecoderHandler* decoder, - FlipperFormat* output_file, - const char* signal_name) { - InfraredSignal* signal = infrared_signal_alloc(); - bool ret = false, level = true, is_decoded = false; - - size_t i; - for(i = 0; i < raw_signal->timings_size; ++i) { - // TODO: Any infrared_check_decoder_ready() magic? - const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]); - - if(message) { - is_decoded = true; - printf( - "Protocol: %s address: 0x%lX command: 0x%lX %s\r\n", - infrared_get_protocol_name(message->protocol), - message->address, - message->command, - (message->repeat ? "R" : "")); - if(output_file && !message->repeat) { - infrared_signal_set_message(signal, message); - if(!infrared_cli_save_signal(signal, output_file, signal_name)) break; - } - } - - level = !level; - } - - if(i == raw_signal->timings_size) { - if(!is_decoded && output_file) { - infrared_signal_set_raw_signal( - signal, - raw_signal->timings, - raw_signal->timings_size, - raw_signal->frequency, - raw_signal->duty_cycle); - ret = infrared_cli_save_signal(signal, output_file, signal_name); - } else { - ret = true; - } - } - - infrared_reset_decoder(decoder); - infrared_signal_free(signal); - return ret; -} - -static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) { - bool ret = false; - - InfraredSignal* signal = infrared_signal_alloc(); - InfraredDecoderHandler* decoder = infrared_alloc_decoder(); - - FuriString* tmp; - tmp = furi_string_alloc(); - - while(infrared_signal_read(signal, input_file, tmp)) { - ret = false; - if(!infrared_signal_is_valid(signal)) { - printf("Invalid signal\r\n"); - break; - } - if(!infrared_signal_is_raw(signal)) { - if(output_file && - !infrared_cli_save_signal(signal, output_file, furi_string_get_cstr(tmp))) { - break; - } else { - printf("Skipping decoded signal\r\n"); - continue; - } - } - InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal); - printf( - "Raw signal: %s, %u samples\r\n", furi_string_get_cstr(tmp), raw_signal->timings_size); - if(!infrared_cli_decode_raw_signal( - raw_signal, decoder, output_file, furi_string_get_cstr(tmp))) - break; - ret = true; - } - - infrared_free_decoder(decoder); - infrared_signal_free(signal); - furi_string_free(tmp); - - return ret; -} - -static void infrared_cli_process_decode(Cli* cli, FuriString* args) { - UNUSED(cli); - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage); - FlipperFormat* output_file = NULL; - - uint32_t version; - FuriString *tmp, *header, *input_path, *output_path; - tmp = furi_string_alloc(); - header = furi_string_alloc(); - input_path = furi_string_alloc(); - output_path = furi_string_alloc(); - - do { - if(!args_read_probably_quoted_string_and_trim(args, input_path)) { - printf("Wrong arguments.\r\n"); - infrared_cli_print_usage(); - break; - } - args_read_probably_quoted_string_and_trim(args, output_path); - if(!flipper_format_buffered_file_open_existing( - input_file, furi_string_get_cstr(input_path))) { - printf( - "Failed to open file for reading: \"%s\"\r\n", furi_string_get_cstr(input_path)); - break; - } - if(!flipper_format_read_header(input_file, header, &version) || - (!furi_string_start_with_str(header, "IR")) || version != 1) { - printf( - "Invalid or corrupted input file: \"%s\"\r\n", furi_string_get_cstr(input_path)); - break; - } - if(!furi_string_empty(output_path)) { - printf("Writing output to file: \"%s\"\r\n", furi_string_get_cstr(output_path)); - output_file = flipper_format_file_alloc(storage); - } - if(output_file && - !flipper_format_file_open_always(output_file, furi_string_get_cstr(output_path))) { - printf( - "Failed to open file for writing: \"%s\"\r\n", furi_string_get_cstr(output_path)); - break; - } - if(output_file && !flipper_format_write_header(output_file, header, version)) { - printf( - "Failed to write to the output file: \"%s\"\r\n", - furi_string_get_cstr(output_path)); - break; - } - if(!infrared_cli_decode_file(input_file, output_file)) { - break; - } - printf("File successfully decoded.\r\n"); - } while(false); - - furi_string_free(tmp); - furi_string_free(header); - furi_string_free(input_path); - furi_string_free(output_path); - - flipper_format_free(input_file); - if(output_file) flipper_format_free(output_file); - furi_record_close(RECORD_STORAGE); -} - -static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { - UNUSED(context); - if(furi_hal_infrared_is_busy()) { - printf("INFRARED is busy. Exiting."); - return; - } - - FuriString* command; - command = furi_string_alloc(); - args_read_string_and_trim(args, command); - - size_t i = 0; - for(; i < COUNT_OF(infrared_cli_commands); ++i) { - size_t cmd_len = strlen(infrared_cli_commands[i].cmd); - if(!strncmp(furi_string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) { - break; - } - } - - if(i < COUNT_OF(infrared_cli_commands)) { - infrared_cli_commands[i].process_function(cli, args); - } else { - infrared_cli_print_usage(); - } - - furi_string_free(command); -} -void infrared_on_system_start() { -#ifdef SRV_CLI - Cli* cli = (Cli*)furi_record_open(RECORD_CLI); - cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); - furi_record_close(RECORD_CLI); -#else - UNUSED(infrared_cli_start_ir); -#endif -} diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h index 6d25d1609..4bf0631f5 100644 --- a/applications/main/infrared/infrared_i.h +++ b/applications/main/infrared/infrared_i.h @@ -32,6 +32,8 @@ #include "rpc/rpc_app.h" +#include + #define INFRARED_FILE_NAME_SIZE 100 #define INFRARED_TEXT_STORE_NUM 2 #define INFRARED_TEXT_STORE_SIZE 128 diff --git a/applications/main/infrared/irIcon.png b/applications/main/infrared/irIcon.png deleted file mode 100644 index ccfcee9c8..000000000 Binary files a/applications/main/infrared/irIcon.png and /dev/null differ diff --git a/applications/main/infrared/ir_10px.png b/applications/main/infrared/ir_10px.png new file mode 100644 index 000000000..22c986180 Binary files /dev/null and b/applications/main/infrared/ir_10px.png differ 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/infrared/views/infrared_progress_view.c b/applications/main/infrared/views/infrared_progress_view.c index 3c50f89e4..bb1f982c5 100644 --- a/applications/main/infrared/views/infrared_progress_view.c +++ b/applications/main/infrared/views/infrared_progress_view.c @@ -1,6 +1,5 @@ #include #include "furi_hal_resources.h" -#include "assets_icons.h" #include "gui/canvas.h" #include "gui/view.h" #include "input/input.h" @@ -9,6 +8,7 @@ #include "infrared_progress_view.h" #include "gui/modules/button_panel.h" #include +#include struct InfraredProgressView { View* view; diff --git a/applications/main/infrared_loader/application.fam b/applications/main/infrared_loader/application.fam new file mode 100644 index 000000000..c3d87a24e --- /dev/null +++ b/applications/main/infrared_loader/application.fam @@ -0,0 +1,14 @@ +App( + appid="infrared_loader", + name="Infrared", + apptype=FlipperAppType.APP, + entry_point="infrared_loader_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=int(2 * 1024), + icon="A_Infrared_14", + order=40, + link="/ext/apps/Main/infrared.fap", +) diff --git a/applications/main/infrared_loader/infrared_loader_app.c b/applications/main/infrared_loader/infrared_loader_app.c new file mode 100644 index 000000000..1fd2a59ed --- /dev/null +++ b/applications/main/infrared_loader/infrared_loader_app.c @@ -0,0 +1,9 @@ +#include + +#define TAG "infrared_loader_app" + +int32_t infrared_loader_app(void* p) { + UNUSED(p); + + return 0; +} \ No newline at end of file diff --git a/applications/main/lfrfid/125_10px.png b/applications/main/lfrfid/125_10px.png new file mode 100644 index 000000000..ce01284a2 Binary files /dev/null and b/applications/main/lfrfid/125_10px.png differ diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 4a1498181..41d3b8fa7 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -8,18 +8,12 @@ App( "gui", "dialogs", ], - provides=[ - "lfrfid_start", - ], + # provides=[ "lfrfid_start", ], icon="A_125khz_14", stack_size=2 * 1024, order=20, -) - -App( - appid="lfrfid_start", - apptype=FlipperAppType.STARTUP, - entry_point="lfrfid_on_system_start", - requires=["lfrfid"], - order=50, + # fap_category="Main", + # fap_icon="125_10px.png", + # fap_icon_assets="images", + # fap_libs=["assets"], ) diff --git a/applications/main/lfrfid/images/125_10px.png b/applications/main/lfrfid/images/125_10px.png new file mode 100644 index 000000000..ce01284a2 Binary files /dev/null and b/applications/main/lfrfid/images/125_10px.png differ diff --git a/applications/main/lfrfid/images/ButtonRight_4x7.png b/applications/main/lfrfid/images/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/applications/main/lfrfid/images/ButtonRight_4x7.png differ diff --git a/applications/main/lfrfid/images/DolphinCommon_56x48.png b/applications/main/lfrfid/images/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/applications/main/lfrfid/images/DolphinCommon_56x48.png differ diff --git a/applications/main/lfrfid/images/DolphinMafia_115x62.png b/applications/main/lfrfid/images/DolphinMafia_115x62.png new file mode 100644 index 000000000..53dffb4fa Binary files /dev/null and b/applications/main/lfrfid/images/DolphinMafia_115x62.png differ diff --git a/applications/main/lfrfid/images/DolphinNice_96x59.png b/applications/main/lfrfid/images/DolphinNice_96x59.png new file mode 100644 index 000000000..b111196c7 Binary files /dev/null and b/applications/main/lfrfid/images/DolphinNice_96x59.png differ diff --git a/applications/main/lfrfid/images/NFC_manual_60x50.png b/applications/main/lfrfid/images/NFC_manual_60x50.png new file mode 100644 index 000000000..787c0bcfe Binary files /dev/null and b/applications/main/lfrfid/images/NFC_manual_60x50.png differ diff --git a/applications/main/lfrfid/images/RFIDDolphinReceive_97x61.png b/applications/main/lfrfid/images/RFIDDolphinReceive_97x61.png new file mode 100644 index 000000000..ff967599c Binary files /dev/null and b/applications/main/lfrfid/images/RFIDDolphinReceive_97x61.png differ diff --git a/applications/main/lfrfid/images/RFIDDolphinSend_97x61.png b/applications/main/lfrfid/images/RFIDDolphinSend_97x61.png new file mode 100644 index 000000000..343b9f734 Binary files /dev/null and b/applications/main/lfrfid/images/RFIDDolphinSend_97x61.png differ diff --git a/applications/main/lfrfid/images/RFIDDolphinSuccess_108x57.png b/applications/main/lfrfid/images/RFIDDolphinSuccess_108x57.png new file mode 100644 index 000000000..341999109 Binary files /dev/null and b/applications/main/lfrfid/images/RFIDDolphinSuccess_108x57.png differ diff --git a/applications/main/lfrfid/images/RFIDSmallChip_14x14.png b/applications/main/lfrfid/images/RFIDSmallChip_14x14.png new file mode 100644 index 000000000..24219a548 Binary files /dev/null and b/applications/main/lfrfid/images/RFIDSmallChip_14x14.png differ diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_01.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_01.png new file mode 100644 index 000000000..a5dc239d8 Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_01.png differ diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_02.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_02.png new file mode 100644 index 000000000..162d8a8f4 Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_02.png differ diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_03.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_03.png new file mode 100644 index 000000000..5483e4734 Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_03.png differ diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_04.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_04.png new file mode 100644 index 000000000..ce2fbbd47 Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_04.png differ diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_05.png b/applications/main/lfrfid/images/Round_loader_8x8/frame_05.png new file mode 100644 index 000000000..8b786c029 Binary files /dev/null and b/applications/main/lfrfid/images/Round_loader_8x8/frame_05.png differ diff --git a/applications/main/lfrfid/images/Round_loader_8x8/frame_rate b/applications/main/lfrfid/images/Round_loader_8x8/frame_rate new file mode 100644 index 000000000..d8263ee98 --- /dev/null +++ b/applications/main/lfrfid/images/Round_loader_8x8/frame_rate @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/applications/main/lfrfid/images/SDQuestion_35x43.png b/applications/main/lfrfid/images/SDQuestion_35x43.png new file mode 100644 index 000000000..9b9c9a58e Binary files /dev/null and b/applications/main/lfrfid/images/SDQuestion_35x43.png differ diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index b0f989374..d391c5e89 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); @@ -31,7 +32,7 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { } static LfRfid* lfrfid_alloc() { - LfRfid* lfrfid = malloc(sizeof(LfRfid)); + LfRfid* lfrfid = malloc(sizeof(LfRfid)); //-V773 lfrfid->storage = furi_record_open(RECORD_STORAGE); lfrfid->dialogs = furi_record_open(RECORD_DIALOGS); @@ -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/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 71917c0c2..201caa4f6 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -33,6 +32,8 @@ #include #include +#include +// #include #define LFRFID_KEY_NAME_SIZE 22 #define LFRFID_TEXT_STORE_SIZE 40 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 39ae3c63b..ecca7cf11 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/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index 0d4db6178..4828e21a7 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -1,5 +1,7 @@ #include "lfrfid_view_read.h" #include +#include +// #include #define TEMP_STR_LEN 128 diff --git a/applications/main/lfrfid_loader/application.fam b/applications/main/lfrfid_loader/application.fam new file mode 100644 index 000000000..947762085 --- /dev/null +++ b/applications/main/lfrfid_loader/application.fam @@ -0,0 +1,14 @@ +App( + appid="lfrfid_loader", + name="125 kHz RFID", + apptype=FlipperAppType.APP, + entry_point="lfrfid_loader_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=int(2 * 1024), + icon="A_125khz_14", + order=20, + link="/ext/apps/Main/lfrfid.fap", +) diff --git a/applications/main/lfrfid_loader/lfrfid_loader_app.c b/applications/main/lfrfid_loader/lfrfid_loader_app.c new file mode 100644 index 000000000..ad6a96c64 --- /dev/null +++ b/applications/main/lfrfid_loader/lfrfid_loader_app.c @@ -0,0 +1,9 @@ +#include + +#define TAG "lfrfid_loader_app" + +int32_t lfrfid_loader_app(void* p) { + UNUSED(p); + + return 0; +} \ No newline at end of file diff --git a/applications/main/nfc/helpers/nfc_generators.c b/applications/main/nfc/helpers/nfc_generators.c index 11083b9f0..5f0527c6a 100644 --- a/applications/main/nfc/helpers/nfc_generators.c +++ b/applications/main/nfc/helpers/nfc_generators.c @@ -314,7 +314,7 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { mful->version.storage_size = 0x15; } -static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { +void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { nfc_generate_common_start(data); nfc_generate_mf_classic_common(data, uid_len, type); @@ -337,6 +337,9 @@ static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClas } mf_classic_set_block_read(mfc, i, &mfc->block[i]); } + // Set SAK to 18 + data->nfc_data.sak = 0x18; + } else if(type == MfClassicType1k) { // Set every block to 0xFF for(uint16_t i = 1; i < MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; i += 1) { @@ -347,6 +350,8 @@ static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClas } mf_classic_set_block_read(mfc, i, &mfc->block[i]); } + // Set SAK to 08 + data->nfc_data.sak = 0x08; } mfc->type = type; diff --git a/applications/main/nfc/helpers/nfc_generators.h b/applications/main/nfc/helpers/nfc_generators.h index 10a05591b..362a19b1e 100644 --- a/applications/main/nfc/helpers/nfc_generators.h +++ b/applications/main/nfc/helpers/nfc_generators.h @@ -11,3 +11,5 @@ struct NfcGenerator { }; extern const NfcGenerator* const nfc_generators[]; + +void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type); diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 0b685f545..cff994baf 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); @@ -85,6 +86,13 @@ Nfc* nfc_alloc() { nfc->view_dispatcher, NfcViewTextBox, text_box_get_view(nfc->text_box)); nfc->text_box_store = furi_string_alloc(); + // Variable Item List + nfc->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + nfc->view_dispatcher, + NfcViewVarItemList, + variable_item_list_get_view(nfc->variable_item_list)); + // Custom Widget nfc->widget = widget_alloc(); view_dispatcher_add_view(nfc->view_dispatcher, NfcViewWidget, widget_get_view(nfc->widget)); @@ -115,7 +123,9 @@ void nfc_free(Nfc* nfc) { // Stop worker nfc_worker_stop(nfc->worker); // Save data in shadow file - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } } if(nfc->rpc_ctx) { rpc_system_app_send_exited(nfc->rpc_ctx); @@ -155,6 +165,10 @@ void nfc_free(Nfc* nfc) { text_box_free(nfc->text_box); furi_string_free(nfc->text_box_store); + // Variable Item List + view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewVarItemList); + variable_item_list_free(nfc->variable_item_list); + // Custom Widget view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewWidget); widget_free(nfc->widget); @@ -217,6 +231,13 @@ void nfc_blink_stop(Nfc* nfc) { notification_message(nfc->notifications, &sequence_blink_stop); } +bool nfc_save_file(Nfc* nfc) { + furi_string_printf( + nfc->dev->load_path, "%s/%s%s", NFC_APP_FOLDER, nfc->dev->dev_name, NFC_APP_EXTENSION); + bool file_saved = nfc_device_save(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + return file_saved; +} + void nfc_show_loading_popup(void* context, bool show) { Nfc* nfc = context; TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); @@ -275,12 +296,13 @@ 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); - } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } } else { // Exit app diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index fa5b54edc..0f2e51dab 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 @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -77,6 +79,7 @@ struct Nfc { TextInput* text_input; ByteInput* byte_input; TextBox* text_box; + VariableItemList* variable_item_list; Widget* widget; DictAttack* dict_attack; DetectReader* detect_reader; @@ -92,6 +95,7 @@ typedef enum { NfcViewTextInput, NfcViewByteInput, NfcViewTextBox, + NfcViewVarItemList, NfcViewWidget, NfcViewDictAttack, NfcViewDetectReader, @@ -113,4 +117,6 @@ void nfc_blink_detect_start(Nfc* nfc); void nfc_blink_stop(Nfc* nfc); +bool nfc_save_file(Nfc* nfc); + void nfc_show_loading_popup(void* context, bool show); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a25850c84..0bebe35f3 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -21,6 +21,7 @@ ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) @@ -36,8 +37,22 @@ 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, passport_read, PassportReadSuccess) +ADD_SCENE(nfc, passport_read_auth, PassportReadAuthSuccess) +ADD_SCENE(nfc, passport_menu, PassportMenu) +ADD_SCENE(nfc, passport_auth, PassportAuth) +ADD_SCENE(nfc, passport_auth_save_name, PassportAuthSaveName) +ADD_SCENE(nfc, passport_date, PassportDate) +ADD_SCENE(nfc, passport_docnr, PassportDocNr) +ADD_SCENE(nfc, passport_pace_todo, PassportPaceTodo) ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) ADD_SCENE(nfc, device_info, DeviceInfo) ADD_SCENE(nfc, delete, Delete) @@ -54,3 +69,4 @@ ADD_SCENE(nfc, detect_reader, DetectReader) ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo) ADD_SCENE(nfc, mfkey_complete, MfkeyComplete) ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) +ADD_SCENE(nfc, read_card_type, ReadCardType) diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_detect_reader.c index f0177f9c1..745946157 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,10 +25,14 @@ 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); + 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_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_menu.c b/applications/main/nfc/scenes/nfc_scene_emv_menu.c index eb1e10043..1da630fcf 100644 --- a/applications/main/nfc/scenes/nfc_scene_emv_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_emv_menu.c @@ -1,6 +1,7 @@ #include "../nfc_i.h" enum SubmenuIndex { + SubmenuIndexSave, SubmenuIndexInfo, }; @@ -14,6 +15,7 @@ void nfc_scene_emv_menu_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; + submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc); submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc); submenu_set_selected_item( nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu)); @@ -26,7 +28,13 @@ bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexInfo) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatBankCard; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); consumed = true; } 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_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index b22dd42a1..3ce4f6de8 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -29,8 +29,13 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { if(event.event == DialogExResultRight) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == DialogExResultLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadCardType)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneReadCardType); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } } } else if(event.type == SceneManagerEventTypeBack) { consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index e888e9d35..fc6021d73 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -1,6 +1,7 @@ #include "../nfc_i.h" enum SubmenuIndex { + SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, }; @@ -15,6 +16,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; + submenu_add_item( + submenu, + "Read Specific Card Type", + SubmenuIndexReadCardType, + nfc_scene_extra_actions_submenu_callback, + nfc); submenu_add_item( submenu, "Mifare Classic Keys", @@ -44,9 +51,15 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == SubmenuIndexMfUltralightUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; + } else if(event.event == SubmenuIndexReadCardType) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); + scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); + consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } + return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_file_select.c b/applications/main/nfc/scenes/nfc_scene_file_select.c index 693fdec20..374a933d1 100644 --- a/applications/main/nfc/scenes/nfc_scene_file_select.c +++ b/applications/main/nfc/scenes/nfc_scene_file_select.c @@ -5,6 +5,9 @@ void nfc_scene_file_select_on_enter(void* context) { Nfc* nfc = context; // Process file_select return nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); + if(!furi_string_size(nfc->dev->load_path)) { + furi_string_set_str(nfc->dev->load_path, NFC_APP_FOLDER); + } if(nfc_file_select(nfc->dev)) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, 0); scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu); 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 1a6dab4e8..1004b3b26 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..68eda7040 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; @@ -50,7 +48,10 @@ bool nfc_scene_mf_classic_emulate_on_event(void* context, SceneManagerEvent even NFC_MF_CLASSIC_DATA_CHANGED) { scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_NOT_CHANGED); - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + // Save shadow file + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } } consumed = false; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index 7874f6e09..e3335308f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -34,8 +34,6 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); - widget_add_button_element( - nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc); widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36); if(user_dict_keys_total > 0) { widget_add_button_element( @@ -57,9 +55,6 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) if(event.event == GuiButtonTypeCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); consumed = true; - } else if(event.event == GuiButtonTypeLeft) { - scene_manager_previous_scene(nfc->scene_manager); - consumed = true; } else if(event.event == GuiButtonTypeRight) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList); consumed = true; 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..444c9a540 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( @@ -27,7 +24,7 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc); - FuriString* temp_str; + FuriString* temp_str = NULL; if(furi_string_size(nfc->dev->dev_data.parsed_data)) { temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data); } else { 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..aacf77f77 --- /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, furi_string_get_cstr(nfc->dev->load_path))) { + 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_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 cea334a25..1505064a4 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -1,9 +1,11 @@ #include "../nfc_i.h" #include -#include #define NFC_SCENE_MF_ULTRALIGHT_EMULATE_LOG_SIZE_MAX (200) +#define NFC_MF_UL_DATA_NOT_CHANGED (0UL) +#define NFC_MF_UL_DATA_CHANGED (1UL) + enum { // View states NfcSceneMfUltralightEmulateStateWidget, @@ -87,7 +89,6 @@ void nfc_scene_mf_ultralight_emulate_on_enter(void* context) { Nfc* nfc = context; uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); // Setup Widget nfc_scene_mf_ultralight_emulate_widget_config(nfc, false); @@ -119,37 +120,18 @@ bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent e scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate); bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - if(state & NfcSceneMfUltralightEmulateStateAuthAttempted) { - if(!(state & NfcSceneMfUltralightEmulateStateLogButtonShown)) { - // Add log button to widget not already showing - nfc_scene_mf_ultralight_emulate_widget_config(nfc, true); - state |= NfcSceneMfUltralightEmulateStateLogButtonShown; - } - // The text box update logic is handled in the worker callback - state &= ~NfcSceneMfUltralightEmulateStateAuthAttempted; - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightEmulate, state); - consumed = true; + if(event.type == SceneManagerEventTypeBack) { + // Stop worker + nfc_worker_stop(nfc->worker); + // Check if data changed and save in shadow file + if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate) == + NFC_MF_UL_DATA_CHANGED) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_NOT_CHANGED); + // Save shadow file + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); } - } else if( - event.event == GuiButtonTypeCenter && (state & NfcSceneMfUltralightEmulateStateMax) == - NfcSceneMfUltralightEmulateStateWidget) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - state = (state & ~NfcSceneMfUltralightEmulateStateMax) | - NfcSceneMfUltralightEmulateStateTextBox; - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - if((state & NfcSceneMfUltralightEmulateStateMax) == - NfcSceneMfUltralightEmulateStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - state = (state & ~NfcSceneMfUltralightEmulateStateMax) | - NfcSceneMfUltralightEmulateStateWidget; - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state); - consumed = true; } } return consumed; 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..ddf30c54a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -1,7 +1,9 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { - SubmenuIndexUnlock, + SubmenuIndexUnlockByReader, + SubmenuIndexUnlockByPassword, SubmenuIndexSave, SubmenuIndexEmulate, SubmenuIndexInfo, @@ -18,11 +20,17 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) { Submenu* submenu = nfc->submenu; MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; - if(data->data_read != data->data_size) { + if(!mf_ul_is_full_capture(data)) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexUnlockByReader, + nfc_scene_mf_ultralight_menu_submenu_callback, + nfc); submenu_add_item( submenu, "Unlock With Password", - SubmenuIndexUnlock, + SubmenuIndexUnlockByPassword, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); } @@ -56,8 +64,16 @@ 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) { + } else if(event.event == SubmenuIndexUnlockByReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } else if(event.event == SubmenuIndexUnlockByPassword) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); consumed = true; } else if(event.event == SubmenuIndexInfo) { 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 fcea79ca2..22a9d51b5 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..ac2eea182 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, @@ -20,16 +19,19 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); Widget* widget = nfc->widget; + const char* title; FuriString* temp_str; temp_str = furi_string_alloc(); if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); + if(mf_ul_data->auth_success) + title = "All pages are unlocked!"; + else + title = "All unlocked but failed auth!"; } else { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); + title = "Not all pages unlocked!"; } + widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title); furi_string_set(temp_str, "UID:"); for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); @@ -37,7 +39,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 +55,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..cb5ccd6e8 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; @@ -33,7 +31,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { nfc_scene_mf_ultralight_read_success_widget_callback, nfc); - FuriString* temp_str; + FuriString* temp_str = NULL; if(furi_string_size(nfc->dev->dev_data.parsed_data)) { temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c new file mode 100644 index 000000000..70a06091f --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c @@ -0,0 +1,56 @@ +#include "../nfc_i.h" + +bool nfc_scene_mf_ultralight_unlock_auto_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + return true; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + popup_set_text(nfc->popup, "Touch the reader", 44, 31, AlignLeft, AlignCenter); + popup_set_icon(nfc->popup, 0, 16, &I_Tap_reader_36x38); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateMfUltralightEmulate, + &nfc->dev->dev_data, + nfc_scene_mf_ultralight_unlock_auto_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventMfUltralightPwdAuth)) { + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + memcpy(nfc->byte_input_store, auth->pwd.raw, sizeof(auth->pwd.raw)); + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual; + nfc_worker_stop(nfc->worker); + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c index 95f1edb62..979aa8eda 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -58,7 +58,8 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); consumed = true; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event); } return consumed; } 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 708f908cd..0b88043e6 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_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 8f33972e0..fa73201d0 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -32,6 +32,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { // Set tag type if(protocol == NfcDeviceProtocolEMV) { furi_string_cat_printf(temp_str, "\e#EMV Bank Card\n"); + } else if(protocol == NfcDeviceProtocolMRTD) { + furi_string_cat_printf(temp_str, "\e#Passport/ID\n"); } else if(protocol == NfcDeviceProtocolMifareUl) { furi_string_cat_printf( temp_str, "\e#%s\n", nfc_mf_ul_type(dev_data->mf_ul_data.type, true)); @@ -87,6 +89,20 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); if(data->data_size > data->data_read) { furi_string_cat_printf(temp_str, "\nPassword-protected"); + } else if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + furi_string_cat_printf( + temp_str, + "\nPassword: %02X %02X %02X %02X", + config_pages->auth_data.pwd.raw[0], + config_pages->auth_data.pwd.raw[1], + config_pages->auth_data.pwd.raw[2], + config_pages->auth_data.pwd.raw[3]); + furi_string_cat_printf( + temp_str, + "\nPACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); } } else if(protocol == NfcDeviceProtocolMifareClassic) { MfClassicData* data = &dev_data->mf_classic_data; @@ -131,4 +147,4 @@ void nfc_scene_nfc_data_info_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_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_passport_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c new file mode 100644 index 000000000..c78c8b70e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -0,0 +1,194 @@ +#include "../nfc_i.h" + +#define TAG "PassportAuth" + +#define MRTD_AUTH_METHOD_COUNT 4 +// Must match MrtdAuthMethod size (lib/nfc/protocols/mrtd_helpers.h) + +typedef enum { + NfcScenePassportAuthSelectDob, + NfcScenePassportAuthSelectDoe, + NfcScenePassportAuthSelectDocNr, + NfcScenePassportAuthSelectMethod, + NfcScenePassportAuthSelectAuth, + NfcScenePassportAuthSelectSave, + NfcScenePassportAuthSelectLoad, +} NfcScenePassportAuthSelect; + +void nfc_scene_passport_auth_var_list_enter_callback(void* context, uint32_t index) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_passport_auth_method_changed(VariableItem* item) { + Nfc* nfc = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + nfc->dev->dev_data.mrtd_data.auth.method = index; + variable_item_set_current_value_text(item, mrtd_auth_method_string(index)); +} + +bool nfc_scene_passport_auth_load(Nfc* nfc) { + const DialogsFileBrowserOptions browser_options = { + .extension = MRTD_APP_EXTENSION, + .skip_assets = true, + .icon = &I_u2f_10px, + .hide_ext = true, + .item_loader_callback = NULL, + .item_loader_context = NULL, + }; + + FuriString* mrtd_app_folder; + mrtd_app_folder = furi_string_alloc_set(MRTD_APP_FOLDER); + + FuriString* file_path; + file_path = furi_string_alloc(); + + bool res = + dialog_file_browser_show(nfc->dev->dialogs, file_path, mrtd_app_folder, &browser_options); + + furi_string_free(mrtd_app_folder); + + if(res) { + mrtd_auth_params_load( + nfc->dev->storage, + nfc->dev->dialogs, + &nfc->dev->dev_data.mrtd_data.auth, + furi_string_get_cstr(file_path), + true); + + nfc_scene_passport_auth_on_enter(nfc); + variable_item_list_set_selected_item( + nfc->variable_item_list, NfcScenePassportAuthSelectAuth); + } + + return res; +} + +void nfc_scene_passport_auth_on_enter(void* context) { + Nfc* nfc = context; + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; + + // By entering the Auth menu, we default to Auth: Any + MrtdAuthMethod* auth_method = &mrtd_data->auth.method; + if(*auth_method == MrtdAuthMethodNone) { + *auth_method = MrtdAuthMethodAny; + } + + VariableItemList* variable_item_list = nfc->variable_item_list; + variable_item_list_reset(variable_item_list); + + VariableItem* item; + uint8_t value_index; + + const size_t temp_str_size = 15; + char temp_str[temp_str_size]; + snprintf( + temp_str, + temp_str_size, + "%02u%02u%02u", + mrtd_data->auth.birth_date.year, + mrtd_data->auth.birth_date.month, + mrtd_data->auth.birth_date.day); + + item = variable_item_list_add(variable_item_list, "Birth Date", 1, NULL, NULL); + variable_item_set_current_value_text(item, temp_str); + + snprintf( + temp_str, + temp_str_size, + "%02u%02u%02u", + mrtd_data->auth.expiry_date.year, + mrtd_data->auth.expiry_date.month, + mrtd_data->auth.expiry_date.day); + + item = variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL); + variable_item_set_current_value_text(item, temp_str); + + item = variable_item_list_add(variable_item_list, "Document Nr.", 1, NULL, NULL); + + strncpy(temp_str, mrtd_data->auth.doc_number, temp_str_size); + temp_str[temp_str_size] = '\x00'; + if(strlen(temp_str) > 8) { + temp_str[8] = '.'; + temp_str[9] = '.'; + temp_str[10] = '.'; + temp_str[11] = '\x00'; + } + variable_item_set_current_value_text(item, temp_str); + + item = variable_item_list_add( + variable_item_list, + "Method", + MRTD_AUTH_METHOD_COUNT, + nfc_scene_passport_auth_method_changed, + nfc); + + value_index = *auth_method; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, mrtd_auth_method_string(value_index)); + + variable_item_list_add(variable_item_list, "Authenticate and read", 1, NULL, NULL); + + variable_item_list_add(variable_item_list, "Save parameters", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Load parameters", 1, NULL, NULL); + + variable_item_list_set_enter_callback( + variable_item_list, nfc_scene_passport_auth_var_list_enter_callback, nfc); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewVarItemList); +} + +bool nfc_scene_passport_auth_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + FURI_LOG_D(TAG, "event.event: %ld", event.event); + switch(event.event) { + case NfcScenePassportAuthSelectLoad: + nfc_scene_passport_auth_load(nfc); + consumed = true; + break; + case NfcScenePassportAuthSelectDob: + scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 0); + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate); + consumed = true; + break; + case NfcScenePassportAuthSelectDoe: + scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 1); + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate); + consumed = true; + break; + case NfcScenePassportAuthSelectDocNr: + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDocNr); + consumed = true; + break; + case NfcScenePassportAuthSelectMethod: + consumed = true; + break; + case NfcScenePassportAuthSelectAuth: + if(nfc->dev->dev_data.mrtd_data.auth.method == MrtdAuthMethodPace) { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportPaceTodo); + } else { + nfc_device_clear(nfc->dev); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + } + consumed = true; + break; + case NfcScenePassportAuthSelectSave: + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuthSaveName); + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_passport_auth_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + variable_item_list_reset(nfc->variable_item_list); +} diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c b/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c new file mode 100644 index 000000000..65fee0bcf --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c @@ -0,0 +1,82 @@ +#include "../nfc_i.h" +#include +#include +#include + +void nfc_scene_passport_auth_save_name_text_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); +} + +void nfc_scene_passport_auth_save_name_on_enter(void* context) { + Nfc* nfc = context; + + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; + + // Setup view + TextInput* text_input = nfc->text_input; + bool docnr_empty = false; + if(!strcmp(mrtd_data->auth.doc_number, "")) { + set_random_name(nfc->text_store, sizeof(nfc->text_store)); + docnr_empty = true; + } else { + nfc_text_store_set(nfc, mrtd_data->auth.doc_number); + } + text_input_set_header_text(text_input, "Name the parameters"); + text_input_set_result_callback( + text_input, + nfc_scene_passport_auth_save_name_text_input_callback, + nfc, + nfc->text_store, + NFC_DEV_NAME_MAX_LEN, + docnr_empty); + + FuriString* folder_path; + folder_path = furi_string_alloc(); + + if(furi_string_end_with(nfc->dev->load_path, NFC_APP_EXTENSION)) { + path_extract_dirname(furi_string_get_cstr(nfc->dev->load_path), folder_path); + } else { + furi_string_set(folder_path, NFC_APP_FOLDER); + } + + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(furi_string_get_cstr(folder_path), NFC_APP_EXTENSION, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); + + furi_string_free(folder_path); +} + +bool nfc_scene_passport_auth_save_name_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventTextInputDone) { + if(mrtd_auth_params_save( + nfc->dev->storage, nfc->dev->dialogs, &mrtd_data->auth, nfc->text_store)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + consumed = true; + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } + } + return consumed; +} + +void nfc_scene_passport_auth_save_name_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + void* validator_context = text_input_get_validator_callback_context(nfc->text_input); + text_input_set_validator(nfc->text_input, NULL, NULL); + validator_is_file_free(validator_context); + + text_input_reset(nfc->text_input); +} diff --git a/applications/main/nfc/scenes/nfc_scene_passport_date.c b/applications/main/nfc/scenes/nfc_scene_passport_date.c new file mode 100644 index 000000000..4dc174326 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_date.c @@ -0,0 +1,120 @@ +#include "../nfc_i.h" +#include "m-string.h" +#include + +#define TAG "PassportDate" + +#define DATE_LENGTH 6 + +//TODO: use types in .h file? also in nfc_scene_passport_bac.c +#define NFC_PASSPORT_DATE_BIRTH 0 +#define NFC_PASSPORT_DATE_EXPIRY 1 + +void nfc_scene_passport_date_text_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); +} + +void nfc_scene_passport_date_on_enter(void* context) { + Nfc* nfc = context; + + MrtdDate date_value; + + uint32_t date_type = scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportDate); + + //TODO: numbers only + TextInput* text_input = nfc->text_input; + + switch(date_type) { + case NFC_PASSPORT_DATE_BIRTH: + text_input_set_header_text(text_input, "Birth Date"); + date_value = nfc->dev->dev_data.mrtd_data.auth.birth_date; + break; + case NFC_PASSPORT_DATE_EXPIRY: + text_input_set_header_text(text_input, "Expiry Date"); + date_value = nfc->dev->dev_data.mrtd_data.auth.expiry_date; + break; + } + + bool date_empty = false; + if(date_value.year == 0 || date_value.month == 0 || date_value.day == 0 || + date_value.year > 100 || date_value.month > 13 || date_value.day > 31) { + nfc_text_store_set(nfc, "YYMMDD"); + date_empty = true; + } else { + char temp_str[10]; + snprintf(temp_str, 10, "%02u%02u%02u", date_value.year, date_value.month, date_value.day); + + memcpy(nfc->text_store, temp_str, DATE_LENGTH); + nfc->text_store[DATE_LENGTH] = '\x00'; + } + + text_input_set_result_callback( + text_input, + nfc_scene_passport_date_text_input_callback, + nfc, + nfc->text_store, + DATE_LENGTH + 1, // incl. '\x00' + date_empty); // Use as template + + //TODO: add validator for valid date (YYMMDD) + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); +} + +bool nfc_scene_passport_date_save(Nfc* nfc) { + int year; + int month; + int day; + int ret = sscanf(nfc->text_store, "%02d%02d%02d", &year, &month, &day); + + if(ret != 3) { + FURI_LOG_E(TAG, "Invalid date entered (YYMMDD): %s", nfc->text_store); + return false; + } + + MrtdDate date_value; + date_value.year = year; + date_value.month = month; + date_value.day = day; + + uint32_t date_type = scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportDate); + + //TODO: use types in .h file? also in nfc_scene_passport_bac.c + switch(date_type) { + case NFC_PASSPORT_DATE_BIRTH: + nfc->dev->dev_data.mrtd_data.auth.birth_date = date_value; + break; + case NFC_PASSPORT_DATE_EXPIRY: + nfc->dev->dev_data.mrtd_data.auth.expiry_date = date_value; + break; + } + + return true; +} + +bool nfc_scene_passport_date_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventTextInputDone) { + nfc_scene_passport_date_save(nfc); + //TODO: handle invalid date (returned false) + + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcScenePassportAuth); + } + } + return consumed; +} + +void nfc_scene_passport_date_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + // TODO: clear validator + + text_input_reset(nfc->text_input); +} diff --git a/applications/main/nfc/scenes/nfc_scene_passport_docnr.c b/applications/main/nfc/scenes/nfc_scene_passport_docnr.c new file mode 100644 index 000000000..109415759 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_docnr.c @@ -0,0 +1,67 @@ +#include "../nfc_i.h" +#include "m-string.h" +#include + +#define TAG "PassportDocnr" + +void nfc_scene_passport_docnr_text_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); +} + +void nfc_scene_passport_docnr_on_enter(void* context) { + Nfc* nfc = context; + + TextInput* text_input = nfc->text_input; + text_input_set_header_text(text_input, "Document Nr."); + + char* docnr = nfc->dev->dev_data.mrtd_data.auth.doc_number; + bool docnr_empty = false; + + if(*docnr) { + nfc_text_store_set(nfc, docnr); + docnr_empty = false; + } else { + nfc_text_store_set(nfc, "PA7HJ34M8"); + docnr_empty = true; + } + + text_input_set_result_callback( + text_input, + nfc_scene_passport_docnr_text_input_callback, + nfc, + nfc->text_store, + MRTD_DOCNR_MAX_LENGTH, // incl. '\x00' + docnr_empty); // Use as template + + //TODO: add validator? + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); +} + +bool nfc_scene_passport_docnr_save(Nfc* nfc) { + strncpy(nfc->dev->dev_data.mrtd_data.auth.doc_number, nfc->text_store, MRTD_DOCNR_MAX_LENGTH); + return true; +} + +bool nfc_scene_passport_docnr_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventTextInputDone) { + nfc_scene_passport_docnr_save(nfc); + + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcScenePassportAuth); + } + } + return consumed; +} + +void nfc_scene_passport_docnr_on_exit(void* context) { + Nfc* nfc = context; + + text_input_reset(nfc->text_input); +} diff --git a/applications/main/nfc/scenes/nfc_scene_passport_menu.c b/applications/main/nfc/scenes/nfc_scene_passport_menu.c new file mode 100644 index 000000000..27febcb18 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_menu.c @@ -0,0 +1,57 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexInfo, +}; + +void nfc_scene_passport_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_passport_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Save", SubmenuIndexSave, nfc_scene_passport_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, nfc_scene_passport_menu_submenu_callback, nfc); + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_passport_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + //TODO: save more than just UID + nfc->dev->format = NfcDeviceSaveFormatUid; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportMenu, event.event); + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_passport_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c b/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c new file mode 100644 index 000000000..c6d7d7f6c --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c @@ -0,0 +1,40 @@ +#include "../nfc_i.h" + +void nfc_scene_passport_pace_todo_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_passport_pace_todo_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 64, 16, &I_DolphinCommon_56x48); + popup_set_header(popup, "PACE not yet implemented", 4, 4, AlignLeft, AlignTop); + popup_set_timeout(popup, 2000); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_passport_pace_todo_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_passport_pace_todo_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, NfcScenePassportAuth); + } + } + return consumed; +} + +void nfc_scene_passport_pace_todo_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read.c b/applications/main/nfc/scenes/nfc_scene_passport_read.c new file mode 100644 index 000000000..74f173b52 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_read.c @@ -0,0 +1,90 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_passport_read_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_passport_read_on_enter(void* context) { + Nfc* nfc = context; + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; + + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + Widget* widget = nfc->widget; + + // Setup Custom Widget view + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_set(temp_str, "\e#Passport\n"); + char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3'; + + char nfc_type; + switch(data->type) { + case FuriHalNfcTypeA: + nfc_type = 'A'; + break; + case FuriHalNfcTypeB: + nfc_type = 'B'; + break; + default: + nfc_type = '?'; + break; + } + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-%c)\n", iso_type, nfc_type); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", data->uid[i]); + } + furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]); + furi_string_cat_printf(temp_str, " SAK: %02X\n", data->sak); + + if(mrtd_data->auth.method != MrtdAuthMethodNone && !mrtd_data->auth_success) { + furi_string_cat_printf(temp_str, "Auth failed. Wrong params?"); + } + + widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + widget_add_button_element( + nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_widget_callback, nfc); + widget_add_button_element( + nfc->widget, GuiButtonTypeCenter, "Auth", nfc_scene_passport_read_widget_callback, nfc); + widget_add_button_element( + nfc->widget, GuiButtonTypeRight, "More", nfc_scene_passport_read_widget_callback, nfc); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_passport_read_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuth); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_passport_read_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c new file mode 100644 index 000000000..31a9e8ce1 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -0,0 +1,142 @@ +#include "../nfc_i.h" +#include + +const char months[13][4] = { + "---", + "JAN", + "FEB", + "MAR", + "APR", + "MAY", + "JUN", + "JUL", + "AUG", + "SEP", + "OCT", + "NOV", + "DEC", +}; + +void nfc_scene_passport_read_auth_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_passport_read_auth_on_enter(void* context) { + Nfc* nfc = context; + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; + + Widget* widget = nfc->widget; + + // Setup Custom Widget view + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_set(temp_str, "\e#Passport\n"); + furi_string_cat_printf( + temp_str, "Auth.method: %s\n", mrtd_auth_method_string(mrtd_data->auth_method_used)); + // TODO: indicate BAC / PACE used + + uint16_t lds_version = mrtd_data->files.EF_COM.lds_version; + furi_string_cat_printf(temp_str, "LDS version: %d.%d\n", lds_version / 100, lds_version % 100); + + uint32_t unicode_version = mrtd_data->files.EF_COM.unicode_version; + furi_string_cat_printf( + temp_str, + "Unicode version: %d.%d.%d\n", + (uint8_t)(unicode_version / 10000), + (uint8_t)(unicode_version / 100 % 100), + (uint8_t)(unicode_version % 100)); + + furi_string_cat_printf(temp_str, "Avail.files: "); + for(size_t i = 0; i < MAX_EFCOM_TAGS; ++i) { + uint8_t tag = mrtd_data->files.EF_COM.tag_list[i]; + const EFFile* file = mrtd_tag_to_file(tag); + if(file->tag) { + if(i > 0) furi_string_cat_printf(temp_str, ", "); + furi_string_cat_printf(temp_str, "%s", file->name); + } + } + furi_string_cat_printf(temp_str, "\n"); + + EF_DIR_contents* EF_DIR = &mrtd_data->files.EF_DIR; + if(EF_DIR->applications_count > 0) { + furi_string_cat_printf(temp_str, "Apps:\n"); + for(uint8_t i = 0; i < EF_DIR->applications_count; ++i) { + for(uint8_t n = 0; n < sizeof(AIDValue); ++n) { + furi_string_cat_printf(temp_str, "%02X ", EF_DIR->applications[i][n]); + } + furi_string_cat_printf(temp_str, "\n"); + } + } + + EF_DG1_contents* DG1 = &mrtd_data->files.DG1; + furi_string_cat_printf(temp_str, "\e#DG1\n"); + furi_string_cat_printf(temp_str, "Doc Type: %s\n", DG1->doctype); + furi_string_cat_printf(temp_str, "Issuing State: %s\n", DG1->issuing_state); + furi_string_cat_printf(temp_str, "Name: %s\n", DG1->name); + furi_string_cat_printf(temp_str, "DocNr: %s\n", DG1->docnr); + furi_string_cat_printf(temp_str, "Nationality: %s\n", DG1->nationality); + furi_string_cat_printf( + temp_str, + "Birth Date: %02d %s %02d\n", + DG1->birth_date.day, + months[DG1->birth_date.month], + DG1->birth_date.year); + furi_string_cat_printf(temp_str, "Sex: %s\n", DG1->sex); + furi_string_cat_printf( + temp_str, + "Expiry Date: %02d %s %02d\n", + DG1->expiry_date.day, + months[DG1->expiry_date.month], + DG1->expiry_date.year); + + widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + widget_add_button_element( + nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_auth_widget_callback, nfc); + /* + widget_add_button_element( + nfc->widget, GuiButtonTypeCenter, "Auth", nfc_scene_passport_read_auth_widget_callback, nfc); + widget_add_button_element( + nfc->widget, GuiButtonTypeRight, "More", nfc_scene_passport_read_auth_widget_callback, nfc); + */ + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_passport_read_auth_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + nfc->dev->dev_data.mrtd_data.auth_success = false; + nfc->dev->dev_data.mrtd_data.auth.method = MrtdAuthMethodNone; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + //scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuth); + //consumed = true; + } else if(event.event == GuiButtonTypeRight) { + //scene_manager_next_scene(nfc->scene_manager, NfcScenePassportMenu); + //consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_passport_read_auth_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 13d5967a4..7b877d645 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,47 @@ 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); + // Set unlock password input to 0xFFFFFFFF only on fresh read + memset(nfc->byte_input_store, 0xFF, 4); 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 == NfcWorkerEventReadPassport) { + notification_message(nfc->notifications, &sequence_success); + FURI_LOG_D( + "NFC", + "Read passport, auth: %d, success: %d", + nfc->dev->dev_data.mrtd_data.auth.method, + nfc->dev->dev_data.mrtd_data.auth_success); + if(nfc->dev->dev_data.mrtd_data.auth_success) { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadAuthSuccess); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadSuccess); + } 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_read_card_type.c b/applications/main/nfc/scenes/nfc_scene_read_card_type.c new file mode 100644 index 000000000..94262aa1e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_read_card_type.c @@ -0,0 +1,97 @@ +#include "../nfc_i.h" +#include "nfc_worker_i.h" + +enum SubmenuIndex { + SubmenuIndexReadMifareClassic, + SubmenuIndexReadMifareDesfire, + SubmenuIndexReadMfUltralight, + SubmenuIndexReadEMV, + SubmenuIndexReadNFCA, +}; + +void nfc_scene_read_card_type_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_read_card_type_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, + "Read Mifare Classic", + SubmenuIndexReadMifareClassic, + nfc_scene_read_card_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Read Mifare DESFire", + SubmenuIndexReadMifareDesfire, + nfc_scene_read_card_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Read NTAG/Ultralight", + SubmenuIndexReadMfUltralight, + nfc_scene_read_card_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Read EMV card", + SubmenuIndexReadEMV, + nfc_scene_read_card_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Read NFC-A data", + SubmenuIndexReadNFCA, + nfc_scene_read_card_type_submenu_callback, + nfc); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadCardType); + submenu_set_selected_item(submenu, state); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexReadMifareClassic) { + nfc->dev->dev_data.read_mode = NfcReadModeMfClassic; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + if(event.event == SubmenuIndexReadMifareDesfire) { + nfc->dev->dev_data.read_mode = NfcReadModeMfDesfire; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + if(event.event == SubmenuIndexReadMfUltralight) { + nfc->dev->dev_data.read_mode = NfcReadModeMfUltralight; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + if(event.event == SubmenuIndexReadEMV) { + nfc->dev->dev_data.read_mode = NfcReadModeEMV; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + if(event.event == SubmenuIndexReadNFCA) { + nfc->dev->dev_data.read_mode = NfcReadModeNFCA; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, event.event); + } + return consumed; +} + +void nfc_scene_read_card_type_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index e807e3d7f..43d476ea7 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; @@ -61,8 +62,15 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; } strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); - if(nfc_device_save(nfc->dev, nfc->text_store)) { + if(nfc_save_file(nfc)) { 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..bd7c9817e 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; @@ -33,6 +31,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSavedMenu); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcScenePassportAuth)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcScenePassportAuth); } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index fe65b5b8a..a8a850ece 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -1,12 +1,18 @@ #include "../nfc_i.h" +#include enum SubmenuIndex { SubmenuIndexEmulate, SubmenuIndexEditUid, + SubmenuIndexDetectReader, + SubmenuIndexWrite, + SubmenuIndexUpdate, SubmenuIndexRename, SubmenuIndexDelete, SubmenuIndexInfo, SubmenuIndexRestoreOriginal, + SubmenuIndexMfUlUnlockByReader, + SubmenuIndexMfUlUnlockByPassword, }; void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -20,7 +26,8 @@ void nfc_scene_saved_menu_on_enter(void* context) { Submenu* submenu = nfc->submenu; if(nfc->dev->format == NfcDeviceSaveFormatUid || - nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { + nfc->dev->format == NfcDeviceSaveFormatMifareDesfire || + nfc->dev->format == NfcDeviceSaveFormatBankCard) { submenu_add_item( submenu, "Emulate UID", @@ -41,8 +48,45 @@ 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->format == NfcDeviceSaveFormatMifareUl && + !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockByReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock With Password", + SubmenuIndexMfUlUnlockByPassword, + nfc_scene_saved_menu_submenu_callback, + nfc); + } if(nfc->dev->shadow_file_exist) { submenu_add_item( submenu, @@ -76,6 +120,16 @@ 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 == 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); @@ -105,6 +159,12 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexRestoreOriginal) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByPassword) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 9622ba213..5f0f52f6e 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,10 +29,9 @@ 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)) { + if(nfc_save_file(nfc)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); consumed = true; } @@ -43,6 +41,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { } } } + return consumed; } diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 1a9051dfd..f8b9f52c6 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -1,4 +1,6 @@ #include "../nfc_i.h" +#include "nfc_worker_i.h" +#include enum SubmenuIndex { SubmenuIndexRead, @@ -46,12 +48,16 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + nfc->dev->dev_data.read_mode = NfcReadModeAuto; 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) { + nfc_device_data_clear(&nfc->dev->dev_data); 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/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index 2dbb4338b..e5951beb2 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -1,7 +1,9 @@ #include "detect_reader.h" - +#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/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c index f821feaa9..dbf34c970 100644 --- a/applications/main/subghz/helpers/subghz_chat.c +++ b/applications/main/subghz/helpers/subghz_chat.c @@ -59,11 +59,8 @@ SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) { instance->cli = cli; - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzChat"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_chat_worker_thread); + instance->thread = + furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance); instance->subghz_txrx = subghz_tx_rx_worker_alloc(); instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent)); return instance; diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 88aa4a18c..c4140b158 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -72,6 +72,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { .frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0}; float rssi = 0; uint32_t frequency = 0; + float rssi_temp = 0; + uint32_t frequency_temp = 0; CC1101Status status; //Start CC1101 @@ -195,6 +197,9 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { TAG, "=:%lu:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine); instance->sample_hold_counter = 20; + rssi_temp = frequency_rssi.rssi_fine; + frequency_temp = frequency_rssi.frequency_fine; + if(instance->filVal) { frequency_rssi.frequency_fine = subghz_frequency_analyzer_worker_expRunningAverageAdaptive( @@ -203,7 +208,10 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // Deliver callback if(instance->pair_callback) { instance->pair_callback( - instance->context, frequency_rssi.frequency_fine, frequency_rssi.rssi_fine); + instance->context, + frequency_rssi.frequency_fine, + frequency_rssi.rssi_fine, + true); } } else if( // Deliver results coarse (frequency_rssi.rssi_coarse > instance->trigger_level) && @@ -215,6 +223,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { (double)frequency_rssi.rssi_coarse); instance->sample_hold_counter = 20; + rssi_temp = frequency_rssi.rssi_coarse; + frequency_temp = frequency_rssi.frequency_coarse; if(instance->filVal) { frequency_rssi.frequency_coarse = subghz_frequency_analyzer_worker_expRunningAverageAdaptive( @@ -225,14 +235,22 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { instance->pair_callback( instance->context, frequency_rssi.frequency_coarse, - frequency_rssi.rssi_coarse); + frequency_rssi.rssi_coarse, + true); } } else { if(instance->sample_hold_counter > 0) { instance->sample_hold_counter--; + if(instance->sample_hold_counter == 18) { + if(instance->pair_callback) { + instance->pair_callback( + instance->context, frequency_temp, rssi_temp, false); + } + } } else { instance->filVal = 0; - if(instance->pair_callback) instance->pair_callback(instance->context, 0, 0); + if(instance->pair_callback) + instance->pair_callback(instance->context, 0, 0, false); } } } @@ -256,7 +274,8 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont SubGhz* subghz = context; instance->setting = subghz->setting; - instance->trigger_level = SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD; + instance->trigger_level = subghz->last_settings->frequency_analyzer_trigger; + //instance->trigger_level = SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD; return instance; } diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h index 3b93f60ad..eba4409ce 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h @@ -5,8 +5,11 @@ typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker; -typedef void ( - *SubGhzFrequencyAnalyzerWorkerPairCallback)(void* context, uint32_t frequency, float rssi); +typedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)( + void* context, + uint32_t frequency, + float rssi, + bool signal); typedef struct { uint32_t frequency_coarse; diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 906879020..a981342b5 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -72,18 +72,7 @@ typedef enum { SubGhzViewIdTestPacket, } SubGhzViewId; -struct SubGhzPresetDefinition { - FuriString* name; - uint32_t frequency; - uint8_t* data; - size_t data_size; -}; - -typedef struct SubGhzPresetDefinition SubGhzPresetDefinition; - typedef enum { SubGhzViewReceiverModeLive, SubGhzViewReceiverModeFile, } SubGhzViewReceiverMode; - -#define SUBGHZ_HISTORY_REMOVE_SAVED_ITEMS 1 diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 9a07be5f5..ca6c49b66 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -44,14 +44,7 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event void subghz_scene_delete_success_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); + + popup_reset(popup); } diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index 1a0992f94..bed044244 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -1,8 +1,19 @@ #include "../subghz_i.h" -#include +#include "../views/subghz_frequency_analyzer.h" #define TAG "SubGhzSceneFrequencyAnalyzer" +static const NotificationSequence sequence_saved = { + &message_blink_stop, + &message_blue_0, + &message_green_255, + &message_red_0, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + NULL, +}; + void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) { furi_assert(context); SubGhz* subghz = context; @@ -11,7 +22,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); subghz_frequency_analyzer_feedback_level( @@ -24,7 +34,28 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) { bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventViewReceiverOK) { + if(event.event == SubGhzCustomEventSceneAnalyzerLock) { + notification_message(subghz->notifications, &sequence_set_green_255); + switch(subghz_frequency_analyzer_feedback_level( + subghz->subghz_frequency_analyzer, + SubGHzFrequencyAnalyzerFeedbackLevelAll, + false)) { + case SubGHzFrequencyAnalyzerFeedbackLevelAll: + notification_message(subghz->notifications, &sequence_success); + break; + case SubGHzFrequencyAnalyzerFeedbackLevelVibro: + notification_message(subghz->notifications, &sequence_single_vibro); + break; + case SubGHzFrequencyAnalyzerFeedbackLevelMute: + break; + } + notification_message(subghz->notifications, &sequence_display_backlight_on); + return true; + } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) { + notification_message(subghz->notifications, &sequence_reset_rgb); + return true; + } else if(event.event == SubGhzCustomEventViewReceiverOK) { + notification_message(subghz->notifications, &sequence_saved); uint32_t frequency = subghz_frequency_analyzer_get_frequency_to_save(subghz->subghz_frequency_analyzer); if(frequency > 0) { @@ -51,5 +82,7 @@ void subghz_scene_frequency_analyzer_on_exit(void* context) { subghz->last_settings->frequency_analyzer_feedback_level = subghz_frequency_analyzer_feedback_level(subghz->subghz_frequency_analyzer, 0, false); + subghz->last_settings->frequency_analyzer_trigger = + subghz_frequency_analyzer_get_trigger_level(subghz->subghz_frequency_analyzer); subghz_last_settings_save(subghz->last_settings); } diff --git a/applications/main/subghz/scenes/subghz_scene_more_raw.c b/applications/main/subghz/scenes/subghz_scene_more_raw.c index f90d95de6..95d586934 100644 --- a/applications/main/subghz/scenes/subghz_scene_more_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_more_raw.c @@ -46,23 +46,47 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexDelete) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); - return true; + if(subghz_file_available(subghz)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); + return true; + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } } else if(event.event == SubmenuIndexEdit) { - furi_string_reset(subghz->file_path_tmp); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - return true; + if(subghz_file_available(subghz)) { + furi_string_reset(subghz->file_path_tmp); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } } else if(event.event == SubmenuIndexDecode) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDecode); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDecodeRAW); - return true; + if(subghz_file_available(subghz)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDecode); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDecodeRAW); + return true; + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } } } return false; diff --git a/applications/main/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c index 6f04be669..2cfe060c7 100644 --- a/applications/main/subghz/scenes/subghz_scene_need_saving.c +++ b/applications/main/subghz/scenes/subghz_scene_need_saving.c @@ -48,12 +48,7 @@ bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubGhzCustomEventSceneExit) { if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - subghz_preset_init( - subghz, - subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), - subghz->last_settings->frequency, - NULL, - 0); + subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0); scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); } else { diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 329d9b7e5..cfd371651 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -7,6 +7,7 @@ #define RAW_FILE_NAME "R_" #define TAG "SubGhzSceneReadRAW" +#define RAW_THRESHOLD_RSSI_LOW_COUNT 10 bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { bool ret = false; @@ -78,24 +79,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; } @@ -152,13 +162,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { NULL, 0); } else { - subghz_preset_init( - subghz, - subghz_setting_get_preset_name( - subghz->setting, subghz->last_settings->preset), - subghz->last_settings->frequency, - NULL, - 0); + subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0); } if(!scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { @@ -207,20 +211,28 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReadRAWMore: - if(subghz_scene_read_raw_update_filename(subghz)) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); - subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); - consumed = true; + if(subghz_file_available(subghz)) { + if(subghz_scene_read_raw_update_filename(subghz)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); + subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); + consumed = true; + } else { + furi_crash("SubGhz: RAW file name update error."); + } } else { - furi_crash("SubGhz: RAW file name update error."); + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } } break; case SubGhzCustomEventViewReadRAWSendStart: - if(subghz_scene_read_raw_update_filename(subghz)) { + if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { //start send subghz->state_notifications = SubGhzNotificationStateIDLE; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { @@ -232,7 +244,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( @@ -242,6 +259,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz->state_notifications = SubGhzNotificationStateTx; } } + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } } consumed = true; break; @@ -331,6 +354,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { __LL_RTC_CONVERT_BCD2BIN((time >> 8) & 0xFF) // DAY ); //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, strings[0], @@ -356,11 +380,17 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReadRAWSave: - if(subghz_scene_read_raw_update_filename(subghz)) { + if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } } consumed = true; break; @@ -376,7 +406,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.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 87a7c9552..cab19f730 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 #define TAG "SubGhzSceneReceiver" @@ -116,12 +117,7 @@ void subghz_scene_receiver_on_enter(void* context) { str_buff = furi_string_alloc(); if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { - subghz_preset_init( - subghz, - subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), - subghz->last_settings->frequency, - NULL, - 0); + subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0); subghz_history_reset(subghz->txrx->history); subghz->txrx->rx_key_state = SubGhzRxKeyStateStart; } @@ -186,12 +182,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); } else { subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - subghz_preset_init( - subghz, - subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), - subghz->last_settings->frequency, - NULL, - 0); + subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0); scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); } @@ -202,6 +193,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_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index f3664a46a..294cc1bc0 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -1,4 +1,5 @@ #include "../subghz_i.h" +#include #include @@ -11,6 +12,36 @@ enum SubGhzSettingIndex { SubGhzSettingIndexDetectRaw, SubGhzSettingIndexRSSIThreshold, 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 @@ -164,7 +195,7 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); const char* preset_name = subghz_setting_get_preset_name(subghz->setting, index); variable_item_set_current_value_text(item, preset_name); - subghz->last_settings->preset = index; + //subghz->last_settings->preset = index; subghz_preset_init( subghz, @@ -249,6 +280,14 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) }*/ } +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; @@ -356,7 +395,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/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 3713b85c0..e9c849e1e 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 #include #include @@ -31,8 +30,8 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { subghz->txrx->decoder_result, subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); - SubGhzPresetDefinition* preset = - subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen); + SubGhzRadioPreset* preset = + subghz_history_get_radio_preset(subghz->txrx->history, subghz->txrx->idx_menu_chosen); subghz_preset_init( subghz, furi_string_get_cstr(preset->name), @@ -115,7 +114,6 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) { void subghz_scene_receiver_info_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); subghz_scene_receiver_info_draw_widget(subghz); } @@ -172,9 +170,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { //CC1101 Stop RX -> Save subghz->state_notifications = SubGhzNotificationStateIDLE; - if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { - subghz->txrx->hopper_state = SubGhzHopperStateOFF; - } + subghz->txrx->hopper_state = SubGhzHopperStateOFF; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index 4ccc15f9e..0422a37a4 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -42,9 +42,8 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { bool result = false; if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) && (state == SubGhzRpcStateLoaded)) { - subghz_blink_start(subghz); result = subghz_tx_start(subghz, subghz->txrx->fff_data); - result = true; + if(result) subghz_blink_start(subghz); } rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 71d16bc65..d0b2af35e 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 23 @@ -132,6 +133,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 62efa5e41..85be80b2a 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; @@ -74,11 +71,6 @@ void subghz_scene_save_success_on_exit(void* context) { // Clear view Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); + + popup_reset(popup); } diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c index dcb7e6850..1d6f9d70b 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c +++ b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c @@ -67,6 +67,8 @@ bool subghz_scene_set_seed_bft_on_event(void* context, SceneManagerEvent event) flipper_format_write_hex( subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT"); + generated_protocol = true; } else { generated_protocol = false; diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index dd36ad08c..3ee823997 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -4,10 +4,9 @@ #include #include #include -#include #include #include -#include +#include #define TAG "SubGhzSetType" @@ -466,7 +465,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_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 2720b2b94..113e7ae74 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -36,16 +36,10 @@ bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event void subghz_scene_show_error_sub_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); + + popup_reset(popup); + furi_string_reset(subghz->error_str); notification_message(subghz->notifications, &sequence_reset_rgb); diff --git a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c index 18608dc5b..febbea5fa 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c +++ b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c @@ -39,14 +39,7 @@ bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) void subghz_scene_show_only_rx_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); + + popup_reset(popup); } diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index cf8cd2499..03e2f9b06 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 #include @@ -96,6 +97,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) { diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 2d172de67..dff4f8384 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -52,7 +52,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); @@ -80,6 +79,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/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 602268024..24a744199 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -3,6 +3,7 @@ #include #include #include "subghz_i.h" +#include #define TAG "SubGhzApp" @@ -190,8 +191,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { // Load last used values for Read, Read RAW, etc. or default if(!alloc_for_tx_only) { subghz->last_settings = subghz_last_settings_alloc(); - subghz_last_settings_load( - subghz->last_settings, subghz_setting_get_preset_count(subghz->setting)); + subghz_last_settings_load(subghz->last_settings, 0); #if FURI_DEBUG #ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING FURI_LOG_D( @@ -213,15 +213,10 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { //init Worker & Protocol & History & KeyBoard subghz->lock = SubGhzLockOff; subghz->txrx = malloc(sizeof(SubGhzTxRx)); - subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition)); + subghz->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); subghz->txrx->preset->name = furi_string_alloc(); if(!alloc_for_tx_only) { - subghz_preset_init( - subghz, - subghz_setting_get_preset_name(subghz->setting, subghz->last_settings->preset), - subghz->last_settings->frequency, - NULL, - 0); + subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0); } else { subghz_preset_init( subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); @@ -233,6 +228,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->txrx->history = subghz_history_alloc(); } + subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN; subghz->txrx->worker = subghz_worker_alloc(); subghz->txrx->fff_data = flipper_format_string_alloc(); @@ -243,7 +239,8 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); - + subghz_environment_set_protocol_registry( + subghz->txrx->environment, (void*)&subghz_protocol_registry); subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); #ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING subghz_last_settings_set_detect_raw_values(subghz); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index e378eaccd..b93dd9d7c 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "helpers/subghz_chat.h" @@ -159,6 +160,7 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string)); SubGhzEnvironment* environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); subghz_transmitter_deserialize(transmitter, flipper_format); @@ -252,6 +254,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -292,7 +295,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_hal_power_suppress_charge_exit(); - printf("\r\nPackets recieved %u\r\n", instance->packet_count); + printf("\r\nPackets received %u\r\n", instance->packet_count); // Cleanup subghz_receiver_free(receiver); @@ -371,6 +374,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); @@ -399,7 +403,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { } } - printf("\r\nPackets recieved \033[0;32m%u\033[0m\r\n", instance->packet_count); + printf("\r\nPackets received \033[0;32m%u\033[0m\r\n", instance->packet_count); // Cleanup subghz_receiver_free(receiver); @@ -429,7 +433,7 @@ static void subghz_cli_command_print_usage() { printf("\r\n"); printf(" debug cmd:\r\n"); printf("\ttx_carrier \t - Transmit carrier\r\n"); - printf("\trx_carrier \t - Receiv carrier\r\n"); + printf("\trx_carrier \t - Receive carrier\r\n"); printf( "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); printf( diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index a5845d543..e8d3acfd7 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -29,7 +29,7 @@ typedef struct { FuriString* protocol_name; bool is_file; uint8_t type; - SubGhzPresetDefinition* preset; + SubGhzRadioPreset* preset; } SubGhzHistoryItem; ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) @@ -63,9 +63,9 @@ FuriString* subghz_history_generate_temp_filename(uint32_t index) { bool subghz_history_is_tmp_dir_exists(SubGhzHistory* instance) { FileInfo file_info; - storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info); + FS_Error error = storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info); - if(storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info) == FSE_OK) { + if(error == FSE_OK) { if(file_info.flags & FSF_DIRECTORY) { return true; } @@ -123,7 +123,7 @@ void subghz_history_clear_tmp_dir(SubGhzHistory* instance) { } // Stage 2 - create dir if necessary - res = !storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR); + res = storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR); if(!res) { FURI_LOG_E(TAG, "Cannot process temp dir!"); } @@ -194,7 +194,7 @@ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { return item->preset->frequency; } -SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) { +SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); return item->preset; @@ -321,7 +321,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp bool subghz_history_add_to_history( SubGhzHistory* instance, void* context, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(instance); furi_assert(context); @@ -342,7 +342,7 @@ bool subghz_history_add_to_history( FuriString* text; text = furi_string_alloc(); SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); - item->preset = malloc(sizeof(SubGhzPresetDefinition)); + item->preset = malloc(sizeof(SubGhzRadioPreset)); item->type = decoder_base->protocol->type; item->preset->frequency = preset->frequency; item->preset->name = furi_string_alloc(); diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index f14253896..ee1ee1a4d 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -5,7 +5,7 @@ #include #include #include -#include "helpers/subghz_types.h" +#include typedef struct SubGhzHistory SubGhzHistory; @@ -35,7 +35,7 @@ void subghz_history_reset(SubGhzHistory* instance); */ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); -SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx); +SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx); /** Get preset to history[idx] * @@ -95,13 +95,13 @@ uint16_t subghz_history_get_last_index(SubGhzHistory* instance); * * @param instance - SubGhzHistory instance * @param context - SubGhzProtocolCommon context - * @param preset - SubGhzPresetDefinition preset + * @param preset - SubGhzRadioPreset preset * @return bool; */ bool subghz_history_add_to_history( SubGhzHistory* instance, void* context, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data * diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index e3d22ed73..9660fa1aa 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; @@ -497,6 +497,23 @@ bool subghz_rename_file(SubGhz* subghz) { return ret; } +bool subghz_file_available(SubGhz* subghz) { + furi_assert(subghz); + bool ret = true; + Storage* storage = furi_record_open(RECORD_STORAGE); + + FS_Error fs_result = + storage_common_stat(storage, furi_string_get_cstr(subghz->file_path), NULL); + + if(fs_result != FSE_OK) { + dialog_message_show_storage_error(subghz->dialogs, "File not available\n file/directory"); + ret = false; + } + + furi_record_close(RECORD_STORAGE); + return ret; +} + bool subghz_delete_file(SubGhz* subghz) { furi_assert(subghz); @@ -520,12 +537,6 @@ bool subghz_path_is_file(FuriString* path) { } uint32_t subghz_random_serial(void) { - static bool rand_generator_inited = false; - - if(!rand_generator_inited) { - srand(DWT->CYCCNT); - rand_generator_inited = true; - } return (uint32_t)rand(); } diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index e7b03e94f..dd511105e 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -1,6 +1,7 @@ #pragma once #include "helpers/subghz_types.h" +#include #include "subghz.h" #include "views/receiver.h" #include "views/transmitter.h" @@ -12,9 +13,8 @@ #include "views/subghz_test_static.h" #include "views/subghz_test_packet.h" #endif -// #include -// #include #include +#include #include #include #include @@ -26,16 +26,13 @@ #include #include - #include - #include - +#include #include #include #include "subghz_history.h" -#include "subghz_setting.h" #include "subghz_last_settings.h" #include @@ -68,7 +65,7 @@ struct SubGhzTxRx { FlipperFormat* fff_data; SecureData* secure_data; - SubGhzPresetDefinition* preset; + SubGhzRadioPreset* preset; SubGhzHistory* history; uint16_t idx_menu_chosen; SubGhzTxRxState txrx_state; @@ -76,6 +73,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; @@ -153,6 +153,7 @@ bool subghz_save_protocol_to_file( const char* dev_file_name); bool subghz_load_protocol_from_file(SubGhz* subghz); bool subghz_rename_file(SubGhz* subghz); +bool subghz_file_available(SubGhz* subghz); bool subghz_delete_file(SubGhz* subghz); void subghz_file_name_clear(SubGhz* subghz); bool subghz_path_is_file(FuriString* path); diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index bf3638908..478e32347 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -14,6 +14,7 @@ #define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1 #define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000 #define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2 +#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER -93.0f #ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING #define SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW 0 @@ -21,8 +22,9 @@ #endif #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY "Frequency" -#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset" +//#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset" #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel" +#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -35,6 +37,7 @@ void subghz_last_settings_free(SubGhzLastSettings* instance) { } void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count) { + UNUSED(preset_count); furi_assert(instance); #ifdef FURI_DEBUG FURI_LOG_I(TAG, "subghz_last_settings_load"); @@ -45,16 +48,19 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count uint32_t temp_frequency = 0; uint32_t temp_frequency_analyzer_feedback_level = 0; - int32_t temp_preset = 0; + float temp_frequency_analyzer_trigger = 0; + //int32_t temp_preset = 0; bool frequency_analyzer_feedback_level_was_read = false; + bool frequency_analyzer_trigger_was_read = false; #ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING uint32_t temp_read_raw = 0; #endif if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH && flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) { + /* flipper_format_read_int32( - fff_data_file, SUBGHZ_LAST_SETTING_FIELD_PRESET, (int32_t*)&temp_preset, 1); + fff_data_file, SUBGHZ_LAST_SETTING_FIELD_PRESET, (int32_t*)&temp_preset, 1);*/ flipper_format_read_uint32( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, (uint32_t*)&temp_frequency, 1); frequency_analyzer_feedback_level_was_read = flipper_format_read_uint32( @@ -62,6 +68,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL, (uint32_t*)&temp_frequency_analyzer_feedback_level, 1); + frequency_analyzer_trigger_was_read = flipper_format_read_float( + fff_data_file, + SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER, + (float*)&temp_frequency_analyzer_trigger, + 1); #ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING flipper_format_read_uint32( fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, (uint32_t*)&temp_read_raw, 1); @@ -76,6 +87,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET; instance->frequency_analyzer_feedback_level = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; + instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; #ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING instance->detect_raw = SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW; #endif @@ -85,16 +97,20 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count frequency_analyzer_feedback_level_was_read ? temp_frequency_analyzer_feedback_level : SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; + + instance->frequency_analyzer_trigger = frequency_analyzer_trigger_was_read ? + temp_frequency_analyzer_trigger : + SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; #ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING instance->detect_raw = temp_read_raw; #endif - if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) { - FURI_LOG_W(TAG, "Last used preset no found"); - instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET; - } else { + /*if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) { + FURI_LOG_W(TAG, "Last used preset no found");*/ + instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET; + /*/} else { instance->preset = temp_preset; - } + }*/ } flipper_format_file_close(fff_data_file); @@ -125,10 +141,11 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { file, SUBGHZ_LAST_SETTING_FILE_TYPE, SUBGHZ_LAST_SETTING_FILE_VERSION)) break; + /* if(!flipper_format_insert_or_update_int32( file, SUBGHZ_LAST_SETTING_FIELD_PRESET, &instance->preset, 1)) { break; - } + }*/ if(!flipper_format_insert_or_update_uint32( file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, &instance->frequency, 1)) { break; @@ -140,6 +157,13 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { 1)) { break; } + if(!flipper_format_insert_or_update_float( + file, + SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER, + &instance->frequency_analyzer_trigger, + 1)) { + break; + } #ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING if(!flipper_format_insert_or_update_uint32( file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, &instance->detect_raw, 1)) { diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index cb6a058fe..ef3674d69 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -20,6 +20,7 @@ typedef struct { #endif int32_t preset; uint32_t frequency_analyzer_feedback_level; + float frequency_analyzer_trigger; } SubGhzLastSettings; SubGhzLastSettings* subghz_last_settings_alloc(void); diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 30372a51d..ebbcc2453 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -30,9 +30,9 @@ typedef struct SubGhzReceiverHistory SubGhzReceiverHistory; static const Icon* ReceiverItemIcons[] = { [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, - [SubGhzProtocolTypeStatic] = &I_Unlock_7x8, - [SubGhzProtocolTypeDynamic] = &I_Lock_7x8, - [SubGhzProtocolTypeRAW] = &I_Unlock_7x8, + [SubGhzProtocolTypeStatic] = &I_Static_9x7, + [SubGhzProtocolTypeDynamic] = &I_Dynamic_9x7, + [SubGhzProtocolTypeRAW] = &I_Raw_9x7, }; typedef enum { @@ -217,7 +217,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } else { canvas_set_color(canvas, ColorBlack); } - canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); + canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); furi_string_reset(str_buff); } diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index aed21b698..711ee5437 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -15,65 +15,17 @@ #define RSSI_MIN -97 #define RSSI_MAX -60 -#define RSSI_SCALE 2 +#define RSSI_SCALE 2.3 #define TRIGGER_STEP 1 - -static const NotificationSequence sequence_hw_blink = { - &message_blink_start_10, - &message_blink_set_color_cyan, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_hw_blink_stop = { - &message_blink_stop, - NULL, -}; - -static const NotificationSequence sequence_saved = { - &message_blink_stop, - &message_blue_0, - &message_green_255, - &message_red_0, - &message_vibro_on, - &message_delay_100, - &message_vibro_off, - NULL, -}; - -static const NotificationSequence sequence_frequency = { - &message_display_backlight_on, - &message_green_255, - &message_vibro_on, - &message_delay_100, - &message_green_0, - &message_blue_255, - &message_vibro_off, - &message_delay_100, - &message_blue_0, - &message_green_255, - &message_vibro_on, - &message_delay_100, - &message_green_0, - &message_vibro_off, - NULL, -}; - -//static const NotificationSequence sequence_not_saved = { -// &message_blink_stop, -// &message_green_255, -// &message_blue_255, -// &message_red_255, -// NULL, -//}; +#define MAX_HISTORY 4 static const uint32_t subghz_frequency_list[] = { - 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, - 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, - 314350000, 315000000, 318000000, 345000000, 348000000, 387000000, 390000000, - 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, - 434176948, 434420000, 434775000, 438900000, 464000000, 779000000, 868350000, - 868400000, 868950000, 906400000, 915000000, 925000000, 928000000}; + 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000, + 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000, + 315000000, 318000000, 330000000, 345000000, 348000000, 387000000, 390000000, 418000000, + 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, 434176948, + 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, 868350000, + 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, @@ -85,21 +37,27 @@ struct SubGhzFrequencyAnalyzer { SubGhzFrequencyAnalyzerCallback callback; void* context; bool locked; - uint8_t feedback_level; // 0 - no feedback, 1 - vibro only, 2 - vibro and sound + SubGHzFrequencyAnalyzerFeedbackLevel + feedback_level; // 0 - no feedback, 1 - vibro only, 2 - vibro and sound float rssi_last; - uint32_t frequency_last; - uint32_t frequency_last_vis; - NotificationApp* notifications; + uint8_t selected_index; + uint8_t max_index; + bool show_frame; }; typedef struct { uint32_t frequency; - uint32_t frequency_last; uint32_t frequency_to_save; float rssi; + uint32_t history_frequency[MAX_HISTORY]; + uint8_t history_frequency_rx_count[MAX_HISTORY]; + bool signal; float rssi_last; float trigger; - uint8_t feedback_level; + SubGHzFrequencyAnalyzerFeedbackLevel feedback_level; + uint8_t selected_index; + uint8_t max_index; + bool show_frame; } SubGhzFrequencyAnalyzerModel; void subghz_frequency_analyzer_set_callback( @@ -121,23 +79,27 @@ void subghz_frequency_analyzer_draw_rssi( uint8_t y) { // Current RSSI if(rssi) { - if(rssi > RSSI_MAX) rssi = RSSI_MAX; + if(rssi > RSSI_MAX) { + rssi = RSSI_MAX; + } rssi = (rssi - RSSI_MIN) / RSSI_SCALE; uint8_t column_number = 0; for(size_t i = 0; i <= (uint8_t)rssi; i++) { if((i + 1) % 4) { column_number++; - canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number); + canvas_draw_box(canvas, x + 2 * i, (y + 4) - column_number, 2, column_number); } } } // Last RSSI if(rssi_last) { - if(rssi_last > RSSI_MAX) rssi_last = RSSI_MAX; + if(rssi_last > RSSI_MAX) { + rssi_last = RSSI_MAX; + } int max_x = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE) * 2; //if(!(max_x % 8)) max_x -= 2; - int max_h = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE) + 4; + int max_h = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE) + 1; max_h -= (max_h / 4) + 3; canvas_draw_line(canvas, x + max_x + 1, y - max_h, x + max_x + 1, y + 3); } @@ -151,18 +113,66 @@ void subghz_frequency_analyzer_draw_rssi( canvas_draw_line(canvas, x, y + 3, x + (RSSI_MAX - RSSI_MIN) * 2 / RSSI_SCALE, y + 3); } +static void subghz_frequency_analyzer_history_frequency_draw( + Canvas* canvas, + SubGhzFrequencyAnalyzerModel* model) { + char buffer[64]; + const uint8_t x1 = 2; + const uint8_t x2 = 66; + const uint8_t y = 37; + + canvas_set_font(canvas, FontSecondary); + uint8_t line = 0; + bool show_frame = model->show_frame && model->max_index > 0; + for(uint8_t i = 0; i < MAX_HISTORY; i++) { + uint8_t current_x; + uint8_t current_y = y + line * 11; + + if(i % 2 == 0) { + current_x = x1; + } else { + current_x = x2; + line++; + } + if(model->history_frequency[i]) { + snprintf( + buffer, + sizeof(buffer), + "%03ld.%03ld", + model->history_frequency[i] / 1000000 % 1000, + model->history_frequency[i] / 1000 % 1000); + canvas_draw_str(canvas, current_x, current_y, buffer); + } else { + canvas_draw_str(canvas, current_x, current_y, "---.---"); + } + if(model->history_frequency_rx_count[i] > 0) { + snprintf(buffer, sizeof(buffer), "x%d", model->history_frequency_rx_count[i]); + canvas_draw_str(canvas, current_x + 41, current_y, buffer); + } else { + canvas_draw_str(canvas, current_x + 41, current_y, "MHz"); + } + + if(show_frame && i == model->selected_index) { + elements_frame(canvas, current_x - 2, current_y - 9, 63, 11); + } + } +} + void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { char buffer[64]; // Title canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); + canvas_draw_str(canvas, 20, 7, "Frequency Analyzer"); // RSSI canvas_draw_str(canvas, 33, 62, "RSSI"); subghz_frequency_analyzer_draw_rssi( - canvas, model->rssi, model->rssi_last, model->trigger, 57, 58); + canvas, model->rssi, model->rssi_last, model->trigger, 56, 57); + + // Last detected frequency + subghz_frequency_analyzer_history_frequency_draw(canvas, model); // Frequency canvas_set_font(canvas, FontBigNumbers); @@ -172,41 +182,40 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel "%03ld.%03ld", model->frequency / 1000000 % 1000, model->frequency / 1000 % 1000); - canvas_draw_str(canvas, 8, 30, buffer); - canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11); - - // Last detected frequency - canvas_set_font(canvas, FontSecondary); - if(model->frequency_last) { - snprintf( - buffer, - sizeof(buffer), - "Last: %03ld.%03ld MHz", - model->frequency_last / 1000000 % 1000, - model->frequency_last / 1000 % 1000); - } else { - strcpy(buffer, "Last: ---.--- MHz"); - } - canvas_draw_str(canvas, 9, 42, buffer); - - switch(model->feedback_level) { - case 2: - canvas_draw_icon(canvas, 128 - 8 - 1, 1, &I_Volup_8x6); - break; - case 1: - canvas_draw_icon(canvas, 128 - 8 - 1, 1, &I_Voldwn_6x6); - break; - case 0: - canvas_draw_icon(canvas, 128 - 8 - 1, 1, &I_Voldwn_6x6); + if(model->signal) { + canvas_draw_box(canvas, 4, 10, 121, 19); canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 128 - 2 - 1 - 2, 1, 2, 6); + } else { + // TODO: Disable this + //canvas_draw_box(canvas, 4, 11, 121, 19); + //canvas_set_color(canvas, ColorWhite); + } + + canvas_draw_str(canvas, 8, 26, buffer); + canvas_draw_icon(canvas, 96, 15, &I_MHz_25x11); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + const uint8_t icon_x = 119; + switch(model->feedback_level) { + case SubGHzFrequencyAnalyzerFeedbackLevelAll: + canvas_draw_icon(canvas, icon_x, 1, &I_Volup_8x6); + break; + case SubGHzFrequencyAnalyzerFeedbackLevelVibro: + canvas_draw_icon(canvas, icon_x, 1, &I_Voldwn_6x6); + break; + case SubGHzFrequencyAnalyzerFeedbackLevelMute: + canvas_draw_icon(canvas, icon_x, 1, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 123, 1, 2, 6); canvas_set_color(canvas, ColorBlack); break; } // Buttons hint + canvas_set_font(canvas, FontSecondary); elements_button_left(canvas, "T-"); - elements_button_right(canvas, "T+"); + elements_button_right(canvas, "+T"); } uint32_t subghz_frequency_find_correct(uint32_t input) { @@ -242,7 +251,9 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { bool need_redraw = false; - if(event->key == InputKeyBack) return false; + if(event->key == InputKeyBack) { + return false; + } if(((event->type == InputTypePress) || (event->type == InputTypeRepeat)) && ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) { @@ -251,61 +262,100 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { switch(event->key) { case InputKeyLeft: trigger_level -= TRIGGER_STEP; - if(trigger_level < RSSI_MIN) trigger_level = RSSI_MIN; + if(trigger_level < RSSI_MIN) { + trigger_level = RSSI_MIN; + } break; default: case InputKeyRight: trigger_level += TRIGGER_STEP; - if(trigger_level > RSSI_MAX) trigger_level = RSSI_MAX; + if(trigger_level > RSSI_MAX) { + trigger_level = RSSI_MAX; + } break; } subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, trigger_level); FURI_LOG_I(TAG, "trigger = %.1f", (double)trigger_level); need_redraw = true; - } - - if(event->type == InputTypePress && event->key == InputKeyDown) { + } else if(event->type == InputTypePress && event->key == InputKeyUp) { if(instance->feedback_level == 0) { instance->feedback_level = 2; } else { instance->feedback_level--; } +#ifdef FURI_DEBUG FURI_LOG_D(TAG, "feedback_level = %d", instance->feedback_level); +#endif + need_redraw = true; + } else if( + ((event->type == InputTypePress) || (event->type == InputTypeRepeat)) && + event->key == InputKeyDown) { + instance->show_frame = instance->max_index > 0; + if(instance->show_frame) { + instance->selected_index = (instance->selected_index + 1) % instance->max_index; + need_redraw = true; + } + } else if(event->key == InputKeyOk) { need_redraw = true; - } - - if(event->key == InputKeyOk) { bool updated = false; + uint32_t frequency_to_save = 0; with_view_model( instance->view, SubGhzFrequencyAnalyzerModel * model, { - uint32_t prev_freq_to_save = model->frequency_to_save; - uint32_t frequency_candidate = 0; - if(model->frequency != 0) { - frequency_candidate = model->frequency; - } else if(model->frequency_last != 0) { - frequency_candidate = model->frequency_last; - } - if(frequency_candidate == 0 || - !furi_hal_subghz_is_frequency_valid(frequency_candidate) || - prev_freq_to_save == frequency_candidate) { - frequency_candidate = 0; - } else { - frequency_candidate = subghz_frequency_find_correct(frequency_candidate); - } - if(frequency_candidate > 0 && frequency_candidate != model->frequency_to_save) { + frequency_to_save = model->frequency_to_save; + if(model->show_frame && !model->signal) { + uint32_t prev_freq_to_save = model->frequency_to_save; + uint32_t frequency_candidate = model->history_frequency[model->selected_index]; + if(frequency_candidate == 0 || + !furi_hal_subghz_is_frequency_valid(frequency_candidate) || + prev_freq_to_save == frequency_candidate) { + frequency_candidate = 0; + } else { + frequency_candidate = subghz_frequency_find_correct(frequency_candidate); + } + if(frequency_candidate > 0 && + frequency_candidate != model->frequency_to_save) { #ifdef FURI_DEBUG - FURI_LOG_D( - TAG, - "frequency_to_save: %ld, candidate: %ld", - model->frequency_to_save, - frequency_candidate); + FURI_LOG_D( + TAG, + "frequency_to_save: %ld, candidate: %ld", + model->frequency_to_save, + frequency_candidate); #endif - model->frequency_to_save = frequency_candidate; - notification_message(instance->notifications, &sequence_saved); - notification_message(instance->notifications, &sequence_hw_blink); - updated = true; + model->frequency_to_save = frequency_candidate; + updated = true; + } + } else if(model->show_frame && model->signal) { + uint32_t prev_freq_to_save = model->frequency_to_save; + uint32_t frequency_candidate = subghz_frequency_find_correct(model->frequency); + if(frequency_candidate == 0 || + !furi_hal_subghz_is_frequency_valid(frequency_candidate) || + prev_freq_to_save == frequency_candidate) { + frequency_candidate = 0; + } else { + frequency_candidate = subghz_frequency_find_correct(frequency_candidate); + } + if(frequency_candidate > 0 && + frequency_candidate != model->frequency_to_save) { + model->frequency_to_save = frequency_candidate; + updated = true; + } + } else if(!model->show_frame && model->signal) { + uint32_t prev_freq_to_save = model->frequency_to_save; + uint32_t frequency_candidate = subghz_frequency_find_correct(model->frequency); + if(frequency_candidate == 0 || + !furi_hal_subghz_is_frequency_valid(frequency_candidate) || + prev_freq_to_save == frequency_candidate) { + frequency_candidate = 0; + } else { + frequency_candidate = subghz_frequency_find_correct(frequency_candidate); + } + if(frequency_candidate > 0 && + frequency_candidate != model->frequency_to_save) { + model->frequency_to_save = frequency_candidate; + updated = true; + } } }, true); @@ -324,17 +374,15 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { } // First device receive short, then when user release button we get long - if(event->type == InputTypeLong) { + if(event->type == InputTypeLong && frequency_to_save > 0) { #ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Longpress!"); + FURI_LOG_I(TAG, "Long press!"); #endif - // Stop blinking - notification_message(instance->notifications, &sequence_hw_blink_stop); - // Stop worker if(subghz_frequency_analyzer_worker_is_running(instance->worker)) { subghz_frequency_analyzer_worker_stop(instance->worker); } + instance->callback(SubGhzCustomEventViewReceiverUnlock, instance->context); } } @@ -346,10 +394,12 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { SubGhzFrequencyAnalyzerModel * model, { model->rssi_last = instance->rssi_last; - model->frequency_last = instance->frequency_last; model->trigger = subghz_frequency_analyzer_worker_get_trigger_level(instance->worker); model->feedback_level = instance->feedback_level; + model->max_index = instance->max_index; + model->show_frame = instance->show_frame; + model->selected_index = instance->selected_index; }, true); } @@ -369,43 +419,91 @@ uint32_t round_int(uint32_t value, uint8_t n) { return value; } -void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) { - furi_assert(context); +void subghz_frequency_analyzer_pair_callback( + void* context, + uint32_t frequency, + float rssi, + bool signal) { SubGhzFrequencyAnalyzer* instance = context; - if((rssi == 0.f) && (instance->locked)) { - notification_message(instance->notifications, &sequence_hw_blink); - instance->frequency_last_vis = instance->frequency_last; - } + if(instance->callback) { + instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); + } + //update history + instance->show_frame = true; + uint8_t max_index = instance->max_index; + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + bool in_array = false; + uint32_t normal_frequency = subghz_frequency_find_correct(model->frequency); + for(size_t i = 0; i < MAX_HISTORY; i++) { + if(model->history_frequency[i] == normal_frequency) { + in_array = true; + if(model->history_frequency[i] > 0) { + if(model->history_frequency_rx_count[i] == 0) { + model->history_frequency_rx_count[i]++; + } + model->history_frequency_rx_count[i]++; + } + if(i > 0) { + size_t offset = 0; + uint8_t temp_rx_count = model->history_frequency_rx_count[i]; - if((rssi != 0.f) && (frequency != 0)) { - // Threre is some signal + for(size_t j = MAX_HISTORY - 1; j > 0; j--) { + if(j == i) { + offset++; + } + model->history_frequency[j] = model->history_frequency[j - offset]; + model->history_frequency_rx_count[j] = + model->history_frequency_rx_count[j - offset]; + } + model->history_frequency[0] = normal_frequency; + model->history_frequency_rx_count[0] = temp_rx_count; + } + + break; + } + } + + if(!in_array) { + model->history_frequency[3] = model->history_frequency[2]; + model->history_frequency[2] = model->history_frequency[1]; + model->history_frequency[1] = model->history_frequency[0]; + model->history_frequency[0] = normal_frequency; + + model->history_frequency_rx_count[3] = model->history_frequency_rx_count[2]; + model->history_frequency_rx_count[2] = model->history_frequency_rx_count[1]; + model->history_frequency_rx_count[1] = model->history_frequency_rx_count[0]; + model->history_frequency_rx_count[0] = 0; + } + + if(max_index < MAX_HISTORY) { + for(size_t i = 0; i < MAX_HISTORY; i++) { + if(model->history_frequency[i] > 0) { + max_index = i + 1; + } + } + } + }, + false); + instance->max_index = max_index; + } else if((rssi != 0.f) && (!instance->locked)) { + // There is some signal FURI_LOG_I(TAG, "rssi = %.2f, frequency = %ld Hz", (double)rssi, frequency); frequency = round_int(frequency, 3); // Round 299999990Hz to 300000000Hz - if(!instance->locked) { - // Triggered! - instance->rssi_last = rssi; - notification_message(instance->notifications, &sequence_hw_blink_stop); - switch(instance->feedback_level) { - case 1: // 1 - only vibro - notification_message(instance->notifications, &sequence_frequency); - break; - case 2: // 2 - vibro and beep - notification_message(instance->notifications, &sequence_success); - break; - default: // 0 - no feedback - notification_message(instance->notifications, &sequence_display_backlight_on); - break; - } + // Triggered! + instance->rssi_last = rssi; + if(instance->callback) { + instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); + } + } - FURI_LOG_D(TAG, "triggered"); - } - // Update values - if(rssi >= instance->rssi_last) { - instance->rssi_last = rssi; - instance->frequency_last = frequency; - } + // Update values + if(rssi >= instance->rssi_last && (frequency != 0)) { + instance->rssi_last = rssi; } instance->locked = (rssi != 0.f); @@ -416,9 +514,12 @@ void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, model->rssi = rssi; model->rssi_last = instance->rssi_last; model->frequency = frequency; - model->frequency_last = instance->frequency_last_vis; + model->signal = signal; model->trigger = subghz_frequency_analyzer_worker_get_trigger_level(instance->worker); model->feedback_level = instance->feedback_level; + model->max_index = instance->max_index; + model->show_frame = instance->show_frame; + model->selected_index = instance->selected_index; }, true); } @@ -427,10 +528,6 @@ void subghz_frequency_analyzer_enter(void* context) { furi_assert(context); SubGhzFrequencyAnalyzer* instance = context; - // Notifications - instance->notifications = furi_record_open(RECORD_NOTIFICATION); - notification_message(instance->notifications, &sequence_hw_blink); - //Start worker instance->worker = subghz_frequency_analyzer_worker_alloc(instance->context); @@ -442,18 +539,29 @@ void subghz_frequency_analyzer_enter(void* context) { subghz_frequency_analyzer_worker_start(instance->worker); instance->rssi_last = 0; - instance->frequency_last = 0; - instance->frequency_last_vis = 0; - subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, RSSI_MIN); + instance->selected_index = 0; + instance->max_index = 0; + instance->show_frame = false; + //subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, RSSI_MIN); with_view_model( instance->view, SubGhzFrequencyAnalyzerModel * model, { + model->selected_index = 0; + model->max_index = 0; + model->show_frame = false; model->rssi = 0; model->rssi_last = 0; model->frequency = 0; - model->frequency_last = 0; + model->history_frequency[3] = 0; + model->history_frequency[2] = 0; + model->history_frequency[1] = 0; + model->history_frequency[0] = 0; + model->history_frequency_rx_count[3] = 0; + model->history_frequency_rx_count[2] = 0; + model->history_frequency_rx_count[1] = 0; + model->history_frequency_rx_count[0] = 0; model->frequency_to_save = 0; model->trigger = RSSI_MIN; }, @@ -464,9 +572,6 @@ void subghz_frequency_analyzer_exit(void* context) { furi_assert(context); SubGhzFrequencyAnalyzer* instance = context; - // Stop blinking - notification_message(instance->notifications, &sequence_hw_blink_stop); - // Stop worker if(subghz_frequency_analyzer_worker_is_running(instance->worker)) { subghz_frequency_analyzer_worker_stop(instance->worker); @@ -478,7 +583,6 @@ void subghz_frequency_analyzer_exit(void* context) { SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer)); - furi_assert(instance); instance->feedback_level = 2; @@ -519,9 +623,9 @@ uint32_t subghz_frequency_analyzer_get_frequency_to_save(SubGhzFrequencyAnalyzer return frequency; } -uint8_t subghz_frequency_analyzer_feedback_level( +SubGHzFrequencyAnalyzerFeedbackLevel subghz_frequency_analyzer_feedback_level( SubGhzFrequencyAnalyzer* instance, - uint8_t level, + SubGHzFrequencyAnalyzerFeedbackLevel level, bool update) { furi_assert(instance); if(update) { @@ -534,4 +638,9 @@ uint8_t subghz_frequency_analyzer_feedback_level( } return instance->feedback_level; +} + +float subghz_frequency_analyzer_get_trigger_level(SubGhzFrequencyAnalyzer* instance) { + furi_assert(instance); + return subghz_frequency_analyzer_worker_get_trigger_level(instance->worker); } \ No newline at end of file diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.h b/applications/main/subghz/views/subghz_frequency_analyzer.h index 196d69c79..95169c08d 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.h +++ b/applications/main/subghz/views/subghz_frequency_analyzer.h @@ -3,6 +3,12 @@ #include #include "../helpers/subghz_custom_event.h" +typedef enum { + SubGHzFrequencyAnalyzerFeedbackLevelAll, + SubGHzFrequencyAnalyzerFeedbackLevelVibro, + SubGHzFrequencyAnalyzerFeedbackLevelMute +} SubGHzFrequencyAnalyzerFeedbackLevel; + typedef struct SubGhzFrequencyAnalyzer SubGhzFrequencyAnalyzer; typedef void (*SubGhzFrequencyAnalyzerCallback)(SubGhzCustomEvent event, void* context); @@ -20,7 +26,9 @@ View* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* subghz_static) uint32_t subghz_frequency_analyzer_get_frequency_to_save(SubGhzFrequencyAnalyzer* instance); -uint8_t subghz_frequency_analyzer_feedback_level( +SubGHzFrequencyAnalyzerFeedbackLevel subghz_frequency_analyzer_feedback_level( SubGhzFrequencyAnalyzer* instance, - uint8_t level, + SubGHzFrequencyAnalyzerFeedbackLevel level, bool update); + +float subghz_frequency_analyzer_get_trigger_level(SubGhzFrequencyAnalyzer* instance); \ No newline at end of file diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 759798f90..f5fdefdd5 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -23,11 +23,13 @@ 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; bool raw_send_only; + float raw_threshold_rssi; } SubGhzReadRAWModel; void subghz_read_raw_set_callback( @@ -55,21 +57,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; @@ -188,24 +196,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, @@ -221,6 +258,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); @@ -281,8 +336,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); } } @@ -440,7 +496,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) { @@ -454,6 +511,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; @@ -544,6 +602,7 @@ SubGhzReadRAW* subghz_read_raw_alloc(bool raw_send_only) { model->file_name = furi_string_alloc(); model->raw_send_only = raw_send_only; 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 a4e16ca98..43afac427 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/applications/main/u2f/application.fam b/applications/main/u2f/application.fam index 5e825c1bf..d2e695a3f 100644 --- a/applications/main/u2f/application.fam +++ b/applications/main/u2f/application.fam @@ -13,4 +13,6 @@ App( order=80, fap_icon="U2FIcon.png", fap_category="Main", + fap_icon_assets="images", + fap_libs=["assets"], ) diff --git a/applications/main/u2f/images/ActiveConnection_50x64.png b/applications/main/u2f/images/ActiveConnection_50x64.png new file mode 100644 index 000000000..1d7686ddd Binary files /dev/null and b/applications/main/u2f/images/ActiveConnection_50x64.png differ diff --git a/applications/main/u2f/images/Auth_62x31.png b/applications/main/u2f/images/Auth_62x31.png new file mode 100644 index 000000000..40f094ac9 Binary files /dev/null and b/applications/main/u2f/images/Auth_62x31.png differ diff --git a/applications/main/u2f/images/Connect_me_62x31.png b/applications/main/u2f/images/Connect_me_62x31.png new file mode 100644 index 000000000..68c48c0e6 Binary files /dev/null and b/applications/main/u2f/images/Connect_me_62x31.png differ diff --git a/applications/main/u2f/images/Connected_62x31.png b/applications/main/u2f/images/Connected_62x31.png new file mode 100644 index 000000000..eeaf660b1 Binary files /dev/null and b/applications/main/u2f/images/Connected_62x31.png differ diff --git a/applications/main/u2f/images/Drive_112x35.png b/applications/main/u2f/images/Drive_112x35.png new file mode 100644 index 000000000..6f7b9c834 Binary files /dev/null and b/applications/main/u2f/images/Drive_112x35.png differ diff --git a/applications/main/u2f/images/Error_62x31.png b/applications/main/u2f/images/Error_62x31.png new file mode 100644 index 000000000..bb280e751 Binary files /dev/null and b/applications/main/u2f/images/Error_62x31.png differ diff --git a/applications/main/u2f/images/SDQuestion_35x43.png b/applications/main/u2f/images/SDQuestion_35x43.png new file mode 100644 index 000000000..9b9c9a58e Binary files /dev/null and b/applications/main/u2f/images/SDQuestion_35x43.png differ diff --git a/applications/main/u2f/u2f_app_i.h b/applications/main/u2f/u2f_app_i.h index 53647859a..c7bac40b4 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/u2f_hid.c b/applications/main/u2f/u2f_hid.c index 4922d6a5a..6e1a51f33 100644 --- a/applications/main/u2f/u2f_hid.c +++ b/applications/main/u2f/u2f_hid.c @@ -58,13 +58,13 @@ struct U2fHid_packet { struct U2fHid { FuriThread* thread; FuriTimer* lock_timer; - struct U2fHid_packet packet; uint8_t seq_id_last; uint16_t req_buf_ptr; uint32_t req_len_left; uint32_t lock_cid; bool lock; U2fData* u2f_instance; + struct U2fHid_packet packet; }; static void u2f_hid_event_callback(HidU2fEvent ev, void* context) { @@ -203,7 +203,7 @@ static int32_t u2f_hid_worker(void* context) { WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest, FuriFlagWaitAny, FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + furi_check(!(flags & FuriFlagError)); if(flags & WorkerEvtStop) break; if(flags & WorkerEvtConnect) { u2f_set_state(u2f_hid->u2f_instance, 1); @@ -215,10 +215,21 @@ static int32_t u2f_hid_worker(void* context) { } if(flags & WorkerEvtRequest) { uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf); - if(len_cur > 0) { + do { + if(len_cur == 0) { + break; + } if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) { + if(len_cur < 7) { + u2f_hid->req_len_left = 0; + break; // Wrong chunk len + } // Init packet u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]); + if(u2f_hid->packet.len > U2F_HID_MAX_PAYLOAD_LEN) { + u2f_hid->req_len_left = 0; + break; // Wrong packet len + } if(u2f_hid->packet.len > (len_cur - 7)) { u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7); len_cur = len_cur - 7; @@ -232,6 +243,10 @@ static int32_t u2f_hid_worker(void* context) { u2f_hid->req_buf_ptr = len_cur; if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur); } else { + if(len_cur < 5) { + u2f_hid->req_len_left = 0; + break; // Wrong chunk len + } // Continuation packet if(u2f_hid->req_len_left > 0) { uint32_t cid_temp = 0; @@ -260,7 +275,7 @@ static int32_t u2f_hid_worker(void* context) { u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD); } } - } + } while(0); } if(flags & WorkerEvtUnlock) { u2f_hid->lock = false; @@ -282,11 +297,7 @@ U2fHid* u2f_hid_start(U2fData* u2f_inst) { u2f_hid->u2f_instance = u2f_inst; - u2f_hid->thread = furi_thread_alloc(); - furi_thread_set_name(u2f_hid->thread, "U2fHidWorker"); - furi_thread_set_stack_size(u2f_hid->thread, 2048); - furi_thread_set_context(u2f_hid->thread, u2f_hid); - furi_thread_set_callback(u2f_hid->thread, u2f_hid_worker); + u2f_hid->thread = furi_thread_alloc_ex("U2fHidWorker", 2048, u2f_hid_worker, u2f_hid); furi_thread_start(u2f_hid->thread); return u2f_hid; } diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index fa3d6cc24..ecb276a3e 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; @@ -36,10 +37,10 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31); canvas_draw_str_aligned( - canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successfull!"); + canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successful!"); } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, &I_Error_62x31); - canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Ceritficate error"); + canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Certificate error"); } } diff --git a/applications/main/unirfremix/application.fam b/applications/main/unirfremix/application.fam index b7b1e59e5..73c063197 100644 --- a/applications/main/unirfremix/application.fam +++ b/applications/main/unirfremix/application.fam @@ -1,13 +1,14 @@ App( appid="SubGHz_Remote", name="Sub-GHz Remote", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.EXTERNAL, entry_point="unirfremix_app", cdefines=["APP_UNIRFREMIX"], requires=["storage", "gui", "dialogs", "subghz"], icon="A_UniRFRemix_14", stack_size=4 * 1024, order=11, - # fap_icon="unirfIcon.png", - # fap_category="Main", + fap_icon="unirfIcon.png", + fap_category="Main", + fap_icon_assets="images", ) diff --git a/applications/main/unirfremix/images/ButtonDown_7x4.png b/applications/main/unirfremix/images/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/applications/main/unirfremix/images/ButtonDown_7x4.png differ diff --git a/applications/main/unirfremix/images/ButtonLeft_4x7.png b/applications/main/unirfremix/images/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/applications/main/unirfremix/images/ButtonLeft_4x7.png differ diff --git a/applications/main/unirfremix/images/ButtonRight_4x7.png b/applications/main/unirfremix/images/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/applications/main/unirfremix/images/ButtonRight_4x7.png differ diff --git a/applications/main/unirfremix/images/ButtonUp_7x4.png b/applications/main/unirfremix/images/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/main/unirfremix/images/ButtonUp_7x4.png differ diff --git a/applications/main/unirfremix/images/Ok_btn_9x9.png b/applications/main/unirfremix/images/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/main/unirfremix/images/Ok_btn_9x9.png differ diff --git a/applications/main/unirfremix/images/Pin_arrow_down_7x9.png b/applications/main/unirfremix/images/Pin_arrow_down_7x9.png new file mode 100644 index 000000000..9687397af Binary files /dev/null and b/applications/main/unirfremix/images/Pin_arrow_down_7x9.png differ diff --git a/applications/main/unirfremix/images/Pin_arrow_left_9x7.png b/applications/main/unirfremix/images/Pin_arrow_left_9x7.png new file mode 100644 index 000000000..fb4ded78f Binary files /dev/null and b/applications/main/unirfremix/images/Pin_arrow_left_9x7.png differ diff --git a/applications/main/unirfremix/images/Pin_arrow_right_9x7.png b/applications/main/unirfremix/images/Pin_arrow_right_9x7.png new file mode 100644 index 000000000..97648d176 Binary files /dev/null and b/applications/main/unirfremix/images/Pin_arrow_right_9x7.png differ diff --git a/applications/main/unirfremix/images/Pin_arrow_up_7x9.png b/applications/main/unirfremix/images/Pin_arrow_up_7x9.png new file mode 100644 index 000000000..a91a6fd5e Binary files /dev/null and b/applications/main/unirfremix/images/Pin_arrow_up_7x9.png differ diff --git a/applications/main/unirfremix/images/Pin_cell_13x13.png b/applications/main/unirfremix/images/Pin_cell_13x13.png new file mode 100644 index 000000000..1b1ff0c2f Binary files /dev/null and b/applications/main/unirfremix/images/Pin_cell_13x13.png differ diff --git a/applications/main/unirfremix/images/Pin_star_7x7.png b/applications/main/unirfremix/images/Pin_star_7x7.png new file mode 100644 index 000000000..42fdea86e Binary files /dev/null and b/applications/main/unirfremix/images/Pin_star_7x7.png differ diff --git a/applications/main/unirfremix/images/back_10px.png b/applications/main/unirfremix/images/back_10px.png new file mode 100644 index 000000000..f9c615a99 Binary files /dev/null and b/applications/main/unirfremix/images/back_10px.png differ diff --git a/applications/main/unirfremix/images/sub1_10px.png b/applications/main/unirfremix/images/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/applications/main/unirfremix/images/sub1_10px.png differ diff --git a/applications/main/unirfremix/unirfremix_app.c b/applications/main/unirfremix/unirfremix_app.c index b9f7aa5e6..cb1fd7871 100644 --- a/applications/main/unirfremix/unirfremix_app.c +++ b/applications/main/unirfremix/unirfremix_app.c @@ -8,23 +8,43 @@ #include #include -#include - #include #include #include #include -#include +#include #include #include #include +#include + #define UNIRFMAP_FOLDER "/ext/subghz/unirf" #define UNIRFMAP_EXTENSION ".txt" #define TAG "UniRF Remix" +static const char* mfname; + +static int kl_type; + +void keeloq_reset_mfname() { + mfname = ""; +} + +void keeloq_reset_kl_type() { + kl_type = 0; +} + +void star_line_reset_mfname() { + mfname = ""; +} + +void star_line_reset_kl_type() { + kl_type = 0; +} + typedef struct { uint32_t frequency; FuriString* name; @@ -58,12 +78,6 @@ typedef struct { FuriString* right_file; FuriString* ok_file; - FuriString* up_l; - FuriString* left_l; - FuriString* right_l; - FuriString* down_l; - FuriString* ok_l; - FuriString* file_path; char* up_label; @@ -89,7 +103,6 @@ typedef struct { int file_result; bool tx_not_allowed; - int file_blank; FuriString* signal; } UniRFRemix; @@ -116,34 +129,7 @@ static char* char_to_str(char* str, int i) { return converted; } -/* -static const char* int_to_char(int number) { - switch(number) { - case 0: - return "0"; - case 1: - return "1"; - case 2: - return "2"; - case 3: - return "3"; - case 4: - return "4"; - case 5: - return "5"; - case 6: - return "6"; - case 7: - return "7"; - case 8: - return "8"; - case 9: - return "9"; - default: - return "0"; - } -} -*/ + //get filename without path static char* extract_filename(const char* name, int len) { FuriString* tmp; @@ -155,6 +141,43 @@ static char* extract_filename(const char* name, int len) { return char_to_str((char*)furi_string_get_cstr(tmp), len); } +static void cfg_read_file_path( + FlipperFormat* fff_file, + FuriString* text_file_path, + char** text_file_label, + const char* read_key, + int* is_enabled) { + if(!flipper_format_read_string(fff_file, read_key, text_file_path)) { + FURI_LOG_W(TAG, "Could not read %s string", read_key); + *text_file_label = "N/A"; + *is_enabled = 0; + } else { + *text_file_label = extract_filename(furi_string_get_cstr(text_file_path), 16); + FURI_LOG_D(TAG, "%s file: %s", read_key, furi_string_get_cstr(text_file_path)); + *is_enabled = 1; + } + flipper_format_rewind(fff_file); +} + +static void cfg_read_file_label( + FlipperFormat* fff_file, + char** text_file_label, + const char* read_key, + bool is_enabled) { + FuriString* temp_label = furi_string_alloc(); + + if(!flipper_format_read_string(fff_file, read_key, temp_label)) { + FURI_LOG_W(TAG, "Could not read %s string", read_key); + } else { + if(is_enabled == 1) { + *text_file_label = char_to_str((char*)furi_string_get_cstr(temp_label), 16); + } + FURI_LOG_D(TAG, "%s label: %s", read_key, *text_file_label); + } + flipper_format_rewind(fff_file); + furi_string_free(temp_label); +} + /* * check that map file exists * assign variables to values within map file @@ -168,16 +191,13 @@ void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - app->file_result = 3; - app->file_blank = 0; + app->file_result = 1; - app->up_enabled = 1; - app->down_enabled = 1; - app->left_enabled = 1; - app->right_enabled = 1; - app->ok_enabled = 1; - - int label_len = 16; + app->up_enabled = 0; + app->down_enabled = 0; + app->left_enabled = 0; + app->right_enabled = 0; + app->ok_enabled = 0; //check that map file exists if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { @@ -187,146 +207,29 @@ void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) { //assign variables to values within map file //set missing filenames to N/A - if(!flipper_format_read_string(fff_data_file, "UP", app->up_file)) { - FURI_LOG_W(TAG, "Could not read UP string"); - //increment file_blank for processing later - app->file_blank++; - //set label to "N/A" - app->up_label = "N/A"; - //disable the ability to process the signal on button press - app->up_enabled = 0; - } else { - //check name length for proper screen fit - //then set filename as label. Might be replaced with defined label later on below. - app->up_label = extract_filename(furi_string_get_cstr(app->up_file), label_len); - FURI_LOG_I(TAG, "UP file: %s", furi_string_get_cstr(app->up_file)); - } + cfg_read_file_path(fff_data_file, app->up_file, &app->up_label, "UP", &app->up_enabled); - //Repeat process for Down - if(!flipper_format_read_string(fff_data_file, "DOWN", app->down_file)) { - FURI_LOG_W(TAG, "Could not read DOWN string"); - app->file_blank++; - app->down_label = "N/A"; - app->down_enabled = 0; - } else { - app->down_label = extract_filename(furi_string_get_cstr(app->down_file), label_len); - FURI_LOG_I(TAG, "DOWN file: %s", furi_string_get_cstr(app->down_file)); - } + cfg_read_file_path( + fff_data_file, app->down_file, &app->down_label, "DOWN", &app->down_enabled); - //Repeat process for Left - if(!flipper_format_read_string(fff_data_file, "LEFT", app->left_file)) { - FURI_LOG_W(TAG, "Could not read LEFT string"); - app->file_blank++; - app->left_label = "N/A"; - app->left_enabled = 0; - } else { - app->left_label = extract_filename(furi_string_get_cstr(app->left_file), label_len); - FURI_LOG_I(TAG, "LEFT file: %s", furi_string_get_cstr(app->left_file)); - } + cfg_read_file_path( + fff_data_file, app->left_file, &app->left_label, "LEFT", &app->left_enabled); - //Repeat process for Right - if(!flipper_format_read_string(fff_data_file, "RIGHT", app->right_file)) { - FURI_LOG_W(TAG, "Could not read RIGHT string"); - app->file_blank++; - app->right_label = "N/A"; - app->right_enabled = 0; - } else { - app->right_label = extract_filename(furi_string_get_cstr(app->right_file), label_len); - FURI_LOG_I(TAG, "RIGHT file: %s", furi_string_get_cstr(app->right_file)); - } + cfg_read_file_path( + fff_data_file, app->right_file, &app->right_label, "RIGHT", &app->right_enabled); - //Repeat process for Ok - if(!flipper_format_read_string(fff_data_file, "OK", app->ok_file)) { - FURI_LOG_W(TAG, "Could not read OK string"); - app->file_blank++; - app->ok_label = "N/A"; - app->ok_enabled = 0; - } else { - app->ok_label = extract_filename(furi_string_get_cstr(app->ok_file), label_len); - FURI_LOG_I(TAG, "OK file: %s", furi_string_get_cstr(app->ok_file)); - } + cfg_read_file_path(fff_data_file, app->ok_file, &app->ok_label, "OK", &app->ok_enabled); //File definitions are done. //File checks will follow after label assignment in order to close the universal_rf_map file without the need to reopen it again. //Label Assignment/Check Start - //assign variables to values within map file - if(!flipper_format_read_string(fff_data_file, "ULABEL", app->up_l)) { - FURI_LOG_W(TAG, "Could not read ULABEL string"); - //if Up button is disabled, set the label to "N/A"; - if(app->up_enabled == 0) { - app->up_label = "N/A"; - } - } else { - //check if button is disabled, and set label to "N/A" from missing map definition above - if(app->up_enabled == 0) { - app->up_label = "N/A"; - } else { - //set label from map to variable and shrink to fit screen - app->up_label = char_to_str((char*)furi_string_get_cstr(app->up_l), label_len); - } - FURI_LOG_I(TAG, "UP label: %s", app->up_label); - } - - if(!flipper_format_read_string(fff_data_file, "DLABEL", app->down_l)) { - FURI_LOG_W(TAG, "Could not read DLABEL string"); - if(app->down_enabled == 0) { - app->down_label = "N/A"; - } - } else { - if(app->down_enabled == 0) { - app->down_label = "N/A"; - } else { - app->down_label = char_to_str((char*)furi_string_get_cstr(app->down_l), label_len); - } - FURI_LOG_I(TAG, "DOWN label: %s", app->down_label); - } - - if(!flipper_format_read_string(fff_data_file, "LLABEL", app->left_l)) { - FURI_LOG_W(TAG, "Could not read LLABEL string"); - if(app->left_enabled == 0) { - app->left_label = "N/A"; - } - } else { - if(app->left_enabled == 0) { - app->left_label = "N/A"; - } else { - app->left_label = char_to_str((char*)furi_string_get_cstr(app->left_l), label_len); - } - FURI_LOG_I(TAG, "LEFT label: %s", app->left_label); - } - - if(!flipper_format_read_string(fff_data_file, "RLABEL", app->right_l)) { - FURI_LOG_W(TAG, "Could not read RLABEL string"); - if(app->right_enabled == 0) { - app->right_label = "N/A"; - } - } else { - if(app->right_enabled == 0) { - app->right_label = "N/A"; - } else { - app->right_label = - char_to_str((char*)furi_string_get_cstr(app->right_l), label_len); - } - FURI_LOG_I(TAG, "RIGHT label: %s", app->right_label); - } - - if(!flipper_format_read_string(fff_data_file, "OKLABEL", app->ok_l)) { - FURI_LOG_W(TAG, "Could not read OKLABEL string"); - if(app->ok_enabled == 0) { - app->ok_label = "N/A"; - } - } else { - if(app->ok_enabled == 0) { - app->ok_label = "N/A"; - } else { - app->ok_label = char_to_str((char*)furi_string_get_cstr(app->ok_l), label_len); - } - FURI_LOG_I(TAG, "OK label: %s", app->ok_label); - } - - app->file_result = 2; + cfg_read_file_label(fff_data_file, &app->up_label, "ULABEL", app->up_enabled); + cfg_read_file_label(fff_data_file, &app->down_label, "DLABEL", app->down_enabled); + cfg_read_file_label(fff_data_file, &app->left_label, "LLABEL", app->left_enabled); + cfg_read_file_label(fff_data_file, &app->right_label, "RLABEL", app->right_enabled); + cfg_read_file_label(fff_data_file, &app->ok_label, "OKLABEL", app->ok_enabled); } flipper_format_file_close(fff_data_file); @@ -339,106 +242,91 @@ void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) { //determine whether or not to continue to launch app with missing variables //if 5 files are missing, throw error - FURI_LOG_D(TAG, "app->file_blank: %d", app->file_blank); + //if button is still enabled, check that file exists + if(app->up_enabled == 1) { + furi_string_set(file_name, app->up_file); + fff_data_file = flipper_format_file_alloc(storage); - if(app->file_blank == 5) { - //trigger invalid file error screen + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + FURI_LOG_W(TAG, "Could not open UP file %s", furi_string_get_cstr(file_name)); + + //disable button, and set label to "N/A" + app->up_enabled = 0; + app->up_label = "N/A"; + } + + //close the file + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + } + + if(app->down_enabled == 1) { + furi_string_set(file_name, app->down_file); + fff_data_file = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + FURI_LOG_W(TAG, "Could not open DOWN file %s", furi_string_get_cstr(file_name)); + + app->down_enabled = 0; + app->down_label = "N/A"; + } + + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + } + + if(app->left_enabled == 1) { + furi_string_set(file_name, app->left_file); + fff_data_file = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + FURI_LOG_W(TAG, "Could not open LEFT file %s", furi_string_get_cstr(file_name)); + + app->left_enabled = 0; + app->left_label = "N/A"; + } + + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + } + + if(app->right_enabled == 1) { + furi_string_set(file_name, app->right_file); + fff_data_file = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + FURI_LOG_W(TAG, "Could not open RIGHT file %s", furi_string_get_cstr(file_name)); + + app->right_enabled = 0; + app->right_label = "N/A"; + } + + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + } + + if(app->ok_enabled == 1) { + furi_string_set(file_name, app->ok_file); + fff_data_file = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + FURI_LOG_W(TAG, "Could not open OK file %s", furi_string_get_cstr(file_name)); + + app->ok_enabled = 0; + app->ok_label = "N/A"; + } + + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + } + + furi_record_close(RECORD_STORAGE); + + if(app->up_enabled == 0 && app->down_enabled == 0 && app->left_enabled == 0 && + app->right_enabled == 0 && app->ok_enabled == 0) { app->file_result = 1; } else { - //check all files - //reset app->file_blank to redetermine if error needs to be thrown - app->file_blank = 0; - - //if button is still enabled, check that file exists - if(app->up_enabled == 1) { - furi_string_set(file_name, app->up_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open UP file %s", furi_string_get_cstr(file_name)); - - //disable button, and set label to "N/A" - app->up_enabled = 0; - app->up_label = "N/A"; - app->file_blank++; - } - - //close the file - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - if(app->down_enabled == 1) { - furi_string_set(file_name, app->down_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open DOWN file %s", furi_string_get_cstr(file_name)); - - app->down_enabled = 0; - app->down_label = "N/A"; - app->file_blank++; - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - if(app->left_enabled == 1) { - furi_string_set(file_name, app->left_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open LEFT file %s", furi_string_get_cstr(file_name)); - - app->left_enabled = 0; - app->left_label = "N/A"; - app->file_blank++; - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - if(app->right_enabled == 1) { - furi_string_set(file_name, app->right_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open RIGHT file %s", furi_string_get_cstr(file_name)); - - app->right_enabled = 0; - app->right_label = "N/A"; - app->file_blank++; - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - if(app->ok_enabled == 1) { - furi_string_set(file_name, app->ok_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open OK file %s", furi_string_get_cstr(file_name)); - - app->ok_enabled = 0; - app->ok_label = "N/A"; - app->file_blank++; - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - furi_record_close(RECORD_STORAGE); - - if(app->file_blank == 5) { - app->file_result = 1; - } else { - app->file_result = 2; - } + app->file_result = 2; } } @@ -598,14 +486,16 @@ void unirfremix_tx_stop(UniRFRemix* app) { //Stop TX furi_hal_subghz_stop_async_tx(); - FURI_LOG_I(TAG, "TX Done!"); + //FURI_LOG_I(TAG, "TX Done!"); subghz_transmitter_stop(app->tx_transmitter); FURI_LOG_D(TAG, "Checking if protocol is dynamic"); - const SubGhzProtocol* registry = - subghz_protocol_registry_get_by_name(furi_string_get_cstr(app->txpreset->protocol)); - FURI_LOG_D(TAG, "Protocol-TYPE %d", registry->type); - if(registry && registry->type == SubGhzProtocolTypeDynamic) { + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(app->environment); + const SubGhzProtocol* proto = subghz_protocol_registry_get_by_name( + protocol_registry_items, furi_string_get_cstr(app->txpreset->protocol)); + FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type); + if(proto && proto->type == SubGhzProtocolTypeDynamic) { FURI_LOG_D(TAG, "Protocol is dynamic. Saving key"); unirfremix_save_protocol_to_file(app->tx_fff_data, app->tx_file_path); @@ -655,16 +545,17 @@ static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_idle(); + furi_hal_subghz_set_frequency_and_path(app->txpreset->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); if(!furi_hal_subghz_tx()) { FURI_LOG_E(TAG, "Sending not allowed"); break; } - FURI_LOG_I(TAG, "Sending..."); + //FURI_LOG_I(TAG, "Sending..."); notification_message(app->notification, &sequence_blink_start_magenta); furi_hal_subghz_start_async_tx(subghz_transmitter_yield, app->tx_transmitter); @@ -676,7 +567,7 @@ static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) { } static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char* path) { - FURI_LOG_I(TAG, "Sending: %s", path); + FURI_LOG_D(TAG, "Sending: %s", path); app->tx_file_path = path; @@ -716,7 +607,7 @@ static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char static void unirfremix_process_signal(UniRFRemix* app, FuriString* signal) { view_port_update(app->view_port); - FURI_LOG_I(TAG, "signal = %s", furi_string_get_cstr(signal)); + FURI_LOG_D(TAG, "signal = %s", furi_string_get_cstr(signal)); if(strlen(furi_string_get_cstr(signal)) > 12) { Storage* storage = furi_record_open(RECORD_STORAGE); @@ -740,6 +631,14 @@ static void render_callback(Canvas* canvas, void* ctx) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignTop, "Please configure map."); canvas_draw_str_aligned(canvas, 62, 60, AlignCenter, AlignBottom, "Press Back to Exit."); + } else if(app->file_result == 3) { + //if map has no valid filenames defined + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 62, 5, AlignCenter, AlignTop, "Checking config."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignTop, "If app is stuck..."); + canvas_draw_str_aligned(canvas, 62, 60, AlignCenter, AlignBottom, "Press Back to Exit."); } else if(app->tx_not_allowed) { canvas_clear(canvas); canvas_set_font(canvas, FontPrimary); @@ -808,6 +707,8 @@ static void render_callback(Canvas* canvas, void* ctx) { canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); canvas_draw_icon(canvas, 116, 18, &I_Pin_star_7x7); break; + default: + break; } //Repeat indicator @@ -838,6 +739,7 @@ void unirfremix_subghz_alloc(UniRFRemix* app) { app->environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( app->environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_protocol_registry(app->environment, (void*)&subghz_protocol_registry); app->subghz_receiver = subghz_receiver_alloc_init(app->environment); } @@ -873,12 +775,6 @@ void unirfremix_free(UniRFRemix* app, bool with_subghz) { furi_string_free(app->right_file); furi_string_free(app->ok_file); - furi_string_free(app->up_l); - furi_string_free(app->down_l); - furi_string_free(app->left_l); - furi_string_free(app->right_l); - furi_string_free(app->ok_l); - furi_string_free(app->file_path); furi_string_free(app->signal); @@ -918,12 +814,6 @@ int32_t unirfremix_app(void* p) { app->right_file = furi_string_alloc(); app->ok_file = furi_string_alloc(); - app->up_l = furi_string_alloc(); - app->down_l = furi_string_alloc(); - app->left_l = furi_string_alloc(); - app->right_l = furi_string_alloc(); - app->ok_l = furi_string_alloc(); - app->file_result = 3; Storage* storage = furi_record_open(RECORD_STORAGE); @@ -1083,6 +973,8 @@ int32_t unirfremix_app(void* p) { unirfremix_tx_stop(app); exit_loop = true; break; + default: + break; } if(app->processing == 0) { @@ -1111,6 +1003,8 @@ int32_t unirfremix_app(void* p) { case 5: app->send_status_c = 5; break; + default: + break; } app->processing = 2; @@ -1126,7 +1020,7 @@ int32_t unirfremix_app(void* p) { furi_mutex_release(app->model_mutex); view_port_update(app->view_port); } - } else if(app->file_result == 1) { + } else if(app->file_result == 1 || app->file_result == 3) { //refresh screen to update variables before processing main screen or error screens view_port_update(app->view_port); @@ -1154,6 +1048,8 @@ int32_t unirfremix_app(void* p) { case InputKeyBack: exit_loop = true; break; + default: + break; } if(exit_loop == true) { diff --git a/applications/main/unirfremix_loader/application.fam b/applications/main/unirfremix_loader/application.fam new file mode 100644 index 000000000..070bc9cf7 --- /dev/null +++ b/applications/main/unirfremix_loader/application.fam @@ -0,0 +1,11 @@ +App( + appid="SubGHz_Remote_loader", + name="Sub-GHz Remote", + apptype=FlipperAppType.APP, + entry_point="unirfremix_loader_app", + requires=["gui"], + stack_size=int(1.5 * 1024), + icon="A_UniRFRemix_14", + order=11, + link="/ext/apps/Main/SubGHz_Remote.fap", +) diff --git a/applications/main/unirfremix_loader/unirfremix_loader_app.c b/applications/main/unirfremix_loader/unirfremix_loader_app.c new file mode 100644 index 000000000..1acfe41fb --- /dev/null +++ b/applications/main/unirfremix_loader/unirfremix_loader_app.c @@ -0,0 +1,9 @@ +#include + +#define TAG "unirfremix_loader_app" + +int32_t unirfremix_loader_app(void* p) { + UNUSED(p); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/Tuning Fork/tuning_fork.c b/applications/plugins/Tuning Fork/tuning_fork.c deleted file mode 100644 index 7cc2675b0..000000000 --- a/applications/plugins/Tuning Fork/tuning_fork.c +++ /dev/null @@ -1,395 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "notes.h" -#include "tunings.h" - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType type; - InputEvent input; -} PluginEvent; - -enum Page { - Tunings, - Notes -}; - -typedef struct { - bool playing; - enum Page page; - int current_tuning_note_index; - int current_tuning_index; - float volume; - TUNING tuning; -} TuningForkState; - -static TUNING current_tuning(TuningForkState* tuningForkState) { - return tuningForkState->tuning; -} - -static NOTE current_tuning_note(TuningForkState* tuningForkState) { - return current_tuning(tuningForkState).notes[tuningForkState->current_tuning_note_index]; -} - -static float current_tuning_note_freq(TuningForkState* tuningForkState) { - return current_tuning_note(tuningForkState).frequency; -} - -static void current_tuning_note_label(TuningForkState* tuningForkState, char* outNoteLabel) { - for(int i=0; i < 20; ++i){ - outNoteLabel[i] = current_tuning_note(tuningForkState).label[i]; - } -} - -static void current_tuning_label(TuningForkState* tuningForkState, char* outTuningLabel) { - for(int i=0; i < 20; ++i){ - outTuningLabel[i] = current_tuning(tuningForkState).label[i]; - } -} - -static void updateTuning(TuningForkState* tuning_fork_state) { - tuning_fork_state->tuning = TuningList[tuning_fork_state->current_tuning_index]; - tuning_fork_state->current_tuning_note_index = 0; -} - -static void next_tuning(TuningForkState* tuning_fork_state) { - if (tuning_fork_state->current_tuning_index == TUNINGS_COUNT - 1) { - tuning_fork_state->current_tuning_index = 0; - } else { - tuning_fork_state->current_tuning_index += 1; - } - updateTuning(tuning_fork_state); -} - -static void prev_tuning(TuningForkState* tuning_fork_state) { - if (tuning_fork_state->current_tuning_index - 1 < 0) { - tuning_fork_state->current_tuning_index = TUNINGS_COUNT - 1; - } else { - tuning_fork_state->current_tuning_index -= 1; - } - updateTuning(tuning_fork_state); -} - -static void next_note(TuningForkState* tuning_fork_state) { - if (tuning_fork_state->current_tuning_note_index == current_tuning(tuning_fork_state).notes_length - 1) { - tuning_fork_state->current_tuning_note_index = 0; - } else { - tuning_fork_state->current_tuning_note_index += 1; - } -} - -static void prev_note(TuningForkState* tuning_fork_state) { - if (tuning_fork_state->current_tuning_note_index == 0) { - tuning_fork_state->current_tuning_note_index = current_tuning(tuning_fork_state).notes_length - 1; - } else { - tuning_fork_state->current_tuning_note_index -= 1; - } -} - -static void increase_volume(TuningForkState* tuning_fork_state) { - if (tuning_fork_state->volume < 1.0f) { - tuning_fork_state->volume += 0.1f; - } -} - -static void decrease_volume(TuningForkState* tuning_fork_state) { - if (tuning_fork_state->volume > 0.0f) { - tuning_fork_state->volume -= 0.1f; - } -} - -static void play(TuningForkState* tuning_fork_state) { - furi_hal_speaker_start(current_tuning_note_freq(tuning_fork_state), tuning_fork_state->volume); -} - -static void stop() { - furi_hal_speaker_stop(); -} - -static void replay(TuningForkState* tuning_fork_state) { - stop(); - play(tuning_fork_state); -} - -static void render_callback(Canvas* const canvas, void* ctx) { - TuningForkState* tuning_fork_state = acquire_mutex((ValueMutex*)ctx, 25); - if(tuning_fork_state == NULL) { - return; - } - - string_t tempStr; - string_init(tempStr); - - canvas_draw_frame(canvas, 0, 0, 128, 64); - - canvas_set_font(canvas, FontPrimary); - - if (tuning_fork_state->page == Tunings) { - char tuningLabel[20]; - current_tuning_label(tuning_fork_state, tuningLabel); - string_printf(tempStr, "< %s >", tuningLabel); - canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); - } else { - char tuningLabel[20]; - current_tuning_label(tuning_fork_state, tuningLabel); - string_printf(tempStr, "%s", tuningLabel); - canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); - - char tuningNoteLabel[20]; - current_tuning_note_label(tuning_fork_state, tuningNoteLabel); - string_printf(tempStr, "< %s >", tuningNoteLabel); - canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); - } - - canvas_set_font(canvas, FontSecondary); - elements_button_left(canvas, "Prev"); - elements_button_right(canvas, "Next"); - - if (tuning_fork_state->page == Notes) { - if (tuning_fork_state->playing) { - elements_button_center(canvas, "Stop "); - } else { - elements_button_center(canvas, "Play"); - } - } else { - elements_button_center(canvas, "Select"); - } - if (tuning_fork_state->page == Notes) { - elements_progress_bar(canvas, 8, 36, 112, tuning_fork_state->volume); - } - - string_clear(tempStr); - release_mutex((ValueMutex*)ctx, tuning_fork_state); -} - -static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - PluginEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -static void tuning_fork_state_init(TuningForkState* const tuning_fork_state) { - tuning_fork_state->playing = false; - tuning_fork_state->page = Tunings; - tuning_fork_state->volume = 1.0f; - tuning_fork_state->tuning = GuitarStandard6; - tuning_fork_state->current_tuning_index = 2; - tuning_fork_state->current_tuning_note_index = 0; -} - -int32_t tuning_fork_app() { - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - - TuningForkState* tuning_fork_state = malloc(sizeof(TuningForkState)); - tuning_fork_state_init(tuning_fork_state); - - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, tuning_fork_state, sizeof(TuningForkState))) { - FURI_LOG_E("TuningFork", "cannot create mutex\r\n"); - free(tuning_fork_state); - return 255; - } - - // Set system callbacks - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); - view_port_input_callback_set(view_port, input_callback, event_queue); - - Gui* gui = furi_record_open("gui"); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - PluginEvent event; - for(bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - - TuningForkState* tuning_fork_state = (TuningForkState*)acquire_mutex_block(&state_mutex); - - if(event_status == FuriStatusOk) { - if(event.type == EventTypeKey) { - if(event.input.type == InputTypeShort) { - // push events - switch(event.input.key) { - case InputKeyUp: - if (tuning_fork_state->page == Notes) { - increase_volume(tuning_fork_state); - if (tuning_fork_state->playing) { - replay(tuning_fork_state); - } - } - break; - case InputKeyDown: - if (tuning_fork_state->page == Notes) { - decrease_volume(tuning_fork_state); - if (tuning_fork_state->playing) { - replay(tuning_fork_state); - } - } - break; - case InputKeyRight: - if (tuning_fork_state->page == Tunings) { - next_tuning(tuning_fork_state); - } else { - next_note(tuning_fork_state); - if (tuning_fork_state->playing) { - replay(tuning_fork_state); - } - } - break; - case InputKeyLeft: - if (tuning_fork_state->page == Tunings) { - prev_tuning(tuning_fork_state); - } else { - prev_note(tuning_fork_state); - if (tuning_fork_state->playing) { - replay(tuning_fork_state); - } - } - break; - case InputKeyOk: - if (tuning_fork_state->page == Tunings) { - tuning_fork_state->page = Notes; - } else { - tuning_fork_state->playing = !tuning_fork_state->playing; - if (tuning_fork_state->playing) { - play(tuning_fork_state); - } else { - stop(); - } - } - break; - case InputKeyBack: - if (tuning_fork_state->page == Tunings) { - processing = false; - } else { - tuning_fork_state->playing = false; - tuning_fork_state->current_tuning_note_index = 0; - stop(); - tuning_fork_state->page = Tunings; - } - break; - } - } else if (event.input.type == InputTypeLong) { - // hold events - switch(event.input.key) { - case InputKeyUp: - break; - case InputKeyDown: - break; - case InputKeyRight: - if (tuning_fork_state->page == Tunings) { - next_tuning(tuning_fork_state); - } else { - next_note(tuning_fork_state); - if (tuning_fork_state->playing) { - replay(tuning_fork_state); - } - } - - break; - case InputKeyLeft: - if (tuning_fork_state->page == Tunings) { - prev_tuning(tuning_fork_state); - } else { - prev_note(tuning_fork_state); - if (tuning_fork_state->playing) { - replay(tuning_fork_state); - } - } - - break; - case InputKeyOk: - break; - case InputKeyBack: - if (tuning_fork_state->page == Tunings) { - processing = false; - } else { - tuning_fork_state->playing = false; - stop(); - tuning_fork_state->page = Tunings; - tuning_fork_state->current_tuning_note_index = 0; - } - break; - } - } else if (event.input.type == InputTypeRepeat) { - // repeat events - switch(event.input.key) { - case InputKeyUp: - break; - case InputKeyDown: - break; - case InputKeyRight: - if (tuning_fork_state->page == Tunings) { - next_tuning(tuning_fork_state); - } else { - next_note(tuning_fork_state); - if (tuning_fork_state->playing) { - replay(tuning_fork_state); - } - } - - break; - case InputKeyLeft: - if (tuning_fork_state->page == Tunings) { - prev_tuning(tuning_fork_state); - } else { - prev_note(tuning_fork_state); - if (tuning_fork_state->playing) { - replay(tuning_fork_state); - } - } - - break; - case InputKeyOk: - break; - case InputKeyBack: - if (tuning_fork_state->page == Tunings) { - processing = false; - } else { - tuning_fork_state->playing = false; - stop(); - tuning_fork_state->page = Tunings; - tuning_fork_state->current_tuning_note_index = 0; - } - break; - } - } - } - } else { - FURI_LOG_D("TuningFork", "FuriMessageQueue: event timeout"); - } - - view_port_update(view_port); - release_mutex(&state_mutex, tuning_fork_state); - } - - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close("gui"); - view_port_free(view_port); - furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); - furi_record_close(RECORD_NOTIFICATION); - free(tuning_fork_state); - - return 0; -} diff --git a/applications/plugins/Tuning Fork/tunings.h b/applications/plugins/Tuning Fork/tunings.h deleted file mode 100644 index 32d1b5e04..000000000 --- a/applications/plugins/Tuning Fork/tunings.h +++ /dev/null @@ -1,188 +0,0 @@ -#include "notes.h" - -#ifndef TUNINGS -#define TUNINGS - -typedef struct { - char label[20]; - float frequency; -} NOTE; - -typedef struct { - char label[20]; - int notes_length; - NOTE notes[20]; -} TUNING; - -const TUNING TuningForks = { - "Tuning forks", 6, { - { "Common A4 (440)", 440.00f}, - { "Sarti's A4 (436)", 436.00f}, - { "1858 A4 (435)", 435.00f}, - { "Verdi's A4 (432)", 432.00f}, - { "1750-1820 A4 (423.5)", 423.50f}, - { "Verdi's C4 (256.00)", 256.00f}, - } -}; - -const TUNING ScientificPitch = { - "Scientific pitch", 12, { - { "C0 (16Hz)", 16.0f}, - { "C1 (32Hz)", 32.0f}, - { "C2 (64Hz)", 64.0f}, - { "C3 (128Hz)", 128.0f}, - { "C4 (256Hz)", 256.0f}, - { "C5 (512Hz)", 512.0f}, - { "C6 (1024Hz)", 1024.0f}, - { "C7 (2048Hz)", 2048.0f}, - { "C8 (4096Hz)", 4096.0f}, - { "C9 (8192Hz)", 8192.0f}, - { "C10 (16384Hz)", 16384.0f}, - { "C11 (32768Hz)", 32768.0f} - } -}; - -const TUNING GuitarStandard6 = { - "Guitar Standard 6", 6, { - {"String 1", E4}, - {"String 2", B3}, - {"String 3", G3}, - {"String 4", D3}, - {"String 5", A2}, - {"String 6", E2} - } -}; - -const TUNING GuitarDropD6 = { - "Guitar Drop D 6", 6, { - {"String 1", E4}, - {"String 2", B3}, - {"String 3", G3}, - {"String 4", D3}, - {"String 5", A2}, - {"String 6", D2} - } -}; - -const TUNING GuitarD6 = { - "Guitar D 6", 6, { - {"String 1", D4}, - {"String 2", A3}, - {"String 3", F3}, - {"String 4", C3}, - {"String 5", G2}, - {"String 6", D2} - } -}; - -const TUNING GuitarDropC6 = { - "Guitar Drop C 6", 6, { - {"String 1", D4}, - {"String 2", A3}, - {"String 3", F3}, - {"String 4", C3}, - {"String 5", G2}, - {"String 6", C2} - } -}; - -const TUNING GuitarStandard7 = { - "Guitar Standard 7", 7, { - {"String 1", E4}, - {"String 2", B3}, - {"String 3", G3}, - {"String 4", D3}, - {"String 5", A2}, - {"String 6", E2}, - {"String 7", B1} - } -}; - -const TUNING BassStandard4 = { - "Bass Standard 4", 4, { - {"String 1", G2}, - {"String 2", D2}, - {"String 3", A1}, - {"String 4", E1} - } -}; - -const TUNING BassStandardTenor4 = { - "Bass Stand Tenor 4", 4, { - {"String 1", C3}, - {"String 2", G2}, - {"String 3", D2}, - {"String 4", A1} - } -}; - -const TUNING BassStandard5 = { - "Bass Standard 5", 5, { - {"String 1", G2}, - {"String 2", D2}, - {"String 3", A1}, - {"String 4", E1}, - {"String 5", B0} - } -}; - -const TUNING BassStandardTenor5 = { - "Bass Stand Tenor 5", 5, { - {"String 1", C3}, - {"String 2", G2}, - {"String 3", D2}, - {"String 4", A1}, - {"String 5", E1} - } -}; - -const TUNING BassDropD4 = { - "Bass Drop D 4", 4, { - {"String 1", G2}, - {"String 2", D2}, - {"String 3", A1}, - {"String 4", D1} - } -}; - -const TUNING BassD4 = { - "Bass D 4", 4, { - {"String 1", F2}, - {"String 2", C2}, - {"String 3", G1}, - {"String 4", D1} - } -}; - -const TUNING BassDropA5 = { - "Bass Drop A 5", 5, { - {"String 1", G2}, - {"String 2", D2}, - {"String 3", A1}, - {"String 4", E1}, - {"String 5", A0} - } -}; - -#define TUNINGS_COUNT 14 - -TUNING TuningList[TUNINGS_COUNT] = { - ScientificPitch, - TuningForks, - - GuitarStandard6, - GuitarDropD6, - GuitarD6, - GuitarDropC6, - GuitarStandard7, - - BassStandard4, - BassStandardTenor4, - BassStandard5, - BassStandardTenor5, - BassDropD4, - BassD4, - BassDropA5 -}; - -#endif //TUNINGS diff --git a/applications/plugins/airmouse/LICENSE b/applications/plugins/airmouse/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/applications/plugins/airmouse/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/applications/plugins/airmouse/README.md b/applications/plugins/airmouse/README.md new file mode 100644 index 000000000..9df0d69b0 --- /dev/null +++ b/applications/plugins/airmouse/README.md @@ -0,0 +1,56 @@ +# Flipper Air Mouse + +## Brief + +> "You can turn anything into an air mouse if you're brave enough" + + β€” Piper, a.k.a. Pez + +Naturally, the quote above applies to [Flipper](https://flipperzero.one/) as well. + +## What? + +The app allows you to turn your Flipper into a USB or Bluetooth air mouse (you do need an extra module, see the Hardware section below)... + +Using it is really simple: + * Connect the Flipper via a USB cable and pick `USB`, or pick `Bluetooth` and pair it with your PC; + * Hold the Flipper in your hand with the buttons pointing towards the screen; + * Wave your Flipper like you don't care to move the cursor; + * Up button for Left mouse click; + * Down button for Right mouse click; + * Center button for Middle mouse click; + * Use calibration menu option if you notice significant drift (place your Flipper onto a level surface, make sure it doesn't move, run this option, wait 2 seconds, done). + +See early prototype [in action](https://www.youtube.com/watch?v=DdxAmmsYfMA). + +## Hardware + +The custom module is using Bosch BMI160 accelerometer/gyroscope chip connected via I2C. + +Take a look into the [schematic](https://github.com/ginkage/FlippAirMouse/tree/main/schematic) folder for Gerber, BOM and CPL files, so you can order directly from JLCPCB. + +Original idea: + +![What I thought it would look like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/schematic.png) + +Expectation: + +![What EDA though it would look like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/render.png) + +Reality: + +![What it looks like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/flipper.jpg) + + +## Software + +The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project + +If you're familiar with Flipper applications, start in the [firmware](https://github.com/flipperdevices/flipperzero-firmware) checkout folder and do the following: +``` +cd applications/plugins +git clone https://github.com/ginkage/FlippAirMouse +cd ../.. +./fbt fap_air_mouse +``` +If you're not familiar with those, just grab a `fap` file from Releases. diff --git a/applications/plugins/airmouse/air_mouse.c b/applications/plugins/airmouse/air_mouse.c new file mode 100644 index 000000000..7a90e49f1 --- /dev/null +++ b/applications/plugins/airmouse/air_mouse.c @@ -0,0 +1,156 @@ +#include "air_mouse.h" + +#include +#include + +#include "tracking/imu/imu.h" + +#define TAG "AirMouseApp" + +enum AirMouseSubmenuIndex { + AirMouseSubmenuIndexBtMouse, + AirMouseSubmenuIndexUsbMouse, + AirMouseSubmenuIndexCalibration, +}; + +void air_mouse_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + AirMouse* app = context; + if(index == AirMouseSubmenuIndexBtMouse) { + app->view_id = AirMouseViewBtMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewBtMouse); + } else if(index == AirMouseSubmenuIndexUsbMouse) { + app->view_id = AirMouseViewUsbMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewUsbMouse); + } else if(index == AirMouseSubmenuIndexCalibration) { + app->view_id = AirMouseViewCalibration; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewCalibration); + } +} + +void air_mouse_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + AirMouse* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); // Exit + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewSubmenu); // Menu + } +} + +uint32_t air_mouse_exit_confirm_view(void* context) { + UNUSED(context); + return AirMouseViewExitConfirm; +} + +uint32_t air_mouse_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +AirMouse* air_mouse_app_alloc() { + AirMouse* app = malloc(sizeof(AirMouse)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu view + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, "Bluetooth", AirMouseSubmenuIndexBtMouse, air_mouse_submenu_callback, app); + submenu_add_item( + app->submenu, "USB", AirMouseSubmenuIndexUsbMouse, air_mouse_submenu_callback, app); + submenu_add_item( + app->submenu, + "Calibration", + AirMouseSubmenuIndexCalibration, + air_mouse_submenu_callback, + app); + view_set_previous_callback(submenu_get_view(app->submenu), air_mouse_exit); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewSubmenu, submenu_get_view(app->submenu)); + + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, air_mouse_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // Bluetooth view + app->bt_mouse = bt_mouse_alloc(app->view_dispatcher); + view_set_previous_callback(bt_mouse_get_view(app->bt_mouse), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewBtMouse, bt_mouse_get_view(app->bt_mouse)); + + // USB view + app->usb_mouse = usb_mouse_alloc(app->view_dispatcher); + view_set_previous_callback(usb_mouse_get_view(app->usb_mouse), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewUsbMouse, usb_mouse_get_view(app->usb_mouse)); + + // Calibration view + app->calibration = calibration_alloc(app->view_dispatcher); + view_set_previous_callback( + calibration_get_view(app->calibration), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewCalibration, calibration_get_view(app->calibration)); + + app->view_id = AirMouseViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + + return app; +} + +void air_mouse_app_free(AirMouse* app) { + furi_assert(app); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewBtMouse); + bt_mouse_free(app->bt_mouse); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewUsbMouse); + usb_mouse_free(app->usb_mouse); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewCalibration); + calibration_free(app->calibration); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t air_mouse_app(void* p) { + UNUSED(p); + + AirMouse* app = air_mouse_app_alloc(); + if(!imu_begin()) { + air_mouse_app_free(app); + return -1; + } + + DOLPHIN_DEED(DolphinDeedPluginStart); + view_dispatcher_run(app->view_dispatcher); + + imu_end(); + air_mouse_app_free(app); + + return 0; +} diff --git a/applications/plugins/airmouse/air_mouse.h b/applications/plugins/airmouse/air_mouse.h new file mode 100644 index 000000000..3a1ba783e --- /dev/null +++ b/applications/plugins/airmouse/air_mouse.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "views/bt_mouse.h" +#include "views/usb_mouse.h" +#include "views/calibration.h" + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + DialogEx* dialog; + BtMouse* bt_mouse; + UsbMouse* usb_mouse; + Calibration* calibration; + uint32_t view_id; +} AirMouse; + +typedef enum { + AirMouseViewSubmenu, + AirMouseViewBtMouse, + AirMouseViewUsbMouse, + AirMouseViewCalibration, + AirMouseViewExitConfirm, +} AirMouseView; diff --git a/applications/plugins/airmouse/application.fam b/applications/plugins/airmouse/application.fam new file mode 100644 index 000000000..97003ef89 --- /dev/null +++ b/applications/plugins/airmouse/application.fam @@ -0,0 +1,9 @@ +App( + appid="Air_Mouse", + name="[BMI160] Air Mouse", + apptype=FlipperAppType.EXTERNAL, + entry_point="air_mouse_app", + stack_size=10 * 1024, + fap_icon="mouse_10px.png", + fap_category="NeedsTesting", +) diff --git a/applications/plugins/airmouse/mouse_10px.png b/applications/plugins/airmouse/mouse_10px.png new file mode 100644 index 000000000..94c3a7a14 Binary files /dev/null and b/applications/plugins/airmouse/mouse_10px.png differ diff --git a/applications/plugins/airmouse/tracking/calibration_data.cc b/applications/plugins/airmouse/tracking/calibration_data.cc new file mode 100644 index 000000000..e62311c7a --- /dev/null +++ b/applications/plugins/airmouse/tracking/calibration_data.cc @@ -0,0 +1,85 @@ +#include +#include + +#define TAG "tracker" + +#include "calibration_data.h" + +#include +#include + +// Student's distribution T value for 95% (two-sided) confidence interval. +static const double Tn = 1.960; + +// Number of samples (degrees of freedom) for the corresponding T values. +static const int Nn = 200; + +void CalibrationData::reset() +{ + complete = false; + count = 0; + sum = Vector::Zero(); + sumSq = Vector::Zero(); + mean = Vector::Zero(); + median = Vector::Zero(); + sigma = Vector::Zero(); + delta = Vector::Zero(); + xData.clear(); + yData.clear(); + zData.clear(); +} + +bool CalibrationData::add(Vector& data) +{ + if (complete) { + return true; + } + + xData.push_back(data[0]); + yData.push_back(data[1]); + zData.push_back(data[2]); + + sum += data; + sumSq += data * data; + count++; + + if (count >= Nn) { + calcDelta(); + complete = true; + } + + return complete; +} + +static inline double medianOf(std::vector& list) +{ + std::sort(list.begin(), list.end()); + int count = list.size(); + int middle = count / 2; + return (count % 2 == 1) ? list[middle] : (list[middle - 1] + list[middle]) / 2.0l; +} + +void CalibrationData::calcDelta() +{ + median.Set(medianOf(xData), medianOf(yData), medianOf(zData)); + + mean = sum / count; + Vector m2 = mean * mean; + Vector d = sumSq / count - m2; + Vector s2 = (d * count) / (count - 1); + sigma = Vector(std::sqrt(d[0]), std::sqrt(d[1]), std::sqrt(d[2])); + Vector s = Vector(std::sqrt(s2[0]), std::sqrt(s2[1]), std::sqrt(s2[2])); + delta = s * Tn / std::sqrt((double)count); + Vector low = mean - delta; + Vector high = mean + delta; + + FURI_LOG_I(TAG, + "M[x] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[0], high[0], median[0], mean[0], delta[0], sigma[0]); + FURI_LOG_I(TAG, + "M[y] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[1], high[1], median[1], mean[1], delta[1], sigma[1]); + FURI_LOG_I(TAG, + "M[z] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[2], high[2], median[2], mean[2], delta[2], sigma[2]); +} \ No newline at end of file diff --git a/applications/plugins/airmouse/tracking/calibration_data.h b/applications/plugins/airmouse/tracking/calibration_data.h new file mode 100644 index 000000000..d47dab08d --- /dev/null +++ b/applications/plugins/airmouse/tracking/calibration_data.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include + +#include "util/vector.h" + +#define CALIBRATION_DATA_VER (1) +#define CALIBRATION_DATA_FILE_NAME ".calibration.data" +#define CALIBRATION_DATA_PATH INT_PATH(CALIBRATION_DATA_FILE_NAME) +#define CALIBRATION_DATA_MAGIC (0x23) + +#define CALIBRATION_DATA_SAVE(x) \ + saved_struct_save( \ + CALIBRATION_DATA_PATH, \ + (x), \ + sizeof(CalibrationMedian), \ + CALIBRATION_DATA_MAGIC, \ + CALIBRATION_DATA_VER) + +#define CALIBRATION_DATA_LOAD(x) \ + saved_struct_load( \ + CALIBRATION_DATA_PATH, \ + (x), \ + sizeof(CalibrationMedian), \ + CALIBRATION_DATA_MAGIC, \ + CALIBRATION_DATA_VER) + +typedef struct { + double x; + double y; + double z; +} CalibrationMedian; + +typedef cardboard::Vector3 Vector; + +/** + * Helper class to gather some stats and store the calibration data. Right now it calculates a lot + * more stats than actually needed. Some of them are used for logging the sensors quality (and + * filing bugs), other may be required in the future, e.g. for bias. + */ +class CalibrationData { +public: + /** + * Check if the sensors were calibrated before. + * + * @return {@code true} if calibration data is available, or {@code false} otherwise. + */ + bool isComplete() { + return complete; + } + + /** Prepare to collect new calibration data. */ + void reset(); + + /** + * Retrieve the median gyroscope readings. + * + * @return Three-axis median vector. + */ + Vector getMedian() { + return median; + } + + /** + * Retrieve the mean gyroscope readings. + * + * @return Three-axis mean vector. + */ + Vector getMean() { + return mean; + } + + /** + * Retrieve the standard deviation of gyroscope readings. + * + * @return Three-axis standard deviation vector. + */ + Vector getSigma() { + return sigma; + } + + /** + * Retrieve the confidence interval size of gyroscope readings. + * + * @return Three-axis confidence interval size vector. + */ + Vector getDelta() { + return delta; + } + + /** + * Add a new gyroscope reading to the stats. + * + * @param data gyroscope values vector. + * @return {@code true} if we now have enough data for calibration, or {@code false} otherwise. + */ + bool add(Vector& data); + +private: + // Calculates the confidence interval (mean +- delta) and some other related values, like + // standard deviation, etc. See https://en.wikipedia.org/wiki/Student%27s_t-distribution + void calcDelta(); + + int count; + bool complete; + Vector sum; + Vector sumSq; + Vector mean; + Vector median; + Vector sigma; + Vector delta; + std::vector xData; + std::vector yData; + std::vector zData; +}; diff --git a/applications/plugins/airmouse/tracking/imu/bmi160.c b/applications/plugins/airmouse/tracking/imu/bmi160.c new file mode 100644 index 000000000..968dddd4d --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/bmi160.c @@ -0,0 +1,5988 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160.c +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +#include "bmi160.h" + +/* Below look up table follows the enum bmi160_int_types. + * Hence any change should match to the enum bmi160_int_types + */ +const uint8_t int_mask_lookup_table[13] = { + BMI160_INT1_SLOPE_MASK, + BMI160_INT1_SLOPE_MASK, + BMI160_INT2_LOW_STEP_DETECT_MASK, + BMI160_INT1_DOUBLE_TAP_MASK, + BMI160_INT1_SINGLE_TAP_MASK, + BMI160_INT1_ORIENT_MASK, + BMI160_INT1_FLAT_MASK, + BMI160_INT1_HIGH_G_MASK, + BMI160_INT1_LOW_G_MASK, + BMI160_INT1_NO_MOTION_MASK, + BMI160_INT2_DATA_READY_MASK, + BMI160_INT2_FIFO_FULL_MASK, + BMI160_INT2_FIFO_WM_MASK}; + +/*********************************************************************/ +/* Static function declarations */ + +/*! + * @brief This API configures the pins to fire the + * interrupt signal when it occurs + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_intr_pin_config(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the any-motion interrupt of the sensor. + * This interrupt occurs when accel values exceeds preset threshold + * for a certain period of time. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_any_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/*! + * @brief This API sets tap interrupts.Interrupt is fired when + * tap movements happen. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_tap_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the data ready interrupt for both accel and gyro. + * This interrupt occurs when new accel and gyro data come. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_gyro_data_ready_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev); + +/*! + * @brief This API sets the significant motion interrupt of the sensor.This + * interrupt occurs when there is change in user location. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_sig_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/*! + * @brief This API sets the no motion/slow motion interrupt of the sensor. + * Slow motion is similar to any motion interrupt.No motion interrupt + * occurs when slope bet. two accel values falls below preset threshold + * for preset duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_no_motion_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the step detection interrupt.This interrupt + * occurs when the single step causes accel values to go above + * preset threshold. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_step_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the orientation interrupt of the sensor.This + * interrupt occurs when there is orientation change in the sensor + * with respect to gravitational field vector g. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_orientation_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the flat interrupt of the sensor.This interrupt + * occurs in case of flat orientation + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_flat_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the low-g interrupt of the sensor.This interrupt + * occurs during free-fall. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_low_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the high-g interrupt of the sensor.The interrupt + * occurs if the absolute value of acceleration data of any enabled axis + * exceeds the programmed threshold and the sign of the value does not + * change for a preset duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_high_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the default configuration parameters of accel & gyro. + * Also maintain the previous state of configurations. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static void default_param_settg(struct bmi160_dev* dev); + +/*! + * @brief This API is used to validate the device structure pointer for + * null conditions. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t null_ptr_check(const struct bmi160_dev* dev); + +/*! + * @brief This API set the accel configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_conf(struct bmi160_dev* dev); + +/*! + * @brief This API gets the accel configuration. + * + * @param[out] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t get_accel_conf(struct bmi160_dev* dev); + +/*! + * @brief This API check the accel configuration. + * + * @param[in] data : Pointer to store the updated accel config. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_accel_config(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel odr. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_odr(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel bandwidth. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_bw(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel range. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_range(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API checks the invalid settings for ODR & Bw for Accel and Gyro. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_invalid_settg(const struct bmi160_dev* dev); + +/*! + * @brief This API set the gyro configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_gyro_conf(struct bmi160_dev* dev); + +/*! + * @brief This API get the gyro configuration. + * + * @param[out] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t get_gyro_conf(struct bmi160_dev* dev); + +/*! + * @brief This API check the gyro configuration. + * + * @param[in] data : Pointer to store the updated gyro config. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_gyro_config(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro odr. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_odr(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro bandwidth. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_bw(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro range. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_range(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the accel power mode. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_pwr(struct bmi160_dev* dev); + +/*! + * @brief This API process the undersampling setting of Accel. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_under_sampling(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the gyro power mode. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_gyro_pwr(struct bmi160_dev* dev); + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] accel : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + get_accel_data(uint8_t len, struct bmi160_sensor_data* accel, const struct bmi160_dev* dev); + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] gyro : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + get_gyro_data(uint8_t len, struct bmi160_sensor_data* gyro, const struct bmi160_dev* dev); + +/*! + * @brief This API reads accel and gyro data along with sensor time + * if time is requested by user. + * Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] accel : Structure pointer to store accel data + * @param[out] gyro : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t get_accel_gyro_data( + uint8_t len, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the any-motion interrupt for accel. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_accel_any_motion_int( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + struct bmi160_dev* dev); + +/*! + * @brief This API disable the sig-motion interrupt. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t disable_sig_motion_int(const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for any-motion interrupt. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_motion_src( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the duration and threshold of + * any-motion interrupt. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_dur_threshold( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure necessary setting of any-motion interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enable the data ready interrupt. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_data_ready_int(const struct bmi160_dev* dev); + +/*! + * @brief This API enables the no motion/slow motion interrupt. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_no_motion_int( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * no motion/slow motion interrupt. + * + * @param[in] int_config : structure instance of bmi160_int_settg. + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of interrupt for no motion. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_data_src( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the duration and threshold of + * no motion/slow motion interrupt along with selection of no/slow motion. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_dur_thr( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the sig-motion motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_sig_motion_int( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * significant motion interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for sig motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_motion_data_src( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the threshold, skip and proof time of + * sig motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_dur_threshold( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the step detector interrupt. + * + * @param[in] step_detect_int_cfg : Structure instance of + * bmi160_acc_step_detect_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_step_detect_int( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the step detector parameter. + * + * @param[in] step_detect_int_cfg : Structure instance of + * bmi160_acc_step_detect_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_step_detect( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the single/double tap interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_tap_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * tap interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for tap interrupt. + * + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_data_src( + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the parameters of tap interrupt. + * Threshold, quite, shock, and duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_param( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enable the external mode configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sec_if(const struct bmi160_dev* dev); + +/*! + * @brief This API configure the ODR of the auxiliary sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_aux_odr(const struct bmi160_dev* dev); + +/*! + * @brief This API maps the actual burst read length set by user. + * + * @param[in] len : Pointer to store the read length. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t map_read_len(uint16_t* len, const struct bmi160_dev* dev); + +/*! + * @brief This API configure the settings of auxiliary sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_aux_settg(const struct bmi160_dev* dev); + +/*! + * @brief This API extract the read data from auxiliary sensor. + * + * @param[in] map_len : burst read value. + * @param[in] reg_addr : Address of register to read. + * @param[in] aux_data : Pointer to store the read data. + * @param[in] len : length to read the data. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t extract_aux_read( + uint16_t map_len, + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the orient interrupt. + * + * @param[in] orient_int_cfg : Structure instance of bmi160_acc_orient_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_orient_int( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of orientation interrupt. + * + * @param[in] orient_int_cfg : Structure instance of bmi160_acc_orient_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_orient_int_settg( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the flat interrupt. + * + * @param[in] flat_int : Structure instance of bmi160_acc_flat_detect_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_flat_int( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of flat interrupt. + * + * @param[in] flat_int : Structure instance of bmi160_acc_flat_detect_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_flat_int_settg( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the Low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_low_g_int( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) for low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_low_g_data_src( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_low_g_int_settg( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_high_g_int( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_high_g_data_src( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_high_g_int_settg( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the behavioural setting of interrupt pin. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + config_int_out_ctrl(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API configure the mode(input enable, latch or non-latch) of interrupt pin. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + config_int_latch(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API performs the self test for accelerometer of BMI160 + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t perform_accel_self_test(struct bmi160_dev* dev); + +/*! + * @brief This API enables to perform the accel self test by setting proper + * configurations to facilitate accel self test + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_accel_self_test(struct bmi160_dev* dev); + +/*! + * @brief This API performs accel self test with positive excitation + * + * @param[in] accel_pos : Structure pointer to store accel data + * for positive excitation + * @param[in] dev : structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t accel_self_test_positive_excitation( + struct bmi160_sensor_data* accel_pos, + const struct bmi160_dev* dev); + +/*! + * @brief This API performs accel self test with negative excitation + * + * @param[in] accel_neg : Structure pointer to store accel data + * for negative excitation + * @param[in] dev : structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t accel_self_test_negative_excitation( + struct bmi160_sensor_data* accel_neg, + const struct bmi160_dev* dev); + +/*! + * @brief This API validates the accel self test results + * + * @param[in] accel_pos : Structure pointer to store accel data + * for positive excitation + * @param[in] accel_neg : Structure pointer to store accel data + * for negative excitation + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error / +ve value -> Self test fail + */ +static int8_t validate_accel_self_test( + const struct bmi160_sensor_data* accel_pos, + const struct bmi160_sensor_data* accel_neg); + +/*! + * @brief This API performs the self test for gyroscope of BMI160 + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t perform_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API enables the self test bit to trigger self test for gyro + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API validates the self test results of gyro + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t validate_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API sets FIFO full interrupt of the sensor.This interrupt + * occurs when the FIFO is full and the next full data sample would cause + * a FIFO overflow, which may delete the old samples. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + set_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This enable the FIFO full interrupt engine. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + enable_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets FIFO watermark interrupt of the sensor.The FIFO + * watermark interrupt is fired, when the FIFO fill level is above a fifo + * watermark. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + set_fifo_watermark_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This enable the FIFO watermark interrupt engine. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + enable_fifo_wtm_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to reset the FIFO related configurations + * in the fifo_frame structure. + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void reset_fifo_data_structure(const struct bmi160_dev* dev); + +/*! + * @brief This API is used to read number of bytes filled + * currently in FIFO buffer. + * + * @param[in] bytes_to_read : Number of bytes available in FIFO at the + * instant which is obtained from FIFO counter. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + * @retval Any non zero value -> Fail + * + */ +static int8_t get_fifo_byte_counter(uint16_t* bytes_to_read, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to compute the number of bytes of accel FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : Number of bytes to be parsed + * @param[in] acc_frame_count : Number of accelerometer frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + * + */ +static void get_accel_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* acc_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + * + * @param[in,out] acc : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] acc_idx : Index value of accelerometer data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_accel_frame( + struct bmi160_sensor_data* acc, + uint16_t* idx, + uint8_t* acc_idx, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + * + * @param[in,out] accel_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_accel_data( + struct bmi160_sensor_data* accel_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in header mode. + * + * @param[in,out] accel_data : Structure instance of sensor data + * @param[in,out] accel_length : Number of accelerometer frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_accel_header_mode( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API computes the number of bytes of gyro FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : No of bytes to be parsed from FIFO buffer + * @param[in] gyro_frame_count : Number of Gyro data frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + */ +static void get_gyro_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* gyro_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyroscope's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + * + * @param[in,out] gyro : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] gyro_idx : Index value of gyro data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_gyro_frame( + struct bmi160_sensor_data* gyro, + uint16_t* idx, + uint8_t* gyro_idx, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + * + * @param[in,out] gyro_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_gyro_data( + struct bmi160_sensor_data* gyro_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data in header mode. + * + * @param[in,out] gyro_data : Structure instance of sensor data + * @param[in,out] gyro_length : Number of gyro frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_gyro_header_mode( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API computes the number of bytes of aux FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : No of bytes to be parsed from FIFO buffer + * @param[in] aux_frame_count : Number of Aux data frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + */ +static void get_aux_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* aux_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed + * + * @param[in,out] aux_data : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] aux_index : Index value of gyro data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_aux_frame( + struct bmi160_aux_data* aux_data, + uint16_t* idx, + uint8_t* aux_index, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data and store it in the instance of the structure bmi160_aux_data. + * + * @param[in,out] aux_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_aux_data( + struct bmi160_aux_data* aux_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data in header mode. + * + * @param[in,out] aux_data : Structure instance of sensor data + * @param[in,out] aux_length : Number of aux frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_aux_header_mode( + struct bmi160_aux_data* aux_data, + uint8_t* aux_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API checks the presence of non-valid frames in the read fifo data. + * + * @param[in,out] data_index : The index of the current data to + * be parsed from fifo data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void check_frame_validity(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to move the data index ahead of the + * current_frame_length parameter when unnecessary FIFO data appears while + * extracting the user specified data. + * + * @param[in,out] data_index : Index of the FIFO data which + * is to be moved ahead of the + * current_frame_length + * @param[in] current_frame_length : Number of bytes in a particular frame + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void move_next_frame( + uint16_t* data_index, + uint8_t current_frame_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse and store the sensor time from the + * FIFO data in the structure instance dev. + * + * @param[in,out] data_index : Index of the FIFO data which + * has the sensor time. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_sensortime_frame(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse and store the skipped_frame_count from + * the FIFO data in the structure instance dev. + * + * @param[in,out] data_index : Index of the FIFO data which + * has the skipped frame count. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_skipped_frame(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to get the FOC status from the sensor + * + * @param[in,out] foc_status : Result of FOC status. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t get_foc_status(uint8_t* foc_status, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to configure the offset enable bits in the sensor + * + * @param[in,out] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC and offset configurations + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + configure_offset_enable(const struct bmi160_foc_conf* foc_conf, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to trigger the FOC in the sensor + * + * @param[in,out] offset : Structure instance of bmi160_offsets which + * reads and stores the offset values after FOC + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t trigger_foc(struct bmi160_offsets* offset, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to map/unmap the Dataready(Accel & Gyro), FIFO full + * and FIFO watermark interrupt + * + * @param[in] int_config : Structure instance of bmi160_int_settg which + * stores the interrupt type and interrupt channel + * configurations to map/unmap the interrupt pins + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + map_hardware_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to map/unmap the Any/Sig motion, Step det/Low-g, + * Double tap, Single tap, Orientation, Flat, High-G, Nomotion interrupt pins. + * + * @param[in] int_config : Structure instance of bmi160_int_settg which + * stores the interrupt type and interrupt channel + * configurations to map/unmap the interrupt pins + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + map_feature_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*********************** User function definitions ****************************/ + +/*! + * @brief This API reads the data from the given register address + * of sensor. + */ +int8_t + bmi160_get_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->read == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else if(len == 0) { + rslt = BMI160_E_READ_WRITE_LENGTH_INVALID; + } else { + /* Configuring reg_addr for SPI Interface */ + if(dev->intf == BMI160_SPI_INTF) { + reg_addr = (reg_addr | BMI160_SPI_RD_MASK); + } + + rslt = dev->read(dev->id, reg_addr, data, len); + } + + return rslt; +} + +/*! + * @brief This API writes the given data to the register address + * of sensor. + */ +int8_t + bmi160_set_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t count = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->write == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else if(len == 0) { + rslt = BMI160_E_READ_WRITE_LENGTH_INVALID; + } else { + /* Configuring reg_addr for SPI Interface */ + if(dev->intf == BMI160_SPI_INTF) { + reg_addr = (reg_addr & BMI160_SPI_WR_MASK); + } + + if((dev->prev_accel_cfg.power == BMI160_ACCEL_NORMAL_MODE) || + (dev->prev_gyro_cfg.power == BMI160_GYRO_NORMAL_MODE)) { + rslt = dev->write(dev->id, reg_addr, data, len); + + /* Kindly refer bmi160 data sheet section 3.2.4 */ + dev->delay_ms(1); + + } else { + /*Burst write is not allowed in + * suspend & low power mode */ + for(; count < len; count++) { + rslt = dev->write(dev->id, reg_addr, &data[count], 1); + reg_addr++; + + /* Kindly refer bmi160 data sheet section 3.2.4 */ + dev->delay_ms(1); + } + } + + if(rslt != BMI160_OK) { + rslt = BMI160_E_COM_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API is the entry point for sensor.It performs + * the selection of I2C/SPI read mechanism according to the + * selected interface and reads the chip-id of bmi160 sensor. + */ +int8_t bmi160_init(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data; + uint8_t try = 3; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + + /* Dummy read of 0x7F register to enable SPI Interface + * if SPI is used */ + if((rslt == BMI160_OK) && (dev->intf == BMI160_SPI_INTF)) { + rslt = bmi160_get_regs(BMI160_SPI_COMM_TEST_ADDR, &data, 1, dev); + } + + if(rslt == BMI160_OK) { + /* Assign chip id as zero */ + dev->chip_id = 0; + + while((try--) && (dev->chip_id != BMI160_CHIP_ID)) { + /* Read chip_id */ + rslt = bmi160_get_regs(BMI160_CHIP_ID_ADDR, &dev->chip_id, 1, dev); + } + + if((rslt == BMI160_OK) && (dev->chip_id == BMI160_CHIP_ID)) { + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + + /* Soft reset */ + rslt = bmi160_soft_reset(dev); + } else { + rslt = BMI160_E_DEV_NOT_FOUND; + } + } + + return rslt; +} + +/*! + * @brief This API resets and restarts the device. + * All register values are overwritten with default parameters. + */ +int8_t bmi160_soft_reset(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = BMI160_SOFT_RESET_CMD; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Reset the device */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &data, 1, dev); + dev->delay_ms(BMI160_SOFT_RESET_DELAY_MS); + if((rslt == BMI160_OK) && (dev->intf == BMI160_SPI_INTF)) { + /* Dummy read of 0x7F register to enable SPI Interface + * if SPI is used */ + rslt = bmi160_get_regs(BMI160_SPI_COMM_TEST_ADDR, &data, 1, dev); + } + + if(rslt == BMI160_OK) { + /* Update the default parameters */ + default_param_settg(dev); + } + } + + return rslt; +} + +/*! + * @brief This API configures the power mode, range and bandwidth + * of sensor. + */ +int8_t bmi160_set_sens_conf(struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = set_accel_conf(dev); + if(rslt == BMI160_OK) { + rslt = set_gyro_conf(dev); + if(rslt == BMI160_OK) { + /* write power mode for accel and gyro */ + rslt = bmi160_set_power_mode(dev); + if(rslt == BMI160_OK) { + rslt = check_invalid_settg(dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API gets accel and gyro configurations. + */ +int8_t bmi160_get_sens_conf(struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_conf(dev); + if(rslt == BMI160_OK) { + rslt = get_gyro_conf(dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the power mode of the sensor. + */ +int8_t bmi160_set_power_mode(struct bmi160_dev* dev) { + int8_t rslt = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = set_accel_pwr(dev); + if(rslt == BMI160_OK) { + rslt = set_gyro_pwr(dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the power mode of the sensor. + */ +int8_t bmi160_get_power_mode(struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t power_mode = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_PMU_STATUS_ADDR, &power_mode, 1, dev); + if(rslt == BMI160_OK) { + /* Power mode of the accel, gyro sensor is obtained */ + dev->gyro_cfg.power = BMI160_GET_BITS(power_mode, BMI160_GYRO_POWER_MODE); + dev->accel_cfg.power = BMI160_GET_BITS(power_mode, BMI160_ACCEL_POWER_MODE); + } + } + + return rslt; +} + +/*! + * @brief This API reads sensor data, stores it in + * the bmi160_sensor_data structure pointer passed by the user. + */ +int8_t bmi160_get_sensor_data( + uint8_t select_sensor, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t time_sel; + uint8_t sen_sel; + uint8_t len = 0; + + /*Extract the sensor and time select information*/ + sen_sel = select_sensor & BMI160_SEN_SEL_MASK; + time_sel = ((sen_sel & BMI160_TIME_SEL) >> 2); + sen_sel = sen_sel & (BMI160_ACCEL_SEL | BMI160_GYRO_SEL); + if(time_sel == 1) { + len = 3; + } + + /* Null-pointer check */ + if(dev != NULL) { + switch(sen_sel) { + case BMI160_ACCEL_ONLY: + + /* Null-pointer check */ + if(accel == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_data(len, accel, dev); + } + + break; + case BMI160_GYRO_ONLY: + + /* Null-pointer check */ + if(gyro == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_gyro_data(len, gyro, dev); + } + + break; + case BMI160_BOTH_ACCEL_AND_GYRO: + + /* Null-pointer check */ + if((gyro == NULL) || (accel == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_gyro_data(len, accel, gyro, dev); + } + + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + } else { + rslt = BMI160_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API configures the necessary interrupt based on + * the user settings in the bmi160_int_settg structure instance. + */ +int8_t bmi160_set_int_config(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + switch(int_config->int_type) { + case BMI160_ACC_ANY_MOTION_INT: + + /*Any-motion interrupt*/ + rslt = set_accel_any_motion_int(int_config, dev); + break; + case BMI160_ACC_SIG_MOTION_INT: + + /* Significant motion interrupt */ + rslt = set_accel_sig_motion_int(int_config, dev); + break; + case BMI160_ACC_SLOW_NO_MOTION_INT: + + /* Slow or no motion interrupt */ + rslt = set_accel_no_motion_int(int_config, dev); + break; + case BMI160_ACC_DOUBLE_TAP_INT: + case BMI160_ACC_SINGLE_TAP_INT: + + /* Double tap and single tap Interrupt */ + rslt = set_accel_tap_int(int_config, dev); + break; + case BMI160_STEP_DETECT_INT: + + /* Step detector interrupt */ + rslt = set_accel_step_detect_int(int_config, dev); + break; + case BMI160_ACC_ORIENT_INT: + + /* Orientation interrupt */ + rslt = set_accel_orientation_int(int_config, dev); + break; + case BMI160_ACC_FLAT_INT: + + /* Flat detection interrupt */ + rslt = set_accel_flat_detect_int(int_config, dev); + break; + case BMI160_ACC_LOW_G_INT: + + /* Low-g interrupt */ + rslt = set_accel_low_g_int(int_config, dev); + break; + case BMI160_ACC_HIGH_G_INT: + + /* High-g interrupt */ + rslt = set_accel_high_g_int(int_config, dev); + break; + case BMI160_ACC_GYRO_DATA_RDY_INT: + + /* Data ready interrupt */ + rslt = set_accel_gyro_data_ready_int(int_config, dev); + break; + case BMI160_ACC_GYRO_FIFO_FULL_INT: + + /* Fifo full interrupt */ + rslt = set_fifo_full_int(int_config, dev); + break; + case BMI160_ACC_GYRO_FIFO_WATERMARK_INT: + + /* Fifo water-mark interrupt */ + rslt = set_fifo_watermark_int(int_config, dev); + break; + case BMI160_FIFO_TAG_INT_PIN: + + /* Fifo tagging feature support */ + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + break; + default: + break; + } + + return rslt; +} + +/*! + * @brief This API enables or disable the step counter feature. + * 1 - enable step counter (0 - disable) + */ +int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_INT_STEP_CONFIG_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(step_cnt_enable == BMI160_ENABLE) { + data |= (uint8_t)(step_cnt_enable << 3); + } else { + data &= ~BMI160_STEP_COUNT_EN_BIT_MASK; + } + + rslt = bmi160_set_regs(BMI160_INT_STEP_CONFIG_1_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API reads the step counter value. + */ +int8_t bmi160_read_step_counter(uint16_t* step_val, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0, 0}; + uint16_t msb = 0; + uint8_t lsb = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_INT_STEP_CNT_0_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + lsb = data[0]; + msb = data[1] << 8; + *step_val = msb | lsb; + } + } + + return rslt; +} + +/*! + * @brief This API reads the mention no of byte of data from the given + * register address of auxiliary sensor. + */ +int8_t bmi160_aux_read( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint16_t map_len = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->read == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + rslt = map_read_len(&map_len, dev); + if(rslt == BMI160_OK) { + rslt = extract_aux_read(map_len, reg_addr, aux_data, len, dev); + } + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API writes the mention no of byte of data to the given + * register address of auxiliary sensor. + */ +int8_t bmi160_aux_write( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t count = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->write == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + for(; count < len; count++) { + /* set data to write */ + rslt = bmi160_set_regs(BMI160_AUX_IF_4_ADDR, aux_data, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + /* set address to write */ + rslt = bmi160_set_regs(BMI160_AUX_IF_3_ADDR, ®_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK && (count < len - 1)) { + aux_data++; + reg_addr++; + } + } + } + } + + return rslt; +} + +/*! + * @brief This API initialize the auxiliary sensor + * in order to access it. + */ +int8_t bmi160_aux_init(const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + /* Configures the auxiliary sensor interface settings */ + rslt = config_aux_settg(dev); + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API is used to setup the auxiliary sensor of bmi160 in auto mode + * Thus enabling the auto update of 8 bytes of data from auxiliary sensor + * to BMI160 register address 0x04 to 0x0B + */ +int8_t bmi160_set_aux_auto_mode(uint8_t* data_addr, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + /* Write the aux. address to read in 0x4D of BMI160*/ + rslt = bmi160_set_regs(BMI160_AUX_IF_2_ADDR, data_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + /* Configure the polling ODR for + * auxiliary sensor */ + rslt = config_aux_odr(dev); + if(rslt == BMI160_OK) { + /* Disable the aux. manual mode, i.e aux. + * sensor is in auto-mode (data-mode) */ + dev->aux_cfg.manual_enable = BMI160_DISABLE; + rslt = bmi160_config_aux_mode(dev); + + /* Auxiliary sensor data is obtained + * in auto mode from this point */ + } + } + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API configures the 0x4C register and settings like + * Auxiliary sensor manual enable/ disable and aux burst read length. + */ +int8_t bmi160_config_aux_mode(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t aux_if[2] = {(uint8_t)(dev->aux_cfg.aux_i2c_addr * 2), 0}; + + rslt = bmi160_get_regs(BMI160_AUX_IF_1_ADDR, &aux_if[1], 1, dev); + if(rslt == BMI160_OK) { + /* update the Auxiliary interface to manual/auto mode */ + aux_if[1] = BMI160_SET_BITS(aux_if[1], BMI160_MANUAL_MODE_EN, dev->aux_cfg.manual_enable); + + /* update the burst read length defined by user */ + aux_if[1] = + BMI160_SET_BITS_POS_0(aux_if[1], BMI160_AUX_READ_BURST, dev->aux_cfg.aux_rd_burst_len); + + /* Set the secondary interface address and manual mode + * along with burst read length */ + rslt = bmi160_set_regs(BMI160_AUX_IF_0_ADDR, &aux_if[0], 2, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + } + + return rslt; +} + +/*! + * @brief This API is used to read the raw uncompensated auxiliary sensor + * data of 8 bytes from BMI160 register address 0x04 to 0x0B + */ +int8_t bmi160_read_aux_data_auto_mode(uint8_t* aux_data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if((dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) && + (dev->aux_cfg.manual_enable == BMI160_DISABLE)) { + /* Read the aux. sensor's raw data */ + rslt = bmi160_get_regs(BMI160_AUX_DATA_ADDR, aux_data, 8, dev); + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This is used to perform self test of accel/gyro of the BMI160 sensor + */ +int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev* dev) { + int8_t rslt; + int8_t self_test_rslt = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Proceed if null check is fine */ + switch(select_sensor) { + case BMI160_ACCEL_ONLY: + rslt = perform_accel_self_test(dev); + break; + case BMI160_GYRO_ONLY: + + /* Set the power mode as normal mode */ + dev->gyro_cfg.power = BMI160_GYRO_NORMAL_MODE; + rslt = bmi160_set_power_mode(dev); + + /* Perform gyro self test */ + if(rslt == BMI160_OK) { + /* Perform gyro self test */ + rslt = perform_gyro_self_test(dev); + } + + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + + /* Check to ensure bus error does not occur */ + if(rslt >= BMI160_OK) { + /* Store the status of self test result */ + self_test_rslt = rslt; + + /* Perform soft reset */ + rslt = bmi160_soft_reset(dev); + } + + /* Check to ensure bus operations are success */ + if(rslt == BMI160_OK) { + /* Restore self_test_rslt as return value */ + rslt = self_test_rslt; + } + } + + return rslt; +} + +/*! + * @brief This API reads the data from fifo buffer. + */ +int8_t bmi160_get_fifo_data(struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t bytes_to_read = 0; + uint16_t user_fifo_len = 0; + + /* check the bmi160 structure as NULL*/ + if((dev == NULL) || (dev->fifo->data == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + reset_fifo_data_structure(dev); + + /* get current FIFO fill-level*/ + rslt = get_fifo_byte_counter(&bytes_to_read, dev); + if(rslt == BMI160_OK) { + user_fifo_len = dev->fifo->length; + if((dev->fifo->length > bytes_to_read)) { + /* Handling the case where user requests + * more data than available in FIFO */ + dev->fifo->length = bytes_to_read; + } + + if((dev->fifo->fifo_time_enable == BMI160_FIFO_TIME_ENABLE) && + (bytes_to_read + BMI160_FIFO_BYTES_OVERREAD <= user_fifo_len)) { + /* Handling case of sensor time availability*/ + dev->fifo->length = dev->fifo->length + BMI160_FIFO_BYTES_OVERREAD; + } + + /* read only the filled bytes in the FIFO Buffer */ + rslt = bmi160_get_regs(BMI160_FIFO_DATA_ADDR, dev->fifo->data, dev->fifo->length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API writes fifo_flush command to command register.This + * action clears all data in the Fifo without changing fifo configuration + * settings + */ +int8_t bmi160_set_fifo_flush(const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = BMI160_FIFO_FLUSH_VALUE; + uint8_t reg_addr = BMI160_COMMAND_REG_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + + return rslt; +} + +/*! + * @brief This API sets the FIFO configuration in the sensor. + */ +int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint8_t data = 0; + uint8_t reg_addr = BMI160_FIFO_CONFIG_1_ADDR; + uint8_t fifo_config = config & BMI160_FIFO_CONFIG_1_MASK; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + if(fifo_config > 0) { + if(enable == BMI160_ENABLE) { + data = data | fifo_config; + } else { + data = data & (~fifo_config); + } + } + + /* write fifo frame content configuration*/ + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + /* read fifo frame content configuration*/ + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + /* extract fifo header enabled status */ + dev->fifo->fifo_header_enable = data & BMI160_FIFO_HEAD_ENABLE; + + /* extract accel/gyr/aux. data enabled status */ + dev->fifo->fifo_data_enable = data & BMI160_FIFO_M_G_A_ENABLE; + + /* extract fifo sensor time enabled status */ + dev->fifo->fifo_time_enable = data & BMI160_FIFO_TIME_ENABLE; + } + } + } + } + + return rslt; +} + +/*! @brief This API is used to configure the down sampling ratios of + * the accel and gyro data for FIFO.Also, it configures filtered or + * pre-filtered data for accel and gyro. + * + */ +int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = 0; + uint8_t reg_addr = BMI160_FIFO_DOWN_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + data = data | fifo_down; + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the FIFO watermark level in the sensor. + * + */ +int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = fifo_wm; + uint8_t reg_addr = BMI160_FIFO_CONFIG_0_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the accelerometer frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "accel_data" structure instance. + */ +int8_t bmi160_extract_accel( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t accel_index = 0; + uint8_t fifo_data_enable = 0; + + if(dev == NULL || dev->fifo == NULL || dev->fifo->data == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_accel_len_to_parse(&data_index, &data_read_length, accel_length, dev); + for(; data_index < data_read_length;) { + /*Check for the availability of next two bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_accel_frame(accel_data, &data_index, &accel_index, fifo_data_enable, dev); + } + + /* update number of accel data read*/ + *accel_length = accel_index; + + /*update the accel byte index*/ + dev->fifo->accel_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_accel_header_mode(accel_data, accel_length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the gyro frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "gyro_data" structure instance. + */ +int8_t bmi160_extract_gyro( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t gyro_index = 0; + uint8_t fifo_data_enable = 0; + + if(dev == NULL || dev->fifo->data == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_gyro_len_to_parse(&data_index, &data_read_length, gyro_length, dev); + for(; data_index < data_read_length;) { + /*Check for the availability of next two bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_gyro_frame(gyro_data, &data_index, &gyro_index, fifo_data_enable, dev); + } + + /* update number of gyro data read */ + *gyro_length = gyro_index; + + /* update the gyro byte index */ + dev->fifo->gyro_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_gyro_header_mode(gyro_data, gyro_length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the aux frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "aux_data" structure instance. + */ +int8_t bmi160_extract_aux( + struct bmi160_aux_data* aux_data, + uint8_t* aux_len, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t aux_index = 0; + uint8_t fifo_data_enable = 0; + + if((dev == NULL) || (dev->fifo->data == NULL) || (aux_data == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_aux_len_to_parse(&data_index, &data_read_length, aux_len, dev); + for(; data_index < data_read_length;) { + /* Check for the availability of next two + * bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_aux_frame(aux_data, &data_index, &aux_index, fifo_data_enable, dev); + } + + /* update number of aux data read */ + *aux_len = aux_index; + + /* update the aux byte index */ + dev->fifo->aux_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_aux_header_mode(aux_data, aux_len, dev); + } + } + + return rslt; +} + +/*! + * @brief This API starts the FOC of accel and gyro + * + * @note FOC should not be used in low-power mode of sensor + * + * @note Accel FOC targets values of +1g , 0g , -1g + * Gyro FOC always targets value of 0 dps + */ +int8_t bmi160_start_foc( + const struct bmi160_foc_conf* foc_conf, + struct bmi160_offsets* offset, + struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Set the offset enable bits */ + rslt = configure_offset_enable(foc_conf, dev); + if(rslt == BMI160_OK) { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_FOC_CONF_ADDR, &data, 1, dev); + + /* Set the FOC config for gyro */ + data = BMI160_SET_BITS(data, BMI160_GYRO_FOC_EN, foc_conf->foc_gyr_en); + + /* Set the FOC config for accel xyz axes */ + data = BMI160_SET_BITS(data, BMI160_ACCEL_FOC_X_CONF, foc_conf->foc_acc_x); + data = BMI160_SET_BITS(data, BMI160_ACCEL_FOC_Y_CONF, foc_conf->foc_acc_y); + data = BMI160_SET_BITS_POS_0(data, BMI160_ACCEL_FOC_Z_CONF, foc_conf->foc_acc_z); + if(rslt == BMI160_OK) { + /* Set the FOC config in the sensor */ + rslt = bmi160_set_regs(BMI160_FOC_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Procedure to trigger + * FOC and check status */ + rslt = trigger_foc(offset, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API reads and stores the offset values of accel and gyro + */ +int8_t bmi160_get_offsets(struct bmi160_offsets* offset, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[7]; + uint8_t lsb, msb; + int16_t offset_msb, offset_lsb; + int16_t offset_data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_OFFSET_ADDR, data, 7, dev); + + /* Accel offsets */ + offset->off_acc_x = (int8_t)data[0]; + offset->off_acc_y = (int8_t)data[1]; + offset->off_acc_z = (int8_t)data[2]; + + /* Gyro x-axis offset */ + lsb = data[3]; + msb = BMI160_GET_BITS_POS_0(data[6], BMI160_GYRO_OFFSET_X); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_x = (int16_t)(offset_data / 64); + + /* Gyro y-axis offset */ + lsb = data[4]; + msb = BMI160_GET_BITS(data[6], BMI160_GYRO_OFFSET_Y); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_y = (int16_t)(offset_data / 64); + + /* Gyro z-axis offset */ + lsb = data[5]; + msb = BMI160_GET_BITS(data[6], BMI160_GYRO_OFFSET_Z); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_z = (int16_t)(offset_data / 64); + } + + return rslt; +} + +/*! + * @brief This API writes the offset values of accel and gyro to + * the sensor but these values will be reset on POR or soft reset. + */ +int8_t bmi160_set_offsets( + const struct bmi160_foc_conf* foc_conf, + const struct bmi160_offsets* offset, + struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data[7]; + uint8_t x_msb, y_msb, z_msb; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Update the accel offset */ + data[0] = (uint8_t)offset->off_acc_x; + data[1] = (uint8_t)offset->off_acc_y; + data[2] = (uint8_t)offset->off_acc_z; + + /* Update the LSB of gyro offset */ + data[3] = BMI160_GET_LSB(offset->off_gyro_x); + data[4] = BMI160_GET_LSB(offset->off_gyro_y); + data[5] = BMI160_GET_LSB(offset->off_gyro_z); + + /* Update the MSB of gyro offset */ + x_msb = BMI160_GET_BITS(offset->off_gyro_x, BMI160_GYRO_OFFSET); + y_msb = BMI160_GET_BITS(offset->off_gyro_y, BMI160_GYRO_OFFSET); + z_msb = BMI160_GET_BITS(offset->off_gyro_z, BMI160_GYRO_OFFSET); + data[6] = (uint8_t)(z_msb << 4 | y_msb << 2 | x_msb); + + /* Set the offset enable/disable for gyro and accel */ + data[6] = BMI160_SET_BITS(data[6], BMI160_GYRO_OFFSET_EN, foc_conf->gyro_off_en); + data[6] = BMI160_SET_BITS(data[6], BMI160_ACCEL_OFFSET_EN, foc_conf->acc_off_en); + + /* Set the offset config and values in the sensor */ + rslt = bmi160_set_regs(BMI160_OFFSET_ADDR, data, 7, dev); + } + + return rslt; +} + +/*! + * @brief This API writes the image registers values to NVM which is + * stored even after POR or soft reset + */ +int8_t bmi160_update_nvm(struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + uint8_t cmd = BMI160_NVM_BACKUP_EN; + + /* Read the nvm_prog_en configuration */ + rslt = bmi160_get_regs(BMI160_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_NVM_UPDATE, 1); + + /* Set the nvm_prog_en bit in the sensor */ + rslt = bmi160_set_regs(BMI160_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Update NVM */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* Check for NVM ready status */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_GET_BITS(data, BMI160_NVM_STATUS); + if(data != BMI160_ENABLE) { + /* Delay to update NVM */ + dev->delay_ms(25); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the interrupt status from the sensor. + */ +int8_t bmi160_get_int_status( + enum bmi160_int_status_sel int_status_sel, + union bmi160_int_status* int_status, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + + /* To get the status of all interrupts */ + if(int_status_sel == BMI160_INT_STATUS_ALL) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR, &int_status->data[0], 4, dev); + } else { + if(int_status_sel & BMI160_INT_STATUS_0) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR, &int_status->data[0], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_1) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 1, &int_status->data[1], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_2) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 2, &int_status->data[2], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_3) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 3, &int_status->data[3], 1, dev); + } + } + + return rslt; +} + +/*********************** Local function definitions ***************************/ + +/*! + * @brief This API sets the any-motion interrupt of the sensor. + * This interrupt occurs when accel values exceeds preset threshold + * for a certain period of time. + */ +static int8_t + set_accel_any_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg = + &(int_config->int_type_cfg.acc_any_motion_int); + rslt = enable_accel_any_motion_int(any_motion_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_any_motion_int_settg(int_config, any_motion_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets tap interrupts.Interrupt is fired when + * tap movements happen. + */ +static int8_t + set_accel_tap_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_tap_int_cfg* tap_int_cfg = &(int_config->int_type_cfg.acc_tap_int); + rslt = enable_tap_int(int_config, tap_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_int_settg(int_config, tap_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the data ready interrupt for both accel and gyro. + * This interrupt occurs when new accel and gyro data comes. + */ +static int8_t set_accel_gyro_data_ready_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = enable_data_ready_int(dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the significant motion interrupt of the sensor.This + * interrupt occurs when there is change in user location. + */ +static int8_t + set_accel_sig_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg = + &(int_config->int_type_cfg.acc_sig_motion_int); + rslt = enable_sig_motion_int(sig_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_motion_int_settg(int_config, sig_mot_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the no motion/slow motion interrupt of the sensor. + * Slow motion is similar to any motion interrupt.No motion interrupt + * occurs when slope bet. two accel values falls below preset threshold + * for preset duration. + */ +static int8_t + set_accel_no_motion_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg = + &(int_config->int_type_cfg.acc_no_motion_int); + rslt = enable_no_motion_int(no_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure the INT PIN settings*/ + rslt = config_no_motion_int_settg(int_config, no_mot_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the step detection interrupt.This interrupt + * occurs when the single step causes accel values to go above + * preset threshold. + */ +static int8_t + set_accel_step_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg = + &(int_config->int_type_cfg.acc_step_detect_int); + rslt = enable_step_detect_int(step_detect_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_step_detect(step_detect_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the orientation interrupt of the sensor.This + * interrupt occurs when there is orientation change in the sensor + * with respect to gravitational field vector g. + */ +static int8_t + set_accel_orientation_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_orient_int_cfg* orient_int_cfg = + &(int_config->int_type_cfg.acc_orient_int); + rslt = enable_orient_int(orient_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* map INT pin to orient interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the + * orientation setting*/ + rslt = config_orient_int_settg(orient_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the flat interrupt of the sensor.This interrupt + * occurs in case of flat orientation + */ +static int8_t + set_accel_flat_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_flat_detect_int_cfg* flat_detect_int = + &(int_config->int_type_cfg.acc_flat_int); + + /* enable the flat interrupt */ + rslt = enable_flat_int(flat_detect_int, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* map INT pin to flat interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the flat setting*/ + rslt = config_flat_int_settg(flat_detect_int, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the low-g interrupt of the sensor.This interrupt + * occurs during free-fall. + */ +static int8_t + set_accel_low_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_low_g_int_cfg* low_g_int = &(int_config->int_type_cfg.acc_low_g_int); + + /* Enable the low-g interrupt*/ + rslt = enable_low_g_int(low_g_int, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* Map INT pin to low-g interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the data source + * for low-g interrupt*/ + rslt = config_low_g_data_src(low_g_int, dev); + if(rslt == BMI160_OK) { + rslt = config_low_g_int_settg(low_g_int, dev); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the high-g interrupt of the sensor.The interrupt + * occurs if the absolute value of acceleration data of any enabled axis + * exceeds the programmed threshold and the sign of the value does not + * change for a preset duration. + */ +static int8_t + set_accel_high_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_high_g_int_cfg* high_g_int_cfg = + &(int_config->int_type_cfg.acc_high_g_int); + + /* Enable the high-g interrupt */ + rslt = enable_high_g_int(high_g_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* Map INT pin to high-g interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the data source + * for high-g interrupt*/ + rslt = config_high_g_data_src(high_g_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_high_g_int_settg(high_g_int_cfg, dev); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API configures the pins to fire the + * interrupt signal when it occurs. + */ +static int8_t + set_intr_pin_config(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* configure the behavioural settings of interrupt pin */ + rslt = config_int_out_ctrl(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_int_latch(int_config, dev); + } + + return rslt; +} + +/*! + * @brief This internal API is used to validate the device structure pointer for + * null conditions. + */ +static int8_t null_ptr_check(const struct bmi160_dev* dev) { + int8_t rslt; + + if((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Device structure is fine */ + rslt = BMI160_OK; + } + + return rslt; +} + +/*! + * @brief This API sets the default configuration parameters of accel & gyro. + * Also maintain the previous state of configurations. + */ +static void default_param_settg(struct bmi160_dev* dev) { + /* Initializing accel and gyro params with + * default values */ + dev->accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; + dev->accel_cfg.odr = BMI160_ACCEL_ODR_100HZ; + dev->accel_cfg.power = BMI160_ACCEL_SUSPEND_MODE; + dev->accel_cfg.range = BMI160_ACCEL_RANGE_2G; + dev->gyro_cfg.bw = BMI160_GYRO_BW_NORMAL_MODE; + dev->gyro_cfg.odr = BMI160_GYRO_ODR_100HZ; + dev->gyro_cfg.power = BMI160_GYRO_SUSPEND_MODE; + dev->gyro_cfg.range = BMI160_GYRO_RANGE_2000_DPS; + + /* To maintain the previous state of accel configuration */ + dev->prev_accel_cfg = dev->accel_cfg; + + /* To maintain the previous state of gyro configuration */ + dev->prev_gyro_cfg = dev->gyro_cfg; +} + +/*! + * @brief This API set the accel configuration. + */ +static int8_t set_accel_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + rslt = check_accel_config(data, dev); + if(rslt == BMI160_OK) { + /* Write output data rate and bandwidth */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, &data[0], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_accel_cfg.odr = dev->accel_cfg.odr; + dev->prev_accel_cfg.bw = dev->accel_cfg.bw; + + /* write accel range */ + rslt = bmi160_set_regs(BMI160_ACCEL_RANGE_ADDR, &data[1], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_accel_cfg.range = dev->accel_cfg.range; + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the accel configuration. + */ +static int8_t get_accel_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + /* Get accel configurations */ + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + dev->accel_cfg.odr = (data[0] & BMI160_ACCEL_ODR_MASK); + dev->accel_cfg.bw = (data[0] & BMI160_ACCEL_BW_MASK) >> BMI160_ACCEL_BW_POS; + dev->accel_cfg.range = (data[1] & BMI160_ACCEL_RANGE_MASK); + } + + return rslt; +} + +/*! + * @brief This API check the accel configuration. + */ +static int8_t check_accel_config(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* read accel Output data rate and bandwidth */ + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + rslt = process_accel_odr(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_accel_bw(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_accel_range(&data[1], dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API process the accel odr. + */ +static int8_t process_accel_odr(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t odr = 0; + + if(dev->accel_cfg.odr <= BMI160_ACCEL_ODR_1600HZ) { + if(dev->accel_cfg.odr != dev->prev_accel_cfg.odr) { + odr = (uint8_t)dev->accel_cfg.odr; + temp = *data & ~BMI160_ACCEL_ODR_MASK; + + /* Adding output data rate */ + *data = temp | (odr & BMI160_ACCEL_ODR_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the accel bandwidth. + */ +static int8_t process_accel_bw(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t bw = 0; + + if(dev->accel_cfg.bw <= BMI160_ACCEL_BW_RES_AVG128) { + if(dev->accel_cfg.bw != dev->prev_accel_cfg.bw) { + bw = (uint8_t)dev->accel_cfg.bw; + temp = *data & ~BMI160_ACCEL_BW_MASK; + + /* Adding bandwidth */ + *data = temp | ((bw << 4) & BMI160_ACCEL_BW_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the accel range. + */ +static int8_t process_accel_range(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t range = 0; + + if(dev->accel_cfg.range <= BMI160_ACCEL_RANGE_16G) { + if(dev->accel_cfg.range != dev->prev_accel_cfg.range) { + range = (uint8_t)dev->accel_cfg.range; + temp = *data & ~BMI160_ACCEL_RANGE_MASK; + + /* Adding range */ + *data = temp | (range & BMI160_ACCEL_RANGE_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API checks the invalid settings for ODR & Bw for + * Accel and Gyro. + */ +static int8_t check_invalid_settg(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + /* read the error reg */ + rslt = bmi160_get_regs(BMI160_ERROR_REG_ADDR, &data, 1, dev); + data = data >> 1; + data = data & BMI160_ERR_REG_MASK; + if(data == 1) { + rslt = BMI160_E_ACCEL_ODR_BW_INVALID; + } else if(data == 2) { + rslt = BMI160_E_GYRO_ODR_BW_INVALID; + } else if(data == 3) { + rslt = BMI160_E_LWP_PRE_FLTR_INT_INVALID; + } else if(data == 7) { + rslt = BMI160_E_LWP_PRE_FLTR_INVALID; + } + + return rslt; +} +static int8_t set_gyro_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + rslt = check_gyro_config(data, dev); + if(rslt == BMI160_OK) { + /* Write output data rate and bandwidth */ + rslt = bmi160_set_regs(BMI160_GYRO_CONFIG_ADDR, &data[0], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_gyro_cfg.odr = dev->gyro_cfg.odr; + dev->prev_gyro_cfg.bw = dev->gyro_cfg.bw; + + /* Write gyro range */ + rslt = bmi160_set_regs(BMI160_GYRO_RANGE_ADDR, &data[1], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_gyro_cfg.range = dev->gyro_cfg.range; + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the gyro configuration. + */ +static int8_t get_gyro_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + /* Get accel configurations */ + rslt = bmi160_get_regs(BMI160_GYRO_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + dev->gyro_cfg.odr = (data[0] & BMI160_GYRO_ODR_MASK); + dev->gyro_cfg.bw = (data[0] & BMI160_GYRO_BW_MASK) >> BMI160_GYRO_BW_POS; + dev->gyro_cfg.range = (data[1] & BMI160_GYRO_RANGE_MASK); + } + + return rslt; +} + +/*! + * @brief This API check the gyro configuration. + */ +static int8_t check_gyro_config(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* read gyro Output data rate and bandwidth */ + rslt = bmi160_get_regs(BMI160_GYRO_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_odr(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_bw(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_range(&data[1], dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API process the gyro odr. + */ +static int8_t process_gyro_odr(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t odr = 0; + + if(dev->gyro_cfg.odr <= BMI160_GYRO_ODR_3200HZ) { + if(dev->gyro_cfg.odr != dev->prev_gyro_cfg.odr) { + odr = (uint8_t)dev->gyro_cfg.odr; + temp = (*data & ~BMI160_GYRO_ODR_MASK); + + /* Adding output data rate */ + *data = temp | (odr & BMI160_GYRO_ODR_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the gyro bandwidth. + */ +static int8_t process_gyro_bw(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t bw = 0; + + if(dev->gyro_cfg.bw <= BMI160_GYRO_BW_NORMAL_MODE) { + bw = (uint8_t)dev->gyro_cfg.bw; + temp = *data & ~BMI160_GYRO_BW_MASK; + + /* Adding bandwidth */ + *data = temp | ((bw << 4) & BMI160_GYRO_BW_MASK); + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the gyro range. + */ +static int8_t process_gyro_range(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t range = 0; + + if(dev->gyro_cfg.range <= BMI160_GYRO_RANGE_125_DPS) { + if(dev->gyro_cfg.range != dev->prev_gyro_cfg.range) { + range = (uint8_t)dev->gyro_cfg.range; + temp = *data & ~BMI160_GYRO_RANGE_MASK; + + /* Adding range */ + *data = temp | (range & BMI160_GYRO_RANGE_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API sets the accel power. + */ +static int8_t set_accel_pwr(struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = 0; + + if((dev->accel_cfg.power >= BMI160_ACCEL_SUSPEND_MODE) && + (dev->accel_cfg.power <= BMI160_ACCEL_LOWPOWER_MODE)) { + if(dev->accel_cfg.power != dev->prev_accel_cfg.power) { + rslt = process_under_sampling(&data, dev); + if(rslt == BMI160_OK) { + /* Write accel power */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &dev->accel_cfg.power, 1, dev); + + /* Add delay of 3.8 ms - refer data sheet table 24*/ + if(dev->prev_accel_cfg.power == BMI160_ACCEL_SUSPEND_MODE) { + dev->delay_ms(BMI160_ACCEL_DELAY_MS); + } + + dev->prev_accel_cfg.power = dev->accel_cfg.power; + } + } + } else { + rslt = BMI160_E_INVALID_CONFIG; + } + + return rslt; +} + +/*! + * @brief This API process the undersampling setting of Accel. + */ +static int8_t process_under_sampling(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t pre_filter[2] = {0}; + + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + if(rslt == BMI160_OK) { + if(dev->accel_cfg.power == BMI160_ACCEL_LOWPOWER_MODE) { + temp = *data & ~BMI160_ACCEL_UNDERSAMPLING_MASK; + + /* Set under-sampling parameter */ + *data = temp | ((1 << 7) & BMI160_ACCEL_UNDERSAMPLING_MASK); + + /* Write data */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + + /* Disable the pre-filter data in low power mode */ + if(rslt == BMI160_OK) { + /* Disable the Pre-filter data*/ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, pre_filter, 2, dev); + } + } else if(*data & BMI160_ACCEL_UNDERSAMPLING_MASK) { + temp = *data & ~BMI160_ACCEL_UNDERSAMPLING_MASK; + + /* Disable under-sampling parameter if already enabled */ + *data = temp; + + /* Write data */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the gyro power mode. + */ +static int8_t set_gyro_pwr(struct bmi160_dev* dev) { + int8_t rslt = 0; + + if((dev->gyro_cfg.power == BMI160_GYRO_SUSPEND_MODE) || + (dev->gyro_cfg.power == BMI160_GYRO_NORMAL_MODE) || + (dev->gyro_cfg.power == BMI160_GYRO_FASTSTARTUP_MODE)) { + if(dev->gyro_cfg.power != dev->prev_gyro_cfg.power) { + /* Write gyro power */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &dev->gyro_cfg.power, 1, dev); + if(dev->prev_gyro_cfg.power == BMI160_GYRO_SUSPEND_MODE) { + /* Delay of 80 ms - datasheet Table 24 */ + dev->delay_ms(BMI160_GYRO_DELAY_MS); + } else if( + (dev->prev_gyro_cfg.power == BMI160_GYRO_FASTSTARTUP_MODE) && + (dev->gyro_cfg.power == BMI160_GYRO_NORMAL_MODE)) { + /* This delay is required for transition from + * fast-startup mode to normal mode - datasheet Table 3 */ + dev->delay_ms(10); + } else { + /* do nothing */ + } + + dev->prev_gyro_cfg.power = dev->gyro_cfg.power; + } + } else { + rslt = BMI160_E_INVALID_CONFIG; + } + + return rslt; +} + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + */ +static int8_t + get_accel_data(uint8_t len, struct bmi160_sensor_data* accel, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[9] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + /* read accel sensor data along with time if requested */ + rslt = bmi160_get_regs(BMI160_ACCEL_DATA_ADDR, data_array, 6 + len, dev); + if(rslt == BMI160_OK) { + /* Accel Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->x = msblsb; /* Data in X axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->y = msblsb; /* Data in Y axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->z = msblsb; /* Data in Z axis */ + if(len == 3) { + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + accel->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + accel->sensortime = 0; + } + } else { + rslt = BMI160_E_COM_FAIL; + } + + return rslt; +} + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + */ +static int8_t + get_gyro_data(uint8_t len, struct bmi160_sensor_data* gyro, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[15] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + if(len == 0) { + /* read gyro data only */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 6, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* Data in X axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* Data in Y axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* Data in Z axis */ + gyro->sensortime = 0; + } else { + rslt = BMI160_E_COM_FAIL; + } + } else { + /* read gyro sensor data along with time */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 12 + len, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* gyro X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* gyro Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* gyro Z axis data */ + idx = idx + 6; + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + gyro->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + rslt = BMI160_E_COM_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API reads accel and gyro data along with sensor time + * if time is requested by user. + * Kindly refer the user guide(README.md) for more info. + */ +static int8_t get_accel_gyro_data( + uint8_t len, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[15] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + /* read both accel and gyro sensor data + * along with time if requested */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 12 + len, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* gyro X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* gyro Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* gyro Z axis data */ + /* Accel Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->x = (int16_t)msblsb; /* accel X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->y = (int16_t)msblsb; /* accel Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->z = (int16_t)msblsb; /* accel Z axis data */ + if(len == 3) { + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + accel->sensortime = (uint32_t)(time_2 | time_1 | time_0); + gyro->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + accel->sensortime = 0; + gyro->sensortime = 0; + } + } else { + rslt = BMI160_E_COM_FAIL; + } + + return rslt; +} + +/*! + * @brief This API enables the any-motion interrupt for accel. + */ +static int8_t enable_accel_any_motion_int( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable any motion x, any motion y, any motion z + * in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(any_motion_int_cfg->anymotion_en == BMI160_ENABLE) { + temp = data & ~BMI160_ANY_MOTION_X_INT_EN_MASK; + + /* Adding Any_motion x axis */ + data = temp | (any_motion_int_cfg->anymotion_x & BMI160_ANY_MOTION_X_INT_EN_MASK); + temp = data & ~BMI160_ANY_MOTION_Y_INT_EN_MASK; + + /* Adding Any_motion y axis */ + data = temp | + ((any_motion_int_cfg->anymotion_y << 1) & BMI160_ANY_MOTION_Y_INT_EN_MASK); + temp = data & ~BMI160_ANY_MOTION_Z_INT_EN_MASK; + + /* Adding Any_motion z axis */ + data = temp | + ((any_motion_int_cfg->anymotion_z << 2) & BMI160_ANY_MOTION_Z_INT_EN_MASK); + + /* any-motion feature selected*/ + dev->any_sig_sel = BMI160_ANY_MOTION_ENABLED; + } else { + data = data & ~BMI160_ANY_MOTION_ALL_INT_EN_MASK; + + /* neither any-motion feature nor sig-motion selected */ + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + } + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API disable the sig-motion interrupt. + */ +static int8_t disable_sig_motion_int(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Disabling Significant motion interrupt if enabled */ + rslt = bmi160_get_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = (data & BMI160_SIG_MOTION_SEL_MASK); + if(temp) { + temp = data & ~BMI160_SIG_MOTION_SEL_MASK; + data = temp; + + /* Write data to register */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API is used to map/unmap the Any/Sig motion, Step det/Low-g, + * Double tap, Single tap, Orientation, Flat, High-G, Nomotion interrupt pins. + */ +static int8_t + map_feature_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[3] = {0, 0, 0}; + uint8_t temp[3] = {0, 0, 0}; + + rslt = bmi160_get_regs(BMI160_INT_MAP_0_ADDR, data, 3, dev); + if(rslt == BMI160_OK) { + temp[0] = data[0] & ~int_mask_lookup_table[int_config->int_type]; + temp[2] = data[2] & ~int_mask_lookup_table[int_config->int_type]; + switch(int_config->int_channel) { + case BMI160_INT_CHANNEL_NONE: + data[0] = temp[0]; + data[2] = temp[2]; + break; + case BMI160_INT_CHANNEL_1: + data[0] = temp[0] | int_mask_lookup_table[int_config->int_type]; + data[2] = temp[2]; + break; + case BMI160_INT_CHANNEL_2: + data[2] = temp[2] | int_mask_lookup_table[int_config->int_type]; + data[0] = temp[0]; + break; + case BMI160_INT_CHANNEL_BOTH: + data[0] = temp[0] | int_mask_lookup_table[int_config->int_type]; + data[2] = temp[2] | int_mask_lookup_table[int_config->int_type]; + break; + default: + rslt = BMI160_E_OUT_OF_RANGE; + } + if(rslt == BMI160_OK) { + rslt = bmi160_set_regs(BMI160_INT_MAP_0_ADDR, data, 3, dev); + } + } + + return rslt; +} + +/*! + * @brief This API is used to map/unmap the Dataready(Accel & Gyro), FIFO full + * and FIFO watermark interrupt. + */ +static int8_t map_hardware_interrupt( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + rslt = bmi160_get_regs(BMI160_INT_MAP_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~int_mask_lookup_table[int_config->int_type]; + temp = temp & ~((uint8_t)(int_mask_lookup_table[int_config->int_type] << 4)); + switch(int_config->int_channel) { + case BMI160_INT_CHANNEL_NONE: + data = temp; + break; + case BMI160_INT_CHANNEL_1: + data = temp | (uint8_t)((int_mask_lookup_table[int_config->int_type]) << 4); + break; + case BMI160_INT_CHANNEL_2: + data = temp | int_mask_lookup_table[int_config->int_type]; + break; + case BMI160_INT_CHANNEL_BOTH: + data = temp | int_mask_lookup_table[int_config->int_type]; + data = data | (uint8_t)((int_mask_lookup_table[int_config->int_type]) << 4); + break; + default: + rslt = BMI160_E_OUT_OF_RANGE; + } + if(rslt == BMI160_OK) { + rslt = bmi160_set_regs(BMI160_INT_MAP_1_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for any-motion interrupt. + */ +static int8_t config_any_motion_src( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((any_motion_int_cfg->anymotion_data_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the duration and threshold of + * any-motion interrupt. + */ +static int8_t config_any_dur_threshold( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0}; + uint8_t dur; + + /* Configure Int Motion 0 register */ + rslt = bmi160_get_regs(BMI160_INT_MOTION_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* slope duration */ + dur = (uint8_t)any_motion_int_cfg->anymotion_dur; + temp = data & ~BMI160_SLOPE_INT_DUR_MASK; + data = temp | (dur & BMI160_MOTION_SRC_INT_MASK); + data_array[0] = data; + + /* add slope threshold */ + data_array[1] = any_motion_int_cfg->anymotion_thr; + + /* INT MOTION 0 and INT MOTION 1 address lie consecutively, + * hence writing data to respective registers at one go */ + + /* Writing to Int_motion 0 and + * Int_motion 1 Address simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API configure necessary setting of any-motion interrupt. + */ +static int8_t config_any_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = disable_sig_motion_int(dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_any_motion_src(any_motion_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_any_dur_threshold(any_motion_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API enable the data ready interrupt. + */ +static int8_t enable_data_ready_int(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_DATA_RDY_INT_EN_MASK; + data = temp | ((1 << 4) & BMI160_DATA_RDY_INT_EN_MASK); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the no motion/slow motion interrupt. + */ +static int8_t enable_no_motion_int( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable no motion x, no motion y, no motion z + * in Int Enable 2 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(no_mot_int_cfg->no_motion_x == 1) { + temp = data & ~BMI160_NO_MOTION_X_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | (1 & BMI160_NO_MOTION_X_INT_EN_MASK); + } + + if(no_mot_int_cfg->no_motion_y == 1) { + temp = data & ~BMI160_NO_MOTION_Y_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | ((1 << 1) & BMI160_NO_MOTION_Y_INT_EN_MASK); + } + + if(no_mot_int_cfg->no_motion_z == 1) { + temp = data & ~BMI160_NO_MOTION_Z_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | ((1 << 2) & BMI160_NO_MOTION_Z_INT_EN_MASK); + } + + /* write data to Int Enable 2 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * no motion/slow motion interrupt. + */ +static int8_t config_no_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_no_motion_data_src(no_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_no_motion_dur_thr(no_mot_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of interrupt for no motion. + */ +static int8_t config_no_motion_data_src( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((no_mot_int_cfg->no_motion_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the duration and threshold of + * no motion/slow motion interrupt along with selection of no/slow motion. + */ +static int8_t config_no_motion_dur_thr( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t temp_1 = 0; + uint8_t reg_addr; + uint8_t data_array[2] = {0}; + + /* Configuring INT_MOTION register */ + reg_addr = BMI160_INT_MOTION_0_ADDR; + rslt = bmi160_get_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_NO_MOTION_INT_DUR_MASK; + + /* Adding no_motion duration */ + data = temp | ((no_mot_int_cfg->no_motion_dur << 2) & BMI160_NO_MOTION_INT_DUR_MASK); + + /* Write data to NO_MOTION 0 address */ + rslt = bmi160_set_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + reg_addr = BMI160_INT_MOTION_3_ADDR; + rslt = bmi160_get_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_NO_MOTION_SEL_BIT_MASK; + + /* Adding no_motion_sel bit */ + temp_1 = (no_mot_int_cfg->no_motion_sel & BMI160_NO_MOTION_SEL_BIT_MASK); + data = (temp | temp_1); + data_array[1] = data; + + /* Adding no motion threshold */ + data_array[0] = no_mot_int_cfg->no_motion_thres; + reg_addr = BMI160_INT_MOTION_2_ADDR; + + /* writing data to INT_MOTION 2 and INT_MOTION 3 + * address simultaneously */ + rslt = bmi160_set_regs(reg_addr, data_array, 2, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API enables the sig-motion motion interrupt. + */ +static int8_t enable_sig_motion_int( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* For significant motion,enable any motion x,any motion y, + * any motion z in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(sig_mot_int_cfg->sig_en == BMI160_ENABLE) { + temp = data & ~BMI160_SIG_MOTION_INT_EN_MASK; + data = temp | (7 & BMI160_SIG_MOTION_INT_EN_MASK); + + /* sig-motion feature selected*/ + dev->any_sig_sel = BMI160_SIG_MOTION_ENABLED; + } else { + data = data & ~BMI160_SIG_MOTION_INT_EN_MASK; + + /* neither any-motion feature nor sig-motion selected */ + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + } + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * significant motion interrupt. + */ +static int8_t config_sig_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_motion_data_src(sig_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_dur_threshold(sig_mot_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for sig motion interrupt. + */ +static int8_t config_sig_motion_data_src( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((sig_mot_int_cfg->sig_data_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the threshold, skip and proof time of + * sig motion interrupt. + */ +static int8_t config_sig_dur_threshold( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data; + uint8_t temp = 0; + + /* Configuring INT_MOTION registers */ + + /* Write significant motion threshold. + * This threshold is same as any motion threshold */ + data = sig_mot_int_cfg->sig_mot_thres; + + /* Write data to INT_MOTION 1 address */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + rslt = bmi160_get_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_SIG_MOTION_SKIP_MASK; + + /* adding skip time of sig_motion interrupt*/ + data = temp | ((sig_mot_int_cfg->sig_mot_skip << 2) & BMI160_SIG_MOTION_SKIP_MASK); + temp = data & ~BMI160_SIG_MOTION_PROOF_MASK; + + /* adding proof time of sig_motion interrupt */ + data = temp | ((sig_mot_int_cfg->sig_mot_proof << 4) & BMI160_SIG_MOTION_PROOF_MASK); + + /* configure the int_sig_mot_sel bit to select + * significant motion interrupt */ + temp = data & ~BMI160_SIG_MOTION_SEL_MASK; + data = temp | ((sig_mot_int_cfg->sig_en << 1) & BMI160_SIG_MOTION_SEL_MASK); + rslt = bmi160_set_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API enables the step detector interrupt. + */ +static int8_t enable_step_detect_int( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 2 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_STEP_DETECT_INT_EN_MASK; + data = temp | + ((step_detect_int_cfg->step_detector_en << 3) & BMI160_STEP_DETECT_INT_EN_MASK); + + /* Writing data to INT ENABLE 2 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the step detector parameter. + */ +static int8_t config_step_detect( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[2] = {0}; + + if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_NORMAL) { + /* Normal mode setting */ + data_array[0] = 0x15; + data_array[1] = 0x03; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_SENSITIVE) { + /* Sensitive mode setting */ + data_array[0] = 0x2D; + data_array[1] = 0x00; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_ROBUST) { + /* Robust mode setting */ + data_array[0] = 0x1D; + data_array[1] = 0x07; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_USER_DEFINE) { + /* Non recommended User defined setting */ + /* Configuring STEP_CONFIG register */ + rslt = bmi160_get_regs(BMI160_INT_STEP_CONFIG_0_ADDR, &data_array[0], 2, dev); + if(rslt == BMI160_OK) { + temp = data_array[0] & ~BMI160_STEP_DETECT_MIN_THRES_MASK; + + /* Adding min_threshold */ + data_array[0] = temp | ((step_detect_int_cfg->min_threshold << 3) & + BMI160_STEP_DETECT_MIN_THRES_MASK); + temp = data_array[0] & ~BMI160_STEP_DETECT_STEPTIME_MIN_MASK; + + /* Adding steptime_min */ + data_array[0] = temp | ((step_detect_int_cfg->steptime_min) & + BMI160_STEP_DETECT_STEPTIME_MIN_MASK); + temp = data_array[1] & ~BMI160_STEP_MIN_BUF_MASK; + + /* Adding steptime_min */ + data_array[1] = temp | + ((step_detect_int_cfg->step_min_buf) & BMI160_STEP_MIN_BUF_MASK); + } + } + + /* Write data to STEP_CONFIG register */ + rslt = bmi160_set_regs(BMI160_INT_STEP_CONFIG_0_ADDR, data_array, 2, dev); + + return rslt; +} + +/*! + * @brief This API enables the single/double tap interrupt. + */ +static int8_t enable_tap_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable single tap or double tap interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(int_config->int_type == BMI160_ACC_SINGLE_TAP_INT) { + temp = data & ~BMI160_SINGLE_TAP_INT_EN_MASK; + data = temp | ((tap_int_cfg->tap_en << 5) & BMI160_SINGLE_TAP_INT_EN_MASK); + } else { + temp = data & ~BMI160_DOUBLE_TAP_INT_EN_MASK; + data = temp | ((tap_int_cfg->tap_en << 4) & BMI160_DOUBLE_TAP_INT_EN_MASK); + } + + /* Write to Enable 0 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * tap interrupt. + */ +static int8_t config_tap_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_data_src(tap_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_param(int_config, tap_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for tap interrupt. + */ +static int8_t config_tap_data_src( + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_TAP_SRC_INT_MASK; + data = temp | ((tap_int_cfg->tap_data_src << 3) & BMI160_TAP_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the parameters of tap interrupt. + * Threshold, quite, shock, and duration. + */ +static int8_t config_tap_param( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + uint8_t data_array[2] = {0}; + uint8_t count = 0; + uint8_t dur, shock, quiet, thres; + + /* Configure tap 0 register for tap shock,tap quiet duration + * in case of single tap interrupt */ + rslt = bmi160_get_regs(BMI160_INT_TAP_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[count]; + if(int_config->int_type == BMI160_ACC_DOUBLE_TAP_INT) { + dur = (uint8_t)tap_int_cfg->tap_dur; + temp = (data & ~BMI160_TAP_DUR_MASK); + + /* Add tap duration data in case of + * double tap interrupt */ + data = temp | (dur & BMI160_TAP_DUR_MASK); + } + + shock = (uint8_t)tap_int_cfg->tap_shock; + temp = data & ~BMI160_TAP_SHOCK_DUR_MASK; + data = temp | ((shock << 6) & BMI160_TAP_SHOCK_DUR_MASK); + quiet = (uint8_t)tap_int_cfg->tap_quiet; + temp = data & ~BMI160_TAP_QUIET_DUR_MASK; + data = temp | ((quiet << 7) & BMI160_TAP_QUIET_DUR_MASK); + data_array[count++] = data; + data = data_array[count]; + thres = (uint8_t)tap_int_cfg->tap_thr; + temp = data & ~BMI160_TAP_THRES_MASK; + data = temp | (thres & BMI160_TAP_THRES_MASK); + data_array[count++] = data; + + /* TAP 0 and TAP 1 address lie consecutively, + * hence writing data to respective registers at one go */ + + /* Writing to Tap 0 and Tap 1 Address simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_TAP_0_ADDR, data_array, count, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the secondary interface. + */ +static int8_t config_sec_if(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t if_conf = 0; + uint8_t cmd = BMI160_AUX_NORMAL_MODE; + + /* set the aux power mode to normal*/ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* 0.5ms delay - refer datasheet table 24*/ + dev->delay_ms(1); + rslt = bmi160_get_regs(BMI160_IF_CONF_ADDR, &if_conf, 1, dev); + if_conf |= (uint8_t)(1 << 5); + if(rslt == BMI160_OK) { + /*enable the secondary interface also*/ + rslt = bmi160_set_regs(BMI160_IF_CONF_ADDR, &if_conf, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API configure the ODR of the auxiliary sensor. + */ +static int8_t config_aux_odr(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t aux_odr; + + rslt = bmi160_get_regs(BMI160_AUX_ODR_ADDR, &aux_odr, 1, dev); + if(rslt == BMI160_OK) { + aux_odr = (uint8_t)(dev->aux_cfg.aux_odr); + + /* Set the secondary interface ODR + * i.e polling rate of secondary sensor */ + rslt = bmi160_set_regs(BMI160_AUX_ODR_ADDR, &aux_odr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + } + + return rslt; +} + +/*! + * @brief This API maps the actual burst read length set by user. + */ +static int8_t map_read_len(uint16_t* len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + switch(dev->aux_cfg.aux_rd_burst_len) { + case BMI160_AUX_READ_LEN_0: + *len = 1; + break; + case BMI160_AUX_READ_LEN_1: + *len = 2; + break; + case BMI160_AUX_READ_LEN_2: + *len = 6; + break; + case BMI160_AUX_READ_LEN_3: + *len = 8; + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + + return rslt; +} + +/*! + * @brief This API configure the settings of auxiliary sensor. + */ +static int8_t config_aux_settg(const struct bmi160_dev* dev) { + int8_t rslt; + + rslt = config_sec_if(dev); + if(rslt == BMI160_OK) { + /* Configures the auxiliary interface settings */ + rslt = bmi160_config_aux_mode(dev); + } + + return rslt; +} + +/*! + * @brief This API extract the read data from auxiliary sensor. + */ +static int8_t extract_aux_read( + uint16_t map_len, + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t data[8] = { + 0, + }; + uint8_t read_addr = BMI160_AUX_DATA_ADDR; + uint8_t count = 0; + uint8_t read_count; + uint8_t read_len = (uint8_t)map_len; + + for(; count < len;) { + /* set address to read */ + rslt = bmi160_set_regs(BMI160_AUX_IF_2_ADDR, ®_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + rslt = bmi160_get_regs(read_addr, data, map_len, dev); + if(rslt == BMI160_OK) { + read_count = 0; + + /* if read len is less the burst read len + * mention by user*/ + if(len < map_len) { + read_len = (uint8_t)len; + } else if((len - count) < map_len) { + read_len = (uint8_t)(len - count); + } + + for(; read_count < read_len; read_count++) { + aux_data[count + read_count] = data[read_count]; + } + + reg_addr += (uint8_t)map_len; + count += (uint8_t)map_len; + } else { + rslt = BMI160_E_COM_FAIL; + break; + } + } + } + + return rslt; +} + +/*! + * @brief This API enables the orient interrupt. + */ +static int8_t enable_orient_int( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_ORIENT_INT_EN_MASK; + data = temp | ((orient_int_cfg->orient_en << 6) & BMI160_ORIENT_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of orientation interrupt. + */ +static int8_t config_orient_int_settg( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0, 0}; + + /* Configuring INT_ORIENT registers */ + rslt = bmi160_get_regs(BMI160_INT_ORIENT_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[0]; + temp = data & ~BMI160_ORIENT_MODE_MASK; + + /* Adding Orientation mode */ + data = temp | ((orient_int_cfg->orient_mode) & BMI160_ORIENT_MODE_MASK); + temp = data & ~BMI160_ORIENT_BLOCK_MASK; + + /* Adding Orientation blocking */ + data = temp | ((orient_int_cfg->orient_blocking << 2) & BMI160_ORIENT_BLOCK_MASK); + temp = data & ~BMI160_ORIENT_HYST_MASK; + + /* Adding Orientation hysteresis */ + data = temp | ((orient_int_cfg->orient_hyst << 4) & BMI160_ORIENT_HYST_MASK); + data_array[0] = data; + data = data_array[1]; + temp = data & ~BMI160_ORIENT_THETA_MASK; + + /* Adding Orientation threshold */ + data = temp | ((orient_int_cfg->orient_theta) & BMI160_ORIENT_THETA_MASK); + temp = data & ~BMI160_ORIENT_UD_ENABLE; + + /* Adding Orient_ud_en */ + data = temp | ((orient_int_cfg->orient_ud_en << 6) & BMI160_ORIENT_UD_ENABLE); + temp = data & ~BMI160_AXES_EN_MASK; + + /* Adding axes_en */ + data = temp | ((orient_int_cfg->axes_ex << 7) & BMI160_AXES_EN_MASK); + data_array[1] = data; + + /* Writing data to INT_ORIENT 0 and INT_ORIENT 1 + * registers simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_ORIENT_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the flat interrupt. + */ +static int8_t enable_flat_int( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable flat interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_FLAT_INT_EN_MASK; + data = temp | ((flat_int->flat_en << 7) & BMI160_FLAT_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of flat interrupt. + */ +static int8_t config_flat_int_settg( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0, 0}; + + /* Configuring INT_FLAT register */ + rslt = bmi160_get_regs(BMI160_INT_FLAT_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[0]; + temp = data & ~BMI160_FLAT_THRES_MASK; + + /* Adding flat theta */ + data = temp | ((flat_int->flat_theta) & BMI160_FLAT_THRES_MASK); + data_array[0] = data; + data = data_array[1]; + temp = data & ~BMI160_FLAT_HOLD_TIME_MASK; + + /* Adding flat hold time */ + data = temp | ((flat_int->flat_hold_time << 4) & BMI160_FLAT_HOLD_TIME_MASK); + temp = data & ~BMI160_FLAT_HYST_MASK; + + /* Adding flat hysteresis */ + data = temp | ((flat_int->flat_hy) & BMI160_FLAT_HYST_MASK); + data_array[1] = data; + + /* Writing data to INT_FLAT 0 and INT_FLAT 1 + * registers simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_FLAT_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the Low-g interrupt. + */ +static int8_t enable_low_g_int( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable low-g interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_G_INT_EN_MASK; + data = temp | ((low_g_int->low_en << 3) & BMI160_LOW_G_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for low-g interrupt. + */ +static int8_t config_low_g_data_src( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_HIGH_SRC_INT_MASK; + data = temp | ((low_g_int->low_data_src << 7) & BMI160_LOW_HIGH_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of low-g interrupt. + */ +static int8_t config_low_g_int_settg( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[3] = {0, 0, 0}; + + /* Configuring INT_LOWHIGH register for low-g interrupt */ + rslt = bmi160_get_regs(BMI160_INT_LOWHIGH_2_ADDR, &data_array[2], 1, dev); + if(rslt == BMI160_OK) { + temp = data_array[2] & ~BMI160_LOW_G_HYST_MASK; + + /* Adding low-g hysteresis */ + data_array[2] = temp | (low_g_int->low_hyst & BMI160_LOW_G_HYST_MASK); + temp = data_array[2] & ~BMI160_LOW_G_LOW_MODE_MASK; + + /* Adding low-mode */ + data_array[2] = temp | ((low_g_int->low_mode << 2) & BMI160_LOW_G_LOW_MODE_MASK); + + /* Adding low-g threshold */ + data_array[1] = low_g_int->low_thres; + + /* Adding low-g interrupt delay */ + data_array[0] = low_g_int->low_dur; + + /* Writing data to INT_LOWHIGH 0,1,2 registers simultaneously*/ + rslt = bmi160_set_regs(BMI160_INT_LOWHIGH_0_ADDR, data_array, 3, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the high-g interrupt. + */ +static int8_t enable_high_g_int( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable low-g interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Adding high-g X-axis */ + temp = data & ~BMI160_HIGH_G_X_INT_EN_MASK; + data = temp | (high_g_int_cfg->high_g_x & BMI160_HIGH_G_X_INT_EN_MASK); + + /* Adding high-g Y-axis */ + temp = data & ~BMI160_HIGH_G_Y_INT_EN_MASK; + data = temp | ((high_g_int_cfg->high_g_y << 1) & BMI160_HIGH_G_Y_INT_EN_MASK); + + /* Adding high-g Z-axis */ + temp = data & ~BMI160_HIGH_G_Z_INT_EN_MASK; + data = temp | ((high_g_int_cfg->high_g_z << 2) & BMI160_HIGH_G_Z_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for high-g interrupt. + */ +static int8_t config_high_g_data_src( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_HIGH_SRC_INT_MASK; + data = temp | ((high_g_int_cfg->high_data_src << 7) & BMI160_LOW_HIGH_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of high-g interrupt. + */ +static int8_t config_high_g_int_settg( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[3] = {0, 0, 0}; + + rslt = bmi160_get_regs(BMI160_INT_LOWHIGH_2_ADDR, &data_array[0], 1, dev); + if(rslt == BMI160_OK) { + temp = data_array[0] & ~BMI160_HIGH_G_HYST_MASK; + + /* Adding high-g hysteresis */ + data_array[0] = temp | ((high_g_int_cfg->high_hy << 6) & BMI160_HIGH_G_HYST_MASK); + + /* Adding high-g duration */ + data_array[1] = high_g_int_cfg->high_dur; + + /* Adding high-g threshold */ + data_array[2] = high_g_int_cfg->high_thres; + rslt = bmi160_set_regs(BMI160_INT_LOWHIGH_2_ADDR, data_array, 3, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the behavioural setting of interrupt pin. + */ +static int8_t + config_int_out_ctrl(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + + /* Configuration of output interrupt signals on pins INT1 and INT2 are + * done in BMI160_INT_OUT_CTRL_ADDR register*/ + rslt = bmi160_get_regs(BMI160_INT_OUT_CTRL_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* updating the interrupt pin structure to local structure */ + const struct bmi160_int_pin_settg* intr_pin_sett = &(int_config->int_pin_settg); + + /* Configuring channel 1 */ + if(int_config->int_channel == BMI160_INT_CHANNEL_1) { + /* Output enable */ + temp = data & ~BMI160_INT1_OUTPUT_EN_MASK; + data = temp | ((intr_pin_sett->output_en << 3) & BMI160_INT1_OUTPUT_EN_MASK); + + /* Output mode */ + temp = data & ~BMI160_INT1_OUTPUT_MODE_MASK; + data = temp | ((intr_pin_sett->output_mode << 2) & BMI160_INT1_OUTPUT_MODE_MASK); + + /* Output type */ + temp = data & ~BMI160_INT1_OUTPUT_TYPE_MASK; + data = temp | ((intr_pin_sett->output_type << 1) & BMI160_INT1_OUTPUT_TYPE_MASK); + + /* edge control */ + temp = data & ~BMI160_INT1_EDGE_CTRL_MASK; + data = temp | ((intr_pin_sett->edge_ctrl) & BMI160_INT1_EDGE_CTRL_MASK); + } else { + /* Configuring channel 2 */ + /* Output enable */ + temp = data & ~BMI160_INT2_OUTPUT_EN_MASK; + data = temp | ((intr_pin_sett->output_en << 7) & BMI160_INT2_OUTPUT_EN_MASK); + + /* Output mode */ + temp = data & ~BMI160_INT2_OUTPUT_MODE_MASK; + data = temp | ((intr_pin_sett->output_mode << 6) & BMI160_INT2_OUTPUT_MODE_MASK); + + /* Output type */ + temp = data & ~BMI160_INT2_OUTPUT_TYPE_MASK; + data = temp | ((intr_pin_sett->output_type << 5) & BMI160_INT2_OUTPUT_TYPE_MASK); + + /* edge control */ + temp = data & ~BMI160_INT2_EDGE_CTRL_MASK; + data = temp | ((intr_pin_sett->edge_ctrl << 4) & BMI160_INT2_EDGE_CTRL_MASK); + } + + rslt = bmi160_set_regs(BMI160_INT_OUT_CTRL_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the mode(input enable, latch or non-latch) of interrupt pin. + */ +static int8_t + config_int_latch(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + + /* Configuration of latch on pins INT1 and INT2 are done in + * BMI160_INT_LATCH_ADDR register*/ + rslt = bmi160_get_regs(BMI160_INT_LATCH_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* updating the interrupt pin structure to local structure */ + const struct bmi160_int_pin_settg* intr_pin_sett = &(int_config->int_pin_settg); + if(int_config->int_channel == BMI160_INT_CHANNEL_1) { + /* Configuring channel 1 */ + /* Input enable */ + temp = data & ~BMI160_INT1_INPUT_EN_MASK; + data = temp | ((intr_pin_sett->input_en << 4) & BMI160_INT1_INPUT_EN_MASK); + } else { + /* Configuring channel 2 */ + /* Input enable */ + temp = data & ~BMI160_INT2_INPUT_EN_MASK; + data = temp | ((intr_pin_sett->input_en << 5) & BMI160_INT2_INPUT_EN_MASK); + } + + /* In case of latch interrupt,update the latch duration */ + + /* Latching holds the interrupt for the amount of latch + * duration time */ + temp = data & ~BMI160_INT_LATCH_MASK; + data = temp | (intr_pin_sett->latch_dur & BMI160_INT_LATCH_MASK); + + /* OUT_CTRL_INT and LATCH_INT address lie consecutively, + * hence writing data to respective registers at one go */ + rslt = bmi160_set_regs(BMI160_INT_LATCH_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API performs the self test for accelerometer of BMI160 + */ +static int8_t perform_accel_self_test(struct bmi160_dev* dev) { + int8_t rslt; + struct bmi160_sensor_data accel_pos, accel_neg; + + /* Enable Gyro self test bit */ + rslt = enable_accel_self_test(dev); + if(rslt == BMI160_OK) { + /* Perform accel self test with positive excitation */ + rslt = accel_self_test_positive_excitation(&accel_pos, dev); + if(rslt == BMI160_OK) { + /* Perform accel self test with negative excitation */ + rslt = accel_self_test_negative_excitation(&accel_neg, dev); + if(rslt == BMI160_OK) { + /* Validate the self test result */ + rslt = validate_accel_self_test(&accel_pos, &accel_neg); + } + } + } + + return rslt; +} + +/*! + * @brief This API enables to perform the accel self test by setting proper + * configurations to facilitate accel self test + */ +static int8_t enable_accel_self_test(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Set the Accel power mode as normal mode */ + dev->accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; + + /* Set the sensor range configuration as 8G */ + dev->accel_cfg.range = BMI160_ACCEL_RANGE_8G; + rslt = bmi160_set_sens_conf(dev); + if(rslt == BMI160_OK) { + /* Accel configurations are set to facilitate self test + * acc_odr - 1600Hz ; acc_bwp = 2 ; acc_us = 0 */ + reg_data = BMI160_ACCEL_SELF_TEST_CONFIG; + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API performs accel self test with positive excitation + */ +static int8_t accel_self_test_positive_excitation( + struct bmi160_sensor_data* accel_pos, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable accel self test with positive self-test excitation + * and with amplitude of deflection set as high */ + reg_data = BMI160_ACCEL_SELF_TEST_POSITIVE_EN; + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Read the data after a delay of 50ms - refer datasheet 2.8.1 accel self test*/ + dev->delay_ms(BMI160_ACCEL_SELF_TEST_DELAY); + rslt = bmi160_get_sensor_data(BMI160_ACCEL_ONLY, accel_pos, NULL, dev); + } + + return rslt; +} + +/*! + * @brief This API performs accel self test with negative excitation + */ +static int8_t accel_self_test_negative_excitation( + struct bmi160_sensor_data* accel_neg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable accel self test with negative self-test excitation + * and with amplitude of deflection set as high */ + reg_data = BMI160_ACCEL_SELF_TEST_NEGATIVE_EN; + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Read the data after a delay of 50ms */ + dev->delay_ms(BMI160_ACCEL_SELF_TEST_DELAY); + rslt = bmi160_get_sensor_data(BMI160_ACCEL_ONLY, accel_neg, NULL, dev); + } + + return rslt; +} + +/*! + * @brief This API validates the accel self test results + */ +static int8_t validate_accel_self_test( + const struct bmi160_sensor_data* accel_pos, + const struct bmi160_sensor_data* accel_neg) { + int8_t rslt; + + /* Validate the results of self test */ + if(((accel_neg->x - accel_pos->x) > BMI160_ACCEL_SELF_TEST_LIMIT) && + ((accel_neg->y - accel_pos->y) > BMI160_ACCEL_SELF_TEST_LIMIT) && + ((accel_neg->z - accel_pos->z) > BMI160_ACCEL_SELF_TEST_LIMIT)) { + /* Self test pass condition */ + rslt = BMI160_OK; + } else { + rslt = BMI160_W_ACCEl_SELF_TEST_FAIL; + } + + return rslt; +} + +/*! + * @brief This API performs the self test for gyroscope of BMI160 + */ +static int8_t perform_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + + /* Enable Gyro self test bit */ + rslt = enable_gyro_self_test(dev); + if(rslt == BMI160_OK) { + /* Validate the gyro self test a delay of 50ms */ + dev->delay_ms(50); + + /* Validate the gyro self test results */ + rslt = validate_gyro_self_test(dev); + } + + return rslt; +} + +/*! + * @brief This API enables the self test bit to trigger self test for Gyro + */ +static int8_t enable_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable the Gyro self test bit to trigger the self test */ + rslt = bmi160_get_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + reg_data = BMI160_SET_BITS(reg_data, BMI160_GYRO_SELF_TEST, 1); + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Delay to enable gyro self test */ + dev->delay_ms(15); + } + } + + return rslt; +} + +/*! + * @brief This API validates the self test results of Gyro + */ +static int8_t validate_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Validate the Gyro self test result */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + reg_data = BMI160_GET_BITS(reg_data, BMI160_GYRO_SELF_TEST_STATUS); + if(reg_data == BMI160_ENABLE) { + /* Gyro self test success case */ + rslt = BMI160_OK; + } else { + rslt = BMI160_W_GYRO_SELF_TEST_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API sets FIFO full interrupt of the sensor.This interrupt + * occurs when the FIFO is full and the next full data sample would cause + * a FIFO overflow, which may delete the old samples. + */ +static int8_t + set_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /*enable the fifo full interrupt */ + rslt = enable_fifo_full_int(int_config, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This enable the FIFO full interrupt engine. + */ +static int8_t + enable_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_FIFO_FULL_INT, int_config->fifo_full_int_en); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API sets FIFO watermark interrupt of the sensor.The FIFO + * watermark interrupt is fired, when the FIFO fill level is above a fifo + * watermark. + */ +static int8_t set_fifo_watermark_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Enable fifo-watermark interrupt in Int Enable 1 register */ + rslt = enable_fifo_wtm_int(int_config, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This enable the FIFO watermark interrupt engine. + */ +static int8_t + enable_fifo_wtm_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_FIFO_WTM_INT, int_config->fifo_wtm_int_en); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API is used to reset the FIFO related configurations + * in the fifo_frame structure. + */ +static void reset_fifo_data_structure(const struct bmi160_dev* dev) { + /*Prepare for next FIFO read by resetting FIFO's + * internal data structures*/ + dev->fifo->accel_byte_start_idx = 0; + dev->fifo->gyro_byte_start_idx = 0; + dev->fifo->aux_byte_start_idx = 0; + dev->fifo->sensor_time = 0; + dev->fifo->skipped_frame_count = 0; +} + +/*! + * @brief This API is used to read fifo_byte_counter value (i.e) + * current fill-level in Fifo buffer. + */ +static int8_t get_fifo_byte_counter(uint16_t* bytes_to_read, struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint8_t data[2]; + uint8_t addr = BMI160_FIFO_LENGTH_ADDR; + + rslt |= bmi160_get_regs(addr, data, 2, dev); + data[1] = data[1] & BMI160_FIFO_BYTE_COUNTER_MASK; + + /* Available data in FIFO is stored in bytes_to_read*/ + *bytes_to_read = (((uint16_t)data[1] << 8) | ((uint16_t)data[0])); + + return rslt; +} + +/*! + * @brief This API is used to compute the number of bytes of accel FIFO data + * which is to be parsed in header-less mode + */ +static void get_accel_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* acc_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->accel_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_A_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_GA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_MA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When accel is not enabled ,there will be no accel data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available*/ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + */ +static void unpack_accel_frame( + struct bmi160_sensor_data* acc, + uint16_t* idx, + uint8_t* acc_idx, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_A_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into the structure instance "acc" */ + unpack_accel_data(&acc[*acc_idx], *idx, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_A_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_GA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_G_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_MG_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_M_LENGTH; + break; + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + */ +static void unpack_accel_data( + struct bmi160_sensor_data* accel_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + uint16_t data_lsb; + uint16_t data_msb; + + /* Accel raw x data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->x = (int16_t)((data_msb << 8) | data_lsb); + + /* Accel raw y data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->y = (int16_t)((data_msb << 8) | data_lsb); + + /* Accel raw z data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->z = (int16_t)((data_msb << 8) | data_lsb); +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in header mode. + */ +static void extract_accel_header_mode( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t accel_index = 0; + + for(data_index = dev->fifo->accel_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* Accel frame */ + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_HEAD_M_G_A: + unpack_accel_frame(accel_data, &data_index, &accel_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_M: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_G: + move_next_frame(&data_index, BMI160_FIFO_G_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M_G: + move_next_frame(&data_index, BMI160_FIFO_MG_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case of Over read */ + data_index = dev->fifo->length; + break; + default: + break; + } + if(*accel_length == accel_index) { + /* Number of frames to read completed */ + break; + } + } + + /*Update number of accel data read*/ + *accel_length = accel_index; + + /*Update the accel frame index*/ + dev->fifo->accel_byte_start_idx = data_index; +} + +/*! + * @brief This API computes the number of bytes of gyro FIFO data + * which is to be parsed in header-less mode + */ +static void get_gyro_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* gyro_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->gyro_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_G_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_A_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_GA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_MG_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When gyro is not enabled ,there will be no gyro data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available*/ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the gyroscope's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + */ +static void unpack_gyro_frame( + struct bmi160_sensor_data* gyro, + uint16_t* idx, + uint8_t* gyro_idx, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_G_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx, dev); + + /*Move the data index*/ + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_GA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance "gyro" */ + unpack_gyro_data(&gyro[*gyro_idx], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + break; + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_M_LENGTH; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MG_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_A_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + */ +static void unpack_gyro_data( + struct bmi160_sensor_data* gyro_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + uint16_t data_lsb; + uint16_t data_msb; + + /* Gyro raw x data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->x = (int16_t)((data_msb << 8) | data_lsb); + + /* Gyro raw y data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->y = (int16_t)((data_msb << 8) | data_lsb); + + /* Gyro raw z data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->z = (int16_t)((data_msb << 8) | data_lsb); +} + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data in header mode. + */ +static void extract_gyro_header_mode( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t gyro_index = 0; + + for(data_index = dev->fifo->gyro_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* GYRO frame */ + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_HEAD_M_G_A: + unpack_gyro_frame(gyro_data, &data_index, &gyro_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_A: + move_next_frame(&data_index, BMI160_FIFO_A_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M_A: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case of over read */ + data_index = dev->fifo->length; + break; + default: + break; + } + if(*gyro_length == gyro_index) { + /*Number of frames to read completed*/ + break; + } + } + + /*Update number of gyro data read*/ + *gyro_length = gyro_index; + + /*Update the gyro frame index*/ + dev->fifo->gyro_byte_start_idx = data_index; +} + +/*! + * @brief This API computes the number of bytes of aux FIFO data + * which is to be parsed in header-less mode + */ +static void get_aux_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* aux_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->gyro_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_M_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_A_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MG_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When aux is not enabled ,there will be no aux data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available */ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the aux's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed + */ +static void unpack_aux_frame( + struct bmi160_aux_data* aux_data, + uint16_t* idx, + uint8_t* aux_index, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_M_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_M_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_MA_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_MG_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + + /* Move the data index */ + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + break; + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_A_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data and store it in the instance of the structure bmi160_aux_data. + */ +static void unpack_aux_data( + struct bmi160_aux_data* aux_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + /* Aux data bytes */ + aux_data->data[0] = dev->fifo->data[data_start_index++]; + aux_data->data[1] = dev->fifo->data[data_start_index++]; + aux_data->data[2] = dev->fifo->data[data_start_index++]; + aux_data->data[3] = dev->fifo->data[data_start_index++]; + aux_data->data[4] = dev->fifo->data[data_start_index++]; + aux_data->data[5] = dev->fifo->data[data_start_index++]; + aux_data->data[6] = dev->fifo->data[data_start_index++]; + aux_data->data[7] = dev->fifo->data[data_start_index++]; +} + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data in header mode. + */ +static void extract_aux_header_mode( + struct bmi160_aux_data* aux_data, + uint8_t* aux_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t aux_index = 0; + + for(data_index = dev->fifo->aux_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* Aux frame */ + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_HEAD_M_G_A: + unpack_aux_frame(aux_data, &data_index, &aux_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_G: + move_next_frame(&data_index, BMI160_FIFO_G_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_G_A: + move_next_frame(&data_index, BMI160_FIFO_GA_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_A: + move_next_frame(&data_index, BMI160_FIFO_A_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case + * of over read */ + data_index = dev->fifo->length; + break; + default: + + /* Update the data index as complete in case of + * getting other headers like 0x00 */ + data_index = dev->fifo->length; + break; + } + if(*aux_length == aux_index) { + /*Number of frames to read completed*/ + break; + } + } + + /* Update number of aux data read */ + *aux_length = aux_index; + + /* Update the aux frame index */ + dev->fifo->aux_byte_start_idx = data_index; +} + +/*! + * @brief This API checks the presence of non-valid frames in the read fifo data. + */ +static void check_frame_validity(uint16_t* data_index, const struct bmi160_dev* dev) { + if((*data_index + 2) < dev->fifo->length) { + /* Check if FIFO is empty */ + if((dev->fifo->data[*data_index] == FIFO_CONFIG_MSB_CHECK) && + (dev->fifo->data[*data_index + 1] == FIFO_CONFIG_LSB_CHECK)) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } + } +} + +/*! + * @brief This API is used to move the data index ahead of the + * current_frame_length parameter when unnecessary FIFO data appears while + * extracting the user specified data. + */ +static void move_next_frame( + uint16_t* data_index, + uint8_t current_frame_length, + const struct bmi160_dev* dev) { + /*Partial read, then move the data index to last data*/ + if((*data_index + current_frame_length) > dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + /*Move the data index to next frame*/ + *data_index = *data_index + current_frame_length; + } +} + +/*! + * @brief This API is used to parse and store the sensor time from the + * FIFO data in the structure instance dev. + */ +static void unpack_sensortime_frame(uint16_t* data_index, const struct bmi160_dev* dev) { + uint32_t sensor_time_byte3 = 0; + uint16_t sensor_time_byte2 = 0; + uint8_t sensor_time_byte1 = 0; + + /*Partial read, then move the data index to last data*/ + if((*data_index + BMI160_SENSOR_TIME_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + sensor_time_byte3 = dev->fifo->data[(*data_index) + BMI160_SENSOR_TIME_MSB_BYTE] << 16; + sensor_time_byte2 = dev->fifo->data[(*data_index) + BMI160_SENSOR_TIME_XLSB_BYTE] << 8; + sensor_time_byte1 = dev->fifo->data[(*data_index)]; + + /* Sensor time */ + dev->fifo->sensor_time = + (uint32_t)(sensor_time_byte3 | sensor_time_byte2 | sensor_time_byte1); + *data_index = (*data_index) + BMI160_SENSOR_TIME_LENGTH; + } +} + +/*! + * @brief This API is used to parse and store the skipped_frame_count from + * the FIFO data in the structure instance dev. + */ +static void unpack_skipped_frame(uint16_t* data_index, const struct bmi160_dev* dev) { + /*Partial read, then move the data index to last data*/ + if(*data_index >= dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + dev->fifo->skipped_frame_count = dev->fifo->data[*data_index]; + + /*Move the data index*/ + *data_index = (*data_index) + 1; + } +} + +/*! + * @brief This API is used to get the FOC status from the sensor + */ +static int8_t get_foc_status(uint8_t* foc_status, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Read the FOC status from sensor */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Get the foc_status bit */ + *foc_status = BMI160_GET_BITS(data, BMI160_FOC_STATUS); + } + + return rslt; +} + +/*! + * @brief This API is used to configure the offset enable bits in the sensor + */ +static int8_t + configure_offset_enable(const struct bmi160_foc_conf* foc_conf, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_OFFSET_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Set the offset enable/disable for gyro */ + data = BMI160_SET_BITS(data, BMI160_GYRO_OFFSET_EN, foc_conf->gyro_off_en); + + /* Set the offset enable/disable for accel */ + data = BMI160_SET_BITS(data, BMI160_ACCEL_OFFSET_EN, foc_conf->acc_off_en); + + /* Set the offset config in the sensor */ + rslt = bmi160_set_regs(BMI160_OFFSET_CONF_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +static int8_t trigger_foc(struct bmi160_offsets* offset, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t foc_status = BMI160_ENABLE; + uint8_t cmd = BMI160_START_FOC_CMD; + uint8_t timeout = 0; + uint8_t data_array[20]; + + /* Start the FOC process */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* Check the FOC status*/ + rslt = get_foc_status(&foc_status, dev); + + if((rslt != BMI160_OK) || (foc_status != BMI160_ENABLE)) { + while((foc_status != BMI160_ENABLE) && (timeout < 11)) { + /* Maximum time of 250ms is given in 10 + * steps of 25ms each - 250ms refer datasheet 2.9.1 */ + dev->delay_ms(25); + + /* Check the FOC status*/ + rslt = get_foc_status(&foc_status, dev); + timeout++; + } + + if((rslt == BMI160_OK) && (foc_status == BMI160_ENABLE)) { + /* Get offset values from sensor */ + rslt = bmi160_get_offsets(offset, dev); + } else { + /* FOC failure case */ + rslt = BMI160_E_FOC_FAILURE; + } + } + + if(rslt == BMI160_OK) { + /* Read registers 0x04-0x17 */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 20, dev); + } + } + + return rslt; +} diff --git a/applications/plugins/airmouse/tracking/imu/bmi160.h b/applications/plugins/airmouse/tracking/imu/bmi160.h new file mode 100644 index 000000000..d4d98094c --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/bmi160.h @@ -0,0 +1,992 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160.h +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +/*! + * @defgroup bmi160 BMI160 + */ + +#ifndef BMI160_H_ +#define BMI160_H_ + +/*************************** C++ guard macro *****************************/ +#ifdef __cplusplus +extern "C" { +#endif + +#include "bmi160_defs.h" +#ifdef __KERNEL__ +#include +#else +#include +#include +#include +#endif + +/*********************** User function prototypes ************************/ + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInit Initialization + * @brief Initialize the sensor and device structure + */ + +/*! + * \ingroup bmi160ApiInit + * \page bmi160_api_bmi160_init bmi160_init + * \code + * int8_t bmi160_init(struct bmi160_dev *dev); + * \endcode + * @details This API is the entry point for sensor.It performs + * the selection of I2C/SPI read mechanism according to the + * selected interface and reads the chip-id of bmi160 sensor. + * + * @param[in,out] dev : Structure instance of bmi160_dev + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_init(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiRegs Registers + * @brief Read data from the given register address of sensor + */ + +/*! + * \ingroup bmi160ApiRegs + * \page bmi160_api_bmi160_get_regs bmi160_get_regs + * \code + * int8_t bmi160_get_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the data from the given register address of sensor. + * + * @param[in] reg_addr : Register address from where the data to be read + * @param[out] data : Pointer to data buffer to store the read data. + * @param[in] len : No of bytes of data to be read. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note For most of the registers auto address increment applies, with the + * exception of a few special registers, which trap the address. For e.g., + * Register address - 0x24(BMI160_FIFO_DATA_ADDR) + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t + bmi160_get_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiRegs + * \page bmi160_api_bmi160_set_regs bmi160_set_regs + * \code + * int8_t bmi160_set_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API writes the given data to the register address + * of sensor. + * + * @param[in] reg_addr : Register address from where the data to be written. + * @param[in] data : Pointer to data buffer which is to be written + * in the sensor. + * @param[in] len : No of bytes of data to write.. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t + bmi160_set_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiSoftreset Soft reset + * @brief Perform soft reset of the sensor + */ + +/*! + * \ingroup bmi160ApiSoftreset + * \page bmi160_api_bmi160_soft_reset bmi160_soft_reset + * \code + * int8_t bmi160_soft_reset(struct bmi160_dev *dev); + * \endcode + * @details This API resets and restarts the device. + * All register values are overwritten with default parameters. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_soft_reset(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiConfig Configuration + * @brief Configuration of the sensor + */ + +/*! + * \ingroup bmi160ApiConfig + * \page bmi160_api_bmi160_set_sens_conf bmi160_set_sens_conf + * \code + * int8_t bmi160_set_sens_conf(struct bmi160_dev *dev); + * \endcode + * @details This API configures the power mode, range and bandwidth + * of sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_sens_conf(struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiConfig + * \page bmi160_api_bmi160_get_sens_conf bmi160_get_sens_conf + * \code + * int8_t bmi160_get_sens_conf(struct bmi160_dev *dev); + * \endcode + * @details This API gets accel and gyro configurations. + * + * @param[out] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_sens_conf(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiPowermode Power mode + * @brief Set / Get power mode of the sensor + */ + +/*! + * \ingroup bmi160ApiPowermode + * \page bmi160_api_bmi160_set_power_mode bmi160_set_power_mode + * \code + * int8_t bmi160_set_power_mode(struct bmi160_dev *dev); + * \endcode + * @details This API sets the power mode of the sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_power_mode(struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiPowermode + * \page bmi160_api_bmi160_get_power_mode bmi160_get_power_mode + * \code + * int8_t bmi160_get_power_mode(struct bmi160_dev *dev); + * \endcode + * @details This API gets the power mode of the sensor. + * + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_power_mode(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiData Sensor Data + * @brief Read sensor data + */ + +/*! + * \ingroup bmi160ApiData + * \page bmi160_api_bmi160_get_sensor_data bmi160_get_sensor_data + * \code + * int8_t bmi160_get_sensor_data(uint8_t select_sensor, + * struct bmi160_sensor_data *accel, + * struct bmi160_sensor_data *gyro, + * const struct bmi160_dev *dev); + * + * \endcode + * @details This API reads sensor data, stores it in + * the bmi160_sensor_data structure pointer passed by the user. + * The user can ask for accel data ,gyro data or both sensor + * data using bmi160_select_sensor enum + * + * @param[in] select_sensor : enum to choose accel,gyro or both sensor data + * @param[out] accel : Structure pointer to store accel data + * @param[out] gyro : Structure pointer to store gyro data + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_sensor_data( + uint8_t select_sensor, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInt Interrupt configuration + * @brief Set interrupt configuration of the sensor + */ + +/*! + * \ingroup bmi160ApiInt + * \page bmi160_api_bmi160_set_int_config bmi160_set_int_config + * \code + * int8_t bmi160_set_int_config(struct bmi160_int_settg *int_config, struct bmi160_dev *dev); + * \endcode + * @details This API configures the necessary interrupt based on + * the user settings in the bmi160_int_settg structure instance. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_int_config(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiStepC Step counter + * @brief Step counter operations + */ + +/*! + * \ingroup bmi160ApiStepC + * \page bmi160_api_bmi160_set_step_counter bmi160_set_step_counter + * \code + * int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev *dev); + * \endcode + * @details This API enables the step counter feature. + * + * @param[in] step_cnt_enable : value to enable or disable + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiStepC + * \page bmi160_api_bmi160_read_step_counter bmi160_read_step_counter + * \code + * int8_t bmi160_read_step_counter(uint16_t *step_val, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the step counter value. + * + * @param[in] step_val : Pointer to store the step counter value. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_read_step_counter(uint16_t* step_val, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiAux Auxiliary sensor + * @brief Auxiliary sensor operations + */ + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_read bmi160_aux_read + * \code + * int8_t bmi160_aux_read(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the mention no of byte of data from the given + * register address of auxiliary sensor. + * + * @param[in] reg_addr : Address of register to read. + * @param[in] aux_data : Pointer to store the read data. + * @param[in] len : No of bytes to read. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_read( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_write bmi160_aux_write + * \code + * int8_t bmi160_aux_write(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API writes the mention no of byte of data to the given + * register address of auxiliary sensor. + * + * @param[in] reg_addr : Address of register to write. + * @param[in] aux_data : Pointer to write data. + * @param[in] len : No of bytes to write. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_write( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_init bmi160_aux_init + * \code + * int8_t bmi160_aux_init(const struct bmi160_dev *dev); + * \endcode + * @details This API initialize the auxiliary sensor + * in order to access it. + * + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_init(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_set_aux_auto_mode bmi160_set_aux_auto_mode + * \code + * int8_t bmi160_set_aux_auto_mode(uint8_t *data_addr, struct bmi160_dev *dev); + * \endcode + * @details This API is used to setup the auxiliary sensor of bmi160 in auto mode + * Thus enabling the auto update of 8 bytes of data from auxiliary sensor + * to BMI160 register address 0x04 to 0x0B + * + * @param[in] data_addr : Starting address of aux. sensor's data register + * (BMI160 registers 0x04 to 0x0B will be updated + * with 8 bytes of data from auxiliary sensor + * starting from this register address.) + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note : Set the value of auxiliary polling rate by setting + * dev->aux_cfg.aux_odr to the required value from the table + * before calling this API + * + *@verbatim + * dev->aux_cfg.aux_odr | Auxiliary ODR (Hz) + * -----------------------|----------------------- + * BMI160_AUX_ODR_0_78HZ | 25/32 + * BMI160_AUX_ODR_1_56HZ | 25/16 + * BMI160_AUX_ODR_3_12HZ | 25/8 + * BMI160_AUX_ODR_6_25HZ | 25/4 + * BMI160_AUX_ODR_12_5HZ | 25/2 + * BMI160_AUX_ODR_25HZ | 25 + * BMI160_AUX_ODR_50HZ | 50 + * BMI160_AUX_ODR_100HZ | 100 + * BMI160_AUX_ODR_200HZ | 200 + * BMI160_AUX_ODR_400HZ | 400 + * BMI160_AUX_ODR_800HZ | 800 + *@endverbatim + * + * @note : Other values of dev->aux_cfg.aux_odr are reserved and not for use + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_aux_auto_mode(uint8_t* data_addr, struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_config_aux_mode bmi160_config_aux_mode + * \code + * int8_t bmi160_config_aux_mode(const struct bmi160_dev *dev); + * \endcode + * @details This API configures the 0x4C register and settings like + * Auxiliary sensor manual enable/ disable and aux burst read length. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_config_aux_mode(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_read_aux_data_auto_mode bmi160_read_aux_data_auto_mode + * \code + * int8_t bmi160_read_aux_data_auto_mode(uint8_t *aux_data, const struct bmi160_dev *dev); + * \endcode + * @details This API is used to read the raw uncompensated auxiliary sensor + * data of 8 bytes from BMI160 register address 0x04 to 0x0B + * + * @param[in] aux_data : Pointer to user array of length 8 bytes + * Ensure that the aux_data array is of + * length 8 bytes + * @param[in] dev : Structure instance of bmi160_dev + * + * @retval zero -> Success / -ve value -> Error + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_read_aux_data_auto_mode(uint8_t* aux_data, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiSelfTest Self test + * @brief Perform self test of the sensor + */ + +/*! + * \ingroup bmi160ApiSelfTest + * \page bmi160_api_bmi160_perform_self_test bmi160_perform_self_test + * \code + * int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev *dev); + * \endcode + * @details This is used to perform self test of accel/gyro of the BMI160 sensor + * + * @param[in] select_sensor : enum to choose accel or gyro for self test + * @param[in] dev : Structure instance of bmi160_dev + * + * @note self test can be performed either for accel/gyro at any instant. + * + *@verbatim + * value of select_sensor | Inference + *----------------------------------|-------------------------------- + * BMI160_ACCEL_ONLY | Accel self test enabled + * BMI160_GYRO_ONLY | Gyro self test enabled + * BMI160_BOTH_ACCEL_AND_GYRO | NOT TO BE USED + *@endverbatim + * + * @note The return value of this API gives us the result of self test. + * + * @note Performing self test does soft reset of the sensor, User can + * set the desired settings after performing the self test. + * + * @return Result of API execution status + * @retval BMI160_OK Self test success + * @retval BMI160_W_GYRO_SELF_TEST_FAIL Gyro self test fail + * @retval BMI160_W_ACCEl_SELF_TEST_FAIL Accel self test fail + */ +int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiFIFO FIFO + * @brief FIFO operations of the sensor + */ + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_get_fifo_data bmi160_get_fifo_data + * \code + * int8_t bmi160_get_fifo_data(struct bmi160_dev const *dev); + * \endcode + * @details This API reads data from the fifo buffer. + * + * @note User has to allocate the FIFO buffer along with + * corresponding fifo length from his side before calling this API + * as mentioned in the readme.md + * + * @note User must specify the number of bytes to read from the FIFO in + * dev->fifo->length , It will be updated by the number of bytes actually + * read from FIFO after calling this API + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_fifo_data(struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_flush bmi160_set_fifo_flush + * \code + * int8_t bmi160_set_fifo_flush(const struct bmi160_dev *dev); + * \endcode + * @details This API writes fifo_flush command to command register.This + * action clears all data in the Fifo without changing fifo configuration + * settings. + * + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_flush(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_config bmi160_set_fifo_config + * \code + * int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const *dev); + * \endcode + * @details This API sets the FIFO configuration in the sensor. + * + * @param[in] config : variable used to specify the FIFO + * configurations which are to be enabled or disabled in the sensor. + * + * @note : User can set either set one or more or all FIFO configurations + * by ORing the below mentioned macros. + * + *@verbatim + * config | Value + * ------------------------|--------------------------- + * BMI160_FIFO_TIME | 0x02 + * BMI160_FIFO_TAG_INT2 | 0x04 + * BMI160_FIFO_TAG_INT1 | 0x08 + * BMI160_FIFO_HEADER | 0x10 + * BMI160_FIFO_AUX | 0x20 + * BMI160_FIFO_ACCEL | 0x40 + * BMI160_FIFO_GYRO | 0x80 + *@endverbatim + * + * @param[in] enable : Parameter used to enable or disable the above + * FIFO configuration + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return status of bus communication result + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_down bmi160_set_fifo_down + * \code + * int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev *dev); + * \endcode + * @details This API is used to configure the down sampling ratios of + * the accel and gyro data for FIFO.Also, it configures filtered or + * pre-filtered data for the fifo for accel and gyro. + * + * @param[in] fifo_down : variable used to specify the FIFO down + * configurations which are to be enabled or disabled in the sensor. + * + * @note The user must select one among the following macros to + * select down-sampling ratio for accel + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_ACCEL_FIFO_DOWN_ZERO | 0x00 + * BMI160_ACCEL_FIFO_DOWN_ONE | 0x10 + * BMI160_ACCEL_FIFO_DOWN_TWO | 0x20 + * BMI160_ACCEL_FIFO_DOWN_THREE | 0x30 + * BMI160_ACCEL_FIFO_DOWN_FOUR | 0x40 + * BMI160_ACCEL_FIFO_DOWN_FIVE | 0x50 + * BMI160_ACCEL_FIFO_DOWN_SIX | 0x60 + * BMI160_ACCEL_FIFO_DOWN_SEVEN | 0x70 + *@endverbatim + * + * @note The user must select one among the following macros to + * select down-sampling ratio for gyro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_GYRO_FIFO_DOWN_ZERO | 0x00 + * BMI160_GYRO_FIFO_DOWN_ONE | 0x01 + * BMI160_GYRO_FIFO_DOWN_TWO | 0x02 + * BMI160_GYRO_FIFO_DOWN_THREE | 0x03 + * BMI160_GYRO_FIFO_DOWN_FOUR | 0x04 + * BMI160_GYRO_FIFO_DOWN_FIVE | 0x05 + * BMI160_GYRO_FIFO_DOWN_SIX | 0x06 + * BMI160_GYRO_FIFO_DOWN_SEVEN | 0x07 + *@endverbatim + * + * @note The user can enable filtered accel data by the following macro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_ACCEL_FIFO_FILT_EN | 0x80 + *@endverbatim + * + * @note The user can enable filtered gyro data by the following macro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_GYRO_FIFO_FILT_EN | 0x08 + *@endverbatim + * + * @note : By ORing the above mentioned macros, the user can select + * the required FIFO down config settings + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return status of bus communication result + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_wm bmi160_set_fifo_wm + * \code + * int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev *dev); + * \endcode + * @details This API sets the FIFO watermark level in the sensor. + * + * @note The FIFO watermark is issued when the FIFO fill level is + * equal or above the watermark level and units of watermark is 4 bytes. + * + * @param[in] fifo_wm : Variable used to set the FIFO water mark level + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_accel bmi160_extract_accel + * \code + * int8_t bmi160_extract_accel(struct bmi160_sensor_data *accel_data, uint8_t *accel_length, struct bmi160_dev const + **dev); + * \endcode + * @details This API parses and extracts the accelerometer frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "accel_data" structure instance. + * + * @note The bmi160_extract_accel API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] accel_data : Structure instance of bmi160_sensor_data + * where the accelerometer data in FIFO is stored. + * @param[in,out] accel_length : Number of valid accelerometer frames + * (x,y,z axes data) read out from fifo. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note accel_length is updated with the number of valid accelerometer + * frames extracted from fifo (1 accel frame = 6 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_accel( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_gyro bmi160_extract_gyro + * \code + * int8_t bmi160_extract_gyro(struct bmi160_sensor_data *gyro_data, uint8_t *gyro_length, struct bmi160_dev const *dev); + * \endcode + * @details This API parses and extracts the gyro frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "gyro_data" structure instance. + * + * @note The bmi160_extract_gyro API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] gyro_data : Structure instance of bmi160_sensor_data + * where the gyro data in FIFO is stored. + * @param[in,out] gyro_length : Number of valid gyro frames + * (x,y,z axes data) read out from fifo. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note gyro_length is updated with the number of valid gyro + * frames extracted from fifo (1 gyro frame = 6 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_gyro( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_aux bmi160_extract_aux + * \code + * int8_t bmi160_extract_aux(struct bmi160_aux_data *aux_data, uint8_t *aux_len, struct bmi160_dev const *dev); + * \endcode + * @details This API parses and extracts the aux frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the bmi160_aux_data structure instance. + * + * @note The bmi160_extract_aux API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] aux_data : Structure instance of bmi160_aux_data + * where the aux data in FIFO is stored. + * @param[in,out] aux_len : Number of valid aux frames (8bytes) + * read out from FIFO. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note aux_len is updated with the number of valid aux + * frames extracted from fifo (1 aux frame = 8 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_aux( + struct bmi160_aux_data* aux_data, + uint8_t* aux_len, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiFOC FOC + * @brief Start FOC of accel and gyro sensors + */ + +/*! + * \ingroup bmi160ApiFOC + * \page bmi160_api_bmi160_start_foc bmi160_start_foc + * \code + * int8_t bmi160_start_foc(const struct bmi160_foc_conf *foc_conf, + * \endcode + * @details This API starts the FOC of accel and gyro + * + * @note FOC should not be used in low-power mode of sensor + * + * @note Accel FOC targets values of +1g , 0g , -1g + * Gyro FOC always targets value of 0 dps + * + * @param[in] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC configuration + * @param[in,out] offset : Structure instance to store Offset + * values read from sensor + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note Pre-requisites for triggering FOC in accel , Set the following, + * Enable the acc_off_en + * Ex : foc_conf.acc_off_en = BMI160_ENABLE; + * + * Set the desired target values of FOC to each axes (x,y,z) by using the + * following macros + * - BMI160_FOC_ACCEL_DISABLED + * - BMI160_FOC_ACCEL_POSITIVE_G + * - BMI160_FOC_ACCEL_NEGATIVE_G + * - BMI160_FOC_ACCEL_0G + * + * Ex : foc_conf.foc_acc_x = BMI160_FOC_ACCEL_0G; + * foc_conf.foc_acc_y = BMI160_FOC_ACCEL_0G; + * foc_conf.foc_acc_z = BMI160_FOC_ACCEL_POSITIVE_G; + * + * @note Pre-requisites for triggering FOC in gyro , + * Set the following parameters, + * + * Ex : foc_conf.foc_gyr_en = BMI160_ENABLE; + * foc_conf.gyro_off_en = BMI160_ENABLE; + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_start_foc( + const struct bmi160_foc_conf* foc_conf, + struct bmi160_offsets* offset, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiOffsets Offsets + * @brief Set / Get offset values of accel and gyro sensors + */ + +/*! + * \ingroup bmi160ApiOffsets + * \page bmi160_api_bmi160_get_offsets bmi160_get_offsets + * \code + * int8_t bmi160_get_offsets(struct bmi160_offsets *offset, const struct bmi160_dev *dev); + * \endcode + * @details This API reads and stores the offset values of accel and gyro + * + * @param[in,out] offset : Structure instance of bmi160_offsets in which + * the offset values are read and stored + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_get_offsets(struct bmi160_offsets* offset, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiOffsets + * \page bmi160_api_bmi160_set_offsets bmi160_set_offsets + * \code + * int8_t bmi160_set_offsets(const struct bmi160_foc_conf *foc_conf, + * const struct bmi160_offsets *offset, + * struct bmi160_dev const *dev); + * \endcode + * @details This API writes the offset values of accel and gyro to + * the sensor but these values will be reset on POR or soft reset. + * + * @param[in] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC configuration + * @param[in] offset : Structure instance in which user updates offset + * values which are to be written in the sensor + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note Offsets can be set by user like offset->off_acc_x = 10; + * where 1LSB = 3.9mg and for gyro 1LSB = 0.061degrees/second + * + * @note BMI160 offset values for xyz axes of accel should be within range of + * BMI160_ACCEL_MIN_OFFSET (-128) to BMI160_ACCEL_MAX_OFFSET (127) + * + * @note BMI160 offset values for xyz axes of gyro should be within range of + * BMI160_GYRO_MIN_OFFSET (-512) to BMI160_GYRO_MAX_OFFSET (511) + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_set_offsets( + const struct bmi160_foc_conf* foc_conf, + const struct bmi160_offsets* offset, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiNVM NVM + * @brief Write image registers values to NVM + */ + +/*! + * \ingroup bmi160ApiNVM + * \page bmi160_api_bmi160_update_nvm bmi160_update_nvm + * \code + * int8_t bmi160_update_nvm(struct bmi160_dev const *dev); + * \endcode + * @details This API writes the image registers values to NVM which is + * stored even after POR or soft reset + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_update_nvm(struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInts Interrupt status + * @brief Read interrupt status from the sensor + */ + +/*! + * \ingroup bmi160ApiInts + * \page bmi160_api_bmi160_get_int_status bmi160_get_int_status + * \code + * int8_t bmi160_get_int_status(enum bmi160_int_status_sel int_status_sel, + * union bmi160_int_status *int_status, + * struct bmi160_dev const *dev); + * \endcode + * @details This API gets the interrupt status from the sensor. + * + * @param[in] int_status_sel : Enum variable to select either individual or all the + * interrupt status bits. + * @param[in] int_status : pointer variable to get the interrupt status + * from the sensor. + * param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_get_int_status( + enum bmi160_int_status_sel int_status_sel, + union bmi160_int_status* int_status, + struct bmi160_dev const* dev); + +/*************************** C++ guard macro *****************************/ +#ifdef __cplusplus +} +#endif + +#endif /* BMI160_H_ */ diff --git a/applications/plugins/airmouse/tracking/imu/bmi160_defs.h b/applications/plugins/airmouse/tracking/imu/bmi160_defs.h new file mode 100644 index 000000000..458ecaad5 --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/bmi160_defs.h @@ -0,0 +1,1619 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160_defs.h +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +#ifndef BMI160_DEFS_H_ +#define BMI160_DEFS_H_ + +/*************************** C types headers *****************************/ +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif + +/*************************** Common macros *****************************/ + +#if !defined(UINT8_C) && !defined(INT8_C) +#define INT8_C(x) S8_C(x) +#define UINT8_C(x) U8_C(x) +#endif + +#if !defined(UINT16_C) && !defined(INT16_C) +#define INT16_C(x) S16_C(x) +#define UINT16_C(x) U16_C(x) +#endif + +#if !defined(INT32_C) && !defined(UINT32_C) +#define INT32_C(x) S32_C(x) +#define UINT32_C(x) U32_C(x) +#endif + +#if !defined(INT64_C) && !defined(UINT64_C) +#define INT64_C(x) S64_C(x) +#define UINT64_C(x) U64_C(x) +#endif + +/**@}*/ +/**\name C standard macros */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void*)0) +#endif +#endif + +/*************************** Sensor macros *****************************/ +/* Test for an endian machine */ +#ifndef __ORDER_LITTLE_ENDIAN__ +#define __ORDER_LITTLE_ENDIAN__ 0 +#endif + +#ifndef __BYTE_ORDER__ +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ +#endif + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1 +#endif +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 1 +#endif +#else +#error "Code does not support Endian format of the processor" +#endif + +/** Mask definitions */ +#define BMI160_ACCEL_BW_MASK UINT8_C(0x70) +#define BMI160_ACCEL_ODR_MASK UINT8_C(0x0F) +#define BMI160_ACCEL_UNDERSAMPLING_MASK UINT8_C(0x80) +#define BMI160_ACCEL_RANGE_MASK UINT8_C(0x0F) +#define BMI160_GYRO_BW_MASK UINT8_C(0x30) +#define BMI160_GYRO_ODR_MASK UINT8_C(0x0F) +#define BMI160_GYRO_RANGE_MASK UINT8_C(0x07) + +#define BMI160_ACCEL_BW_POS UINT8_C(4) +#define BMI160_GYRO_BW_POS UINT8_C(4) + +/** Mask definitions for INT_EN registers */ +#define BMI160_ANY_MOTION_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_HIGH_G_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_NO_MOTION_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_ANY_MOTION_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_HIGH_G_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_NO_MOTION_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_ANY_MOTION_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_HIGH_G_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_NO_MOTION_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_SIG_MOTION_INT_EN_MASK UINT8_C(0x07) +#define BMI160_ANY_MOTION_ALL_INT_EN_MASK UINT8_C(0x07) +#define BMI160_STEP_DETECT_INT_EN_MASK UINT8_C(0x08) +#define BMI160_DOUBLE_TAP_INT_EN_MASK UINT8_C(0x10) +#define BMI160_SINGLE_TAP_INT_EN_MASK UINT8_C(0x20) +#define BMI160_FIFO_FULL_INT_EN_MASK UINT8_C(0x20) +#define BMI160_ORIENT_INT_EN_MASK UINT8_C(0x40) +#define BMI160_FIFO_WATERMARK_INT_EN_MASK UINT8_C(0x40) +#define BMI160_LOW_G_INT_EN_MASK UINT8_C(0x08) +#define BMI160_STEP_DETECT_EN_MASK UINT8_C(0x08) +#define BMI160_FLAT_INT_EN_MASK UINT8_C(0x80) +#define BMI160_DATA_RDY_INT_EN_MASK UINT8_C(0x10) + +/** PMU status Macros */ +#define BMI160_AUX_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_AUX_PMU_NORMAL UINT8_C(0x01) +#define BMI160_AUX_PMU_LOW_POWER UINT8_C(0x02) + +#define BMI160_GYRO_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_GYRO_PMU_NORMAL UINT8_C(0x01) +#define BMI160_GYRO_PMU_FSU UINT8_C(0x03) + +#define BMI160_ACCEL_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_ACCEL_PMU_NORMAL UINT8_C(0x01) +#define BMI160_ACCEL_PMU_LOW_POWER UINT8_C(0x02) + +/** Mask definitions for INT_OUT_CTRL register */ +#define BMI160_INT1_EDGE_CTRL_MASK UINT8_C(0x01) +#define BMI160_INT1_OUTPUT_MODE_MASK UINT8_C(0x04) +#define BMI160_INT1_OUTPUT_TYPE_MASK UINT8_C(0x02) +#define BMI160_INT1_OUTPUT_EN_MASK UINT8_C(0x08) +#define BMI160_INT2_EDGE_CTRL_MASK UINT8_C(0x10) +#define BMI160_INT2_OUTPUT_MODE_MASK UINT8_C(0x40) +#define BMI160_INT2_OUTPUT_TYPE_MASK UINT8_C(0x20) +#define BMI160_INT2_OUTPUT_EN_MASK UINT8_C(0x80) + +/** Mask definitions for INT_LATCH register */ +#define BMI160_INT1_INPUT_EN_MASK UINT8_C(0x10) +#define BMI160_INT2_INPUT_EN_MASK UINT8_C(0x20) +#define BMI160_INT_LATCH_MASK UINT8_C(0x0F) + +/** Mask definitions for INT_MAP register */ +#define BMI160_INT1_LOW_G_MASK UINT8_C(0x01) +#define BMI160_INT1_HIGH_G_MASK UINT8_C(0x02) +#define BMI160_INT1_SLOPE_MASK UINT8_C(0x04) +#define BMI160_INT1_NO_MOTION_MASK UINT8_C(0x08) +#define BMI160_INT1_DOUBLE_TAP_MASK UINT8_C(0x10) +#define BMI160_INT1_SINGLE_TAP_MASK UINT8_C(0x20) +#define BMI160_INT1_FIFO_FULL_MASK UINT8_C(0x20) +#define BMI160_INT1_FIFO_WM_MASK UINT8_C(0x40) +#define BMI160_INT1_ORIENT_MASK UINT8_C(0x40) +#define BMI160_INT1_FLAT_MASK UINT8_C(0x80) +#define BMI160_INT1_DATA_READY_MASK UINT8_C(0x80) +#define BMI160_INT2_LOW_G_MASK UINT8_C(0x01) +#define BMI160_INT1_LOW_STEP_DETECT_MASK UINT8_C(0x01) +#define BMI160_INT2_LOW_STEP_DETECT_MASK UINT8_C(0x01) +#define BMI160_INT2_HIGH_G_MASK UINT8_C(0x02) +#define BMI160_INT2_FIFO_FULL_MASK UINT8_C(0x02) +#define BMI160_INT2_FIFO_WM_MASK UINT8_C(0x04) +#define BMI160_INT2_SLOPE_MASK UINT8_C(0x04) +#define BMI160_INT2_DATA_READY_MASK UINT8_C(0x08) +#define BMI160_INT2_NO_MOTION_MASK UINT8_C(0x08) +#define BMI160_INT2_DOUBLE_TAP_MASK UINT8_C(0x10) +#define BMI160_INT2_SINGLE_TAP_MASK UINT8_C(0x20) +#define BMI160_INT2_ORIENT_MASK UINT8_C(0x40) +#define BMI160_INT2_FLAT_MASK UINT8_C(0x80) + +/** Mask definitions for INT_DATA register */ +#define BMI160_TAP_SRC_INT_MASK UINT8_C(0x08) +#define BMI160_LOW_HIGH_SRC_INT_MASK UINT8_C(0x80) +#define BMI160_MOTION_SRC_INT_MASK UINT8_C(0x80) + +/** Mask definitions for INT_MOTION register */ +#define BMI160_SLOPE_INT_DUR_MASK UINT8_C(0x03) +#define BMI160_NO_MOTION_INT_DUR_MASK UINT8_C(0xFC) +#define BMI160_NO_MOTION_SEL_BIT_MASK UINT8_C(0x01) + +/** Mask definitions for INT_TAP register */ +#define BMI160_TAP_DUR_MASK UINT8_C(0x07) +#define BMI160_TAP_SHOCK_DUR_MASK UINT8_C(0x40) +#define BMI160_TAP_QUIET_DUR_MASK UINT8_C(0x80) +#define BMI160_TAP_THRES_MASK UINT8_C(0x1F) + +/** Mask definitions for INT_FLAT register */ +#define BMI160_FLAT_THRES_MASK UINT8_C(0x3F) +#define BMI160_FLAT_HOLD_TIME_MASK UINT8_C(0x30) +#define BMI160_FLAT_HYST_MASK UINT8_C(0x07) + +/** Mask definitions for INT_LOWHIGH register */ +#define BMI160_LOW_G_HYST_MASK UINT8_C(0x03) +#define BMI160_LOW_G_LOW_MODE_MASK UINT8_C(0x04) +#define BMI160_HIGH_G_HYST_MASK UINT8_C(0xC0) + +/** Mask definitions for INT_SIG_MOTION register */ +#define BMI160_SIG_MOTION_SEL_MASK UINT8_C(0x02) +#define BMI160_SIG_MOTION_SKIP_MASK UINT8_C(0x0C) +#define BMI160_SIG_MOTION_PROOF_MASK UINT8_C(0x30) + +/** Mask definitions for INT_ORIENT register */ +#define BMI160_ORIENT_MODE_MASK UINT8_C(0x03) +#define BMI160_ORIENT_BLOCK_MASK UINT8_C(0x0C) +#define BMI160_ORIENT_HYST_MASK UINT8_C(0xF0) +#define BMI160_ORIENT_THETA_MASK UINT8_C(0x3F) +#define BMI160_ORIENT_UD_ENABLE UINT8_C(0x40) +#define BMI160_AXES_EN_MASK UINT8_C(0x80) + +/** Mask definitions for FIFO_CONFIG register */ +#define BMI160_FIFO_GYRO UINT8_C(0x80) +#define BMI160_FIFO_ACCEL UINT8_C(0x40) +#define BMI160_FIFO_AUX UINT8_C(0x20) +#define BMI160_FIFO_TAG_INT1 UINT8_C(0x08) +#define BMI160_FIFO_TAG_INT2 UINT8_C(0x04) +#define BMI160_FIFO_TIME UINT8_C(0x02) +#define BMI160_FIFO_HEADER UINT8_C(0x10) +#define BMI160_FIFO_CONFIG_1_MASK UINT8_C(0xFE) + +/** Mask definitions for STEP_CONF register */ +#define BMI160_STEP_COUNT_EN_BIT_MASK UINT8_C(0x08) +#define BMI160_STEP_DETECT_MIN_THRES_MASK UINT8_C(0x18) +#define BMI160_STEP_DETECT_STEPTIME_MIN_MASK UINT8_C(0x07) +#define BMI160_STEP_MIN_BUF_MASK UINT8_C(0x07) + +/** Mask definition for FIFO Header Data Tag */ +#define BMI160_FIFO_TAG_INTR_MASK UINT8_C(0xFC) + +/** Fifo byte counter mask definitions */ +#define BMI160_FIFO_BYTE_COUNTER_MASK UINT8_C(0x07) + +/** Enable/disable bit value */ +#define BMI160_ENABLE UINT8_C(0x01) +#define BMI160_DISABLE UINT8_C(0x00) + +/** Latch Duration */ +#define BMI160_LATCH_DUR_NONE UINT8_C(0x00) +#define BMI160_LATCH_DUR_312_5_MICRO_SEC UINT8_C(0x01) +#define BMI160_LATCH_DUR_625_MICRO_SEC UINT8_C(0x02) +#define BMI160_LATCH_DUR_1_25_MILLI_SEC UINT8_C(0x03) +#define BMI160_LATCH_DUR_2_5_MILLI_SEC UINT8_C(0x04) +#define BMI160_LATCH_DUR_5_MILLI_SEC UINT8_C(0x05) +#define BMI160_LATCH_DUR_10_MILLI_SEC UINT8_C(0x06) +#define BMI160_LATCH_DUR_20_MILLI_SEC UINT8_C(0x07) +#define BMI160_LATCH_DUR_40_MILLI_SEC UINT8_C(0x08) +#define BMI160_LATCH_DUR_80_MILLI_SEC UINT8_C(0x09) +#define BMI160_LATCH_DUR_160_MILLI_SEC UINT8_C(0x0A) +#define BMI160_LATCH_DUR_320_MILLI_SEC UINT8_C(0x0B) +#define BMI160_LATCH_DUR_640_MILLI_SEC UINT8_C(0x0C) +#define BMI160_LATCH_DUR_1_28_SEC UINT8_C(0x0D) +#define BMI160_LATCH_DUR_2_56_SEC UINT8_C(0x0E) +#define BMI160_LATCHED UINT8_C(0x0F) + +/** BMI160 Register map */ +#define BMI160_CHIP_ID_ADDR UINT8_C(0x00) +#define BMI160_ERROR_REG_ADDR UINT8_C(0x02) +#define BMI160_PMU_STATUS_ADDR UINT8_C(0x03) +#define BMI160_AUX_DATA_ADDR UINT8_C(0x04) +#define BMI160_GYRO_DATA_ADDR UINT8_C(0x0C) +#define BMI160_ACCEL_DATA_ADDR UINT8_C(0x12) +#define BMI160_STATUS_ADDR UINT8_C(0x1B) +#define BMI160_INT_STATUS_ADDR UINT8_C(0x1C) +#define BMI160_FIFO_LENGTH_ADDR UINT8_C(0x22) +#define BMI160_FIFO_DATA_ADDR UINT8_C(0x24) +#define BMI160_ACCEL_CONFIG_ADDR UINT8_C(0x40) +#define BMI160_ACCEL_RANGE_ADDR UINT8_C(0x41) +#define BMI160_GYRO_CONFIG_ADDR UINT8_C(0x42) +#define BMI160_GYRO_RANGE_ADDR UINT8_C(0x43) +#define BMI160_AUX_ODR_ADDR UINT8_C(0x44) +#define BMI160_FIFO_DOWN_ADDR UINT8_C(0x45) +#define BMI160_FIFO_CONFIG_0_ADDR UINT8_C(0x46) +#define BMI160_FIFO_CONFIG_1_ADDR UINT8_C(0x47) +#define BMI160_AUX_IF_0_ADDR UINT8_C(0x4B) +#define BMI160_AUX_IF_1_ADDR UINT8_C(0x4C) +#define BMI160_AUX_IF_2_ADDR UINT8_C(0x4D) +#define BMI160_AUX_IF_3_ADDR UINT8_C(0x4E) +#define BMI160_AUX_IF_4_ADDR UINT8_C(0x4F) +#define BMI160_INT_ENABLE_0_ADDR UINT8_C(0x50) +#define BMI160_INT_ENABLE_1_ADDR UINT8_C(0x51) +#define BMI160_INT_ENABLE_2_ADDR UINT8_C(0x52) +#define BMI160_INT_OUT_CTRL_ADDR UINT8_C(0x53) +#define BMI160_INT_LATCH_ADDR UINT8_C(0x54) +#define BMI160_INT_MAP_0_ADDR UINT8_C(0x55) +#define BMI160_INT_MAP_1_ADDR UINT8_C(0x56) +#define BMI160_INT_MAP_2_ADDR UINT8_C(0x57) +#define BMI160_INT_DATA_0_ADDR UINT8_C(0x58) +#define BMI160_INT_DATA_1_ADDR UINT8_C(0x59) +#define BMI160_INT_LOWHIGH_0_ADDR UINT8_C(0x5A) +#define BMI160_INT_LOWHIGH_1_ADDR UINT8_C(0x5B) +#define BMI160_INT_LOWHIGH_2_ADDR UINT8_C(0x5C) +#define BMI160_INT_LOWHIGH_3_ADDR UINT8_C(0x5D) +#define BMI160_INT_LOWHIGH_4_ADDR UINT8_C(0x5E) +#define BMI160_INT_MOTION_0_ADDR UINT8_C(0x5F) +#define BMI160_INT_MOTION_1_ADDR UINT8_C(0x60) +#define BMI160_INT_MOTION_2_ADDR UINT8_C(0x61) +#define BMI160_INT_MOTION_3_ADDR UINT8_C(0x62) +#define BMI160_INT_TAP_0_ADDR UINT8_C(0x63) +#define BMI160_INT_TAP_1_ADDR UINT8_C(0x64) +#define BMI160_INT_ORIENT_0_ADDR UINT8_C(0x65) +#define BMI160_INT_ORIENT_1_ADDR UINT8_C(0x66) +#define BMI160_INT_FLAT_0_ADDR UINT8_C(0x67) +#define BMI160_INT_FLAT_1_ADDR UINT8_C(0x68) +#define BMI160_FOC_CONF_ADDR UINT8_C(0x69) +#define BMI160_CONF_ADDR UINT8_C(0x6A) + +#define BMI160_IF_CONF_ADDR UINT8_C(0x6B) +#define BMI160_SELF_TEST_ADDR UINT8_C(0x6D) +#define BMI160_OFFSET_ADDR UINT8_C(0x71) +#define BMI160_OFFSET_CONF_ADDR UINT8_C(0x77) +#define BMI160_INT_STEP_CNT_0_ADDR UINT8_C(0x78) +#define BMI160_INT_STEP_CONFIG_0_ADDR UINT8_C(0x7A) +#define BMI160_INT_STEP_CONFIG_1_ADDR UINT8_C(0x7B) +#define BMI160_COMMAND_REG_ADDR UINT8_C(0x7E) +#define BMI160_SPI_COMM_TEST_ADDR UINT8_C(0x7F) +#define BMI160_INTL_PULLUP_CONF_ADDR UINT8_C(0x85) + +/** Error code definitions */ +#define BMI160_OK INT8_C(0) +#define BMI160_E_NULL_PTR INT8_C(-1) +#define BMI160_E_COM_FAIL INT8_C(-2) +#define BMI160_E_DEV_NOT_FOUND INT8_C(-3) +#define BMI160_E_OUT_OF_RANGE INT8_C(-4) +#define BMI160_E_INVALID_INPUT INT8_C(-5) +#define BMI160_E_ACCEL_ODR_BW_INVALID INT8_C(-6) +#define BMI160_E_GYRO_ODR_BW_INVALID INT8_C(-7) +#define BMI160_E_LWP_PRE_FLTR_INT_INVALID INT8_C(-8) +#define BMI160_E_LWP_PRE_FLTR_INVALID INT8_C(-9) +#define BMI160_E_AUX_NOT_FOUND INT8_C(-10) +#define BMI160_E_FOC_FAILURE INT8_C(-11) +#define BMI160_E_READ_WRITE_LENGTH_INVALID INT8_C(-12) +#define BMI160_E_INVALID_CONFIG INT8_C(-13) + +/**\name API warning codes */ +#define BMI160_W_GYRO_SELF_TEST_FAIL INT8_C(1) +#define BMI160_W_ACCEl_SELF_TEST_FAIL INT8_C(2) + +/** BMI160 unique chip identifier */ +#define BMI160_CHIP_ID UINT8_C(0xD1) + +/** Soft reset command */ +#define BMI160_SOFT_RESET_CMD UINT8_C(0xb6) +#define BMI160_SOFT_RESET_DELAY_MS UINT8_C(1) + +/** Start FOC command */ +#define BMI160_START_FOC_CMD UINT8_C(0x03) + +/** NVM backup enabling command */ +#define BMI160_NVM_BACKUP_EN UINT8_C(0xA0) + +/* Delay in ms settings */ +#define BMI160_ACCEL_DELAY_MS UINT8_C(5) +#define BMI160_GYRO_DELAY_MS UINT8_C(80) +#define BMI160_ONE_MS_DELAY UINT8_C(1) +#define BMI160_AUX_COM_DELAY UINT8_C(10) +#define BMI160_GYRO_SELF_TEST_DELAY UINT8_C(20) +#define BMI160_ACCEL_SELF_TEST_DELAY UINT8_C(50) + +/** Self test configurations */ +#define BMI160_ACCEL_SELF_TEST_CONFIG UINT8_C(0x2C) +#define BMI160_ACCEL_SELF_TEST_POSITIVE_EN UINT8_C(0x0D) +#define BMI160_ACCEL_SELF_TEST_NEGATIVE_EN UINT8_C(0x09) +#define BMI160_ACCEL_SELF_TEST_LIMIT UINT16_C(8192) + +/** Power mode settings */ +/* Accel power mode */ +#define BMI160_ACCEL_NORMAL_MODE UINT8_C(0x11) +#define BMI160_ACCEL_LOWPOWER_MODE UINT8_C(0x12) +#define BMI160_ACCEL_SUSPEND_MODE UINT8_C(0x10) + +/* Gyro power mode */ +#define BMI160_GYRO_SUSPEND_MODE UINT8_C(0x14) +#define BMI160_GYRO_NORMAL_MODE UINT8_C(0x15) +#define BMI160_GYRO_FASTSTARTUP_MODE UINT8_C(0x17) + +/* Aux power mode */ +#define BMI160_AUX_SUSPEND_MODE UINT8_C(0x18) +#define BMI160_AUX_NORMAL_MODE UINT8_C(0x19) +#define BMI160_AUX_LOWPOWER_MODE UINT8_C(0x1A) + +/** Range settings */ +/* Accel Range */ +#define BMI160_ACCEL_RANGE_2G UINT8_C(0x03) +#define BMI160_ACCEL_RANGE_4G UINT8_C(0x05) +#define BMI160_ACCEL_RANGE_8G UINT8_C(0x08) +#define BMI160_ACCEL_RANGE_16G UINT8_C(0x0C) + +/* Gyro Range */ +#define BMI160_GYRO_RANGE_2000_DPS UINT8_C(0x00) +#define BMI160_GYRO_RANGE_1000_DPS UINT8_C(0x01) +#define BMI160_GYRO_RANGE_500_DPS UINT8_C(0x02) +#define BMI160_GYRO_RANGE_250_DPS UINT8_C(0x03) +#define BMI160_GYRO_RANGE_125_DPS UINT8_C(0x04) + +/** Bandwidth settings */ +/* Accel Bandwidth */ +#define BMI160_ACCEL_BW_OSR4_AVG1 UINT8_C(0x00) +#define BMI160_ACCEL_BW_OSR2_AVG2 UINT8_C(0x01) +#define BMI160_ACCEL_BW_NORMAL_AVG4 UINT8_C(0x02) +#define BMI160_ACCEL_BW_RES_AVG8 UINT8_C(0x03) +#define BMI160_ACCEL_BW_RES_AVG16 UINT8_C(0x04) +#define BMI160_ACCEL_BW_RES_AVG32 UINT8_C(0x05) +#define BMI160_ACCEL_BW_RES_AVG64 UINT8_C(0x06) +#define BMI160_ACCEL_BW_RES_AVG128 UINT8_C(0x07) + +#define BMI160_GYRO_BW_OSR4_MODE UINT8_C(0x00) +#define BMI160_GYRO_BW_OSR2_MODE UINT8_C(0x01) +#define BMI160_GYRO_BW_NORMAL_MODE UINT8_C(0x02) + +/* Output Data Rate settings */ +/* Accel Output data rate */ +#define BMI160_ACCEL_ODR_RESERVED UINT8_C(0x00) +#define BMI160_ACCEL_ODR_0_78HZ UINT8_C(0x01) +#define BMI160_ACCEL_ODR_1_56HZ UINT8_C(0x02) +#define BMI160_ACCEL_ODR_3_12HZ UINT8_C(0x03) +#define BMI160_ACCEL_ODR_6_25HZ UINT8_C(0x04) +#define BMI160_ACCEL_ODR_12_5HZ UINT8_C(0x05) +#define BMI160_ACCEL_ODR_25HZ UINT8_C(0x06) +#define BMI160_ACCEL_ODR_50HZ UINT8_C(0x07) +#define BMI160_ACCEL_ODR_100HZ UINT8_C(0x08) +#define BMI160_ACCEL_ODR_200HZ UINT8_C(0x09) +#define BMI160_ACCEL_ODR_400HZ UINT8_C(0x0A) +#define BMI160_ACCEL_ODR_800HZ UINT8_C(0x0B) +#define BMI160_ACCEL_ODR_1600HZ UINT8_C(0x0C) +#define BMI160_ACCEL_ODR_RESERVED0 UINT8_C(0x0D) +#define BMI160_ACCEL_ODR_RESERVED1 UINT8_C(0x0E) +#define BMI160_ACCEL_ODR_RESERVED2 UINT8_C(0x0F) + +/* Gyro Output data rate */ +#define BMI160_GYRO_ODR_RESERVED UINT8_C(0x00) +#define BMI160_GYRO_ODR_25HZ UINT8_C(0x06) +#define BMI160_GYRO_ODR_50HZ UINT8_C(0x07) +#define BMI160_GYRO_ODR_100HZ UINT8_C(0x08) +#define BMI160_GYRO_ODR_200HZ UINT8_C(0x09) +#define BMI160_GYRO_ODR_400HZ UINT8_C(0x0A) +#define BMI160_GYRO_ODR_800HZ UINT8_C(0x0B) +#define BMI160_GYRO_ODR_1600HZ UINT8_C(0x0C) +#define BMI160_GYRO_ODR_3200HZ UINT8_C(0x0D) + +/* Auxiliary sensor Output data rate */ +#define BMI160_AUX_ODR_RESERVED UINT8_C(0x00) +#define BMI160_AUX_ODR_0_78HZ UINT8_C(0x01) +#define BMI160_AUX_ODR_1_56HZ UINT8_C(0x02) +#define BMI160_AUX_ODR_3_12HZ UINT8_C(0x03) +#define BMI160_AUX_ODR_6_25HZ UINT8_C(0x04) +#define BMI160_AUX_ODR_12_5HZ UINT8_C(0x05) +#define BMI160_AUX_ODR_25HZ UINT8_C(0x06) +#define BMI160_AUX_ODR_50HZ UINT8_C(0x07) +#define BMI160_AUX_ODR_100HZ UINT8_C(0x08) +#define BMI160_AUX_ODR_200HZ UINT8_C(0x09) +#define BMI160_AUX_ODR_400HZ UINT8_C(0x0A) +#define BMI160_AUX_ODR_800HZ UINT8_C(0x0B) + +/** FIFO_CONFIG Definitions */ +#define BMI160_FIFO_TIME_ENABLE UINT8_C(0x02) +#define BMI160_FIFO_TAG_INT2_ENABLE UINT8_C(0x04) +#define BMI160_FIFO_TAG_INT1_ENABLE UINT8_C(0x08) +#define BMI160_FIFO_HEAD_ENABLE UINT8_C(0x10) +#define BMI160_FIFO_M_ENABLE UINT8_C(0x20) +#define BMI160_FIFO_A_ENABLE UINT8_C(0x40) +#define BMI160_FIFO_M_A_ENABLE UINT8_C(0x60) +#define BMI160_FIFO_G_ENABLE UINT8_C(0x80) +#define BMI160_FIFO_M_G_ENABLE UINT8_C(0xA0) +#define BMI160_FIFO_G_A_ENABLE UINT8_C(0xC0) +#define BMI160_FIFO_M_G_A_ENABLE UINT8_C(0xE0) + +/* Macro to specify the number of bytes over-read from the + * FIFO in order to get the sensor time at the end of FIFO */ +#ifndef BMI160_FIFO_BYTES_OVERREAD +#define BMI160_FIFO_BYTES_OVERREAD UINT8_C(25) +#endif + +/* Accel, gyro and aux. sensor length and also their combined + * length definitions in FIFO */ +#define BMI160_FIFO_G_LENGTH UINT8_C(6) +#define BMI160_FIFO_A_LENGTH UINT8_C(6) +#define BMI160_FIFO_M_LENGTH UINT8_C(8) +#define BMI160_FIFO_GA_LENGTH UINT8_C(12) +#define BMI160_FIFO_MA_LENGTH UINT8_C(14) +#define BMI160_FIFO_MG_LENGTH UINT8_C(14) +#define BMI160_FIFO_MGA_LENGTH UINT8_C(20) + +/** FIFO Header Data definitions */ +#define BMI160_FIFO_HEAD_SKIP_FRAME UINT8_C(0x40) +#define BMI160_FIFO_HEAD_SENSOR_TIME UINT8_C(0x44) +#define BMI160_FIFO_HEAD_INPUT_CONFIG UINT8_C(0x48) +#define BMI160_FIFO_HEAD_OVER_READ UINT8_C(0x80) +#define BMI160_FIFO_HEAD_A UINT8_C(0x84) +#define BMI160_FIFO_HEAD_G UINT8_C(0x88) +#define BMI160_FIFO_HEAD_G_A UINT8_C(0x8C) +#define BMI160_FIFO_HEAD_M UINT8_C(0x90) +#define BMI160_FIFO_HEAD_M_A UINT8_C(0x94) +#define BMI160_FIFO_HEAD_M_G UINT8_C(0x98) +#define BMI160_FIFO_HEAD_M_G_A UINT8_C(0x9C) + +/** FIFO sensor time length definitions */ +#define BMI160_SENSOR_TIME_LENGTH UINT8_C(3) + +/** FIFO DOWN selection */ +/* Accel fifo down-sampling values*/ +#define BMI160_ACCEL_FIFO_DOWN_ZERO UINT8_C(0x00) +#define BMI160_ACCEL_FIFO_DOWN_ONE UINT8_C(0x10) +#define BMI160_ACCEL_FIFO_DOWN_TWO UINT8_C(0x20) +#define BMI160_ACCEL_FIFO_DOWN_THREE UINT8_C(0x30) +#define BMI160_ACCEL_FIFO_DOWN_FOUR UINT8_C(0x40) +#define BMI160_ACCEL_FIFO_DOWN_FIVE UINT8_C(0x50) +#define BMI160_ACCEL_FIFO_DOWN_SIX UINT8_C(0x60) +#define BMI160_ACCEL_FIFO_DOWN_SEVEN UINT8_C(0x70) + +/* Gyro fifo down-smapling values*/ +#define BMI160_GYRO_FIFO_DOWN_ZERO UINT8_C(0x00) +#define BMI160_GYRO_FIFO_DOWN_ONE UINT8_C(0x01) +#define BMI160_GYRO_FIFO_DOWN_TWO UINT8_C(0x02) +#define BMI160_GYRO_FIFO_DOWN_THREE UINT8_C(0x03) +#define BMI160_GYRO_FIFO_DOWN_FOUR UINT8_C(0x04) +#define BMI160_GYRO_FIFO_DOWN_FIVE UINT8_C(0x05) +#define BMI160_GYRO_FIFO_DOWN_SIX UINT8_C(0x06) +#define BMI160_GYRO_FIFO_DOWN_SEVEN UINT8_C(0x07) + +/* Accel Fifo filter enable*/ +#define BMI160_ACCEL_FIFO_FILT_EN UINT8_C(0x80) + +/* Gyro Fifo filter enable*/ +#define BMI160_GYRO_FIFO_FILT_EN UINT8_C(0x08) + +/** Definitions to check validity of FIFO frames */ +#define FIFO_CONFIG_MSB_CHECK UINT8_C(0x80) +#define FIFO_CONFIG_LSB_CHECK UINT8_C(0x00) + +/*! BMI160 accel FOC configurations */ +#define BMI160_FOC_ACCEL_DISABLED UINT8_C(0x00) +#define BMI160_FOC_ACCEL_POSITIVE_G UINT8_C(0x01) +#define BMI160_FOC_ACCEL_NEGATIVE_G UINT8_C(0x02) +#define BMI160_FOC_ACCEL_0G UINT8_C(0x03) + +/** Array Parameter DefinItions */ +#define BMI160_SENSOR_TIME_LSB_BYTE UINT8_C(0) +#define BMI160_SENSOR_TIME_XLSB_BYTE UINT8_C(1) +#define BMI160_SENSOR_TIME_MSB_BYTE UINT8_C(2) + +/** Interface settings */ +#define BMI160_SPI_INTF UINT8_C(1) +#define BMI160_I2C_INTF UINT8_C(0) +#define BMI160_SPI_RD_MASK UINT8_C(0x80) +#define BMI160_SPI_WR_MASK UINT8_C(0x7F) + +/* Sensor & time select definition*/ +#define BMI160_ACCEL_SEL UINT8_C(0x01) +#define BMI160_GYRO_SEL UINT8_C(0x02) +#define BMI160_TIME_SEL UINT8_C(0x04) + +/* Sensor select mask*/ +#define BMI160_SEN_SEL_MASK UINT8_C(0x07) + +/* Error code mask */ +#define BMI160_ERR_REG_MASK UINT8_C(0x0F) + +/* BMI160 I2C address */ +#define BMI160_I2C_ADDR UINT8_C(0x68) + +/* BMI160 secondary IF address */ +#define BMI160_AUX_BMM150_I2C_ADDR UINT8_C(0x10) + +/** BMI160 Length definitions */ +#define BMI160_ONE UINT8_C(1) +#define BMI160_TWO UINT8_C(2) +#define BMI160_THREE UINT8_C(3) +#define BMI160_FOUR UINT8_C(4) +#define BMI160_FIVE UINT8_C(5) + +/** BMI160 fifo level Margin */ +#define BMI160_FIFO_LEVEL_MARGIN UINT8_C(16) + +/** BMI160 fifo flush Command */ +#define BMI160_FIFO_FLUSH_VALUE UINT8_C(0xB0) + +/** BMI160 offset values for xyz axes of accel */ +#define BMI160_ACCEL_MIN_OFFSET INT8_C(-128) +#define BMI160_ACCEL_MAX_OFFSET INT8_C(127) + +/** BMI160 offset values for xyz axes of gyro */ +#define BMI160_GYRO_MIN_OFFSET INT16_C(-512) +#define BMI160_GYRO_MAX_OFFSET INT16_C(511) + +/** BMI160 fifo full interrupt position and mask */ +#define BMI160_FIFO_FULL_INT_POS UINT8_C(5) +#define BMI160_FIFO_FULL_INT_MSK UINT8_C(0x20) +#define BMI160_FIFO_WTM_INT_POS UINT8_C(6) +#define BMI160_FIFO_WTM_INT_MSK UINT8_C(0x40) + +#define BMI160_FIFO_FULL_INT_PIN1_POS UINT8_C(5) +#define BMI160_FIFO_FULL_INT_PIN1_MSK UINT8_C(0x20) +#define BMI160_FIFO_FULL_INT_PIN2_POS UINT8_C(1) +#define BMI160_FIFO_FULL_INT_PIN2_MSK UINT8_C(0x02) + +#define BMI160_FIFO_WTM_INT_PIN1_POS UINT8_C(6) +#define BMI160_FIFO_WTM_INT_PIN1_MSK UINT8_C(0x40) +#define BMI160_FIFO_WTM_INT_PIN2_POS UINT8_C(2) +#define BMI160_FIFO_WTM_INT_PIN2_MSK UINT8_C(0x04) + +#define BMI160_MANUAL_MODE_EN_POS UINT8_C(7) +#define BMI160_MANUAL_MODE_EN_MSK UINT8_C(0x80) +#define BMI160_AUX_READ_BURST_POS UINT8_C(0) +#define BMI160_AUX_READ_BURST_MSK UINT8_C(0x03) + +#define BMI160_GYRO_SELF_TEST_POS UINT8_C(4) +#define BMI160_GYRO_SELF_TEST_MSK UINT8_C(0x10) +#define BMI160_GYRO_SELF_TEST_STATUS_POS UINT8_C(1) +#define BMI160_GYRO_SELF_TEST_STATUS_MSK UINT8_C(0x02) + +#define BMI160_GYRO_FOC_EN_POS UINT8_C(6) +#define BMI160_GYRO_FOC_EN_MSK UINT8_C(0x40) + +#define BMI160_ACCEL_FOC_X_CONF_POS UINT8_C(4) +#define BMI160_ACCEL_FOC_X_CONF_MSK UINT8_C(0x30) + +#define BMI160_ACCEL_FOC_Y_CONF_POS UINT8_C(2) +#define BMI160_ACCEL_FOC_Y_CONF_MSK UINT8_C(0x0C) + +#define BMI160_ACCEL_FOC_Z_CONF_MSK UINT8_C(0x03) + +#define BMI160_FOC_STATUS_POS UINT8_C(3) +#define BMI160_FOC_STATUS_MSK UINT8_C(0x08) + +#define BMI160_GYRO_OFFSET_X_MSK UINT8_C(0x03) + +#define BMI160_GYRO_OFFSET_Y_POS UINT8_C(2) +#define BMI160_GYRO_OFFSET_Y_MSK UINT8_C(0x0C) + +#define BMI160_GYRO_OFFSET_Z_POS UINT8_C(4) +#define BMI160_GYRO_OFFSET_Z_MSK UINT8_C(0x30) + +#define BMI160_GYRO_OFFSET_EN_POS UINT8_C(7) +#define BMI160_GYRO_OFFSET_EN_MSK UINT8_C(0x80) + +#define BMI160_ACCEL_OFFSET_EN_POS UINT8_C(6) +#define BMI160_ACCEL_OFFSET_EN_MSK UINT8_C(0x40) + +#define BMI160_GYRO_OFFSET_POS UINT16_C(8) +#define BMI160_GYRO_OFFSET_MSK UINT16_C(0x0300) + +#define BMI160_NVM_UPDATE_POS UINT8_C(1) +#define BMI160_NVM_UPDATE_MSK UINT8_C(0x02) + +#define BMI160_NVM_STATUS_POS UINT8_C(4) +#define BMI160_NVM_STATUS_MSK UINT8_C(0x10) + +#define BMI160_MAG_POWER_MODE_MSK UINT8_C(0x03) + +#define BMI160_ACCEL_POWER_MODE_MSK UINT8_C(0x30) +#define BMI160_ACCEL_POWER_MODE_POS UINT8_C(4) + +#define BMI160_GYRO_POWER_MODE_MSK UINT8_C(0x0C) +#define BMI160_GYRO_POWER_MODE_POS UINT8_C(2) + +/* BIT SLICE GET AND SET FUNCTIONS */ +#define BMI160_GET_BITS(regvar, bitname) ((regvar & bitname##_MSK) >> bitname##_POS) +#define BMI160_SET_BITS(regvar, bitname, val) \ + ((regvar & ~bitname##_MSK) | ((val << bitname##_POS) & bitname##_MSK)) + +#define BMI160_SET_BITS_POS_0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | (data & bitname##_MSK)) + +#define BMI160_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MSK)) + +/**\name UTILITY MACROS */ +#define BMI160_SET_LOW_BYTE UINT16_C(0x00FF) +#define BMI160_SET_HIGH_BYTE UINT16_C(0xFF00) + +#define BMI160_GET_LSB(var) (uint8_t)(var & BMI160_SET_LOW_BYTE) +#define BMI160_GET_MSB(var) (uint8_t)((var & BMI160_SET_HIGH_BYTE) >> 8) + +/*****************************************************************************/ +/* type definitions */ + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific read functions of the user + */ +typedef int8_t ( + *bmi160_read_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t len); + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific write functions of the user + */ +typedef int8_t ( + *bmi160_write_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t* read_data, uint16_t len); +typedef void (*bmi160_delay_fptr_t)(uint32_t period); + +/*************************** Data structures *********************************/ + +/*! + * @brief bmi160 interrupt status selection enum. + */ +enum bmi160_int_status_sel { + BMI160_INT_STATUS_0 = 1, + BMI160_INT_STATUS_1 = 2, + BMI160_INT_STATUS_2 = 4, + BMI160_INT_STATUS_3 = 8, + BMI160_INT_STATUS_ALL = 15 +}; + +/*! + * @brief bmi160 interrupt status bits structure + */ +struct bmi160_int_status_bits { +#ifdef LITTLE_ENDIAN + + uint32_t step : 1; + uint32_t sigmot : 1; + uint32_t anym : 1; + + /* pmu trigger will be handled later */ + uint32_t pmu_trigger_reserved : 1; + uint32_t d_tap : 1; + uint32_t s_tap : 1; + uint32_t orient : 1; + uint32_t flat_int : 1; + uint32_t reserved : 2; + uint32_t high_g : 1; + uint32_t low_g : 1; + uint32_t drdy : 1; + uint32_t ffull : 1; + uint32_t fwm : 1; + uint32_t nomo : 1; + uint32_t anym_first_x : 1; + uint32_t anym_first_y : 1; + uint32_t anym_first_z : 1; + uint32_t anym_sign : 1; + uint32_t tap_first_x : 1; + uint32_t tap_first_y : 1; + uint32_t tap_first_z : 1; + uint32_t tap_sign : 1; + uint32_t high_first_x : 1; + uint32_t high_first_y : 1; + uint32_t high_first_z : 1; + uint32_t high_sign : 1; + uint32_t orient_1_0 : 2; + uint32_t orient_2 : 1; + uint32_t flat : 1; +#else + uint32_t high_first_x : 1; + uint32_t high_first_y : 1; + uint32_t high_first_z : 1; + uint32_t high_sign : 1; + uint32_t orient_1_0 : 2; + uint32_t orient_2 : 1; + uint32_t flat : 1; + uint32_t anym_first_x : 1; + uint32_t anym_first_y : 1; + uint32_t anym_first_z : 1; + uint32_t anym_sign : 1; + uint32_t tap_first_x : 1; + uint32_t tap_first_y : 1; + uint32_t tap_first_z : 1; + uint32_t tap_sign : 1; + uint32_t reserved : 2; + uint32_t high_g : 1; + uint32_t low_g : 1; + uint32_t drdy : 1; + uint32_t ffull : 1; + uint32_t fwm : 1; + uint32_t nomo : 1; + uint32_t step : 1; + uint32_t sigmot : 1; + uint32_t anym : 1; + + /* pmu trigger will be handled later */ + uint32_t pmu_trigger_reserved : 1; + uint32_t d_tap : 1; + uint32_t s_tap : 1; + uint32_t orient : 1; + uint32_t flat_int : 1; +#endif +}; + +/*! + * @brief bmi160 interrupt status structure + */ +union bmi160_int_status { + uint8_t data[4]; + struct bmi160_int_status_bits bit; +}; + +/*! + * @brief bmi160 sensor data structure which comprises of accel data + */ +struct bmi160_sensor_data { + /*! X-axis sensor data */ + int16_t x; + + /*! Y-axis sensor data */ + int16_t y; + + /*! Z-axis sensor data */ + int16_t z; + + /*! sensor time */ + uint32_t sensortime; +}; + +/*! + * @brief bmi160 aux data structure which comprises of 8 bytes of accel data + */ +struct bmi160_aux_data { + /*! Auxiliary data */ + uint8_t data[8]; +}; + +/*! + * @brief bmi160 FOC configuration structure + */ +struct bmi160_foc_conf { + /*! Enabling FOC in gyro + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t foc_gyr_en; + + /*! Accel FOC configurations + * Assignable macros : + * - BMI160_FOC_ACCEL_DISABLED + * - BMI160_FOC_ACCEL_POSITIVE_G + * - BMI160_FOC_ACCEL_NEGATIVE_G + * - BMI160_FOC_ACCEL_0G + */ + uint8_t foc_acc_x; + uint8_t foc_acc_y; + uint8_t foc_acc_z; + + /*! Enabling offset compensation for accel in data registers + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t acc_off_en; + + /*! Enabling offset compensation for gyro in data registers + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t gyro_off_en; +}; + +/*! + * @brief bmi160 accel gyro offsets + */ +struct bmi160_offsets { + /*! Accel offset for x axis */ + int8_t off_acc_x; + + /*! Accel offset for y axis */ + int8_t off_acc_y; + + /*! Accel offset for z axis */ + int8_t off_acc_z; + + /*! Gyro offset for x axis */ + int16_t off_gyro_x; + + /*! Gyro offset for y axis */ + int16_t off_gyro_y; + + /*! Gyro offset for z axis */ + int16_t off_gyro_z; +}; + +/*! + * @brief FIFO aux. sensor data structure + */ +struct bmi160_aux_fifo_data { + /*! The value of aux. sensor x LSB data */ + uint8_t aux_x_lsb; + + /*! The value of aux. sensor x MSB data */ + uint8_t aux_x_msb; + + /*! The value of aux. sensor y LSB data */ + uint8_t aux_y_lsb; + + /*! The value of aux. sensor y MSB data */ + uint8_t aux_y_msb; + + /*! The value of aux. sensor z LSB data */ + uint8_t aux_z_lsb; + + /*! The value of aux. sensor z MSB data */ + uint8_t aux_z_msb; + + /*! The value of aux. sensor r for BMM150 LSB data */ + uint8_t aux_r_y2_lsb; + + /*! The value of aux. sensor r for BMM150 MSB data */ + uint8_t aux_r_y2_msb; +}; + +/*! + * @brief bmi160 sensor select structure + */ +enum bmi160_select_sensor { BMI160_ACCEL_ONLY = 1, BMI160_GYRO_ONLY, BMI160_BOTH_ACCEL_AND_GYRO }; + +/*! + * @brief bmi160 sensor step detector mode structure + */ +enum bmi160_step_detect_mode { + BMI160_STEP_DETECT_NORMAL, + BMI160_STEP_DETECT_SENSITIVE, + BMI160_STEP_DETECT_ROBUST, + + /*! Non recommended User defined setting */ + BMI160_STEP_DETECT_USER_DEFINE +}; + +/*! + * @brief enum for auxiliary burst read selection + */ +enum bmi160_aux_read_len { + BMI160_AUX_READ_LEN_0, + BMI160_AUX_READ_LEN_1, + BMI160_AUX_READ_LEN_2, + BMI160_AUX_READ_LEN_3 +}; + +/*! + * @brief bmi160 sensor configuration structure + */ +struct bmi160_cfg { + /*! power mode */ + uint8_t power; + + /*! output data rate */ + uint8_t odr; + + /*! range */ + uint8_t range; + + /*! bandwidth */ + uint8_t bw; +}; + +/*! + * @brief Aux sensor configuration structure + */ +struct bmi160_aux_cfg { + /*! Aux sensor, 1 - enable 0 - disable */ + uint8_t aux_sensor_enable : 1; + + /*! Aux manual/auto mode status */ + uint8_t manual_enable : 1; + + /*! Aux read burst length */ + uint8_t aux_rd_burst_len : 2; + + /*! output data rate */ + uint8_t aux_odr : 4; + + /*! i2c addr of auxiliary sensor */ + uint8_t aux_i2c_addr; +}; + +/*! + * @brief bmi160 interrupt channel selection structure + */ +enum bmi160_int_channel { + /*! Un-map both channels */ + BMI160_INT_CHANNEL_NONE, + + /*! interrupt Channel 1 */ + BMI160_INT_CHANNEL_1, + + /*! interrupt Channel 2 */ + BMI160_INT_CHANNEL_2, + + /*! Map both channels */ + BMI160_INT_CHANNEL_BOTH +}; +enum bmi160_int_types { + /*! Slope/Any-motion interrupt */ + BMI160_ACC_ANY_MOTION_INT, + + /*! Significant motion interrupt */ + BMI160_ACC_SIG_MOTION_INT, + + /*! Step detector interrupt */ + BMI160_STEP_DETECT_INT, + + /*! double tap interrupt */ + BMI160_ACC_DOUBLE_TAP_INT, + + /*! single tap interrupt */ + BMI160_ACC_SINGLE_TAP_INT, + + /*! orientation interrupt */ + BMI160_ACC_ORIENT_INT, + + /*! flat interrupt */ + BMI160_ACC_FLAT_INT, + + /*! high-g interrupt */ + BMI160_ACC_HIGH_G_INT, + + /*! low-g interrupt */ + BMI160_ACC_LOW_G_INT, + + /*! slow/no-motion interrupt */ + BMI160_ACC_SLOW_NO_MOTION_INT, + + /*! data ready interrupt */ + BMI160_ACC_GYRO_DATA_RDY_INT, + + /*! fifo full interrupt */ + BMI160_ACC_GYRO_FIFO_FULL_INT, + + /*! fifo watermark interrupt */ + BMI160_ACC_GYRO_FIFO_WATERMARK_INT, + + /*! fifo tagging feature support */ + BMI160_FIFO_TAG_INT_PIN +}; + +/*! + * @brief bmi160 active state of any & sig motion interrupt. + */ +enum bmi160_any_sig_motion_active_interrupt_state { + /*! Both any & sig motion are disabled */ + BMI160_BOTH_ANY_SIG_MOTION_DISABLED = -1, + + /*! Any-motion selected */ + BMI160_ANY_MOTION_ENABLED, + + /*! Sig-motion selected */ + BMI160_SIG_MOTION_ENABLED +}; +struct bmi160_acc_tap_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! tap threshold */ + uint16_t tap_thr : 5; + + /*! tap shock */ + uint16_t tap_shock : 1; + + /*! tap quiet */ + uint16_t tap_quiet : 1; + + /*! tap duration */ + uint16_t tap_dur : 3; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t tap_data_src : 1; + + /*! tap enable, 1 - enable, 0 - disable */ + uint16_t tap_en : 1; +#else + + /*! tap enable, 1 - enable, 0 - disable */ + uint16_t tap_en : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t tap_data_src : 1; + + /*! tap duration */ + uint16_t tap_dur : 3; + + /*! tap quiet */ + uint16_t tap_quiet : 1; + + /*! tap shock */ + uint16_t tap_shock : 1; + + /*! tap threshold */ + uint16_t tap_thr : 5; +#endif +}; +struct bmi160_acc_any_mot_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! 1 any-motion enable, 0 - any-motion disable */ + uint8_t anymotion_en : 1; + + /*! slope interrupt x, 1 - enable, 0 - disable */ + uint8_t anymotion_x : 1; + + /*! slope interrupt y, 1 - enable, 0 - disable */ + uint8_t anymotion_y : 1; + + /*! slope interrupt z, 1 - enable, 0 - disable */ + uint8_t anymotion_z : 1; + + /*! slope duration */ + uint8_t anymotion_dur : 2; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t anymotion_data_src : 1; + + /*! slope threshold */ + uint8_t anymotion_thr; +#else + + /*! slope threshold */ + uint8_t anymotion_thr; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t anymotion_data_src : 1; + + /*! slope duration */ + uint8_t anymotion_dur : 2; + + /*! slope interrupt z, 1 - enable, 0 - disable */ + uint8_t anymotion_z : 1; + + /*! slope interrupt y, 1 - enable, 0 - disable */ + uint8_t anymotion_y : 1; + + /*! slope interrupt x, 1 - enable, 0 - disable */ + uint8_t anymotion_x : 1; + + /*! 1 any-motion enable, 0 - any-motion disable */ + uint8_t anymotion_en : 1; +#endif +}; +struct bmi160_acc_sig_mot_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! skip time of sig-motion interrupt */ + uint8_t sig_mot_skip : 2; + + /*! proof time of sig-motion interrupt */ + uint8_t sig_mot_proof : 2; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t sig_data_src : 1; + + /*! 1 - enable sig, 0 - disable sig & enable anymotion */ + uint8_t sig_en : 1; + + /*! sig-motion threshold */ + uint8_t sig_mot_thres; +#else + + /*! sig-motion threshold */ + uint8_t sig_mot_thres; + + /*! 1 - enable sig, 0 - disable sig & enable anymotion */ + uint8_t sig_en : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t sig_data_src : 1; + + /*! proof time of sig-motion interrupt */ + uint8_t sig_mot_proof : 2; + + /*! skip time of sig-motion interrupt */ + uint8_t sig_mot_skip : 2; +#endif +}; +struct bmi160_acc_step_detect_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! 1- step detector enable, 0- step detector disable */ + uint16_t step_detector_en : 1; + + /*! minimum threshold */ + uint16_t min_threshold : 2; + + /*! minimal detectable step time */ + uint16_t steptime_min : 3; + + /*! enable step counter mode setting */ + uint16_t step_detector_mode : 2; + + /*! minimum step buffer size*/ + uint16_t step_min_buf : 3; +#else + + /*! minimum step buffer size*/ + uint16_t step_min_buf : 3; + + /*! enable step counter mode setting */ + uint16_t step_detector_mode : 2; + + /*! minimal detectable step time */ + uint16_t steptime_min : 3; + + /*! minimum threshold */ + uint16_t min_threshold : 2; + + /*! 1- step detector enable, 0- step detector disable */ + uint16_t step_detector_en : 1; +#endif +}; +struct bmi160_acc_no_motion_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! no motion interrupt x */ + uint16_t no_motion_x : 1; + + /*! no motion interrupt y */ + uint16_t no_motion_y : 1; + + /*! no motion interrupt z */ + uint16_t no_motion_z : 1; + + /*! no motion duration */ + uint16_t no_motion_dur : 6; + + /*! no motion sel , 1 - enable no-motion ,0- enable slow-motion */ + uint16_t no_motion_sel : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t no_motion_src : 1; + + /*! no motion threshold */ + uint8_t no_motion_thres; +#else + + /*! no motion threshold */ + uint8_t no_motion_thres; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t no_motion_src : 1; + + /*! no motion sel , 1 - enable no-motion ,0- enable slow-motion */ + uint16_t no_motion_sel : 1; + + /*! no motion duration */ + uint16_t no_motion_dur : 6; + + /* no motion interrupt z */ + uint16_t no_motion_z : 1; + + /*! no motion interrupt y */ + uint16_t no_motion_y : 1; + + /*! no motion interrupt x */ + uint16_t no_motion_x : 1; +#endif +}; +struct bmi160_acc_orient_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! thresholds for switching between the different orientations */ + uint16_t orient_mode : 2; + + /*! blocking_mode */ + uint16_t orient_blocking : 2; + + /*! Orientation interrupt hysteresis */ + uint16_t orient_hyst : 4; + + /*! Orientation interrupt theta */ + uint16_t orient_theta : 6; + + /*! Enable/disable Orientation interrupt */ + uint16_t orient_ud_en : 1; + + /*! exchange x- and z-axis in algorithm ,0 - z, 1 - x */ + uint16_t axes_ex : 1; + + /*! 1 - orient enable, 0 - orient disable */ + uint8_t orient_en : 1; +#else + + /*! 1 - orient enable, 0 - orient disable */ + uint8_t orient_en : 1; + + /*! exchange x- and z-axis in algorithm ,0 - z, 1 - x */ + uint16_t axes_ex : 1; + + /*! Enable/disable Orientation interrupt */ + uint16_t orient_ud_en : 1; + + /*! Orientation interrupt theta */ + uint16_t orient_theta : 6; + + /*! Orientation interrupt hysteresis */ + uint16_t orient_hyst : 4; + + /*! blocking_mode */ + uint16_t orient_blocking : 2; + + /*! thresholds for switching between the different orientations */ + uint16_t orient_mode : 2; +#endif +}; +struct bmi160_acc_flat_detect_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! flat threshold */ + uint16_t flat_theta : 6; + + /*! flat interrupt hysteresis */ + uint16_t flat_hy : 3; + + /*! delay time for which the flat value must remain stable for the + * flat interrupt to be generated */ + uint16_t flat_hold_time : 2; + + /*! 1 - flat enable, 0 - flat disable */ + uint16_t flat_en : 1; +#else + + /*! 1 - flat enable, 0 - flat disable */ + uint16_t flat_en : 1; + + /*! delay time for which the flat value must remain stable for the + * flat interrupt to be generated */ + uint16_t flat_hold_time : 2; + + /*! flat interrupt hysteresis */ + uint16_t flat_hy : 3; + + /*! flat threshold */ + uint16_t flat_theta : 6; +#endif +}; +struct bmi160_acc_low_g_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! low-g interrupt trigger delay */ + uint8_t low_dur; + + /*! low-g interrupt trigger threshold */ + uint8_t low_thres; + + /*! hysteresis of low-g interrupt */ + uint8_t low_hyst : 2; + + /*! 0 - single-axis mode ,1 - axis-summing mode */ + uint8_t low_mode : 1; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t low_data_src : 1; + + /*! 1 - enable low-g, 0 - disable low-g */ + uint8_t low_en : 1; +#else + + /*! 1 - enable low-g, 0 - disable low-g */ + uint8_t low_en : 1; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t low_data_src : 1; + + /*! 0 - single-axis mode ,1 - axis-summing mode */ + uint8_t low_mode : 1; + + /*! hysteresis of low-g interrupt */ + uint8_t low_hyst : 2; + + /*! low-g interrupt trigger threshold */ + uint8_t low_thres; + + /*! low-g interrupt trigger delay */ + uint8_t low_dur; +#endif +}; +struct bmi160_acc_high_g_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! High-g interrupt x, 1 - enable, 0 - disable */ + uint8_t high_g_x : 1; + + /*! High-g interrupt y, 1 - enable, 0 - disable */ + uint8_t high_g_y : 1; + + /*! High-g interrupt z, 1 - enable, 0 - disable */ + uint8_t high_g_z : 1; + + /*! High-g hysteresis */ + uint8_t high_hy : 2; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t high_data_src : 1; + + /*! High-g threshold */ + uint8_t high_thres; + + /*! High-g duration */ + uint8_t high_dur; +#else + + /*! High-g duration */ + uint8_t high_dur; + + /*! High-g threshold */ + uint8_t high_thres; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t high_data_src : 1; + + /*! High-g hysteresis */ + uint8_t high_hy : 2; + + /*! High-g interrupt z, 1 - enable, 0 - disable */ + uint8_t high_g_z : 1; + + /*! High-g interrupt y, 1 - enable, 0 - disable */ + uint8_t high_g_y : 1; + + /*! High-g interrupt x, 1 - enable, 0 - disable */ + uint8_t high_g_x : 1; +#endif +}; +struct bmi160_int_pin_settg { +#ifdef LITTLE_ENDIAN + + /*! To enable either INT1 or INT2 pin as output. + * 0- output disabled ,1- output enabled */ + uint16_t output_en : 1; + + /*! 0 - push-pull 1- open drain,only valid if output_en is set 1 */ + uint16_t output_mode : 1; + + /*! 0 - active low , 1 - active high level. + * if output_en is 1,this applies to interrupts,else PMU_trigger */ + uint16_t output_type : 1; + + /*! 0 - level trigger , 1 - edge trigger */ + uint16_t edge_ctrl : 1; + + /*! To enable either INT1 or INT2 pin as input. + * 0 - input disabled ,1 - input enabled */ + uint16_t input_en : 1; + + /*! latch duration*/ + uint16_t latch_dur : 4; +#else + + /*! latch duration*/ + uint16_t latch_dur : 4; + + /*! Latched,non-latched or temporary interrupt modes */ + uint16_t input_en : 1; + + /*! 1 - edge trigger, 0 - level trigger */ + uint16_t edge_ctrl : 1; + + /*! 0 - active low , 1 - active high level. + * if output_en is 1,this applies to interrupts,else PMU_trigger */ + uint16_t output_type : 1; + + /*! 0 - push-pull , 1 - open drain,only valid if output_en is set 1 */ + uint16_t output_mode : 1; + + /*! To enable either INT1 or INT2 pin as output. + * 0 - output disabled , 1 - output enabled */ + uint16_t output_en : 1; +#endif +}; +union bmi160_int_type_cfg { + /*! Tap interrupt structure */ + struct bmi160_acc_tap_int_cfg acc_tap_int; + + /*! Slope interrupt structure */ + struct bmi160_acc_any_mot_int_cfg acc_any_motion_int; + + /*! Significant motion interrupt structure */ + struct bmi160_acc_sig_mot_int_cfg acc_sig_motion_int; + + /*! Step detector interrupt structure */ + struct bmi160_acc_step_detect_int_cfg acc_step_detect_int; + + /*! No motion interrupt structure */ + struct bmi160_acc_no_motion_int_cfg acc_no_motion_int; + + /*! Orientation interrupt structure */ + struct bmi160_acc_orient_int_cfg acc_orient_int; + + /*! Flat interrupt structure */ + struct bmi160_acc_flat_detect_int_cfg acc_flat_int; + + /*! Low-g interrupt structure */ + struct bmi160_acc_low_g_int_cfg acc_low_g_int; + + /*! High-g interrupt structure */ + struct bmi160_acc_high_g_int_cfg acc_high_g_int; +}; +struct bmi160_int_settg { + /*! Interrupt channel */ + enum bmi160_int_channel int_channel; + + /*! Select Interrupt */ + enum bmi160_int_types int_type; + + /*! Structure configuring Interrupt pins */ + struct bmi160_int_pin_settg int_pin_settg; + + /*! Union configures required interrupt */ + union bmi160_int_type_cfg int_type_cfg; + + /*! FIFO FULL INT 1-enable, 0-disable */ + uint8_t fifo_full_int_en : 1; + + /*! FIFO WTM INT 1-enable, 0-disable */ + uint8_t fifo_wtm_int_en : 1; +}; + +/*! + * @brief This structure holds the information for usage of + * FIFO by the user. + */ +struct bmi160_fifo_frame { + /*! Data buffer of user defined length is to be mapped here */ + uint8_t* data; + + /*! While calling the API "bmi160_get_fifo_data" , length stores + * number of bytes in FIFO to be read (specified by user as input) + * and after execution of the API ,number of FIFO data bytes + * available is provided as an output to user + */ + uint16_t length; + + /*! FIFO time enable */ + uint8_t fifo_time_enable; + + /*! Enabling of the FIFO header to stream in header mode */ + uint8_t fifo_header_enable; + + /*! Streaming of the Accelerometer, Gyroscope + * sensor data or both in FIFO */ + uint8_t fifo_data_enable; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t accel_byte_start_idx; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t gyro_byte_start_idx; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t aux_byte_start_idx; + + /*! Value of FIFO sensor time time */ + uint32_t sensor_time; + + /*! Value of Skipped frame counts */ + uint8_t skipped_frame_count; +}; +struct bmi160_dev { + /*! Chip Id */ + uint8_t chip_id; + + /*! Device Id */ + uint8_t id; + + /*! 0 - I2C , 1 - SPI Interface */ + uint8_t intf; + + /*! Hold active interrupts status for any and sig motion + * 0 - Any-motion enable, 1 - Sig-motion enable, + * -1 neither any-motion nor sig-motion selected */ + enum bmi160_any_sig_motion_active_interrupt_state any_sig_sel; + + /*! Structure to configure Accel sensor */ + struct bmi160_cfg accel_cfg; + + /*! Structure to hold previous/old accel config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_cfg prev_accel_cfg; + + /*! Structure to configure Gyro sensor */ + struct bmi160_cfg gyro_cfg; + + /*! Structure to hold previous/old gyro config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_cfg prev_gyro_cfg; + + /*! Structure to configure the auxiliary sensor */ + struct bmi160_aux_cfg aux_cfg; + + /*! Structure to hold previous/old aux config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_aux_cfg prev_aux_cfg; + + /*! FIFO related configurations */ + struct bmi160_fifo_frame* fifo; + + /*! Read function pointer */ + bmi160_read_fptr_t read; + + /*! Write function pointer */ + bmi160_write_fptr_t write; + + /*! Delay function pointer */ + bmi160_delay_fptr_t delay_ms; + + /*! User set read/write length */ + uint16_t read_write_len; +}; + +#endif /* BMI160_DEFS_H_ */ diff --git a/applications/plugins/airmouse/tracking/imu/imu.c b/applications/plugins/airmouse/tracking/imu/imu.c new file mode 100644 index 000000000..5e89c9504 --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/imu.c @@ -0,0 +1,29 @@ +#include "imu.h" +#include + +bool bmi160_begin(); +int bmi160_read(double* vec); + +bool lsm6ds3trc_begin(); +void lsm6ds3trc_end(); +int lsm6ds3trc_read(double* vec); + +bool imu_begin() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + bool ret = bmi160_begin(); // lsm6ds3trc_begin(); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + return ret; +} + +void imu_end() { + // furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + // lsm6ds3trc_end(); + // furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} + +int imu_read(double* vec) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + int ret = bmi160_read(vec); // lsm6ds3trc_read(vec); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + return ret; +} diff --git a/applications/plugins/airmouse/tracking/imu/imu.h b/applications/plugins/airmouse/tracking/imu/imu.h new file mode 100644 index 000000000..f4c5e4b1d --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/imu.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ACC_DATA_READY (1 << 0) +#define GYR_DATA_READY (1 << 1) + +bool imu_begin(); +void imu_end(); +int imu_read(double* vec); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/airmouse/tracking/imu/imu_bmi160.c b/applications/plugins/airmouse/tracking/imu/imu_bmi160.c new file mode 100644 index 000000000..af771302f --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/imu_bmi160.c @@ -0,0 +1,88 @@ +#include "bmi160.h" + +#include + +#include "imu.h" + +#define TAG "BMI160" + +#define BMI160_DEV_ADDR (0x69 << 1) + +static const double DEG_TO_RAD = 0.017453292519943295769236907684886; +static const double G = 9.81; + +struct bmi160_dev bmi160dev; +struct bmi160_sensor_data bmi160_accel; +struct bmi160_sensor_data bmi160_gyro; + +int8_t bmi160_write_i2c(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t len) { + if(furi_hal_i2c_write_mem(&furi_hal_i2c_handle_external, dev_addr, reg_addr, data, len, 50)) + return BMI160_OK; + return BMI160_E_COM_FAIL; +} + +int8_t bmi160_read_i2c(uint8_t dev_addr, uint8_t reg_addr, uint8_t* read_data, uint16_t len) { + if(furi_hal_i2c_read_mem(&furi_hal_i2c_handle_external, dev_addr, reg_addr, read_data, len, 50)) + return BMI160_OK; + return BMI160_E_COM_FAIL; +} + +bool bmi160_begin() { + FURI_LOG_I(TAG, "Init BMI160"); + + if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, BMI160_DEV_ADDR, 50)) { + FURI_LOG_E(TAG, "Device not ready!"); + return false; + } + + FURI_LOG_I(TAG, "Device ready!"); + + bmi160dev.id = BMI160_DEV_ADDR; + bmi160dev.intf = BMI160_I2C_INTF; + bmi160dev.read = bmi160_read_i2c; + bmi160dev.write = bmi160_write_i2c; + bmi160dev.delay_ms = furi_delay_ms; + + if(bmi160_init(&bmi160dev) != BMI160_OK) { + FURI_LOG_E(TAG, "Initialization failure!"); + FURI_LOG_E(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + return false; + } + + bmi160dev.accel_cfg.odr = BMI160_ACCEL_ODR_400HZ; + bmi160dev.accel_cfg.range = BMI160_ACCEL_RANGE_4G; + bmi160dev.accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; + bmi160dev.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; + bmi160dev.gyro_cfg.odr = BMI160_GYRO_ODR_400HZ; + bmi160dev.gyro_cfg.range = BMI160_GYRO_RANGE_2000_DPS; + bmi160dev.gyro_cfg.bw = BMI160_GYRO_BW_NORMAL_MODE; + bmi160dev.gyro_cfg.power = BMI160_GYRO_NORMAL_MODE; + + if(bmi160_set_sens_conf(&bmi160dev) != BMI160_OK) { + FURI_LOG_E(TAG, "Initialization failure!"); + FURI_LOG_E(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + return false; + } + + FURI_LOG_I(TAG, "Initialization success!"); + FURI_LOG_I(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + + return true; +} + +int bmi160_read(double* vec) { + if(bmi160_get_sensor_data( + (BMI160_ACCEL_SEL | BMI160_GYRO_SEL), &bmi160_accel, &bmi160_gyro, &bmi160dev) != + BMI160_OK) { + return 0; + } + + vec[0] = ((double)bmi160_accel.x * 4 / 32768) * G; + vec[1] = ((double)bmi160_accel.y * 4 / 32768) * G; + vec[2] = ((double)bmi160_accel.z * 4 / 32768) * G; + vec[3] = ((double)bmi160_gyro.x * 2000 / 32768) * DEG_TO_RAD; + vec[4] = ((double)bmi160_gyro.y * 2000 / 32768) * DEG_TO_RAD; + vec[5] = ((double)bmi160_gyro.z * 2000 / 32768) * DEG_TO_RAD; + + return ACC_DATA_READY | GYR_DATA_READY; +} diff --git a/applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c b/applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c new file mode 100644 index 000000000..c013fc6e6 --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c @@ -0,0 +1,94 @@ +#include "lsm6ds3tr_c_reg.h" + +#include + +#include "imu.h" + +#define TAG "LSM6DS3TR-C" + +#define LSM6DS3_ADDRESS (0x6A << 1) + +static const double DEG_TO_RAD = 0.017453292519943295769236907684886; + +stmdev_ctx_t lsm6ds3trc_ctx; + +int32_t lsm6ds3trc_write_i2c(void* handle, uint8_t reg_addr, const uint8_t* data, uint16_t len) { + if(furi_hal_i2c_write_mem(handle, LSM6DS3_ADDRESS, reg_addr, (uint8_t*)data, len, 50)) + return 0; + return -1; +} + +int32_t lsm6ds3trc_read_i2c(void* handle, uint8_t reg_addr, uint8_t* read_data, uint16_t len) { + if(furi_hal_i2c_read_mem(handle, LSM6DS3_ADDRESS, reg_addr, read_data, len, 50)) return 0; + return -1; +} + +bool lsm6ds3trc_begin() { + FURI_LOG_I(TAG, "Init LSM6DS3TR-C"); + + if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, LSM6DS3_ADDRESS, 50)) { + FURI_LOG_E(TAG, "Not ready"); + return false; + } + + lsm6ds3trc_ctx.write_reg = lsm6ds3trc_write_i2c; + lsm6ds3trc_ctx.read_reg = lsm6ds3trc_read_i2c; + lsm6ds3trc_ctx.mdelay = furi_delay_ms; + lsm6ds3trc_ctx.handle = &furi_hal_i2c_handle_external; + + uint8_t whoami; + lsm6ds3tr_c_device_id_get(&lsm6ds3trc_ctx, &whoami); + if(whoami != LSM6DS3TR_C_ID) { + FURI_LOG_I(TAG, "Unknown model: %x", (int)whoami); + return false; + } + + lsm6ds3tr_c_reset_set(&lsm6ds3trc_ctx, PROPERTY_ENABLE); + uint8_t rst = PROPERTY_ENABLE; + while(rst) lsm6ds3tr_c_reset_get(&lsm6ds3trc_ctx, &rst); + + lsm6ds3tr_c_block_data_update_set(&lsm6ds3trc_ctx, PROPERTY_ENABLE); + lsm6ds3tr_c_fifo_mode_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_BYPASS_MODE); + + lsm6ds3tr_c_xl_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_ODR_104Hz); + lsm6ds3tr_c_xl_full_scale_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_4g); + lsm6ds3tr_c_xl_lp1_bandwidth_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_LP1_ODR_DIV_4); + + lsm6ds3tr_c_gy_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_ODR_104Hz); + lsm6ds3tr_c_gy_full_scale_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_2000dps); + lsm6ds3tr_c_gy_power_mode_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_HIGH_PERFORMANCE); + lsm6ds3tr_c_gy_band_pass_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_LP2_ONLY); + + FURI_LOG_I(TAG, "Init OK"); + return true; +} + +void lsm6ds3trc_end() { + lsm6ds3tr_c_xl_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_ODR_OFF); + lsm6ds3tr_c_gy_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_ODR_OFF); +} + +int lsm6ds3trc_read(double* vec) { + int ret = 0; + int16_t data[3]; + lsm6ds3tr_c_reg_t reg; + lsm6ds3tr_c_status_reg_get(&lsm6ds3trc_ctx, ®.status_reg); + + if(reg.status_reg.xlda) { + lsm6ds3tr_c_acceleration_raw_get(&lsm6ds3trc_ctx, data); + vec[2] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[0]) / 1000; + vec[0] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[1]) / 1000; + vec[1] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[2]) / 1000; + ret |= ACC_DATA_READY; + } + + if(reg.status_reg.gda) { + lsm6ds3tr_c_angular_rate_raw_get(&lsm6ds3trc_ctx, data); + vec[5] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[0]) * DEG_TO_RAD / 1000; + vec[3] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[1]) * DEG_TO_RAD / 1000; + vec[4] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[2]) * DEG_TO_RAD / 1000; + ret |= GYR_DATA_READY; + } + + return ret; +} diff --git a/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c b/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c new file mode 100644 index 000000000..9f1890d2c --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c @@ -0,0 +1,7105 @@ +/** + ****************************************************************************** + * @file lsm6ds3tr_c_reg.c + * @author Sensors Software Solution Team + * @brief LSM6DS3TR_C driver file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2021 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +#include "lsm6ds3tr_c_reg.h" + +/** + * @defgroup LSM6DS3TR_C + * @brief This file provides a set of functions needed to drive the + * lsm6ds3tr_c enanced inertial module. + * @{ + * + */ + +/** + * @defgroup LSM6DS3TR_C_interfaces_functions + * @brief This section provide a set of functions used to read and + * write a generic register of the device. + * MANDATORY: return 0 -> no Error. + * @{ + * + */ + +/** + * @brief Read generic device register + * + * @param ctx read / write interface definitions(ptr) + * @param reg register to read + * @param data pointer to buffer that store the data read(ptr) + * @param len number of consecutive register to read + * @retval interface status (MANDATORY: return 0 -> no Error) + * + */ +int32_t lsm6ds3tr_c_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len) { + int32_t ret; + + ret = ctx->read_reg(ctx->handle, reg, data, len); + + return ret; +} + +/** + * @brief Write generic device register + * + * @param ctx read / write interface definitions(ptr) + * @param reg register to write + * @param data pointer to data to write in register reg(ptr) + * @param len number of consecutive register to write + * @retval interface status (MANDATORY: return 0 -> no Error) + * + */ +int32_t lsm6ds3tr_c_write_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len) { + int32_t ret; + + ret = ctx->write_reg(ctx->handle, reg, data, len); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Sensitivity + * @brief These functions convert raw-data into engineering units. + * @{ + * + */ + +float_t lsm6ds3tr_c_from_fs2g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.061f); +} + +float_t lsm6ds3tr_c_from_fs4g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.122f); +} + +float_t lsm6ds3tr_c_from_fs8g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.244f); +} + +float_t lsm6ds3tr_c_from_fs16g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.488f); +} + +float_t lsm6ds3tr_c_from_fs125dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 4.375f); +} + +float_t lsm6ds3tr_c_from_fs250dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 8.750f); +} + +float_t lsm6ds3tr_c_from_fs500dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 17.50f); +} + +float_t lsm6ds3tr_c_from_fs1000dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 35.0f); +} + +float_t lsm6ds3tr_c_from_fs2000dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 70.0f); +} + +float_t lsm6ds3tr_c_from_lsb_to_celsius(int16_t lsb) { + return (((float_t)lsb / 256.0f) + 25.0f); +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_data_generation + * @brief This section groups all the functions concerning data + * generation + * @{ + * + */ + +/** + * @brief Accelerometer full-scale selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fs_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.fs_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer full-scale selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fs_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.fs_xl) { + case LSM6DS3TR_C_2g: + *val = LSM6DS3TR_C_2g; + break; + + case LSM6DS3TR_C_16g: + *val = LSM6DS3TR_C_16g; + break; + + case LSM6DS3TR_C_4g: + *val = LSM6DS3TR_C_4g; + break; + + case LSM6DS3TR_C_8g: + *val = LSM6DS3TR_C_8g; + break; + + default: + *val = LSM6DS3TR_C_XL_FS_ND; + break; + } + + return ret; +} + +/** + * @brief Accelerometer data rate selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.odr_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer data rate selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.odr_xl) { + case LSM6DS3TR_C_XL_ODR_OFF: + *val = LSM6DS3TR_C_XL_ODR_OFF; + break; + + case LSM6DS3TR_C_XL_ODR_12Hz5: + *val = LSM6DS3TR_C_XL_ODR_12Hz5; + break; + + case LSM6DS3TR_C_XL_ODR_26Hz: + *val = LSM6DS3TR_C_XL_ODR_26Hz; + break; + + case LSM6DS3TR_C_XL_ODR_52Hz: + *val = LSM6DS3TR_C_XL_ODR_52Hz; + break; + + case LSM6DS3TR_C_XL_ODR_104Hz: + *val = LSM6DS3TR_C_XL_ODR_104Hz; + break; + + case LSM6DS3TR_C_XL_ODR_208Hz: + *val = LSM6DS3TR_C_XL_ODR_208Hz; + break; + + case LSM6DS3TR_C_XL_ODR_416Hz: + *val = LSM6DS3TR_C_XL_ODR_416Hz; + break; + + case LSM6DS3TR_C_XL_ODR_833Hz: + *val = LSM6DS3TR_C_XL_ODR_833Hz; + break; + + case LSM6DS3TR_C_XL_ODR_1k66Hz: + *val = LSM6DS3TR_C_XL_ODR_1k66Hz; + break; + + case LSM6DS3TR_C_XL_ODR_3k33Hz: + *val = LSM6DS3TR_C_XL_ODR_3k33Hz; + break; + + case LSM6DS3TR_C_XL_ODR_6k66Hz: + *val = LSM6DS3TR_C_XL_ODR_6k66Hz; + break; + + case LSM6DS3TR_C_XL_ODR_1Hz6: + *val = LSM6DS3TR_C_XL_ODR_1Hz6; + break; + + default: + *val = LSM6DS3TR_C_XL_ODR_ND; + break; + } + + return ret; +} + +/** + * @brief Gyroscope chain full-scale selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fs_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + if(ret == 0) { + ctrl2_g.fs_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + } + + return ret; +} + +/** + * @brief Gyroscope chain full-scale selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fs_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t* val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + switch(ctrl2_g.fs_g) { + case LSM6DS3TR_C_250dps: + *val = LSM6DS3TR_C_250dps; + break; + + case LSM6DS3TR_C_125dps: + *val = LSM6DS3TR_C_125dps; + break; + + case LSM6DS3TR_C_500dps: + *val = LSM6DS3TR_C_500dps; + break; + + case LSM6DS3TR_C_1000dps: + *val = LSM6DS3TR_C_1000dps; + break; + + case LSM6DS3TR_C_2000dps: + *val = LSM6DS3TR_C_2000dps; + break; + + default: + *val = LSM6DS3TR_C_GY_FS_ND; + break; + } + + return ret; +} + +/** + * @brief Gyroscope data rate selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + if(ret == 0) { + ctrl2_g.odr_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + } + + return ret; +} + +/** + * @brief Gyroscope data rate selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t* val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + switch(ctrl2_g.odr_g) { + case LSM6DS3TR_C_GY_ODR_OFF: + *val = LSM6DS3TR_C_GY_ODR_OFF; + break; + + case LSM6DS3TR_C_GY_ODR_12Hz5: + *val = LSM6DS3TR_C_GY_ODR_12Hz5; + break; + + case LSM6DS3TR_C_GY_ODR_26Hz: + *val = LSM6DS3TR_C_GY_ODR_26Hz; + break; + + case LSM6DS3TR_C_GY_ODR_52Hz: + *val = LSM6DS3TR_C_GY_ODR_52Hz; + break; + + case LSM6DS3TR_C_GY_ODR_104Hz: + *val = LSM6DS3TR_C_GY_ODR_104Hz; + break; + + case LSM6DS3TR_C_GY_ODR_208Hz: + *val = LSM6DS3TR_C_GY_ODR_208Hz; + break; + + case LSM6DS3TR_C_GY_ODR_416Hz: + *val = LSM6DS3TR_C_GY_ODR_416Hz; + break; + + case LSM6DS3TR_C_GY_ODR_833Hz: + *val = LSM6DS3TR_C_GY_ODR_833Hz; + break; + + case LSM6DS3TR_C_GY_ODR_1k66Hz: + *val = LSM6DS3TR_C_GY_ODR_1k66Hz; + break; + + case LSM6DS3TR_C_GY_ODR_3k33Hz: + *val = LSM6DS3TR_C_GY_ODR_3k33Hz; + break; + + case LSM6DS3TR_C_GY_ODR_6k66Hz: + *val = LSM6DS3TR_C_GY_ODR_6k66Hz; + break; + + default: + *val = LSM6DS3TR_C_GY_ODR_ND; + break; + } + + return ret; +} + +/** + * @brief Block data update.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bdu in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_block_data_update_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.bdu = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Block data update.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bdu in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_block_data_update_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.bdu; + + return ret; +} + +/** + * @brief Weight of XL user offset bits of registers + * X_OFS_USR(73h), Y_OFS_USR(74h), Z_OFS_USR(75h).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of usr_off_w in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_offset_weight_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.usr_off_w = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief Weight of XL user offset bits of registers + * X_OFS_USR(73h), Y_OFS_USR(74h), Z_OFS_USR(75h).[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of usr_off_w in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_offset_weight_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.usr_off_w) { + case LSM6DS3TR_C_LSb_1mg: + *val = LSM6DS3TR_C_LSb_1mg; + break; + + case LSM6DS3TR_C_LSb_16mg: + *val = LSM6DS3TR_C_LSb_16mg; + break; + + default: + *val = LSM6DS3TR_C_WEIGHT_ND; + break; + } + + return ret; +} + +/** + * @brief High-performance operating mode for accelerometer[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of xl_hm_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.xl_hm_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief High-performance operating mode for accelerometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of xl_hm_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.xl_hm_mode) { + case LSM6DS3TR_C_XL_HIGH_PERFORMANCE: + *val = LSM6DS3TR_C_XL_HIGH_PERFORMANCE; + break; + + case LSM6DS3TR_C_XL_NORMAL: + *val = LSM6DS3TR_C_XL_NORMAL; + break; + + default: + *val = LSM6DS3TR_C_XL_PW_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Source register rounding function on WAKE_UP_SRC (1Bh), + * TAP_SRC (1Ch), D6D_SRC (1Dh), STATUS_REG (1Eh) and + * FUNC_SRC1 (53h) registers in the primary interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rounding_status in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_on_status_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.rounding_status = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + } + + return ret; +} + +/** + * @brief Source register rounding function on WAKE_UP_SRC (1Bh), + * TAP_SRC (1Ch), D6D_SRC (1Dh), STATUS_REG (1Eh) and + * FUNC_SRC1 (53h) registers in the primary interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rounding_status in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_on_status_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t* val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch(ctrl7_g.rounding_status) { + case LSM6DS3TR_C_STAT_RND_DISABLE: + *val = LSM6DS3TR_C_STAT_RND_DISABLE; + break; + + case LSM6DS3TR_C_STAT_RND_ENABLE: + *val = LSM6DS3TR_C_STAT_RND_ENABLE; + break; + + default: + *val = LSM6DS3TR_C_STAT_RND_ND; + break; + } + + return ret; +} + +/** + * @brief High-performance operating mode disable for gyroscope.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of g_hm_mode in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.g_hm_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + } + + return ret; +} + +/** + * @brief High-performance operating mode disable for gyroscope.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of g_hm_mode in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t* val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch(ctrl7_g.g_hm_mode) { + case LSM6DS3TR_C_GY_HIGH_PERFORMANCE: + *val = LSM6DS3TR_C_GY_HIGH_PERFORMANCE; + break; + + case LSM6DS3TR_C_GY_NORMAL: + *val = LSM6DS3TR_C_GY_NORMAL; + break; + + default: + *val = LSM6DS3TR_C_GY_PW_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Read all the interrupt/status flag of the device.[get] + * + * @param ctx Read / write interface definitions + * @param val WAKE_UP_SRC, TAP_SRC, D6D_SRC, STATUS_REG, + * FUNC_SRC1, FUNC_SRC2, WRIST_TILT_IA, A_WRIST_TILT_Mask + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_sources_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_all_sources_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_SRC, (uint8_t*)&(val->wake_up_src), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_SRC, (uint8_t*)&(val->tap_src), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_D6D_SRC, (uint8_t*)&(val->d6d_src), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&(val->status_reg), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_SRC1, (uint8_t*)&(val->func_src1), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_SRC2, (uint8_t*)&(val->func_src2), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_WRIST_TILT_IA, (uint8_t*)&(val->wrist_tilt_ia), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)&(val->a_wrist_tilt_mask), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + + return ret; +} +/** + * @brief The STATUS_REG register is read by the primary interface[get] + * + * @param ctx Read / write interface definitions + * @param val Registers STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_status_reg_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_status_reg_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)val, 1); + + return ret; +} + +/** + * @brief Accelerometer new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of xlda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.xlda; + + return ret; +} + +/** + * @brief Gyroscope new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of gda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.gda; + + return ret; +} + +/** + * @brief Temperature new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_temp_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.tda; + + return ret; +} + +/** + * @brief Accelerometer axis user offset correction expressed in two’s + * complement, weight depends on USR_OFF_W in CTRL6_C. + * The value must be in the range [-127 127].[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_usr_offset_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_X_OFS_USR, buff, 3); + + return ret; +} + +/** + * @brief Accelerometer axis user offset correction xpressed in two’s + * complement, weight depends on USR_OFF_W in CTRL6_C. + * The value must be in the range [-127 127].[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_usr_offset_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_X_OFS_USR, buff, 3); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Timestamp + * @brief This section groups all the functions that manage the + * timestamp generation. + * @{ + * + */ + +/** + * @brief Enable timestamp count. The count is saved in TIMESTAMP0_REG (40h), + * TIMESTAMP1_REG (41h) and TIMESTAMP2_REG (42h).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.timer_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + + return ret; +} + +/** + * @brief Enable timestamp count. The count is saved in TIMESTAMP0_REG (40h), + * TIMESTAMP1_REG (41h) and TIMESTAMP2_REG (42h).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.timer_en; + + return ret; +} + +/** + * @brief Timestamp register resolution setting. + * Configuration of this bit affects + * TIMESTAMP0_REG(40h), TIMESTAMP1_REG(41h), + * TIMESTAMP2_REG(42h), STEP_TIMESTAMP_L(49h), + * STEP_TIMESTAMP_H(4Ah) and + * STEP_COUNT_DELTA(15h) registers.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_hr in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_res_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.timer_hr = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Timestamp register resolution setting. + * Configuration of this bit affects + * TIMESTAMP0_REG(40h), TIMESTAMP1_REG(41h), + * TIMESTAMP2_REG(42h), STEP_TIMESTAMP_L(49h), + * STEP_TIMESTAMP_H(4Ah) and + * STEP_COUNT_DELTA(15h) registers.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of timer_hr in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_res_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + switch(wake_up_dur.timer_hr) { + case LSM6DS3TR_C_LSB_6ms4: + *val = LSM6DS3TR_C_LSB_6ms4; + break; + + case LSM6DS3TR_C_LSB_25us: + *val = LSM6DS3TR_C_LSB_25us; + break; + + default: + *val = LSM6DS3TR_C_TS_RES_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Dataoutput + * @brief This section groups all the data output functions. + * @{ + * + */ + +/** + * @brief Circular burst-mode (rounding) read from output registers + * through the primary interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rounding in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.rounding = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Circular burst-mode (rounding) read from output registers + * through the primary interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rounding in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.rounding) { + case LSM6DS3TR_C_ROUND_DISABLE: + *val = LSM6DS3TR_C_ROUND_DISABLE; + break; + + case LSM6DS3TR_C_ROUND_XL: + *val = LSM6DS3TR_C_ROUND_XL; + break; + + case LSM6DS3TR_C_ROUND_GY: + *val = LSM6DS3TR_C_ROUND_GY; + break; + + case LSM6DS3TR_C_ROUND_GY_XL: + *val = LSM6DS3TR_C_ROUND_GY_XL; + break; + + case LSM6DS3TR_C_ROUND_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_SH1_TO_SH6; + break; + + case LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6; + break; + + case LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12: + *val = LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12; + break; + + case LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6; + break; + + default: + *val = LSM6DS3TR_C_ROUND_OUT_ND; + break; + } + + return ret; +} + +/** + * @brief Temperature data output register (r). L and H registers together + * express a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_temperature_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[2]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUT_TEMP_L, buff, 2); + *val = (int16_t)buff[1]; + *val = (*val * 256) + (int16_t)buff[0]; + + return ret; +} + +/** + * @brief Angular rate sensor. The value is expressed as a 16-bit word in + * two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_angular_rate_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUTX_L_G, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief Linear acceleration output register. The value is expressed + * as a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_acceleration_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUTX_L_XL, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief External magnetometer raw data.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_calibrated_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUT_MAG_RAW_X_L, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief Read data in FIFO.[get] + * + * @param ctx Read / write interface definitions + * @param buffer Data buffer to store FIFO data. + * @param len Number of data to read from FIFO. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_raw_data_get(stmdev_ctx_t* ctx, uint8_t* buffer, uint8_t len) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_DATA_OUT_L, buffer, len); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_common + * @brief This section groups common useful functions. + * @{ + * + */ + +/** + * @brief Enable access to the embedded functions/sensor hub + * configuration registers[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of func_cfg_en in reg FUNC_CFG_ACCESS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mem_bank_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t val) { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + + if(ret == 0) { + func_cfg_access.func_cfg_en = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + } + + return ret; +} + +/** + * @brief Enable access to the embedded functions/sensor hub configuration + * registers[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of func_cfg_en in reg FUNC_CFG_ACCESS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mem_bank_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t* val) { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + + switch(func_cfg_access.func_cfg_en) { + case LSM6DS3TR_C_USER_BANK: + *val = LSM6DS3TR_C_USER_BANK; + break; + + case LSM6DS3TR_C_BANK_B: + *val = LSM6DS3TR_C_BANK_B; + break; + + default: + *val = LSM6DS3TR_C_BANK_ND; + break; + } + + return ret; +} + +/** + * @brief Data-ready pulsed / letched mode[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_pulsed in reg DRDY_PULSE_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_ready_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t val) { + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + + if(ret == 0) { + drdy_pulse_cfg_g.drdy_pulsed = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + return ret; +} + +/** + * @brief Data-ready pulsed / letched mode[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of drdy_pulsed in reg DRDY_PULSE_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_ready_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t* val) { + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + + switch(drdy_pulse_cfg_g.drdy_pulsed) { + case LSM6DS3TR_C_DRDY_LATCHED: + *val = LSM6DS3TR_C_DRDY_LATCHED; + break; + + case LSM6DS3TR_C_DRDY_PULSED: + *val = LSM6DS3TR_C_DRDY_PULSED; + break; + + default: + *val = LSM6DS3TR_C_DRDY_ND; + break; + } + + return ret; +} + +/** + * @brief DeviceWhoamI.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_device_id_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WHO_AM_I, buff, 1); + + return ret; +} + +/** + * @brief Software reset. Restore the default values in user registers[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sw_reset in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_reset_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.sw_reset = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Software reset. Restore the default values in user registers[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sw_reset in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_reset_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.sw_reset; + + return ret; +} + +/** + * @brief Big/Little Endian Data selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ble in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_format_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.ble = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Big/Little Endian Data selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of ble in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_format_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.ble) { + case LSM6DS3TR_C_LSB_AT_LOW_ADD: + *val = LSM6DS3TR_C_LSB_AT_LOW_ADD; + break; + + case LSM6DS3TR_C_MSB_AT_LOW_ADD: + *val = LSM6DS3TR_C_MSB_AT_LOW_ADD; + break; + + default: + *val = LSM6DS3TR_C_DATA_FMT_ND; + break; + } + + return ret; +} + +/** + * @brief Register address automatically incremented during a multiple byte + * access with a serial interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of if_inc in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_auto_increment_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.if_inc = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Register address automatically incremented during a multiple byte + * access with a serial interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of if_inc in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_auto_increment_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.if_inc; + + return ret; +} + +/** + * @brief Reboot memory content. Reload the calibration parameters.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of boot in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_boot_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.boot = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Reboot memory content. Reload the calibration parameters.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of boot in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_boot_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.boot; + + return ret; +} + +/** + * @brief Linear acceleration sensor self-test enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of st_xl in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.st_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Linear acceleration sensor self-test enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of st_xl in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.st_xl) { + case LSM6DS3TR_C_XL_ST_DISABLE: + *val = LSM6DS3TR_C_XL_ST_DISABLE; + break; + + case LSM6DS3TR_C_XL_ST_POSITIVE: + *val = LSM6DS3TR_C_XL_ST_POSITIVE; + break; + + case LSM6DS3TR_C_XL_ST_NEGATIVE: + *val = LSM6DS3TR_C_XL_ST_NEGATIVE; + break; + + default: + *val = LSM6DS3TR_C_XL_ST_ND; + break; + } + + return ret; +} + +/** + * @brief Angular rate sensor self-test enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of st_g in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.st_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Angular rate sensor self-test enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of st_g in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.st_g) { + case LSM6DS3TR_C_GY_ST_DISABLE: + *val = LSM6DS3TR_C_GY_ST_DISABLE; + break; + + case LSM6DS3TR_C_GY_ST_POSITIVE: + *val = LSM6DS3TR_C_GY_ST_POSITIVE; + break; + + case LSM6DS3TR_C_GY_ST_NEGATIVE: + *val = LSM6DS3TR_C_GY_ST_NEGATIVE; + break; + + default: + *val = LSM6DS3TR_C_GY_ST_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_filters + * @brief This section group all the functions concerning the filters + * configuration that impact both accelerometer and gyro. + * @{ + * + */ + +/** + * @brief Mask DRDY on pin (both XL & Gyro) until filter settling ends + * (XL and Gyro independently masked).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_mask in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_filter_settling_mask_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.drdy_mask = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Mask DRDY on pin (both XL & Gyro) until filter settling ends + * (XL and Gyro independently masked).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_mask in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_filter_settling_mask_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.drdy_mask; + + return ret; +} + +/** + * @brief HPF or SLOPE filter selection on wake-up and Activity/Inactivity + * functions.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slope_fds in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_path_internal_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.slope_fds = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief HPF or SLOPE filter selection on wake-up and Activity/Inactivity + * functions.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slope_fds in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_path_internal_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.slope_fds) { + case LSM6DS3TR_C_USE_SLOPE: + *val = LSM6DS3TR_C_USE_SLOPE; + break; + + case LSM6DS3TR_C_USE_HPF: + *val = LSM6DS3TR_C_USE_HPF; + break; + + default: + *val = LSM6DS3TR_C_HP_PATH_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_accelerometer_filters + * @brief This section group all the functions concerning the filters + * configuration that impact accelerometer in every mode. + * @{ + * + */ + +/** + * @brief Accelerometer analog chain bandwidth selection (only for + * accelerometer ODR β‰₯ 1.67 kHz).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bw0_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_filter_analog_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.bw0_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer analog chain bandwidth selection (only for + * accelerometer ODR β‰₯ 1.67 kHz).[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of bw0_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_filter_analog_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.bw0_xl) { + case LSM6DS3TR_C_XL_ANA_BW_1k5Hz: + *val = LSM6DS3TR_C_XL_ANA_BW_1k5Hz; + break; + + case LSM6DS3TR_C_XL_ANA_BW_400Hz: + *val = LSM6DS3TR_C_XL_ANA_BW_400Hz; + break; + + default: + *val = LSM6DS3TR_C_XL_ANA_BW_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_accelerometer_filters + * @brief This section group all the functions concerning the filters + * configuration that impact accelerometer. + * @{ + * + */ + +/** + * @brief Accelerometer digital LPF (LPF1) bandwidth selection LPF2 is + * not used.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of lpf1_bw_sel in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.lpf1_bw_sel = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.lpf2_xl_en = 0; + ctrl8_xl.hp_slope_xl_en = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + } + } + + return ret; +} + +/** + * @brief Accelerometer digital LPF (LPF1) bandwidth selection LPF2 + * is not used.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of lpf1_bw_sel in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + if((ctrl8_xl.lpf2_xl_en != 0x00U) || (ctrl8_xl.hp_slope_xl_en != 0x00U)) { + *val = LSM6DS3TR_C_XL_LP1_NA; + } + + else { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.lpf1_bw_sel) { + case LSM6DS3TR_C_XL_LP1_ODR_DIV_2: + *val = LSM6DS3TR_C_XL_LP1_ODR_DIV_2; + break; + + case LSM6DS3TR_C_XL_LP1_ODR_DIV_4: + *val = LSM6DS3TR_C_XL_LP1_ODR_DIV_4; + break; + + default: + *val = LSM6DS3TR_C_XL_LP1_NA; + break; + } + } + } + + return ret; +} + +/** + * @brief LPF2 on outputs[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of input_composite in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.input_composite = ((uint8_t)val & 0x10U) >> 4; + ctrl8_xl.hpcf_xl = (uint8_t)val & 0x03U; + ctrl8_xl.lpf2_xl_en = 1; + ctrl8_xl.hp_slope_xl_en = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief LPF2 on outputs[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of input_composite in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + if((ctrl8_xl.lpf2_xl_en == 0x00U) || (ctrl8_xl.hp_slope_xl_en != 0x00U)) { + *val = LSM6DS3TR_C_XL_LP_NA; + } + + else { + switch((ctrl8_xl.input_composite << 4) + ctrl8_xl.hpcf_xl) { + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400; + break; + + default: + *val = LSM6DS3TR_C_XL_LP_NA; + break; + } + } + } + + return ret; +} + +/** + * @brief Enable HP filter reference mode.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hp_ref_mode in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_reference_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.hp_ref_mode = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief Enable HP filter reference mode.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hp_ref_mode in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_reference_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + *val = ctrl8_xl.hp_ref_mode; + + return ret; +} + +/** + * @brief High pass/Slope on outputs.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hpcf_xl in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.input_composite = 0; + ctrl8_xl.hpcf_xl = (uint8_t)val & 0x03U; + ctrl8_xl.hp_slope_xl_en = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief High pass/Slope on outputs.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of hpcf_xl in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ctrl8_xl.hp_slope_xl_en == 0x00U) { + *val = LSM6DS3TR_C_XL_HP_NA; + } + + switch(ctrl8_xl.hpcf_xl) { + case LSM6DS3TR_C_XL_HP_ODR_DIV_4: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_4; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_400; + break; + + default: + *val = LSM6DS3TR_C_XL_HP_NA; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_gyroscope_filters + * @brief This section group all the functions concerning the filters + * configuration that impact gyroscope. + * @{ + * + */ + +/** + * @brief Gyroscope low pass path bandwidth.[set] + * + * @param ctx Read / write interface definitions + * @param val gyroscope filtering chain configuration. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_band_pass_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.hpm_g = ((uint8_t)val & 0x30U) >> 4; + ctrl7_g.hp_en_g = ((uint8_t)val & 0x80U) >> 7; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.ftype = (uint8_t)val & 0x03U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.lpf1_sel_g = ((uint8_t)val & 0x08U) >> 3; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + } + } + } + } + + return ret; +} + +/** + * @brief Gyroscope low pass path bandwidth.[get] + * + * @param ctx Read / write interface definitions + * @param val gyroscope filtering chain + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_band_pass_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch((ctrl7_g.hp_en_g << 7) + (ctrl7_g.hpm_g << 4) + (ctrl4_c.lpf1_sel_g << 3) + + ctrl6_c.ftype) { + case LSM6DS3TR_C_HP_16mHz_LP2: + *val = LSM6DS3TR_C_HP_16mHz_LP2; + break; + + case LSM6DS3TR_C_HP_65mHz_LP2: + *val = LSM6DS3TR_C_HP_65mHz_LP2; + break; + + case LSM6DS3TR_C_HP_260mHz_LP2: + *val = LSM6DS3TR_C_HP_260mHz_LP2; + break; + + case LSM6DS3TR_C_HP_1Hz04_LP2: + *val = LSM6DS3TR_C_HP_1Hz04_LP2; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP_STRONG: + *val = LSM6DS3TR_C_HP_DISABLE_LP_STRONG; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE; + break; + + case LSM6DS3TR_C_HP_16mHz_LP1_LIGHT: + *val = LSM6DS3TR_C_HP_16mHz_LP1_LIGHT; + break; + + case LSM6DS3TR_C_HP_65mHz_LP1_NORMAL: + *val = LSM6DS3TR_C_HP_65mHz_LP1_NORMAL; + break; + + case LSM6DS3TR_C_HP_260mHz_LP1_STRONG: + *val = LSM6DS3TR_C_HP_260mHz_LP1_STRONG; + break; + + case LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE: + *val = LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE; + break; + + default: + *val = LSM6DS3TR_C_HP_GY_BAND_NA; + break; + } + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_serial_interface + * @brief This section groups all the functions concerning serial + * interface management + * @{ + * + */ + +/** + * @brief SPI Serial Interface Mode selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sim in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_spi_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.sim = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief SPI Serial Interface Mode selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of sim in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_spi_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.sim) { + case LSM6DS3TR_C_SPI_4_WIRE: + *val = LSM6DS3TR_C_SPI_4_WIRE; + break; + + case LSM6DS3TR_C_SPI_3_WIRE: + *val = LSM6DS3TR_C_SPI_3_WIRE; + break; + + default: + *val = LSM6DS3TR_C_SPI_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Disable / Enable I2C interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of i2c_disable in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_i2c_interface_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.i2c_disable = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Disable / Enable I2C interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of i2c_disable in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_i2c_interface_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + switch(ctrl4_c.i2c_disable) { + case LSM6DS3TR_C_I2C_ENABLE: + *val = LSM6DS3TR_C_I2C_ENABLE; + break; + + case LSM6DS3TR_C_I2C_DISABLE: + *val = LSM6DS3TR_C_I2C_DISABLE; + break; + + default: + *val = LSM6DS3TR_C_I2C_MODE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_interrupt_pins + * @brief This section groups all the functions that manage + * interrupt pins + * @{ + * + */ + +/** + * @brief Select the signal that need to route on int1 pad[set] + * + * @param ctx Read / write interface definitions + * @param val configure INT1_CTRL, MD1_CFG, CTRL4_C(den_drdy_int1), + * MASTER_CONFIG(drdy_on_int1) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int1_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + + if(ret == 0) { + int1_ctrl.int1_drdy_xl = val.int1_drdy_xl; + int1_ctrl.int1_drdy_g = val.int1_drdy_g; + int1_ctrl.int1_boot = val.int1_boot; + int1_ctrl.int1_fth = val.int1_fth; + int1_ctrl.int1_fifo_ovr = val.int1_fifo_ovr; + int1_ctrl.int1_full_flag = val.int1_full_flag; + int1_ctrl.int1_sign_mot = val.int1_sign_mot; + int1_ctrl.int1_step_detector = val.int1_step_detector; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + md1_cfg.int1_timer = val.int1_timer; + md1_cfg.int1_tilt = val.int1_tilt; + md1_cfg.int1_6d = val.int1_6d; + md1_cfg.int1_double_tap = val.int1_double_tap; + md1_cfg.int1_ff = val.int1_ff; + md1_cfg.int1_wu = val.int1_wu; + md1_cfg.int1_single_tap = val.int1_single_tap; + md1_cfg.int1_inact_state = val.int1_inact_state; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + if(ret == 0) { + ctrl4_c.den_drdy_int1 = val.den_drdy_int1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + if(ret == 0) { + master_config.drdy_on_int1 = val.den_drdy_int1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if((val.int1_6d != 0x00U) || (val.int1_ff != 0x00U) || (val.int1_wu != 0x00U) || + (val.int1_single_tap != 0x00U) || (val.int1_double_tap != 0x00U) || + (val.int1_inact_state != 0x00U) || (md2_cfg.int2_6d != 0x00U) || + (md2_cfg.int2_ff != 0x00U) || (md2_cfg.int2_wu != 0x00U) || + (md2_cfg.int2_single_tap != 0x00U) || (md2_cfg.int2_double_tap != 0x00U) || + (md2_cfg.int2_inact_state != 0x00U)) { + tap_cfg.interrupts_enable = PROPERTY_ENABLE; + } + + else { + tap_cfg.interrupts_enable = PROPERTY_DISABLE; + } + } + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int1 pad[get] + * + * @param ctx Read / write interface definitions + * @param val read INT1_CTRL, MD1_CFG, CTRL4_C(den_drdy_int1), + * MASTER_CONFIG(drdy_on_int1) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int1_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t* val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + + if(ret == 0) { + val->int1_drdy_xl = int1_ctrl.int1_drdy_xl; + val->int1_drdy_g = int1_ctrl.int1_drdy_g; + val->int1_boot = int1_ctrl.int1_boot; + val->int1_fth = int1_ctrl.int1_fth; + val->int1_fifo_ovr = int1_ctrl.int1_fifo_ovr; + val->int1_full_flag = int1_ctrl.int1_full_flag; + val->int1_sign_mot = int1_ctrl.int1_sign_mot; + val->int1_step_detector = int1_ctrl.int1_step_detector; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + + if(ret == 0) { + val->int1_timer = md1_cfg.int1_timer; + val->int1_tilt = md1_cfg.int1_tilt; + val->int1_6d = md1_cfg.int1_6d; + val->int1_double_tap = md1_cfg.int1_double_tap; + val->int1_ff = md1_cfg.int1_ff; + val->int1_wu = md1_cfg.int1_wu; + val->int1_single_tap = md1_cfg.int1_single_tap; + val->int1_inact_state = md1_cfg.int1_inact_state; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + val->den_drdy_int1 = ctrl4_c.den_drdy_int1; + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + val->den_drdy_int1 = master_config.drdy_on_int1; + } + } + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int2 pad[set] + * + * @param ctx Read / write interface definitions + * @param val INT2_CTRL, DRDY_PULSE_CFG(int2_wrist_tilt), MD2_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int2_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t val) { + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + + if(ret == 0) { + int2_ctrl.int2_drdy_xl = val.int2_drdy_xl; + int2_ctrl.int2_drdy_g = val.int2_drdy_g; + int2_ctrl.int2_drdy_temp = val.int2_drdy_temp; + int2_ctrl.int2_fth = val.int2_fth; + int2_ctrl.int2_fifo_ovr = val.int2_fifo_ovr; + int2_ctrl.int2_full_flag = val.int2_full_flag; + int2_ctrl.int2_step_count_ov = val.int2_step_count_ov; + int2_ctrl.int2_step_delta = val.int2_step_delta; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + md2_cfg.int2_iron = val.int2_iron; + md2_cfg.int2_tilt = val.int2_tilt; + md2_cfg.int2_6d = val.int2_6d; + md2_cfg.int2_double_tap = val.int2_double_tap; + md2_cfg.int2_ff = val.int2_ff; + md2_cfg.int2_wu = val.int2_wu; + md2_cfg.int2_single_tap = val.int2_single_tap; + md2_cfg.int2_inact_state = val.int2_inact_state; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + if(ret == 0) { + drdy_pulse_cfg_g.int2_wrist_tilt = val.int2_wrist_tilt; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if((md1_cfg.int1_6d != 0x00U) || (md1_cfg.int1_ff != 0x00U) || + (md1_cfg.int1_wu != 0x00U) || (md1_cfg.int1_single_tap != 0x00U) || + (md1_cfg.int1_double_tap != 0x00U) || (md1_cfg.int1_inact_state != 0x00U) || + (val.int2_6d != 0x00U) || (val.int2_ff != 0x00U) || (val.int2_wu != 0x00U) || + (val.int2_single_tap != 0x00U) || (val.int2_double_tap != 0x00U) || + (val.int2_inact_state != 0x00U)) { + tap_cfg.interrupts_enable = PROPERTY_ENABLE; + } + + else { + tap_cfg.interrupts_enable = PROPERTY_DISABLE; + } + } + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int2 pad[get] + * + * @param ctx Read / write interface definitions + * @param val INT2_CTRL, DRDY_PULSE_CFG(int2_wrist_tilt), MD2_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int2_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t* val) { + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + + if(ret == 0) { + val->int2_drdy_xl = int2_ctrl.int2_drdy_xl; + val->int2_drdy_g = int2_ctrl.int2_drdy_g; + val->int2_drdy_temp = int2_ctrl.int2_drdy_temp; + val->int2_fth = int2_ctrl.int2_fth; + val->int2_fifo_ovr = int2_ctrl.int2_fifo_ovr; + val->int2_full_flag = int2_ctrl.int2_full_flag; + val->int2_step_count_ov = int2_ctrl.int2_step_count_ov; + val->int2_step_delta = int2_ctrl.int2_step_delta; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + + if(ret == 0) { + val->int2_iron = md2_cfg.int2_iron; + val->int2_tilt = md2_cfg.int2_tilt; + val->int2_6d = md2_cfg.int2_6d; + val->int2_double_tap = md2_cfg.int2_double_tap; + val->int2_ff = md2_cfg.int2_ff; + val->int2_wu = md2_cfg.int2_wu; + val->int2_single_tap = md2_cfg.int2_single_tap; + val->int2_inact_state = md2_cfg.int2_inact_state; + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + val->int2_wrist_tilt = drdy_pulse_cfg_g.int2_wrist_tilt; + } + } + + return ret; +} + +/** + * @brief Push-pull/open drain selection on interrupt pads.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pp_od in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.pp_od = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Push-pull/open drain selection on interrupt pads.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pp_od in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.pp_od) { + case LSM6DS3TR_C_PUSH_PULL: + *val = LSM6DS3TR_C_PUSH_PULL; + break; + + case LSM6DS3TR_C_OPEN_DRAIN: + *val = LSM6DS3TR_C_OPEN_DRAIN; + break; + + default: + *val = LSM6DS3TR_C_PIN_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Interrupt active-high/low.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of h_lactive in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.h_lactive = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Interrupt active-high/low.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of h_lactive in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.h_lactive) { + case LSM6DS3TR_C_ACTIVE_HIGH: + *val = LSM6DS3TR_C_ACTIVE_HIGH; + break; + + case LSM6DS3TR_C_ACTIVE_LOW: + *val = LSM6DS3TR_C_ACTIVE_LOW; + break; + + default: + *val = LSM6DS3TR_C_POLARITY_ND; + break; + } + + return ret; +} + +/** + * @brief All interrupt signals become available on INT1 pin.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of int2_on_int1 in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_on_int1_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.int2_on_int1 = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief All interrupt signals become available on INT1 pin.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of int2_on_int1 in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.int2_on_int1; + + return ret; +} + +/** + * @brief Latched/pulsed interrupt.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of lir in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_int_notification_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.lir = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Latched/pulsed interrupt.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of lir in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_int_notification_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.lir) { + case LSM6DS3TR_C_INT_PULSED: + *val = LSM6DS3TR_C_INT_PULSED; + break; + + case LSM6DS3TR_C_INT_LATCHED: + *val = LSM6DS3TR_C_INT_LATCHED; + break; + + default: + *val = LSM6DS3TR_C_INT_MODE; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Wake_Up_event + * @brief This section groups all the functions that manage the + * Wake Up event generation. + * @{ + * + */ + +/** + * @brief Threshold for wakeup.1 LSB = FS_XL / 64.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wk_ths in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_threshold_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + if(ret == 0) { + wake_up_ths.wk_ths = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + } + + return ret; +} + +/** + * @brief Threshold for wakeup.1 LSB = FS_XL / 64.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wk_ths in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_threshold_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + *val = wake_up_ths.wk_ths; + + return ret; +} + +/** + * @brief Wake up duration event.1LSb = 1 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wake_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.wake_dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Wake up duration event.1LSb = 1 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wake_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + *val = wake_up_dur.wake_dur; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Activity/Inactivity_detection + * @brief This section groups all the functions concerning + * activity/inactivity detection. + * @{ + * + */ + +/** + * @brief Enables gyroscope Sleep mode.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_sleep_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.sleep = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Enables gyroscope Sleep mode.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_sleep_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.sleep; + + return ret; +} + +/** + * @brief Enable inactivity function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of inact_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.inact_en = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable inactivity function.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of inact_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.inact_en) { + case LSM6DS3TR_C_PROPERTY_DISABLE: + *val = LSM6DS3TR_C_PROPERTY_DISABLE; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED: + *val = LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_SLEEP: + *val = LSM6DS3TR_C_XL_12Hz5_GY_SLEEP; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_PD: + *val = LSM6DS3TR_C_XL_12Hz5_GY_PD; + break; + + default: + *val = LSM6DS3TR_C_ACT_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Duration to go in sleep mode.1 LSb = 512 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_sleep_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.sleep_dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Duration to go in sleep mode. 1 LSb = 512 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_sleep_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + *val = wake_up_dur.sleep_dur; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_tap_generator + * @brief This section groups all the functions that manage the + * tap and double tap event generation. + * @{ + * + */ + +/** + * @brief Read the tap / double tap source register.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure of registers from TAP_SRC + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_tap_src_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_SRC, (uint8_t*)val, 1); + + return ret; +} + +/** + * @brief Enable Z direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_z_en in reg TAP_CFG + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_z_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_z_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable Z direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_z_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_z_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_z_en; + + return ret; +} + +/** + * @brief Enable Y direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_y_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_y_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_y_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable Y direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_y_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_y_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_y_en; + + return ret; +} + +/** + * @brief Enable X direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_x_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_x_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable X direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_x_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_x_en; + + return ret; +} + +/** + * @brief Threshold for tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_threshold_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.tap_ths = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief Threshold for tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_threshold_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + *val = tap_ths_6d.tap_ths; + + return ret; +} + +/** + * @brief Maximum duration is the maximum time of an overthreshold signal + * detection to be recognized as a tap event. + * The default value of these bits is 00b which corresponds to + * 4*ODR_XL time. + * If the SHOCK[1:0] bits are set to a different + * value, 1LSB corresponds to 8*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of shock in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_shock_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.shock = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief Maximum duration is the maximum time of an overthreshold signal + * detection to be recognized as a tap event. + * The default value of these bits is 00b which corresponds to + * 4*ODR_XL time. + * If the SHOCK[1:0] bits are set to a different value, 1LSB + * corresponds to 8*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of shock in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_shock_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.shock; + + return ret; +} + +/** + * @brief Quiet time is the time after the first detected tap in which there + * must not be any overthreshold event. + * The default value of these bits is 00b which corresponds to + * 2*ODR_XL time. + * If the QUIET[1:0] bits are set to a different value, 1LSB + * corresponds to 4*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of quiet in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_quiet_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.quiet = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief Quiet time is the time after the first detected tap in which there + * must not be any overthreshold event. + * The default value of these bits is 00b which corresponds to + * 2*ODR_XL time. + * If the QUIET[1:0] bits are set to a different value, 1LSB + * corresponds to 4*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of quiet in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_quiet_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.quiet; + + return ret; +} + +/** + * @brief When double tap recognition is enabled, this register expresses the + * maximum time between two consecutive detected taps to determine a + * double tap event. + * The default value of these bits is 0000b which corresponds to + * 16*ODR_XL time. + * If the DUR[3:0] bits are set to a different value,1LSB corresponds + * to 32*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dur in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief When double tap recognition is enabled, this register expresses the + * maximum time between two consecutive detected taps to determine a + * double tap event. + * The default value of these bits is 0000b which corresponds to + * 16*ODR_XL time. + * If the DUR[3:0] bits are set to a different value,1LSB corresponds + * to 32*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dur in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.dur; + + return ret; +} + +/** + * @brief Single/double-tap event enable/disable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of + * single_double_tap in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + if(ret == 0) { + wake_up_ths.single_double_tap = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + } + + return ret; +} + +/** + * @brief Single/double-tap event enable/disable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of single_double_tap + * in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t* val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + switch(wake_up_ths.single_double_tap) { + case LSM6DS3TR_C_ONLY_SINGLE: + *val = LSM6DS3TR_C_ONLY_SINGLE; + break; + + case LSM6DS3TR_C_BOTH_SINGLE_DOUBLE: + *val = LSM6DS3TR_C_BOTH_SINGLE_DOUBLE; + break; + + default: + *val = LSM6DS3TR_C_TAP_MODE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_ Six_position_detection(6D/4D) + * @brief This section groups all the functions concerning six + * position detection (6D). + * @{ + * + */ + +/** + * @brief LPF2 feed 6D function selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of low_pass_on_6d in + * reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_feed_data_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.low_pass_on_6d = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief LPF2 feed 6D function selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of low_pass_on_6d in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_feed_data_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + switch(ctrl8_xl.low_pass_on_6d) { + case LSM6DS3TR_C_ODR_DIV_2_FEED: + *val = LSM6DS3TR_C_ODR_DIV_2_FEED; + break; + + case LSM6DS3TR_C_LPF2_FEED: + *val = LSM6DS3TR_C_LPF2_FEED; + break; + + default: + *val = LSM6DS3TR_C_6D_FEED_ND; + break; + } + + return ret; +} + +/** + * @brief Threshold for 4D/6D function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sixd_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.sixd_ths = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief Threshold for 4D/6D function.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of sixd_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + switch(tap_ths_6d.sixd_ths) { + case LSM6DS3TR_C_DEG_80: + *val = LSM6DS3TR_C_DEG_80; + break; + + case LSM6DS3TR_C_DEG_70: + *val = LSM6DS3TR_C_DEG_70; + break; + + case LSM6DS3TR_C_DEG_60: + *val = LSM6DS3TR_C_DEG_60; + break; + + case LSM6DS3TR_C_DEG_50: + *val = LSM6DS3TR_C_DEG_50; + break; + + default: + *val = LSM6DS3TR_C_6D_TH_ND; + break; + } + + return ret; +} + +/** + * @brief 4D orientation detection enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of d4d_en in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_4d_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.d4d_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief 4D orientation detection enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of d4d_en in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_4d_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + *val = tap_ths_6d.d4d_en; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_free_fall + * @brief This section group all the functions concerning the free + * fall detection. + * @{ + * + */ + +/** + * @brief Free-fall duration event. 1LSb = 1 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + free_fall.ff_dur = (val & 0x1FU); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.ff_dur = (val & 0x20U) >> 5; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + } + } + + return ret; +} + +/** + * @brief Free-fall duration event. 1LSb = 1 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + } + + *val = (wake_up_dur.ff_dur << 5) + free_fall.ff_dur; + + return ret; +} + +/** + * @brief Free fall threshold setting.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_ths in reg FREE_FALL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t val) { + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + free_fall.ff_ths = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + } + + return ret; +} + +/** + * @brief Free fall threshold setting.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of ff_ths in reg FREE_FALL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t* val) { + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + switch(free_fall.ff_ths) { + case LSM6DS3TR_C_FF_TSH_156mg: + *val = LSM6DS3TR_C_FF_TSH_156mg; + break; + + case LSM6DS3TR_C_FF_TSH_219mg: + *val = LSM6DS3TR_C_FF_TSH_219mg; + break; + + case LSM6DS3TR_C_FF_TSH_250mg: + *val = LSM6DS3TR_C_FF_TSH_250mg; + break; + + case LSM6DS3TR_C_FF_TSH_312mg: + *val = LSM6DS3TR_C_FF_TSH_312mg; + break; + + case LSM6DS3TR_C_FF_TSH_344mg: + *val = LSM6DS3TR_C_FF_TSH_344mg; + break; + + case LSM6DS3TR_C_FF_TSH_406mg: + *val = LSM6DS3TR_C_FF_TSH_406mg; + break; + + case LSM6DS3TR_C_FF_TSH_469mg: + *val = LSM6DS3TR_C_FF_TSH_469mg; + break; + + case LSM6DS3TR_C_FF_TSH_500mg: + *val = LSM6DS3TR_C_FF_TSH_500mg; + break; + + default: + *val = LSM6DS3TR_C_FF_TSH_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_fifo + * @brief This section group all the functions concerning the + * fifo usage + * @{ + * + */ + +/** + * @brief FIFO watermark level selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fth in reg FIFO_CTRL1 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_watermark_set(stmdev_ctx_t* ctx, uint16_t val) { + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl1.fth = (uint8_t)(0x00FFU & val); + fifo_ctrl2.fth = (uint8_t)((0x0700U & val) >> 8); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL1, (uint8_t*)&fifo_ctrl1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + } + + return ret; +} + +/** + * @brief FIFO watermark level selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fth in reg FIFO_CTRL1 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_watermark_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL1, (uint8_t*)&fifo_ctrl1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + *val = ((uint16_t)fifo_ctrl2.fth << 8) + (uint16_t)fifo_ctrl1.fth; + + return ret; +} + +/** + * @brief FIFO data level.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of diff_fifo in reg FIFO_STATUS1 and + * FIFO_STATUS2(diff_fifo), it is recommended to set the + * BDU bit. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_level_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_status1_t fifo_status1; + lsm6ds3tr_c_fifo_status2_t fifo_status2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS1, (uint8_t*)&fifo_status1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS2, (uint8_t*)&fifo_status2, 1); + *val = ((uint16_t)fifo_status2.diff_fifo << 8) + (uint16_t)fifo_status1.diff_fifo; + } + + return ret; +} + +/** + * @brief FIFO watermark.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of watermark in reg FIFO_STATUS2 and + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_wtm_flag_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_status2_t fifo_status2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS2, (uint8_t*)&fifo_status2, 1); + *val = fifo_status2.waterm; + + return ret; +} + +/** + * @brief FIFO pattern.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of fifo_pattern in reg FIFO_STATUS3 and + * FIFO_STATUS4, it is recommended to set the BDU bit + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pattern_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_status3_t fifo_status3; + lsm6ds3tr_c_fifo_status4_t fifo_status4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS3, (uint8_t*)&fifo_status3, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS4, (uint8_t*)&fifo_status4, 1); + *val = ((uint16_t)fifo_status4.fifo_pattern << 8) + fifo_status3.fifo_pattern; + } + + return ret; +} + +/** + * @brief Batching of temperature data[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_temp_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_temp_batch_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.fifo_temp_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + return ret; +} + +/** + * @brief Batching of temperature data[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_temp_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_temp_batch_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + *val = fifo_ctrl2.fifo_temp_en; + + return ret; +} + +/** + * @brief Trigger signal for FIFO write operation.[set] + * + * @param ctx Read / write interface definitions + * @param val act on FIFO_CTRL2(timer_pedo_fifo_drdy) + * and MASTER_CONFIG(data_valid_sel_fifo) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_write_trigger_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.timer_pedo_fifo_drdy = (uint8_t)val & 0x01U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + ret = + lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.data_valid_sel_fifo = (((uint8_t)val & 0x02U) >> 1); + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + } + } + + return ret; +} + +/** + * @brief Trigger signal for FIFO write operation.[get] + * + * @param ctx Read / write interface definitions + * @param val act on FIFO_CTRL2(timer_pedo_fifo_drdy) + * and MASTER_CONFIG(data_valid_sel_fifo) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_write_trigger_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch((fifo_ctrl2.timer_pedo_fifo_drdy << 1) + fifo_ctrl2.timer_pedo_fifo_drdy) { + case LSM6DS3TR_C_TRG_XL_GY_DRDY: + *val = LSM6DS3TR_C_TRG_XL_GY_DRDY; + break; + + case LSM6DS3TR_C_TRG_STEP_DETECT: + *val = LSM6DS3TR_C_TRG_STEP_DETECT; + break; + + case LSM6DS3TR_C_TRG_SH_DRDY: + *val = LSM6DS3TR_C_TRG_SH_DRDY; + break; + + default: + *val = LSM6DS3TR_C_TRG_SH_ND; + break; + } + } + + return ret; +} + +/** + * @brief Enable pedometer step counter and timestamp as 4th + * FIFO data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_pedo_fifo_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.timer_pedo_fifo_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + return ret; +} + +/** + * @brief Enable pedometer step counter and timestamp as 4th + * FIFO data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_pedo_fifo_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + *val = fifo_ctrl2.timer_pedo_fifo_en; + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * accelerometer data.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_fifo_xl in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + if(ret == 0) { + fifo_ctrl3.dec_fifo_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * accelerometer data.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_fifo_xl in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t* val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + switch(fifo_ctrl3.dec_fifo_xl) { + case LSM6DS3TR_C_FIFO_XL_DISABLE: + *val = LSM6DS3TR_C_FIFO_XL_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_XL_NO_DEC: + *val = LSM6DS3TR_C_FIFO_XL_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_2: + *val = LSM6DS3TR_C_FIFO_XL_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_3: + *val = LSM6DS3TR_C_FIFO_XL_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_4: + *val = LSM6DS3TR_C_FIFO_XL_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_8: + *val = LSM6DS3TR_C_FIFO_XL_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_16: + *val = LSM6DS3TR_C_FIFO_XL_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_32: + *val = LSM6DS3TR_C_FIFO_XL_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_XL_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for gyroscope data.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_fifo_gyro in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_gy_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + if(ret == 0) { + fifo_ctrl3.dec_fifo_gyro = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for gyroscope data.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_fifo_gyro in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_gy_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t* val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + switch(fifo_ctrl3.dec_fifo_gyro) { + case LSM6DS3TR_C_FIFO_GY_DISABLE: + *val = LSM6DS3TR_C_FIFO_GY_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_GY_NO_DEC: + *val = LSM6DS3TR_C_FIFO_GY_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_2: + *val = LSM6DS3TR_C_FIFO_GY_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_3: + *val = LSM6DS3TR_C_FIFO_GY_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_4: + *val = LSM6DS3TR_C_FIFO_GY_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_8: + *val = LSM6DS3TR_C_FIFO_GY_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_16: + *val = LSM6DS3TR_C_FIFO_GY_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_32: + *val = LSM6DS3TR_C_FIFO_GY_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_GY_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for third data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_ds3_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.dec_ds3_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for third data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_ds3_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + switch(fifo_ctrl4.dec_ds3_fifo) { + case LSM6DS3TR_C_FIFO_DS3_DISABLE: + *val = LSM6DS3TR_C_FIFO_DS3_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_DS3_NO_DEC: + *val = LSM6DS3TR_C_FIFO_DS3_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_2: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_3: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_4: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_8: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_16: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_32: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for fourth data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_ds4_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.dec_ds4_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * fourth data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_ds4_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + switch(fifo_ctrl4.dec_ds4_fifo) { + case LSM6DS3TR_C_FIFO_DS4_DISABLE: + *val = LSM6DS3TR_C_FIFO_DS4_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_DS4_NO_DEC: + *val = LSM6DS3TR_C_FIFO_DS4_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_2: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_3: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_4: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_8: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_16: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_32: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief 8-bit data storage in FIFO.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of only_high_data in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.only_high_data = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief 8-bit data storage in FIFO.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of only_high_data in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + *val = fifo_ctrl4.only_high_data; + + return ret; +} + +/** + * @brief Sensing chain FIFO stop values memorization at threshold + * level.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of stop_on_fth in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.stop_on_fth = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Sensing chain FIFO stop values memorization at threshold + * level.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of stop_on_fth in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + *val = fifo_ctrl4.stop_on_fth; + + return ret; +} + +/** + * @brief FIFO mode selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_mode in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + if(ret == 0) { + fifo_ctrl5.fifo_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + } + + return ret; +} + +/** + * @brief FIFO mode selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fifo_mode in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t* val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + switch(fifo_ctrl5.fifo_mode) { + case LSM6DS3TR_C_BYPASS_MODE: + *val = LSM6DS3TR_C_BYPASS_MODE; + break; + + case LSM6DS3TR_C_FIFO_MODE: + *val = LSM6DS3TR_C_FIFO_MODE; + break; + + case LSM6DS3TR_C_STREAM_TO_FIFO_MODE: + *val = LSM6DS3TR_C_STREAM_TO_FIFO_MODE; + break; + + case LSM6DS3TR_C_BYPASS_TO_STREAM_MODE: + *val = LSM6DS3TR_C_BYPASS_TO_STREAM_MODE; + break; + + case LSM6DS3TR_C_STREAM_MODE: + *val = LSM6DS3TR_C_STREAM_MODE; + break; + + default: + *val = LSM6DS3TR_C_FIFO_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief FIFO ODR selection, setting FIFO_MODE also.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_fifo in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + if(ret == 0) { + fifo_ctrl5.odr_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + } + + return ret; +} + +/** + * @brief FIFO ODR selection, setting FIFO_MODE also.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_fifo in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + switch(fifo_ctrl5.odr_fifo) { + case LSM6DS3TR_C_FIFO_DISABLE: + *val = LSM6DS3TR_C_FIFO_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_12Hz5: + *val = LSM6DS3TR_C_FIFO_12Hz5; + break; + + case LSM6DS3TR_C_FIFO_26Hz: + *val = LSM6DS3TR_C_FIFO_26Hz; + break; + + case LSM6DS3TR_C_FIFO_52Hz: + *val = LSM6DS3TR_C_FIFO_52Hz; + break; + + case LSM6DS3TR_C_FIFO_104Hz: + *val = LSM6DS3TR_C_FIFO_104Hz; + break; + + case LSM6DS3TR_C_FIFO_208Hz: + *val = LSM6DS3TR_C_FIFO_208Hz; + break; + + case LSM6DS3TR_C_FIFO_416Hz: + *val = LSM6DS3TR_C_FIFO_416Hz; + break; + + case LSM6DS3TR_C_FIFO_833Hz: + *val = LSM6DS3TR_C_FIFO_833Hz; + break; + + case LSM6DS3TR_C_FIFO_1k66Hz: + *val = LSM6DS3TR_C_FIFO_1k66Hz; + break; + + case LSM6DS3TR_C_FIFO_3k33Hz: + *val = LSM6DS3TR_C_FIFO_3k33Hz; + break; + + case LSM6DS3TR_C_FIFO_6k66Hz: + *val = LSM6DS3TR_C_FIFO_6k66Hz; + break; + + default: + *val = LSM6DS3TR_C_FIFO_RATE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_DEN_functionality + * @brief This section groups all the functions concerning DEN + * functionality. + * @{ + * + */ + +/** + * @brief DEN active level configuration.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_lh in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.den_lh = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief DEN active level configuration.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of den_lh in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.den_lh) { + case LSM6DS3TR_C_DEN_ACT_LOW: + *val = LSM6DS3TR_C_DEN_ACT_LOW; + break; + + case LSM6DS3TR_C_DEN_ACT_HIGH: + *val = LSM6DS3TR_C_DEN_ACT_HIGH; + break; + + default: + *val = LSM6DS3TR_C_DEN_POL_ND; + break; + } + + return ret; +} + +/** + * @brief DEN functionality marking mode[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.den_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief DEN functionality marking mode[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.den_mode) { + case LSM6DS3TR_C_DEN_DISABLE: + *val = LSM6DS3TR_C_DEN_DISABLE; + break; + + case LSM6DS3TR_C_LEVEL_LETCHED: + *val = LSM6DS3TR_C_LEVEL_LETCHED; + break; + + case LSM6DS3TR_C_LEVEL_TRIGGER: + *val = LSM6DS3TR_C_LEVEL_TRIGGER; + break; + + case LSM6DS3TR_C_EDGE_TRIGGER: + *val = LSM6DS3TR_C_EDGE_TRIGGER; + break; + + default: + *val = LSM6DS3TR_C_DEN_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Extend DEN functionality to accelerometer sensor.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_xl_g in reg CTRL9_XL + * and den_xl_en in CTRL4_C. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_enable_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_xl_g = (uint8_t)val & 0x01U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.den_xl_en = (uint8_t)val & 0x02U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + } + } + + return ret; +} + +/** + * @brief Extend DEN functionality to accelerometer sensor. [get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of den_xl_g in reg CTRL9_XL + * and den_xl_en in CTRL4_C. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_enable_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + switch((ctrl4_c.den_xl_en << 1) + ctrl9_xl.den_xl_g) { + case LSM6DS3TR_C_STAMP_IN_GY_DATA: + *val = LSM6DS3TR_C_STAMP_IN_GY_DATA; + break; + + case LSM6DS3TR_C_STAMP_IN_XL_DATA: + *val = LSM6DS3TR_C_STAMP_IN_XL_DATA; + break; + + case LSM6DS3TR_C_STAMP_IN_GY_XL_DATA: + *val = LSM6DS3TR_C_STAMP_IN_GY_XL_DATA; + break; + + default: + *val = LSM6DS3TR_C_DEN_STAMP_ND; + break; + } + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Z-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_z in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_z_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_z = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Z-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_z in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_z_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_z; + + return ret; +} + +/** + * @brief DEN value stored in LSB of Y-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_y in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_y_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_y = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Y-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_y in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_y_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_y; + + return ret; +} + +/** + * @brief DEN value stored in LSB of X-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_x in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_x = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of X-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_x in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_x; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Pedometer + * @brief This section groups all the functions that manage pedometer. + * @{ + * + */ + +/** + * @brief Reset pedometer step counter.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_rst_step in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_step_reset_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.pedo_rst_step = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Reset pedometer step counter.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_rst_step in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_step_reset_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.pedo_rst_step; + + return ret; +} + +/** + * @brief Enable pedometer algorithm.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.pedo_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief pedo_sens: Enable pedometer algorithm.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.pedo_en; + + return ret; +} + +/** + * @brief Minimum threshold to detect a peak. Default is 10h.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ths_min in reg + * CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_threshold_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + config_pedo_ths_min.ths_min = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Minimum threshold to detect a peak. Default is 10h.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ths_min in reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_threshold_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + *val = config_pedo_ths_min.ths_min; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief pedo_full_scale: Pedometer data range.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_fs in + * reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + config_pedo_ths_min.pedo_fs = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Pedometer data range.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pedo_fs in + * reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t* val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + switch(config_pedo_ths_min.pedo_fs) { + case LSM6DS3TR_C_PEDO_AT_2g: + *val = LSM6DS3TR_C_PEDO_AT_2g; + break; + + case LSM6DS3TR_C_PEDO_AT_4g: + *val = LSM6DS3TR_C_PEDO_AT_4g; + break; + + default: + *val = LSM6DS3TR_C_PEDO_FS_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Pedometer debounce configuration register (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_step in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_debounce_steps_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + pedo_deb_reg.deb_step = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Pedometer debounce configuration register (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_step in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_debounce_steps_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + *val = pedo_deb_reg.deb_step; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Debounce time. If the time between two consecutive steps is + * greater than DEB_TIME*80ms, the debouncer is reactivated. + * Default value: 01101[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_time in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_timeout_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + pedo_deb_reg.deb_time = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Debounce time. If the time between two consecutive steps is + * greater than DEB_TIME*80ms, the debouncer is reactivated. + * Default value: 01101[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_time in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_timeout_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + *val = pedo_deb_reg.deb_time; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Time period register for step detection on delta time (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_steps_period_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_STEP_COUNT_DELTA, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Time period register for step detection on delta time (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_steps_period_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STEP_COUNT_DELTA, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_significant_motion + * @brief This section groups all the functions that manage the + * significant motion detection. + * @{ + * + */ + +/** + * @brief Enable significant motion detection function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sign_motion_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.sign_motion_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + + return ret; +} + +/** + * @brief Enable significant motion detection function.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sign_motion_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.sign_motion_en; + + return ret; +} + +/** + * @brief Significant motion threshold.[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that store significant motion threshold. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SM_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Significant motion threshold.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that store significant motion threshold. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SM_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_tilt_detection + * @brief This section groups all the functions that manage the tilt + * event detection. + * @{ + * + */ + +/** + * @brief Enable tilt calculation.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.tilt_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Enable tilt calculation.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.tilt_en; + + return ret; +} + +/** + * @brief Enable tilt calculation.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wrist_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.wrist_tilt_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Enable tilt calculation.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wrist_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.wrist_tilt_en; + + return ret; +} + +/** + * @brief Absolute Wrist Tilt latency register (r/w). + * Absolute wrist tilt latency parameters. + * 1 LSB = 40 ms. Default value: 0Fh (600 ms).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_latency_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_LAT, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt latency register (r/w). + * Absolute wrist tilt latency parameters. + * 1 LSB = 40 ms. Default value: 0Fh (600 ms).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_latency_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_LAT, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt threshold register(r/w). + * Absolute wrist tilt threshold parameters. + * 1 LSB = 15.625 mg.Default value: 20h (500 mg).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt threshold register(r/w). + * Absolute wrist tilt threshold parameters. + * 1 LSB = 15.625 mg.Default value: 20h (500 mg).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt mask register (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param val Registers A_WRIST_TILT_MASK + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_src_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)val, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt mask register (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param val Registers A_WRIST_TILT_MASK + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)val, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_ magnetometer_sensor + * @brief This section groups all the functions that manage additional + * magnetometer sensor. + * @{ + * + */ + +/** + * @brief Enable soft-iron correction algorithm for magnetometer.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of soft_en in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.soft_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief Enable soft-iron correction algorithm for magnetometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of soft_en in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.soft_en; + + return ret; +} + +/** + * @brief Enable hard-iron correction algorithm for magnetometer.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of iron_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_hard_iron_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.iron_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + } + + return ret; +} + +/** + * @brief Enable hard-iron correction algorithm for magnetometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of iron_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_hard_iron_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.iron_en; + + return ret; +} + +/** + * @brief Soft iron 3x3 matrix. Value are expressed in sign-module format. + * (Es. SVVVVVVVb where S is the sign 0/+1/- and V is the value).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_mat_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MAG_SI_XX, buff, 9); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Soft iron 3x3 matrix. Value are expressed in sign-module format. + * (Es. SVVVVVVVb where S is the sign 0/+1/- and V is the value).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_mat_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MAG_SI_XX, buff, 9); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Offset for hard-iron compensation register (r/w). The value is + * expressed as a 16-bit word in two’s complement.[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_offset_set(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + buff[1] = (uint8_t)((uint16_t)val[0] / 256U); + buff[0] = (uint8_t)((uint16_t)val[0] - (buff[1] * 256U)); + buff[3] = (uint8_t)((uint16_t)val[1] / 256U); + buff[2] = (uint8_t)((uint16_t)val[1] - (buff[3] * 256U)); + buff[5] = (uint8_t)((uint16_t)val[2] / 256U); + buff[4] = (uint8_t)((uint16_t)val[2] - (buff[5] * 256U)); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MAG_OFFX_L, buff, 6); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Offset for hard-iron compensation register(r/w). + * The value is expressed as a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_offset_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MAG_OFFX_L, buff, 6); + + if(ret == 0) { + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Sensor_hub + * @brief This section groups all the functions that manage the sensor + * hub functionality. + * @{ + * + */ + +/** + * @brief Enable function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values func_en + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_func_en_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Sensor synchronization time frame with the step of 500 ms and + * full range of 5s. Unsigned 8-bit.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tph in reg SENSOR_SYNC_TIME_FRAME + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_frame_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + + if(ret == 0) { + sensor_sync_time_frame.tph = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + } + + return ret; +} + +/** + * @brief Sensor synchronization time frame with the step of 500 ms and + * full range of 5s. Unsigned 8-bit.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tph in reg SENSOR_SYNC_TIME_FRAME + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_frame_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + *val = sensor_sync_time_frame.tph; + + return ret; +} + +/** + * @brief Resolution ratio of error code for sensor synchronization.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rr in reg SENSOR_SYNC_RES_RATIO + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t val) { + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + + if(ret == 0) { + sensor_sync_res_ratio.rr = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + } + + return ret; +} + +/** + * @brief Resolution ratio of error code for sensor synchronization.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rr in reg SENSOR_SYNC_RES_RATIO + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t* val) { + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + + switch(sensor_sync_res_ratio.rr) { + case LSM6DS3TR_C_RES_RATIO_2_11: + *val = LSM6DS3TR_C_RES_RATIO_2_11; + break; + + case LSM6DS3TR_C_RES_RATIO_2_12: + *val = LSM6DS3TR_C_RES_RATIO_2_12; + break; + + case LSM6DS3TR_C_RES_RATIO_2_13: + *val = LSM6DS3TR_C_RES_RATIO_2_13; + break; + + case LSM6DS3TR_C_RES_RATIO_2_14: + *val = LSM6DS3TR_C_RES_RATIO_2_14; + break; + + default: + *val = LSM6DS3TR_C_RES_RATIO_ND; + break; + } + + return ret; +} + +/** + * @brief Sensor hub I2C master enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_on in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_master_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.master_on = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Sensor hub I2C master enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_on in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_master_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.master_on; + + return ret; +} + +/** + * @brief I2C interface pass-through.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pass_through_mode in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pass_through_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.pass_through_mode = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief I2C interface pass-through.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pass_through_mode in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pass_through_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.pass_through_mode; + + return ret; +} + +/** + * @brief Master I2C pull-up enable/disable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pull_up_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.pull_up_en = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Master I2C pull-up enable/disable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pull_up_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch(master_config.pull_up_en) { + case LSM6DS3TR_C_EXT_PULL_UP: + *val = LSM6DS3TR_C_EXT_PULL_UP; + break; + + case LSM6DS3TR_C_INTERNAL_PULL_UP: + *val = LSM6DS3TR_C_INTERNAL_PULL_UP; + break; + + default: + *val = LSM6DS3TR_C_SH_PIN_MODE; + break; + } + + return ret; +} + +/** + * @brief Sensor hub trigger signal selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of start_config in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_syncro_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.start_config = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Sensor hub trigger signal selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of start_config in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_syncro_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch(master_config.start_config) { + case LSM6DS3TR_C_XL_GY_DRDY: + *val = LSM6DS3TR_C_XL_GY_DRDY; + break; + + case LSM6DS3TR_C_EXT_ON_INT2_PIN: + *val = LSM6DS3TR_C_EXT_ON_INT2_PIN; + break; + + default: + *val = LSM6DS3TR_C_SH_SYNCRO_ND; + break; + } + + return ret; +} + +/** + * @brief Manage the Master DRDY signal on INT1 pad.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_on_int1 in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_drdy_on_int1_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.drdy_on_int1 = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Manage the Master DRDY signal on INT1 pad.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_on_int1 in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_drdy_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.drdy_on_int1; + + return ret; +} + +/** + * @brief Sensor hub output registers.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure of registers from SENSORHUB1_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_read_data_raw_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_emb_sh_read_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SENSORHUB1_REG, (uint8_t*)&(val->sh_byte_1), 12); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSORHUB13_REG, (uint8_t*)&(val->sh_byte_13), 6); + } + + return ret; +} + +/** + * @brief Master command code used for stamping for sensor sync.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_cmd_code in + * reg MASTER_CMD_CODE + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + + if(ret == 0) { + master_cmd_code.master_cmd_code = val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + } + + return ret; +} + +/** + * @brief Master command code used for stamping for sensor sync.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_cmd_code in + * reg MASTER_CMD_CODE + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + *val = master_cmd_code.master_cmd_code; + + return ret; +} + +/** + * @brief Error code used for sensor synchronization.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of error_code in + * reg SENS_SYNC_SPI_ERROR_CODE. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_spi_sync_error_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + + if(ret == 0) { + sens_sync_spi_error_code.error_code = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + } + + return ret; +} + +/** + * @brief Error code used for sensor synchronization.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of error_code in + * reg SENS_SYNC_SPI_ERROR_CODE. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_spi_sync_error_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + *val = sens_sync_spi_error_code.error_code; + + return ret; +} + +/** + * @brief Number of external sensors to be read by the sensor hub.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of aux_sens_on in reg SLAVE0_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + slave0_config.aux_sens_on = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Number of external sensors to be read by the sensor hub.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of aux_sens_on in reg SLAVE0_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t + lsm6ds3tr_c_sh_num_of_dev_connected_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + switch(slave0_config.aux_sens_on) { + case LSM6DS3TR_C_SLV_0: + *val = LSM6DS3TR_C_SLV_0; + break; + + case LSM6DS3TR_C_SLV_0_1: + *val = LSM6DS3TR_C_SLV_0_1; + break; + + case LSM6DS3TR_C_SLV_0_1_2: + *val = LSM6DS3TR_C_SLV_0_1_2; + break; + + case LSM6DS3TR_C_SLV_0_1_2_3: + *val = LSM6DS3TR_C_SLV_0_1_2_3; + break; + + default: + *val = LSM6DS3TR_C_SLV_EN_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Configure slave 0 for perform a write.[set] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_data; 8 bit data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cfg_write(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_write_t* val) { + lsm6ds3tr_c_slv0_add_t slv0_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv0_add.slave0_add = val->slv0_add; + slv0_add.rw_0 = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_ADD, (uint8_t*)&slv0_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_SUBADD, &(val->slv0_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DATAWRITE_SRC_MODE_SUB_SLV0, &(val->slv0_data), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 0 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv0_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + lsm6ds3tr_c_slv0_add_t slv0_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv0_add.slave0_add = val->slv_add; + slv0_add.rw_0 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_ADD, (uint8_t*)&slv0_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + slave0_config.slave0_numop = val->slv_len; + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 1 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv1_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + lsm6ds3tr_c_slv1_add_t slv1_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv1_add.slave1_add = val->slv_add; + slv1_add.r_1 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV1_ADD, (uint8_t*)&slv1_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV1_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + slave1_config.slave1_numop = val->slv_len; + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 2 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv2_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slv2_add_t slv2_add; + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv2_add.slave2_add = val->slv_add; + slv2_add.r_2 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV2_ADD, (uint8_t*)&slv2_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV2_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + slave2_config.slave2_numop = val->slv_len; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 3 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv3_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + lsm6ds3tr_c_slv3_add_t slv3_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv3_add.slave3_add = val->slv_add; + slv3_add.r_3 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV3_ADD, (uint8_t*)&slv3_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLV3_SUBADD, (uint8_t*)&(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + slave3_config.slave3_numop = val->slv_len; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 0 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave0_rate in reg SLAVE0_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_0_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + slave0_config.slave0_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 0 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave0_rate in reg SLAVE0_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_0_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + switch(slave0_config.slave0_rate) { + case LSM6DS3TR_C_SL0_NO_DEC: + *val = LSM6DS3TR_C_SL0_NO_DEC; + break; + + case LSM6DS3TR_C_SL0_DEC_2: + *val = LSM6DS3TR_C_SL0_DEC_2; + break; + + case LSM6DS3TR_C_SL0_DEC_4: + *val = LSM6DS3TR_C_SL0_DEC_4; + break; + + case LSM6DS3TR_C_SL0_DEC_8: + *val = LSM6DS3TR_C_SL0_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL0_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Slave 0 write operation is performed only at the first sensor + * hub cycle. + * This is effective if the Aux_sens_on[1:0] field in + * SLAVE0_CONFIG(04h) is set to a value other than 00.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of write_once in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_write_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + slave1_config.write_once = (uint8_t)val; + + if(ret == 0) { + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Slave 0 write operation is performed only at the first sensor + * hub cycle. + * This is effective if the Aux_sens_on[1:0] field in + * SLAVE0_CONFIG(04h) is set to a value other than 00.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of write_once in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_write_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + switch(slave1_config.write_once) { + case LSM6DS3TR_C_EACH_SH_CYCLE: + *val = LSM6DS3TR_C_EACH_SH_CYCLE; + break; + + case LSM6DS3TR_C_ONLY_FIRST_CYCLE: + *val = LSM6DS3TR_C_ONLY_FIRST_CYCLE; + break; + + default: + *val = LSM6DS3TR_C_SH_WR_MODE_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 1 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave1_rate in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_1_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + slave1_config.slave1_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 1 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_1_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + switch(slave1_config.slave1_rate) { + case LSM6DS3TR_C_SL1_NO_DEC: + *val = LSM6DS3TR_C_SL1_NO_DEC; + break; + + case LSM6DS3TR_C_SL1_DEC_2: + *val = LSM6DS3TR_C_SL1_DEC_2; + break; + + case LSM6DS3TR_C_SL1_DEC_4: + *val = LSM6DS3TR_C_SL1_DEC_4; + break; + + case LSM6DS3TR_C_SL1_DEC_8: + *val = LSM6DS3TR_C_SL1_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL1_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 2 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave2_rate in reg SLAVE2_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_2_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t val) { + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + slave2_config.slave2_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 2 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave2_rate in reg SLAVE2_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_2_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t* val) { + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + switch(slave2_config.slave2_rate) { + case LSM6DS3TR_C_SL2_NO_DEC: + *val = LSM6DS3TR_C_SL2_NO_DEC; + break; + + case LSM6DS3TR_C_SL2_DEC_2: + *val = LSM6DS3TR_C_SL2_DEC_2; + break; + + case LSM6DS3TR_C_SL2_DEC_4: + *val = LSM6DS3TR_C_SL2_DEC_4; + break; + + case LSM6DS3TR_C_SL2_DEC_8: + *val = LSM6DS3TR_C_SL2_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL2_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 3 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave3_rate in reg SLAVE3_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_3_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + slave3_config.slave3_rate = (uint8_t)val; + + if(ret == 0) { + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 3 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave3_rate in reg SLAVE3_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_3_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t* val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + switch(slave3_config.slave3_rate) { + case LSM6DS3TR_C_SL3_NO_DEC: + *val = LSM6DS3TR_C_SL3_NO_DEC; + break; + + case LSM6DS3TR_C_SL3_DEC_2: + *val = LSM6DS3TR_C_SL3_DEC_2; + break; + + case LSM6DS3TR_C_SL3_DEC_4: + *val = LSM6DS3TR_C_SL3_DEC_4; + break; + + case LSM6DS3TR_C_SL3_DEC_8: + *val = LSM6DS3TR_C_SL3_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL3_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @} + * + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h b/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h new file mode 100644 index 000000000..8cb592c0d --- /dev/null +++ b/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h @@ -0,0 +1,2448 @@ +/** + ****************************************************************************** + * @file lsm6ds3tr_c_reg.h + * @author Sensors Software Solution Team + * @brief This file contains all the functions prototypes for the + * lsm6ds3tr_c_reg.c driver. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2021 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef LSM6DS3TR_C_DRIVER_H +#define LSM6DS3TR_C_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include +#include + +/** @addtogroup LSM6DS3TR_C + * @{ + * + */ + +/** @defgroup Endianness definitions + * @{ + * + */ + +#ifndef DRV_BYTE_ORDER +#ifndef __BYTE_ORDER__ + +#define DRV_LITTLE_ENDIAN 1234 +#define DRV_BIG_ENDIAN 4321 + +/** if _BYTE_ORDER is not defined, choose the endianness of your architecture + * by uncommenting the define which fits your platform endianness + */ +//#define DRV_BYTE_ORDER DRV_BIG_ENDIAN +#define DRV_BYTE_ORDER DRV_LITTLE_ENDIAN + +#else /* defined __BYTE_ORDER__ */ + +#define DRV_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#define DRV_BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#define DRV_BYTE_ORDER __BYTE_ORDER__ + +#endif /* __BYTE_ORDER__*/ +#endif /* DRV_BYTE_ORDER */ + +/** + * @} + * + */ + +/** @defgroup STMicroelectronics sensors common types + * @{ + * + */ + +#ifndef MEMS_SHARED_TYPES +#define MEMS_SHARED_TYPES + +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} bitwise_t; + +#define PROPERTY_DISABLE (0U) +#define PROPERTY_ENABLE (1U) + +/** @addtogroup Interfaces_Functions + * @brief This section provide a set of functions used to read and + * write a generic register of the device. + * MANDATORY: return 0 -> no Error. + * @{ + * + */ + +typedef int32_t (*stmdev_write_ptr)(void*, uint8_t, const uint8_t*, uint16_t); +typedef int32_t (*stmdev_read_ptr)(void*, uint8_t, uint8_t*, uint16_t); +typedef void (*stmdev_mdelay_ptr)(uint32_t millisec); + +typedef struct { + /** Component mandatory fields **/ + stmdev_write_ptr write_reg; + stmdev_read_ptr read_reg; + /** Component optional fields **/ + stmdev_mdelay_ptr mdelay; + /** Customizable optional pointer **/ + void* handle; +} stmdev_ctx_t; + +/** + * @} + * + */ + +#endif /* MEMS_SHARED_TYPES */ + +#ifndef MEMS_UCF_SHARED_TYPES +#define MEMS_UCF_SHARED_TYPES + +/** @defgroup Generic address-data structure definition + * @brief This structure is useful to load a predefined configuration + * of a sensor. + * You can create a sensor configuration by your own or using + * Unico / Unicleo tools available on STMicroelectronics + * web site. + * + * @{ + * + */ + +typedef struct { + uint8_t address; + uint8_t data; +} ucf_line_t; + +/** + * @} + * + */ + +#endif /* MEMS_UCF_SHARED_TYPES */ + +/** + * @} + * + */ + +/** @defgroup LSM6DS3TR_C_Infos + * @{ + * + */ + +/** I2C Device Address 8 bit format if SA0=0 -> D5 if SA0=1 -> D7 **/ +#define LSM6DS3TR_C_I2C_ADD_L 0xD5U +#define LSM6DS3TR_C_I2C_ADD_H 0xD7U + +/** Device Identification (Who am I) **/ +#define LSM6DS3TR_C_ID 0x6AU + +/** + * @} + * + */ + +#define LSM6DS3TR_C_FUNC_CFG_ACCESS 0x01U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 5; + uint8_t func_cfg_en : 3; /* func_cfg_en + func_cfg_en_b */ +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t func_cfg_en : 3; /* func_cfg_en + func_cfg_en_b */ + uint8_t not_used_01 : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_cfg_access_t; + +#define LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME 0x04U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t tph : 4; + uint8_t not_used_01 : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 4; + uint8_t tph : 4; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensor_sync_time_frame_t; + +#define LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO 0x05U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t rr : 2; + uint8_t not_used_01 : 6; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 6; + uint8_t rr : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensor_sync_res_ratio_t; + +#define LSM6DS3TR_C_FIFO_CTRL1 0x06U +typedef struct { + uint8_t fth : 8; /* + FIFO_CTRL2(fth) */ +} lsm6ds3tr_c_fifo_ctrl1_t; + +#define LSM6DS3TR_C_FIFO_CTRL2 0x07U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fth : 3; /* + FIFO_CTRL1(fth) */ + uint8_t fifo_temp_en : 1; + uint8_t not_used_01 : 2; + uint8_t timer_pedo_fifo_drdy : 1; + uint8_t timer_pedo_fifo_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t timer_pedo_fifo_en : 1; + uint8_t timer_pedo_fifo_drdy : 1; + uint8_t not_used_01 : 2; + uint8_t fifo_temp_en : 1; + uint8_t fth : 3; /* + FIFO_CTRL1(fth) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl2_t; + +#define LSM6DS3TR_C_FIFO_CTRL3 0x08U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t dec_fifo_xl : 3; + uint8_t dec_fifo_gyro : 3; + uint8_t not_used_01 : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 2; + uint8_t dec_fifo_gyro : 3; + uint8_t dec_fifo_xl : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl3_t; + +#define LSM6DS3TR_C_FIFO_CTRL4 0x09U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t dec_ds3_fifo : 3; + uint8_t dec_ds4_fifo : 3; + uint8_t only_high_data : 1; + uint8_t stop_on_fth : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t stop_on_fth : 1; + uint8_t only_high_data : 1; + uint8_t dec_ds4_fifo : 3; + uint8_t dec_ds3_fifo : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl4_t; + +#define LSM6DS3TR_C_FIFO_CTRL5 0x0AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fifo_mode : 3; + uint8_t odr_fifo : 4; + uint8_t not_used_01 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 1; + uint8_t odr_fifo : 4; + uint8_t fifo_mode : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl5_t; + +#define LSM6DS3TR_C_DRDY_PULSE_CFG_G 0x0BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_wrist_tilt : 1; + uint8_t not_used_01 : 6; + uint8_t drdy_pulsed : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t drdy_pulsed : 1; + uint8_t not_used_01 : 6; + uint8_t int2_wrist_tilt : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_drdy_pulse_cfg_g_t; + +#define LSM6DS3TR_C_INT1_CTRL 0x0DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int1_drdy_xl : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_boot : 1; + uint8_t int1_fth : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_step_detector : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int1_step_detector : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_fth : 1; + uint8_t int1_boot : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_drdy_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int1_ctrl_t; + +#define LSM6DS3TR_C_INT2_CTRL 0x0EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_drdy_xl : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_fth : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_step_delta : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int2_step_delta : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_fth : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int2_ctrl_t; + +#define LSM6DS3TR_C_WHO_AM_I 0x0FU +#define LSM6DS3TR_C_CTRL1_XL 0x10U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bw0_xl : 1; + uint8_t lpf1_bw_sel : 1; + uint8_t fs_xl : 2; + uint8_t odr_xl : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t odr_xl : 4; + uint8_t fs_xl : 2; + uint8_t lpf1_bw_sel : 1; + uint8_t bw0_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl1_xl_t; + +#define LSM6DS3TR_C_CTRL2_G 0x11U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 1; + uint8_t fs_g : 3; /* fs_g + fs_125 */ + uint8_t odr_g : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t odr_g : 4; + uint8_t fs_g : 3; /* fs_g + fs_125 */ + uint8_t not_used_01 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl2_g_t; + +#define LSM6DS3TR_C_CTRL3_C 0x12U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sw_reset : 1; + uint8_t ble : 1; + uint8_t if_inc : 1; + uint8_t sim : 1; + uint8_t pp_od : 1; + uint8_t h_lactive : 1; + uint8_t bdu : 1; + uint8_t boot : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t boot : 1; + uint8_t bdu : 1; + uint8_t h_lactive : 1; + uint8_t pp_od : 1; + uint8_t sim : 1; + uint8_t if_inc : 1; + uint8_t ble : 1; + uint8_t sw_reset : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl3_c_t; + +#define LSM6DS3TR_C_CTRL4_C 0x13U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 1; + uint8_t lpf1_sel_g : 1; + uint8_t i2c_disable : 1; + uint8_t drdy_mask : 1; + uint8_t den_drdy_int1 : 1; + uint8_t int2_on_int1 : 1; + uint8_t sleep : 1; + uint8_t den_xl_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_xl_en : 1; + uint8_t sleep : 1; + uint8_t int2_on_int1 : 1; + uint8_t den_drdy_int1 : 1; + uint8_t drdy_mask : 1; + uint8_t i2c_disable : 1; + uint8_t lpf1_sel_g : 1; + uint8_t not_used_01 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl4_c_t; + +#define LSM6DS3TR_C_CTRL5_C 0x14U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t st_xl : 2; + uint8_t st_g : 2; + uint8_t den_lh : 1; + uint8_t rounding : 3; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t rounding : 3; + uint8_t den_lh : 1; + uint8_t st_g : 2; + uint8_t st_xl : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl5_c_t; + +#define LSM6DS3TR_C_CTRL6_C 0x15U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ftype : 2; + uint8_t not_used_01 : 1; + uint8_t usr_off_w : 1; + uint8_t xl_hm_mode : 1; + uint8_t den_mode : 3; /* trig_en + lvl_en + lvl2_en */ +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_mode : 3; /* trig_en + lvl_en + lvl2_en */ + uint8_t xl_hm_mode : 1; + uint8_t usr_off_w : 1; + uint8_t not_used_01 : 1; + uint8_t ftype : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl6_c_t; + +#define LSM6DS3TR_C_CTRL7_G 0x16U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t rounding_status : 1; + uint8_t not_used_02 : 1; + uint8_t hpm_g : 2; + uint8_t hp_en_g : 1; + uint8_t g_hm_mode : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t g_hm_mode : 1; + uint8_t hp_en_g : 1; + uint8_t hpm_g : 2; + uint8_t not_used_02 : 1; + uint8_t rounding_status : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl7_g_t; + +#define LSM6DS3TR_C_CTRL8_XL 0x17U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t low_pass_on_6d : 1; + uint8_t not_used_01 : 1; + uint8_t hp_slope_xl_en : 1; + uint8_t input_composite : 1; + uint8_t hp_ref_mode : 1; + uint8_t hpcf_xl : 2; + uint8_t lpf2_xl_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t lpf2_xl_en : 1; + uint8_t hpcf_xl : 2; + uint8_t hp_ref_mode : 1; + uint8_t input_composite : 1; + uint8_t hp_slope_xl_en : 1; + uint8_t not_used_01 : 1; + uint8_t low_pass_on_6d : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl8_xl_t; + +#define LSM6DS3TR_C_CTRL9_XL 0x18U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t soft_en : 1; + uint8_t not_used_02 : 1; + uint8_t den_xl_g : 1; + uint8_t den_z : 1; + uint8_t den_y : 1; + uint8_t den_x : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_x : 1; + uint8_t den_y : 1; + uint8_t den_z : 1; + uint8_t den_xl_g : 1; + uint8_t not_used_02 : 1; + uint8_t soft_en : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl9_xl_t; + +#define LSM6DS3TR_C_CTRL10_C 0x19U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sign_motion_en : 1; + uint8_t pedo_rst_step : 1; + uint8_t func_en : 1; + uint8_t tilt_en : 1; + uint8_t pedo_en : 1; + uint8_t timer_en : 1; + uint8_t not_used_01 : 1; + uint8_t wrist_tilt_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_en : 1; + uint8_t not_used_01 : 1; + uint8_t timer_en : 1; + uint8_t pedo_en : 1; + uint8_t tilt_en : 1; + uint8_t func_en : 1; + uint8_t pedo_rst_step : 1; + uint8_t sign_motion_en : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl10_c_t; + +#define LSM6DS3TR_C_MASTER_CONFIG 0x1AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t master_on : 1; + uint8_t iron_en : 1; + uint8_t pass_through_mode : 1; + uint8_t pull_up_en : 1; + uint8_t start_config : 1; + uint8_t not_used_01 : 1; + uint8_t data_valid_sel_fifo : 1; + uint8_t drdy_on_int1 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t drdy_on_int1 : 1; + uint8_t data_valid_sel_fifo : 1; + uint8_t not_used_01 : 1; + uint8_t start_config : 1; + uint8_t pull_up_en : 1; + uint8_t pass_through_mode : 1; + uint8_t iron_en : 1; + uint8_t master_on : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_master_config_t; + +#define LSM6DS3TR_C_WAKE_UP_SRC 0x1BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t z_wu : 1; + uint8_t y_wu : 1; + uint8_t x_wu : 1; + uint8_t wu_ia : 1; + uint8_t sleep_state_ia : 1; + uint8_t ff_ia : 1; + uint8_t not_used_01 : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 2; + uint8_t ff_ia : 1; + uint8_t sleep_state_ia : 1; + uint8_t wu_ia : 1; + uint8_t x_wu : 1; + uint8_t y_wu : 1; + uint8_t z_wu : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_src_t; + +#define LSM6DS3TR_C_TAP_SRC 0x1CU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t z_tap : 1; + uint8_t y_tap : 1; + uint8_t x_tap : 1; + uint8_t tap_sign : 1; + uint8_t double_tap : 1; + uint8_t single_tap : 1; + uint8_t tap_ia : 1; + uint8_t not_used_01 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 1; + uint8_t tap_ia : 1; + uint8_t single_tap : 1; + uint8_t double_tap : 1; + uint8_t tap_sign : 1; + uint8_t x_tap : 1; + uint8_t y_tap : 1; + uint8_t z_tap : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_src_t; + +#define LSM6DS3TR_C_D6D_SRC 0x1DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t xl : 1; + uint8_t xh : 1; + uint8_t yl : 1; + uint8_t yh : 1; + uint8_t zl : 1; + uint8_t zh : 1; + uint8_t d6d_ia : 1; + uint8_t den_drdy : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_drdy : 1; + uint8_t d6d_ia : 1; + uint8_t zh : 1; + uint8_t zl : 1; + uint8_t yh : 1; + uint8_t yl : 1; + uint8_t xh : 1; + uint8_t xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_d6d_src_t; + +#define LSM6DS3TR_C_STATUS_REG 0x1EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t xlda : 1; + uint8_t gda : 1; + uint8_t tda : 1; + uint8_t not_used_01 : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 5; + uint8_t tda : 1; + uint8_t gda : 1; + uint8_t xlda : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_status_reg_t; + +#define LSM6DS3TR_C_OUT_TEMP_L 0x20U +#define LSM6DS3TR_C_OUT_TEMP_H 0x21U +#define LSM6DS3TR_C_OUTX_L_G 0x22U +#define LSM6DS3TR_C_OUTX_H_G 0x23U +#define LSM6DS3TR_C_OUTY_L_G 0x24U +#define LSM6DS3TR_C_OUTY_H_G 0x25U +#define LSM6DS3TR_C_OUTZ_L_G 0x26U +#define LSM6DS3TR_C_OUTZ_H_G 0x27U +#define LSM6DS3TR_C_OUTX_L_XL 0x28U +#define LSM6DS3TR_C_OUTX_H_XL 0x29U +#define LSM6DS3TR_C_OUTY_L_XL 0x2AU +#define LSM6DS3TR_C_OUTY_H_XL 0x2BU +#define LSM6DS3TR_C_OUTZ_L_XL 0x2CU +#define LSM6DS3TR_C_OUTZ_H_XL 0x2DU +#define LSM6DS3TR_C_SENSORHUB1_REG 0x2EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub1_reg_t; + +#define LSM6DS3TR_C_SENSORHUB2_REG 0x2FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub2_reg_t; + +#define LSM6DS3TR_C_SENSORHUB3_REG 0x30U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub3_reg_t; + +#define LSM6DS3TR_C_SENSORHUB4_REG 0x31U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub4_reg_t; + +#define LSM6DS3TR_C_SENSORHUB5_REG 0x32U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub5_reg_t; + +#define LSM6DS3TR_C_SENSORHUB6_REG 0x33U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub6_reg_t; + +#define LSM6DS3TR_C_SENSORHUB7_REG 0x34U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub7_reg_t; + +#define LSM6DS3TR_C_SENSORHUB8_REG 0x35U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub8_reg_t; + +#define LSM6DS3TR_C_SENSORHUB9_REG 0x36U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub9_reg_t; + +#define LSM6DS3TR_C_SENSORHUB10_REG 0x37U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub10_reg_t; + +#define LSM6DS3TR_C_SENSORHUB11_REG 0x38U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub11_reg_t; + +#define LSM6DS3TR_C_SENSORHUB12_REG 0x39U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub12_reg_t; + +#define LSM6DS3TR_C_FIFO_STATUS1 0x3AU +typedef struct { + uint8_t diff_fifo : 8; /* + FIFO_STATUS2(diff_fifo) */ +} lsm6ds3tr_c_fifo_status1_t; + +#define LSM6DS3TR_C_FIFO_STATUS2 0x3BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t diff_fifo : 3; /* + FIFO_STATUS1(diff_fifo) */ + uint8_t not_used_01 : 1; + uint8_t fifo_empty : 1; + uint8_t fifo_full_smart : 1; + uint8_t over_run : 1; + uint8_t waterm : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t waterm : 1; + uint8_t over_run : 1; + uint8_t fifo_full_smart : 1; + uint8_t fifo_empty : 1; + uint8_t not_used_01 : 1; + uint8_t diff_fifo : 3; /* + FIFO_STATUS1(diff_fifo) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_status2_t; + +#define LSM6DS3TR_C_FIFO_STATUS3 0x3CU +typedef struct { + uint8_t fifo_pattern : 8; /* + FIFO_STATUS4(fifo_pattern) */ +} lsm6ds3tr_c_fifo_status3_t; + +#define LSM6DS3TR_C_FIFO_STATUS4 0x3DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fifo_pattern : 2; /* + FIFO_STATUS3(fifo_pattern) */ + uint8_t not_used_01 : 6; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 6; + uint8_t fifo_pattern : 2; /* + FIFO_STATUS3(fifo_pattern) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_status4_t; + +#define LSM6DS3TR_C_FIFO_DATA_OUT_L 0x3EU +#define LSM6DS3TR_C_FIFO_DATA_OUT_H 0x3FU +#define LSM6DS3TR_C_TIMESTAMP0_REG 0x40U +#define LSM6DS3TR_C_TIMESTAMP1_REG 0x41U +#define LSM6DS3TR_C_TIMESTAMP2_REG 0x42U +#define LSM6DS3TR_C_STEP_TIMESTAMP_L 0x49U +#define LSM6DS3TR_C_STEP_TIMESTAMP_H 0x4AU +#define LSM6DS3TR_C_STEP_COUNTER_L 0x4BU +#define LSM6DS3TR_C_STEP_COUNTER_H 0x4CU + +#define LSM6DS3TR_C_SENSORHUB13_REG 0x4DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub13_reg_t; + +#define LSM6DS3TR_C_SENSORHUB14_REG 0x4EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub14_reg_t; + +#define LSM6DS3TR_C_SENSORHUB15_REG 0x4FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub15_reg_t; + +#define LSM6DS3TR_C_SENSORHUB16_REG 0x50U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub16_reg_t; + +#define LSM6DS3TR_C_SENSORHUB17_REG 0x51U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub17_reg_t; + +#define LSM6DS3TR_C_SENSORHUB18_REG 0x52U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub18_reg_t; + +#define LSM6DS3TR_C_FUNC_SRC1 0x53U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sensorhub_end_op : 1; + uint8_t si_end_op : 1; + uint8_t hi_fail : 1; + uint8_t step_overflow : 1; + uint8_t step_detected : 1; + uint8_t tilt_ia : 1; + uint8_t sign_motion_ia : 1; + uint8_t step_count_delta_ia : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t step_count_delta_ia : 1; + uint8_t sign_motion_ia : 1; + uint8_t tilt_ia : 1; + uint8_t step_detected : 1; + uint8_t step_overflow : 1; + uint8_t hi_fail : 1; + uint8_t si_end_op : 1; + uint8_t sensorhub_end_op : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_src1_t; + +#define LSM6DS3TR_C_FUNC_SRC2 0x54U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t wrist_tilt_ia : 1; + uint8_t not_used_01 : 2; + uint8_t slave0_nack : 1; + uint8_t slave1_nack : 1; + uint8_t slave2_nack : 1; + uint8_t slave3_nack : 1; + uint8_t not_used_02 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_02 : 1; + uint8_t slave3_nack : 1; + uint8_t slave2_nack : 1; + uint8_t slave1_nack : 1; + uint8_t slave0_nack : 1; + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_ia : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_src2_t; + +#define LSM6DS3TR_C_WRIST_TILT_IA 0x55U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_ia_zneg : 1; + uint8_t wrist_tilt_ia_zpos : 1; + uint8_t wrist_tilt_ia_yneg : 1; + uint8_t wrist_tilt_ia_ypos : 1; + uint8_t wrist_tilt_ia_xneg : 1; + uint8_t wrist_tilt_ia_xpos : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_ia_xpos : 1; + uint8_t wrist_tilt_ia_xneg : 1; + uint8_t wrist_tilt_ia_ypos : 1; + uint8_t wrist_tilt_ia_yneg : 1; + uint8_t wrist_tilt_ia_zpos : 1; + uint8_t wrist_tilt_ia_zneg : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wrist_tilt_ia_t; + +#define LSM6DS3TR_C_TAP_CFG 0x58U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t lir : 1; + uint8_t tap_z_en : 1; + uint8_t tap_y_en : 1; + uint8_t tap_x_en : 1; + uint8_t slope_fds : 1; + uint8_t inact_en : 2; + uint8_t interrupts_enable : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t interrupts_enable : 1; + uint8_t inact_en : 2; + uint8_t slope_fds : 1; + uint8_t tap_x_en : 1; + uint8_t tap_y_en : 1; + uint8_t tap_z_en : 1; + uint8_t lir : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_cfg_t; + +#define LSM6DS3TR_C_TAP_THS_6D 0x59U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t tap_ths : 5; + uint8_t sixd_ths : 2; + uint8_t d4d_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t d4d_en : 1; + uint8_t sixd_ths : 2; + uint8_t tap_ths : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_ths_6d_t; + +#define LSM6DS3TR_C_INT_DUR2 0x5AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t shock : 2; + uint8_t quiet : 2; + uint8_t dur : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t dur : 4; + uint8_t quiet : 2; + uint8_t shock : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int_dur2_t; + +#define LSM6DS3TR_C_WAKE_UP_THS 0x5BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t wk_ths : 6; + uint8_t not_used_01 : 1; + uint8_t single_double_tap : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t single_double_tap : 1; + uint8_t not_used_01 : 1; + uint8_t wk_ths : 6; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_ths_t; + +#define LSM6DS3TR_C_WAKE_UP_DUR 0x5CU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sleep_dur : 4; + uint8_t timer_hr : 1; + uint8_t wake_dur : 2; + uint8_t ff_dur : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t ff_dur : 1; + uint8_t wake_dur : 2; + uint8_t timer_hr : 1; + uint8_t sleep_dur : 4; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_dur_t; + +#define LSM6DS3TR_C_FREE_FALL 0x5DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ff_ths : 3; + uint8_t ff_dur : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t ff_dur : 5; + uint8_t ff_ths : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_free_fall_t; + +#define LSM6DS3TR_C_MD1_CFG 0x5EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int1_timer : 1; + uint8_t int1_tilt : 1; + uint8_t int1_6d : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_ff : 1; + uint8_t int1_wu : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_inact_state : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int1_inact_state : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_wu : 1; + uint8_t int1_ff : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_6d : 1; + uint8_t int1_tilt : 1; + uint8_t int1_timer : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_md1_cfg_t; + +#define LSM6DS3TR_C_MD2_CFG 0x5FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_iron : 1; + uint8_t int2_tilt : 1; + uint8_t int2_6d : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_ff : 1; + uint8_t int2_wu : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_inact_state : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int2_inact_state : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_wu : 1; + uint8_t int2_ff : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_6d : 1; + uint8_t int2_tilt : 1; + uint8_t int2_iron : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_md2_cfg_t; + +#define LSM6DS3TR_C_MASTER_CMD_CODE 0x60U +typedef struct { + uint8_t master_cmd_code : 8; +} lsm6ds3tr_c_master_cmd_code_t; + +#define LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE 0x61U +typedef struct { + uint8_t error_code : 8; +} lsm6ds3tr_c_sens_sync_spi_error_code_t; + +#define LSM6DS3TR_C_OUT_MAG_RAW_X_L 0x66U +#define LSM6DS3TR_C_OUT_MAG_RAW_X_H 0x67U +#define LSM6DS3TR_C_OUT_MAG_RAW_Y_L 0x68U +#define LSM6DS3TR_C_OUT_MAG_RAW_Y_H 0x69U +#define LSM6DS3TR_C_OUT_MAG_RAW_Z_L 0x6AU +#define LSM6DS3TR_C_OUT_MAG_RAW_Z_H 0x6BU +#define LSM6DS3TR_C_X_OFS_USR 0x73U +#define LSM6DS3TR_C_Y_OFS_USR 0x74U +#define LSM6DS3TR_C_Z_OFS_USR 0x75U +#define LSM6DS3TR_C_SLV0_ADD 0x02U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t rw_0 : 1; + uint8_t slave0_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave0_add : 7; + uint8_t rw_0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv0_add_t; + +#define LSM6DS3TR_C_SLV0_SUBADD 0x03U +typedef struct { + uint8_t slave0_reg : 8; +} lsm6ds3tr_c_slv0_subadd_t; + +#define LSM6DS3TR_C_SLAVE0_CONFIG 0x04U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave0_numop : 3; + uint8_t src_mode : 1; + uint8_t aux_sens_on : 2; + uint8_t slave0_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave0_rate : 2; + uint8_t aux_sens_on : 2; + uint8_t src_mode : 1; + uint8_t slave0_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave0_config_t; + +#define LSM6DS3TR_C_SLV1_ADD 0x05U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_1 : 1; + uint8_t slave1_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave1_add : 7; + uint8_t r_1 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv1_add_t; + +#define LSM6DS3TR_C_SLV1_SUBADD 0x06U +typedef struct { + uint8_t slave1_reg : 8; +} lsm6ds3tr_c_slv1_subadd_t; + +#define LSM6DS3TR_C_SLAVE1_CONFIG 0x07U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave1_numop : 3; + uint8_t not_used_01 : 2; + uint8_t write_once : 1; + uint8_t slave1_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave1_rate : 2; + uint8_t write_once : 1; + uint8_t not_used_01 : 2; + uint8_t slave1_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave1_config_t; + +#define LSM6DS3TR_C_SLV2_ADD 0x08U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_2 : 1; + uint8_t slave2_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave2_add : 7; + uint8_t r_2 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv2_add_t; + +#define LSM6DS3TR_C_SLV2_SUBADD 0x09U +typedef struct { + uint8_t slave2_reg : 8; +} lsm6ds3tr_c_slv2_subadd_t; + +#define LSM6DS3TR_C_SLAVE2_CONFIG 0x0AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave2_numop : 3; + uint8_t not_used_01 : 3; + uint8_t slave2_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave2_rate : 2; + uint8_t not_used_01 : 3; + uint8_t slave2_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave2_config_t; + +#define LSM6DS3TR_C_SLV3_ADD 0x0BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_3 : 1; + uint8_t slave3_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave3_add : 7; + uint8_t r_3 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv3_add_t; + +#define LSM6DS3TR_C_SLV3_SUBADD 0x0CU +typedef struct { + uint8_t slave3_reg : 8; +} lsm6ds3tr_c_slv3_subadd_t; + +#define LSM6DS3TR_C_SLAVE3_CONFIG 0x0DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave3_numop : 3; + uint8_t not_used_01 : 3; + uint8_t slave3_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave3_rate : 2; + uint8_t not_used_01 : 3; + uint8_t slave3_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave3_config_t; + +#define LSM6DS3TR_C_DATAWRITE_SRC_MODE_SUB_SLV0 0x0EU +typedef struct { + uint8_t slave_dataw : 8; +} lsm6ds3tr_c_datawrite_src_mode_sub_slv0_t; + +#define LSM6DS3TR_C_CONFIG_PEDO_THS_MIN 0x0FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ths_min : 5; + uint8_t not_used_01 : 2; + uint8_t pedo_fs : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t pedo_fs : 1; + uint8_t not_used_01 : 2; + uint8_t ths_min : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_config_pedo_ths_min_t; + +#define LSM6DS3TR_C_SM_THS 0x13U +#define LSM6DS3TR_C_PEDO_DEB_REG 0x14U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t deb_step : 3; + uint8_t deb_time : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t deb_time : 5; + uint8_t deb_step : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_pedo_deb_reg_t; + +#define LSM6DS3TR_C_STEP_COUNT_DELTA 0x15U +#define LSM6DS3TR_C_MAG_SI_XX 0x24U +#define LSM6DS3TR_C_MAG_SI_XY 0x25U +#define LSM6DS3TR_C_MAG_SI_XZ 0x26U +#define LSM6DS3TR_C_MAG_SI_YX 0x27U +#define LSM6DS3TR_C_MAG_SI_YY 0x28U +#define LSM6DS3TR_C_MAG_SI_YZ 0x29U +#define LSM6DS3TR_C_MAG_SI_ZX 0x2AU +#define LSM6DS3TR_C_MAG_SI_ZY 0x2BU +#define LSM6DS3TR_C_MAG_SI_ZZ 0x2CU +#define LSM6DS3TR_C_MAG_OFFX_L 0x2DU +#define LSM6DS3TR_C_MAG_OFFX_H 0x2EU +#define LSM6DS3TR_C_MAG_OFFY_L 0x2FU +#define LSM6DS3TR_C_MAG_OFFY_H 0x30U +#define LSM6DS3TR_C_MAG_OFFZ_L 0x31U +#define LSM6DS3TR_C_MAG_OFFZ_H 0x32U +#define LSM6DS3TR_C_A_WRIST_TILT_LAT 0x50U +#define LSM6DS3TR_C_A_WRIST_TILT_THS 0x54U +#define LSM6DS3TR_C_A_WRIST_TILT_MASK 0x59U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_mask_zneg : 1; + uint8_t wrist_tilt_mask_zpos : 1; + uint8_t wrist_tilt_mask_yneg : 1; + uint8_t wrist_tilt_mask_ypos : 1; + uint8_t wrist_tilt_mask_xneg : 1; + uint8_t wrist_tilt_mask_xpos : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_mask_xpos : 1; + uint8_t wrist_tilt_mask_xneg : 1; + uint8_t wrist_tilt_mask_ypos : 1; + uint8_t wrist_tilt_mask_yneg : 1; + uint8_t wrist_tilt_mask_zpos : 1; + uint8_t wrist_tilt_mask_zneg : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_a_wrist_tilt_mask_t; + +/** + * @defgroup LSM6DS3TR_C_Register_Union + * @brief This union group all the registers having a bit-field + * description. + * This union is useful but it's not needed by the driver. + * + * REMOVING this union you are compliant with: + * MISRA-C 2012 [Rule 19.2] -> " Union are not allowed " + * + * @{ + * + */ +typedef union { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_wake_up_src_t wake_up_src; + lsm6ds3tr_c_tap_src_t tap_src; + lsm6ds3tr_c_d6d_src_t d6d_src; + lsm6ds3tr_c_status_reg_t status_reg; + lsm6ds3tr_c_sensorhub1_reg_t sensorhub1_reg; + lsm6ds3tr_c_sensorhub2_reg_t sensorhub2_reg; + lsm6ds3tr_c_sensorhub3_reg_t sensorhub3_reg; + lsm6ds3tr_c_sensorhub4_reg_t sensorhub4_reg; + lsm6ds3tr_c_sensorhub5_reg_t sensorhub5_reg; + lsm6ds3tr_c_sensorhub6_reg_t sensorhub6_reg; + lsm6ds3tr_c_sensorhub7_reg_t sensorhub7_reg; + lsm6ds3tr_c_sensorhub8_reg_t sensorhub8_reg; + lsm6ds3tr_c_sensorhub9_reg_t sensorhub9_reg; + lsm6ds3tr_c_sensorhub10_reg_t sensorhub10_reg; + lsm6ds3tr_c_sensorhub11_reg_t sensorhub11_reg; + lsm6ds3tr_c_sensorhub12_reg_t sensorhub12_reg; + lsm6ds3tr_c_fifo_status1_t fifo_status1; + lsm6ds3tr_c_fifo_status2_t fifo_status2; + lsm6ds3tr_c_fifo_status3_t fifo_status3; + lsm6ds3tr_c_fifo_status4_t fifo_status4; + lsm6ds3tr_c_sensorhub13_reg_t sensorhub13_reg; + lsm6ds3tr_c_sensorhub14_reg_t sensorhub14_reg; + lsm6ds3tr_c_sensorhub15_reg_t sensorhub15_reg; + lsm6ds3tr_c_sensorhub16_reg_t sensorhub16_reg; + lsm6ds3tr_c_sensorhub17_reg_t sensorhub17_reg; + lsm6ds3tr_c_sensorhub18_reg_t sensorhub18_reg; + lsm6ds3tr_c_func_src1_t func_src1; + lsm6ds3tr_c_func_src2_t func_src2; + lsm6ds3tr_c_wrist_tilt_ia_t wrist_tilt_ia; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + lsm6ds3tr_c_int_dur2_t int_dur2; + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + lsm6ds3tr_c_slv0_add_t slv0_add; + lsm6ds3tr_c_slv0_subadd_t slv0_subadd; + lsm6ds3tr_c_slave0_config_t slave0_config; + lsm6ds3tr_c_slv1_add_t slv1_add; + lsm6ds3tr_c_slv1_subadd_t slv1_subadd; + lsm6ds3tr_c_slave1_config_t slave1_config; + lsm6ds3tr_c_slv2_add_t slv2_add; + lsm6ds3tr_c_slv2_subadd_t slv2_subadd; + lsm6ds3tr_c_slave2_config_t slave2_config; + lsm6ds3tr_c_slv3_add_t slv3_add; + lsm6ds3tr_c_slv3_subadd_t slv3_subadd; + lsm6ds3tr_c_slave3_config_t slave3_config; + lsm6ds3tr_c_datawrite_src_mode_sub_slv0_t datawrite_src_mode_sub_slv0; + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + lsm6ds3tr_c_a_wrist_tilt_mask_t a_wrist_tilt_mask; + bitwise_t bitwise; + uint8_t byte; +} lsm6ds3tr_c_reg_t; + +/** + * @} + * + */ + +int32_t lsm6ds3tr_c_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len); +int32_t lsm6ds3tr_c_write_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len); + +float_t lsm6ds3tr_c_from_fs2g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs4g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs8g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs16g_to_mg(int16_t lsb); + +float_t lsm6ds3tr_c_from_fs125dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs250dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs500dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs1000dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs2000dps_to_mdps(int16_t lsb); + +float_t lsm6ds3tr_c_from_lsb_to_celsius(int16_t lsb); + +typedef enum { + LSM6DS3TR_C_2g = 0, + LSM6DS3TR_C_16g = 1, + LSM6DS3TR_C_4g = 2, + LSM6DS3TR_C_8g = 3, + LSM6DS3TR_C_XL_FS_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_fs_xl_t; +int32_t lsm6ds3tr_c_xl_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t val); +int32_t lsm6ds3tr_c_xl_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ODR_OFF = 0, + LSM6DS3TR_C_XL_ODR_12Hz5 = 1, + LSM6DS3TR_C_XL_ODR_26Hz = 2, + LSM6DS3TR_C_XL_ODR_52Hz = 3, + LSM6DS3TR_C_XL_ODR_104Hz = 4, + LSM6DS3TR_C_XL_ODR_208Hz = 5, + LSM6DS3TR_C_XL_ODR_416Hz = 6, + LSM6DS3TR_C_XL_ODR_833Hz = 7, + LSM6DS3TR_C_XL_ODR_1k66Hz = 8, + LSM6DS3TR_C_XL_ODR_3k33Hz = 9, + LSM6DS3TR_C_XL_ODR_6k66Hz = 10, + LSM6DS3TR_C_XL_ODR_1Hz6 = 11, + LSM6DS3TR_C_XL_ODR_ND = 12, /* ERROR CODE */ +} lsm6ds3tr_c_odr_xl_t; +int32_t lsm6ds3tr_c_xl_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t val); +int32_t lsm6ds3tr_c_xl_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t* val); + +typedef enum { + LSM6DS3TR_C_250dps = 0, + LSM6DS3TR_C_125dps = 1, + LSM6DS3TR_C_500dps = 2, + LSM6DS3TR_C_1000dps = 4, + LSM6DS3TR_C_2000dps = 6, + LSM6DS3TR_C_GY_FS_ND = 7, /* ERROR CODE */ +} lsm6ds3tr_c_fs_g_t; +int32_t lsm6ds3tr_c_gy_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t val); +int32_t lsm6ds3tr_c_gy_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t* val); + +typedef enum { + LSM6DS3TR_C_GY_ODR_OFF = 0, + LSM6DS3TR_C_GY_ODR_12Hz5 = 1, + LSM6DS3TR_C_GY_ODR_26Hz = 2, + LSM6DS3TR_C_GY_ODR_52Hz = 3, + LSM6DS3TR_C_GY_ODR_104Hz = 4, + LSM6DS3TR_C_GY_ODR_208Hz = 5, + LSM6DS3TR_C_GY_ODR_416Hz = 6, + LSM6DS3TR_C_GY_ODR_833Hz = 7, + LSM6DS3TR_C_GY_ODR_1k66Hz = 8, + LSM6DS3TR_C_GY_ODR_3k33Hz = 9, + LSM6DS3TR_C_GY_ODR_6k66Hz = 10, + LSM6DS3TR_C_GY_ODR_ND = 11, /* ERROR CODE */ +} lsm6ds3tr_c_odr_g_t; +int32_t lsm6ds3tr_c_gy_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t val); +int32_t lsm6ds3tr_c_gy_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t* val); + +int32_t lsm6ds3tr_c_block_data_update_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_block_data_update_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSb_1mg = 0, + LSM6DS3TR_C_LSb_16mg = 1, + LSM6DS3TR_C_WEIGHT_ND = 2, +} lsm6ds3tr_c_usr_off_w_t; +int32_t lsm6ds3tr_c_xl_offset_weight_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t val); +int32_t lsm6ds3tr_c_xl_offset_weight_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t* val); + +typedef enum { + LSM6DS3TR_C_XL_HIGH_PERFORMANCE = 0, + LSM6DS3TR_C_XL_NORMAL = 1, + LSM6DS3TR_C_XL_PW_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_xl_hm_mode_t; +int32_t lsm6ds3tr_c_xl_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t val); +int32_t lsm6ds3tr_c_xl_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t* val); + +typedef enum { + LSM6DS3TR_C_STAT_RND_DISABLE = 0, + LSM6DS3TR_C_STAT_RND_ENABLE = 1, + LSM6DS3TR_C_STAT_RND_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_rounding_status_t; +int32_t lsm6ds3tr_c_rounding_on_status_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t val); +int32_t lsm6ds3tr_c_rounding_on_status_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t* val); + +typedef enum { + LSM6DS3TR_C_GY_HIGH_PERFORMANCE = 0, + LSM6DS3TR_C_GY_NORMAL = 1, + LSM6DS3TR_C_GY_PW_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_g_hm_mode_t; +int32_t lsm6ds3tr_c_gy_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t val); +int32_t lsm6ds3tr_c_gy_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t* val); + +typedef struct { + lsm6ds3tr_c_wake_up_src_t wake_up_src; + lsm6ds3tr_c_tap_src_t tap_src; + lsm6ds3tr_c_d6d_src_t d6d_src; + lsm6ds3tr_c_status_reg_t status_reg; + lsm6ds3tr_c_func_src1_t func_src1; + lsm6ds3tr_c_func_src2_t func_src2; + lsm6ds3tr_c_wrist_tilt_ia_t wrist_tilt_ia; + lsm6ds3tr_c_a_wrist_tilt_mask_t a_wrist_tilt_mask; +} lsm6ds3tr_c_all_sources_t; +int32_t lsm6ds3tr_c_all_sources_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_all_sources_t* val); + +int32_t lsm6ds3tr_c_status_reg_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_status_reg_t* val); + +int32_t lsm6ds3tr_c_xl_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_gy_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_temp_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_xl_usr_offset_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_xl_usr_offset_get(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_timestamp_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_timestamp_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSB_6ms4 = 0, + LSM6DS3TR_C_LSB_25us = 1, + LSM6DS3TR_C_TS_RES_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_timer_hr_t; +int32_t lsm6ds3tr_c_timestamp_res_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t val); +int32_t lsm6ds3tr_c_timestamp_res_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t* val); + +typedef enum { + LSM6DS3TR_C_ROUND_DISABLE = 0, + LSM6DS3TR_C_ROUND_XL = 1, + LSM6DS3TR_C_ROUND_GY = 2, + LSM6DS3TR_C_ROUND_GY_XL = 3, + LSM6DS3TR_C_ROUND_SH1_TO_SH6 = 4, + LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6 = 5, + LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12 = 6, + LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6 = 7, + LSM6DS3TR_C_ROUND_OUT_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_rounding_t; +int32_t lsm6ds3tr_c_rounding_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t val); +int32_t lsm6ds3tr_c_rounding_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t* val); + +int32_t lsm6ds3tr_c_temperature_raw_get(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_angular_rate_raw_get(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_acceleration_raw_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_mag_calibrated_raw_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_fifo_raw_data_get(stmdev_ctx_t* ctx, uint8_t* buffer, uint8_t len); + +typedef enum { + LSM6DS3TR_C_USER_BANK = 0, + LSM6DS3TR_C_BANK_A = 4, + LSM6DS3TR_C_BANK_B = 5, + LSM6DS3TR_C_BANK_ND = 6, /* ERROR CODE */ +} lsm6ds3tr_c_func_cfg_en_t; +int32_t lsm6ds3tr_c_mem_bank_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t val); +int32_t lsm6ds3tr_c_mem_bank_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t* val); + +typedef enum { + LSM6DS3TR_C_DRDY_LATCHED = 0, + LSM6DS3TR_C_DRDY_PULSED = 1, + LSM6DS3TR_C_DRDY_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_drdy_pulsed_g_t; +int32_t lsm6ds3tr_c_data_ready_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t val); +int32_t lsm6ds3tr_c_data_ready_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t* val); + +int32_t lsm6ds3tr_c_device_id_get(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_reset_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_reset_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSB_AT_LOW_ADD = 0, + LSM6DS3TR_C_MSB_AT_LOW_ADD = 1, + LSM6DS3TR_C_DATA_FMT_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_ble_t; +int32_t lsm6ds3tr_c_data_format_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t val); +int32_t lsm6ds3tr_c_data_format_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t* val); + +int32_t lsm6ds3tr_c_auto_increment_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_auto_increment_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_boot_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_boot_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ST_DISABLE = 0, + LSM6DS3TR_C_XL_ST_POSITIVE = 1, + LSM6DS3TR_C_XL_ST_NEGATIVE = 2, + LSM6DS3TR_C_XL_ST_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_st_xl_t; +int32_t lsm6ds3tr_c_xl_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t val); +int32_t lsm6ds3tr_c_xl_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t* val); + +typedef enum { + LSM6DS3TR_C_GY_ST_DISABLE = 0, + LSM6DS3TR_C_GY_ST_POSITIVE = 1, + LSM6DS3TR_C_GY_ST_NEGATIVE = 3, + LSM6DS3TR_C_GY_ST_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_st_g_t; +int32_t lsm6ds3tr_c_gy_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t val); +int32_t lsm6ds3tr_c_gy_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t* val); + +int32_t lsm6ds3tr_c_filter_settling_mask_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_filter_settling_mask_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_USE_SLOPE = 0, + LSM6DS3TR_C_USE_HPF = 1, + LSM6DS3TR_C_HP_PATH_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_slope_fds_t; +int32_t lsm6ds3tr_c_xl_hp_path_internal_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t val); +int32_t lsm6ds3tr_c_xl_hp_path_internal_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ANA_BW_1k5Hz = 0, + LSM6DS3TR_C_XL_ANA_BW_400Hz = 1, + LSM6DS3TR_C_XL_ANA_BW_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_bw0_xl_t; +int32_t lsm6ds3tr_c_xl_filter_analog_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t val); +int32_t lsm6ds3tr_c_xl_filter_analog_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t* val); + +typedef enum { + LSM6DS3TR_C_XL_LP1_ODR_DIV_2 = 0, + LSM6DS3TR_C_XL_LP1_ODR_DIV_4 = 1, + LSM6DS3TR_C_XL_LP1_NA = 2, /* ERROR CODE */ +} lsm6ds3tr_c_lpf1_bw_sel_t; +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t val); +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t* val); + +typedef enum { + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50 = 0x00, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100 = 0x01, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9 = 0x02, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400 = 0x03, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50 = 0x10, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100 = 0x11, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9 = 0x12, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400 = 0x13, + LSM6DS3TR_C_XL_LP_NA = 0x20, /* ERROR CODE */ +} lsm6ds3tr_c_input_composite_t; +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t val); +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t* val); + +int32_t lsm6ds3tr_c_xl_reference_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_xl_reference_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_XL_HP_ODR_DIV_4 = 0x00, /* Slope filter */ + LSM6DS3TR_C_XL_HP_ODR_DIV_100 = 0x01, + LSM6DS3TR_C_XL_HP_ODR_DIV_9 = 0x02, + LSM6DS3TR_C_XL_HP_ODR_DIV_400 = 0x03, + LSM6DS3TR_C_XL_HP_NA = 0x10, /* ERROR CODE */ +} lsm6ds3tr_c_hpcf_xl_t; +int32_t lsm6ds3tr_c_xl_hp_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t val); +int32_t lsm6ds3tr_c_xl_hp_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t* val); + +typedef enum { + LSM6DS3TR_C_LP2_ONLY = 0x00, + + LSM6DS3TR_C_HP_16mHz_LP2 = 0x80, + LSM6DS3TR_C_HP_65mHz_LP2 = 0x90, + LSM6DS3TR_C_HP_260mHz_LP2 = 0xA0, + LSM6DS3TR_C_HP_1Hz04_LP2 = 0xB0, + + LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT = 0x0A, + LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL = 0x09, + LSM6DS3TR_C_HP_DISABLE_LP_STRONG = 0x08, + LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE = 0x0B, + + LSM6DS3TR_C_HP_16mHz_LP1_LIGHT = 0x8A, + LSM6DS3TR_C_HP_65mHz_LP1_NORMAL = 0x99, + LSM6DS3TR_C_HP_260mHz_LP1_STRONG = 0xA8, + LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE = 0xBB, + + LSM6DS3TR_C_HP_GY_BAND_NA = 0xFF, /* ERROR CODE */ +} lsm6ds3tr_c_lpf1_sel_g_t; +int32_t lsm6ds3tr_c_gy_band_pass_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t val); +int32_t lsm6ds3tr_c_gy_band_pass_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t* val); + +typedef enum { + LSM6DS3TR_C_SPI_4_WIRE = 0, + LSM6DS3TR_C_SPI_3_WIRE = 1, + LSM6DS3TR_C_SPI_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_sim_t; +int32_t lsm6ds3tr_c_spi_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t val); +int32_t lsm6ds3tr_c_spi_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t* val); + +typedef enum { + LSM6DS3TR_C_I2C_ENABLE = 0, + LSM6DS3TR_C_I2C_DISABLE = 1, + LSM6DS3TR_C_I2C_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_i2c_disable_t; +int32_t lsm6ds3tr_c_i2c_interface_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t val); +int32_t lsm6ds3tr_c_i2c_interface_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t* val); + +typedef struct { + uint8_t int1_drdy_xl : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_boot : 1; + uint8_t int1_fth : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_step_detector : 1; + uint8_t int1_timer : 1; + uint8_t int1_tilt : 1; + uint8_t int1_6d : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_ff : 1; + uint8_t int1_wu : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_inact_state : 1; + uint8_t den_drdy_int1 : 1; + uint8_t drdy_on_int1 : 1; +} lsm6ds3tr_c_int1_route_t; +int32_t lsm6ds3tr_c_pin_int1_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t val); +int32_t lsm6ds3tr_c_pin_int1_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t* val); + +typedef struct { + uint8_t int2_drdy_xl : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_fth : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_step_delta : 1; + uint8_t int2_iron : 1; + uint8_t int2_tilt : 1; + uint8_t int2_6d : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_ff : 1; + uint8_t int2_wu : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_inact_state : 1; + uint8_t int2_wrist_tilt : 1; +} lsm6ds3tr_c_int2_route_t; +int32_t lsm6ds3tr_c_pin_int2_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t val); +int32_t lsm6ds3tr_c_pin_int2_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t* val); + +typedef enum { + LSM6DS3TR_C_PUSH_PULL = 0, + LSM6DS3TR_C_OPEN_DRAIN = 1, + LSM6DS3TR_C_PIN_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pp_od_t; +int32_t lsm6ds3tr_c_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t val); +int32_t lsm6ds3tr_c_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t* val); + +typedef enum { + LSM6DS3TR_C_ACTIVE_HIGH = 0, + LSM6DS3TR_C_ACTIVE_LOW = 1, + LSM6DS3TR_C_POLARITY_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_h_lactive_t; +int32_t lsm6ds3tr_c_pin_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t val); +int32_t lsm6ds3tr_c_pin_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t* val); + +int32_t lsm6ds3tr_c_all_on_int1_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_all_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_INT_PULSED = 0, + LSM6DS3TR_C_INT_LATCHED = 1, + LSM6DS3TR_C_INT_MODE = 2, /* ERROR CODE */ +} lsm6ds3tr_c_lir_t; +int32_t lsm6ds3tr_c_int_notification_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t val); +int32_t lsm6ds3tr_c_int_notification_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t* val); + +int32_t lsm6ds3tr_c_wkup_threshold_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wkup_threshold_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_wkup_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wkup_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_gy_sleep_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_gy_sleep_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_PROPERTY_DISABLE = 0, + LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED = 1, + LSM6DS3TR_C_XL_12Hz5_GY_SLEEP = 2, + LSM6DS3TR_C_XL_12Hz5_GY_PD = 3, + LSM6DS3TR_C_ACT_MODE_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_inact_en_t; +int32_t lsm6ds3tr_c_act_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t val); +int32_t lsm6ds3tr_c_act_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t* val); + +int32_t lsm6ds3tr_c_act_sleep_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_act_sleep_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_tap_src_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_z_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_z_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_y_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_y_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_threshold_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_threshold_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_shock_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_shock_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_quiet_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_quiet_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_ONLY_SINGLE = 0, + LSM6DS3TR_C_BOTH_SINGLE_DOUBLE = 1, + LSM6DS3TR_C_TAP_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_single_double_tap_t; +int32_t lsm6ds3tr_c_tap_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t val); +int32_t lsm6ds3tr_c_tap_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t* val); + +typedef enum { + LSM6DS3TR_C_ODR_DIV_2_FEED = 0, + LSM6DS3TR_C_LPF2_FEED = 1, + LSM6DS3TR_C_6D_FEED_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_low_pass_on_6d_t; +int32_t lsm6ds3tr_c_6d_feed_data_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t val); +int32_t lsm6ds3tr_c_6d_feed_data_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t* val); + +typedef enum { + LSM6DS3TR_C_DEG_80 = 0, + LSM6DS3TR_C_DEG_70 = 1, + LSM6DS3TR_C_DEG_60 = 2, + LSM6DS3TR_C_DEG_50 = 3, + LSM6DS3TR_C_6D_TH_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_sixd_ths_t; +int32_t lsm6ds3tr_c_6d_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t val); +int32_t lsm6ds3tr_c_6d_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t* val); + +int32_t lsm6ds3tr_c_4d_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_4d_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_ff_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_ff_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_FF_TSH_156mg = 0, + LSM6DS3TR_C_FF_TSH_219mg = 1, + LSM6DS3TR_C_FF_TSH_250mg = 2, + LSM6DS3TR_C_FF_TSH_312mg = 3, + LSM6DS3TR_C_FF_TSH_344mg = 4, + LSM6DS3TR_C_FF_TSH_406mg = 5, + LSM6DS3TR_C_FF_TSH_469mg = 6, + LSM6DS3TR_C_FF_TSH_500mg = 7, + LSM6DS3TR_C_FF_TSH_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_ff_ths_t; +int32_t lsm6ds3tr_c_ff_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t val); +int32_t lsm6ds3tr_c_ff_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t* val); + +int32_t lsm6ds3tr_c_fifo_watermark_set(stmdev_ctx_t* ctx, uint16_t val); +int32_t lsm6ds3tr_c_fifo_watermark_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_data_level_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_wtm_flag_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_fifo_pattern_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_temp_batch_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_temp_batch_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_TRG_XL_GY_DRDY = 0, + LSM6DS3TR_C_TRG_STEP_DETECT = 1, + LSM6DS3TR_C_TRG_SH_DRDY = 2, + LSM6DS3TR_C_TRG_SH_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_trigger_fifo_t; +int32_t lsm6ds3tr_c_fifo_write_trigger_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t val); +int32_t lsm6ds3tr_c_fifo_write_trigger_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t* val); + +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_XL_DISABLE = 0, + LSM6DS3TR_C_FIFO_XL_NO_DEC = 1, + LSM6DS3TR_C_FIFO_XL_DEC_2 = 2, + LSM6DS3TR_C_FIFO_XL_DEC_3 = 3, + LSM6DS3TR_C_FIFO_XL_DEC_4 = 4, + LSM6DS3TR_C_FIFO_XL_DEC_8 = 5, + LSM6DS3TR_C_FIFO_XL_DEC_16 = 6, + LSM6DS3TR_C_FIFO_XL_DEC_32 = 7, + LSM6DS3TR_C_FIFO_XL_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_fifo_xl_t; +int32_t lsm6ds3tr_c_fifo_xl_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t val); +int32_t lsm6ds3tr_c_fifo_xl_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_GY_DISABLE = 0, + LSM6DS3TR_C_FIFO_GY_NO_DEC = 1, + LSM6DS3TR_C_FIFO_GY_DEC_2 = 2, + LSM6DS3TR_C_FIFO_GY_DEC_3 = 3, + LSM6DS3TR_C_FIFO_GY_DEC_4 = 4, + LSM6DS3TR_C_FIFO_GY_DEC_8 = 5, + LSM6DS3TR_C_FIFO_GY_DEC_16 = 6, + LSM6DS3TR_C_FIFO_GY_DEC_32 = 7, + LSM6DS3TR_C_FIFO_GY_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_fifo_gyro_t; +int32_t lsm6ds3tr_c_fifo_gy_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t val); +int32_t lsm6ds3tr_c_fifo_gy_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DS3_DISABLE = 0, + LSM6DS3TR_C_FIFO_DS3_NO_DEC = 1, + LSM6DS3TR_C_FIFO_DS3_DEC_2 = 2, + LSM6DS3TR_C_FIFO_DS3_DEC_3 = 3, + LSM6DS3TR_C_FIFO_DS3_DEC_4 = 4, + LSM6DS3TR_C_FIFO_DS3_DEC_8 = 5, + LSM6DS3TR_C_FIFO_DS3_DEC_16 = 6, + LSM6DS3TR_C_FIFO_DS3_DEC_32 = 7, + LSM6DS3TR_C_FIFO_DS3_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_ds3_fifo_t; +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t val); +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DS4_DISABLE = 0, + LSM6DS3TR_C_FIFO_DS4_NO_DEC = 1, + LSM6DS3TR_C_FIFO_DS4_DEC_2 = 2, + LSM6DS3TR_C_FIFO_DS4_DEC_3 = 3, + LSM6DS3TR_C_FIFO_DS4_DEC_4 = 4, + LSM6DS3TR_C_FIFO_DS4_DEC_8 = 5, + LSM6DS3TR_C_FIFO_DS4_DEC_16 = 6, + LSM6DS3TR_C_FIFO_DS4_DEC_32 = 7, + LSM6DS3TR_C_FIFO_DS4_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_ds4_fifo_t; +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t val); +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t* val); + +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_BYPASS_MODE = 0, + LSM6DS3TR_C_FIFO_MODE = 1, + LSM6DS3TR_C_STREAM_TO_FIFO_MODE = 3, + LSM6DS3TR_C_BYPASS_TO_STREAM_MODE = 4, + LSM6DS3TR_C_STREAM_MODE = 6, + LSM6DS3TR_C_FIFO_MODE_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_fifo_mode_t; +int32_t lsm6ds3tr_c_fifo_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t val); +int32_t lsm6ds3tr_c_fifo_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DISABLE = 0, + LSM6DS3TR_C_FIFO_12Hz5 = 1, + LSM6DS3TR_C_FIFO_26Hz = 2, + LSM6DS3TR_C_FIFO_52Hz = 3, + LSM6DS3TR_C_FIFO_104Hz = 4, + LSM6DS3TR_C_FIFO_208Hz = 5, + LSM6DS3TR_C_FIFO_416Hz = 6, + LSM6DS3TR_C_FIFO_833Hz = 7, + LSM6DS3TR_C_FIFO_1k66Hz = 8, + LSM6DS3TR_C_FIFO_3k33Hz = 9, + LSM6DS3TR_C_FIFO_6k66Hz = 10, + LSM6DS3TR_C_FIFO_RATE_ND = 11, /* ERROR CODE */ +} lsm6ds3tr_c_odr_fifo_t; +int32_t lsm6ds3tr_c_fifo_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t val); +int32_t lsm6ds3tr_c_fifo_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t* val); + +typedef enum { + LSM6DS3TR_C_DEN_ACT_LOW = 0, + LSM6DS3TR_C_DEN_ACT_HIGH = 1, + LSM6DS3TR_C_DEN_POL_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_den_lh_t; +int32_t lsm6ds3tr_c_den_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t val); +int32_t lsm6ds3tr_c_den_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t* val); + +typedef enum { + LSM6DS3TR_C_DEN_DISABLE = 0, + LSM6DS3TR_C_LEVEL_FIFO = 6, + LSM6DS3TR_C_LEVEL_LETCHED = 3, + LSM6DS3TR_C_LEVEL_TRIGGER = 2, + LSM6DS3TR_C_EDGE_TRIGGER = 4, + LSM6DS3TR_C_DEN_MODE_ND = 5, /* ERROR CODE */ +} lsm6ds3tr_c_den_mode_t; +int32_t lsm6ds3tr_c_den_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t val); +int32_t lsm6ds3tr_c_den_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t* val); + +typedef enum { + LSM6DS3TR_C_STAMP_IN_GY_DATA = 0, + LSM6DS3TR_C_STAMP_IN_XL_DATA = 1, + LSM6DS3TR_C_STAMP_IN_GY_XL_DATA = 2, + LSM6DS3TR_C_DEN_STAMP_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_den_xl_en_t; +int32_t lsm6ds3tr_c_den_enable_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t val); +int32_t lsm6ds3tr_c_den_enable_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_z_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_z_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_y_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_y_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_step_reset_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_step_reset_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_threshold_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_threshold_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_PEDO_AT_2g = 0, + LSM6DS3TR_C_PEDO_AT_4g = 1, + LSM6DS3TR_C_PEDO_FS_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pedo_fs_t; +int32_t lsm6ds3tr_c_pedo_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t val); +int32_t lsm6ds3tr_c_pedo_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t* val); + +int32_t lsm6ds3tr_c_pedo_debounce_steps_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_debounce_steps_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_timeout_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_timeout_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_steps_period_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_pedo_steps_period_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_motion_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_motion_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_motion_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_motion_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_wrist_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wrist_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tilt_latency_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_tilt_latency_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_tilt_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_src_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val); +int32_t lsm6ds3tr_c_tilt_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val); + +int32_t lsm6ds3tr_c_mag_soft_iron_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_mag_soft_iron_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_mag_hard_iron_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_mag_hard_iron_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_mag_soft_iron_mat_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_mag_soft_iron_mat_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_mag_offset_set(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_mag_offset_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_func_en_set(stmdev_ctx_t* ctx, uint8_t val); + +int32_t lsm6ds3tr_c_sh_sync_sens_frame_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_sync_sens_frame_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_RES_RATIO_2_11 = 0, + LSM6DS3TR_C_RES_RATIO_2_12 = 1, + LSM6DS3TR_C_RES_RATIO_2_13 = 2, + LSM6DS3TR_C_RES_RATIO_2_14 = 3, + LSM6DS3TR_C_RES_RATIO_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_rr_t; +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t val); +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t* val); + +int32_t lsm6ds3tr_c_sh_master_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_master_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_sh_pass_through_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_pass_through_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_EXT_PULL_UP = 0, + LSM6DS3TR_C_INTERNAL_PULL_UP = 1, + LSM6DS3TR_C_SH_PIN_MODE = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pull_up_en_t; +int32_t lsm6ds3tr_c_sh_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t val); +int32_t lsm6ds3tr_c_sh_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t* val); + +typedef enum { + LSM6DS3TR_C_XL_GY_DRDY = 0, + LSM6DS3TR_C_EXT_ON_INT2_PIN = 1, + LSM6DS3TR_C_SH_SYNCRO_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_start_config_t; +int32_t lsm6ds3tr_c_sh_syncro_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t val); +int32_t lsm6ds3tr_c_sh_syncro_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t* val); + +int32_t lsm6ds3tr_c_sh_drdy_on_int1_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_drdy_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef struct { + lsm6ds3tr_c_sensorhub1_reg_t sh_byte_1; + lsm6ds3tr_c_sensorhub2_reg_t sh_byte_2; + lsm6ds3tr_c_sensorhub3_reg_t sh_byte_3; + lsm6ds3tr_c_sensorhub4_reg_t sh_byte_4; + lsm6ds3tr_c_sensorhub5_reg_t sh_byte_5; + lsm6ds3tr_c_sensorhub6_reg_t sh_byte_6; + lsm6ds3tr_c_sensorhub7_reg_t sh_byte_7; + lsm6ds3tr_c_sensorhub8_reg_t sh_byte_8; + lsm6ds3tr_c_sensorhub9_reg_t sh_byte_9; + lsm6ds3tr_c_sensorhub10_reg_t sh_byte_10; + lsm6ds3tr_c_sensorhub11_reg_t sh_byte_11; + lsm6ds3tr_c_sensorhub12_reg_t sh_byte_12; + lsm6ds3tr_c_sensorhub13_reg_t sh_byte_13; + lsm6ds3tr_c_sensorhub14_reg_t sh_byte_14; + lsm6ds3tr_c_sensorhub15_reg_t sh_byte_15; + lsm6ds3tr_c_sensorhub16_reg_t sh_byte_16; + lsm6ds3tr_c_sensorhub17_reg_t sh_byte_17; + lsm6ds3tr_c_sensorhub18_reg_t sh_byte_18; +} lsm6ds3tr_c_emb_sh_read_t; +int32_t lsm6ds3tr_c_sh_read_data_raw_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_emb_sh_read_t* val); + +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_sh_spi_sync_error_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_spi_sync_error_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_SLV_0 = 0, + LSM6DS3TR_C_SLV_0_1 = 1, + LSM6DS3TR_C_SLV_0_1_2 = 2, + LSM6DS3TR_C_SLV_0_1_2_3 = 3, + LSM6DS3TR_C_SLV_EN_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_aux_sens_on_t; +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t val); +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t* val); + +typedef struct { + uint8_t slv0_add; + uint8_t slv0_subadd; + uint8_t slv0_data; +} lsm6ds3tr_c_sh_cfg_write_t; +int32_t lsm6ds3tr_c_sh_cfg_write(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_write_t* val); + +typedef struct { + uint8_t slv_add; + uint8_t slv_subadd; + uint8_t slv_len; +} lsm6ds3tr_c_sh_cfg_read_t; +int32_t lsm6ds3tr_c_sh_slv0_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv1_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv2_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv3_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); + +typedef enum { + LSM6DS3TR_C_SL0_NO_DEC = 0, + LSM6DS3TR_C_SL0_DEC_2 = 1, + LSM6DS3TR_C_SL0_DEC_4 = 2, + LSM6DS3TR_C_SL0_DEC_8 = 3, + LSM6DS3TR_C_SL0_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave0_rate_t; +int32_t lsm6ds3tr_c_sh_slave_0_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_0_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t* val); + +typedef enum { + LSM6DS3TR_C_EACH_SH_CYCLE = 0, + LSM6DS3TR_C_ONLY_FIRST_CYCLE = 1, + LSM6DS3TR_C_SH_WR_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_write_once_t; +int32_t lsm6ds3tr_c_sh_write_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t val); +int32_t lsm6ds3tr_c_sh_write_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t* val); + +typedef enum { + LSM6DS3TR_C_SL1_NO_DEC = 0, + LSM6DS3TR_C_SL1_DEC_2 = 1, + LSM6DS3TR_C_SL1_DEC_4 = 2, + LSM6DS3TR_C_SL1_DEC_8 = 3, + LSM6DS3TR_C_SL1_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave1_rate_t; +int32_t lsm6ds3tr_c_sh_slave_1_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_1_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t* val); + +typedef enum { + LSM6DS3TR_C_SL2_NO_DEC = 0, + LSM6DS3TR_C_SL2_DEC_2 = 1, + LSM6DS3TR_C_SL2_DEC_4 = 2, + LSM6DS3TR_C_SL2_DEC_8 = 3, + LSM6DS3TR_C_SL2_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave2_rate_t; +int32_t lsm6ds3tr_c_sh_slave_2_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_2_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t* val); + +typedef enum { + LSM6DS3TR_C_SL3_NO_DEC = 0, + LSM6DS3TR_C_SL3_DEC_2 = 1, + LSM6DS3TR_C_SL3_DEC_4 = 2, + LSM6DS3TR_C_SL3_DEC_8 = 3, + LSM6DS3TR_C_SL3_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave3_rate_t; +int32_t lsm6ds3tr_c_sh_slave_3_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_3_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t* val); + +/** + * @} + * + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LSM6DS3TR_C_DRIVER_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/applications/plugins/airmouse/tracking/main_loop.cc b/applications/plugins/airmouse/tracking/main_loop.cc new file mode 100644 index 000000000..492157a4d --- /dev/null +++ b/applications/plugins/airmouse/tracking/main_loop.cc @@ -0,0 +1,189 @@ +#include "main_loop.h" + +#include +#include + +#include "imu/imu.h" +#include "orientation_tracker.h" +#include "calibration_data.h" + +#define TAG "tracker" + +static const float CURSOR_SPEED = 1024.0 / (M_PI / 4); +static const float STABILIZE_BIAS = 16.0; + +float g_yaw = 0; +float g_pitch = 0; +float g_dYaw = 0; +float g_dPitch = 0; +bool firstRead = true; +bool stabilize = true; +CalibrationData calibration; +cardboard::OrientationTracker tracker(10000000l); // 10 ms / 100 Hz +uint64_t ippms, ippms2; + +static inline float clamp(float val) +{ + while (val <= -M_PI) { + val += 2 * M_PI; + } + while (val >= M_PI) { + val -= 2 * M_PI; + } + return val; +} + +static inline float highpass(float oldVal, float newVal) +{ + if (!stabilize) { + return newVal; + } + float delta = clamp(oldVal - newVal); + float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0)); + return newVal + alpha * delta; +} + +void sendCurrentState(MouseMoveCallback mouse_move) +{ + float dX = g_dYaw * CURSOR_SPEED; + float dY = g_dPitch * CURSOR_SPEED; + + // Scale the shift down to fit the protocol. + if (dX > 127) { + dY *= 127.0 / dX; + dX = 127; + } + if (dX < -127) { + dY *= -127.0 / dX; + dX = -127; + } + if (dY > 127) { + dX *= 127.0 / dY; + dY = 127; + } + if (dY < -127) { + dX *= -127.0 / dY; + dY = -127; + } + + const int8_t x = (int8_t)std::floor(dX + 0.5); + const int8_t y = (int8_t)std::floor(dY + 0.5); + + mouse_move(x, y); + + // Only subtract the part of the error that was already sent. + if (x != 0) { + g_dYaw -= x / CURSOR_SPEED; + } + if (y != 0) { + g_dPitch -= y / CURSOR_SPEED; + } +} + +void onOrientation(cardboard::Vector4& quaternion) +{ + float q1 = quaternion[0]; // X * sin(T/2) + float q2 = quaternion[1]; // Y * sin(T/2) + float q3 = quaternion[2]; // Z * sin(T/2) + float q0 = quaternion[3]; // cos(T/2) + + float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3))); + float pitch = std::asin(2 * (q0 * q1 + q2 * q3)); + // float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2))); + + if (yaw == NAN || pitch == NAN) { + // NaN case, skip it + return; + } + + if (firstRead) { + g_yaw = yaw; + g_pitch = pitch; + firstRead = false; + } else { + const float newYaw = highpass(g_yaw, yaw); + const float newPitch = highpass(g_pitch, pitch); + + float dYaw = clamp(g_yaw - newYaw); + float dPitch = g_pitch - newPitch; + g_yaw = newYaw; + g_pitch = newPitch; + + // Accumulate the error locally. + g_dYaw += dYaw; + g_dPitch += dPitch; + } +} + +extern "C" { + +void calibration_begin() { + calibration.reset(); + FURI_LOG_I(TAG, "Calibrating"); +} + +bool calibration_step() { + if (calibration.isComplete()) + return true; + + double vec[6]; + if (imu_read(vec) & GYR_DATA_READY) { + cardboard::Vector3 data(vec[3], vec[4], vec[5]); + furi_delay_ms(9); // Artificially limit to ~100Hz + return calibration.add(data); + } + + return false; +} + +void calibration_end() { + CalibrationMedian store; + cardboard::Vector3 median = calibration.getMedian(); + store.x = median[0]; + store.y = median[1]; + store.z = median[2]; + CALIBRATION_DATA_SAVE(&store); +} + +void tracking_begin() { + CalibrationMedian store; + cardboard::Vector3 median = calibration.getMedian(); + if (CALIBRATION_DATA_LOAD(&store)) { + median[0] = store.x; + median[1] = store.y; + median[2] = store.z; + } + + ippms = furi_hal_cortex_instructions_per_microsecond(); + ippms2 = ippms / 2; + tracker.SetCalibration(median); + tracker.Resume(); +} + +void tracking_step(MouseMoveCallback mouse_move) { + double vec[6]; + int ret = imu_read(vec); + if (ret != 0) { + uint64_t t = (DWT->CYCCNT * 1000llu + ippms2) / ippms; + if (ret & ACC_DATA_READY) { + cardboard::AccelerometerData adata + = { .system_timestamp = t, .sensor_timestamp_ns = t, + .data = cardboard::Vector3(vec[0], vec[1], vec[2]) }; + tracker.OnAccelerometerData(adata); + } + if (ret & GYR_DATA_READY) { + cardboard::GyroscopeData gdata + = { .system_timestamp = t, .sensor_timestamp_ns = t, + .data = cardboard::Vector3(vec[3], vec[4], vec[5]) }; + cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata); + onOrientation(pose); + sendCurrentState(mouse_move); + } + } +} + +void tracking_end() { + tracker.Pause(); +} + +} diff --git a/applications/plugins/airmouse/tracking/main_loop.h b/applications/plugins/airmouse/tracking/main_loop.h new file mode 100644 index 000000000..ce0d96568 --- /dev/null +++ b/applications/plugins/airmouse/tracking/main_loop.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bool (*MouseMoveCallback)(int8_t x, int8_t y); + +void calibration_begin(); +bool calibration_step(); +void calibration_end(); + +void tracking_begin(); +void tracking_step(MouseMoveCallback mouse_move); +void tracking_end(); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/airmouse/tracking/orientation_tracker.cc b/applications/plugins/airmouse/tracking/orientation_tracker.cc new file mode 100644 index 000000000..ac20e9672 --- /dev/null +++ b/applications/plugins/airmouse/tracking/orientation_tracker.cc @@ -0,0 +1,95 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "orientation_tracker.h" + +#include "sensors/pose_prediction.h" +#include "util/logging.h" +#include "util/vector.h" +#include "util/vectorutils.h" + +namespace cardboard { + +OrientationTracker::OrientationTracker(const long sampling_period_ns) + : sampling_period_ns_(sampling_period_ns) + , calibration_(Vector3::Zero()) + , is_tracking_(false) + , sensor_fusion_(new SensorFusionEkf()) + , latest_gyroscope_data_({ 0, 0, Vector3::Zero() }) +{ + sensor_fusion_->SetBiasEstimationEnabled(/*kGyroBiasEstimationEnabled*/ true); +} + +void OrientationTracker::SetCalibration(const Vector3& calibration) { + calibration_ = calibration; +} + +void OrientationTracker::Pause() +{ + if (!is_tracking_) { + return; + } + + // Create a gyro event with zero velocity. This effectively stops the prediction. + GyroscopeData event = latest_gyroscope_data_; + event.data = Vector3::Zero(); + + OnGyroscopeData(event); + + is_tracking_ = false; +} + +void OrientationTracker::Resume() { is_tracking_ = true; } + +Vector4 OrientationTracker::GetPose(int64_t timestamp_ns) const +{ + Rotation predicted_rotation; + const PoseState pose_state = sensor_fusion_->GetLatestPoseState(); + if (sensor_fusion_->IsFullyInitialized()) { + predicted_rotation = pose_state.sensor_from_start_rotation; + } else { + CARDBOARD_LOGI("Tracker not fully initialized yet. Using pose prediction only."); + predicted_rotation = pose_prediction::PredictPose(timestamp_ns, pose_state); + } + + return (-predicted_rotation).GetQuaternion(); +} + +void OrientationTracker::OnAccelerometerData(const AccelerometerData& event) +{ + if (!is_tracking_) { + return; + } + sensor_fusion_->ProcessAccelerometerSample(event); +} + +Vector4 OrientationTracker::OnGyroscopeData(const GyroscopeData& event) +{ + if (!is_tracking_) { + return Vector4(); + } + + const GyroscopeData data = { .system_timestamp = event.system_timestamp, + .sensor_timestamp_ns = event.sensor_timestamp_ns, + .data = event.data - calibration_ }; + + latest_gyroscope_data_ = data; + + sensor_fusion_->ProcessGyroscopeSample(data); + + return OrientationTracker::GetPose(data.sensor_timestamp_ns + sampling_period_ns_); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/orientation_tracker.h b/applications/plugins/airmouse/tracking/orientation_tracker.h new file mode 100644 index 000000000..fb49c9859 --- /dev/null +++ b/applications/plugins/airmouse/tracking/orientation_tracker.h @@ -0,0 +1,68 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include // NOLINT + +#include "sensors/accelerometer_data.h" +#include "sensors/gyroscope_data.h" +#include "sensors/sensor_fusion_ekf.h" +#include "util/rotation.h" + +namespace cardboard { + +// OrientationTracker encapsulates pose tracking by connecting sensors +// to SensorFusion. +// This pose tracker reports poses in display space. +class OrientationTracker { +public: + OrientationTracker(const long sampling_period_ns); + + void SetCalibration(const Vector3& calibration); + + // Pauses tracking and sensors. + void Pause(); + + // Resumes tracking ans sensors. + void Resume(); + + // Gets the predicted pose for a given timestamp. + Vector4 GetPose(int64_t timestamp_ns) const; + + // Function called when receiving AccelerometerData. + // + // @param event sensor event. + void OnAccelerometerData(const AccelerometerData& event); + + // Function called when receiving GyroscopeData. + // + // @param event sensor event. + Vector4 OnGyroscopeData(const GyroscopeData& event); + +private: + long sampling_period_ns_; + Vector3 calibration_; + + std::atomic is_tracking_; + // Sensor Fusion object that stores the internal state of the filter. + std::unique_ptr sensor_fusion_; + // Latest gyroscope data. + GyroscopeData latest_gyroscope_data_; +}; + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/accelerometer_data.h b/applications/plugins/airmouse/tracking/sensors/accelerometer_data.h new file mode 100644 index 000000000..bdf3289af --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/accelerometer_data.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ +#define CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ + +#include "../util/vector.h" + +namespace cardboard { + +struct AccelerometerData { + // System wall time. + uint64_t system_timestamp; + + // Sensor clock time in nanoseconds. + uint64_t sensor_timestamp_ns; + + // Acceleration force along the x,y,z axes in m/s^2. This follows android + // specification + // (https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords). + Vector3 data; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc b/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc new file mode 100644 index 000000000..96f2f7346 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc @@ -0,0 +1,313 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "gyroscope_bias_estimator.h" + +#include +#include // NOLINT + +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace { + +// Cutoff frequencies in Hertz applied to our various signals, and their +// corresponding filters. +const float kAccelerometerLowPassCutOffFrequencyHz = 1.0f; +const float kRotationVelocityBasedAccelerometerLowPassCutOffFrequencyHz = 0.15f; +const float kGyroscopeLowPassCutOffFrequencyHz = 1.0f; +const float kGyroscopeBiasLowPassCutOffFrequencyHz = 0.15f; + +// Note that MEMS IMU are not that precise. +const double kEpsilon = 1.0e-8; + +// Size of the filtering window for the mean and median filter. The larger the +// windows the larger the filter delay. +const int kFilterWindowSize = 5; + +// Threshold used to compare rotation computed from the accelerometer and the +// gyroscope bias. +const double kRatioBetweenGyroBiasAndAccel = 1.5; + +// The minimum sum of weights we need to acquire before returning a bias +// estimation. +const float kMinSumOfWeightsGyroBiasThreshold = 25.0f; + +// Amount of change in m/s^3 we allow on the smoothed accelerometer values to +// consider the phone static. +const double kAccelerometerDeltaStaticThreshold = 0.5; + +// Amount of change in radians/s^2 we allow on the smoothed gyroscope values to +// consider the phone static. +const double kGyroscopeDeltaStaticThreshold = 0.03; + +// If the gyroscope value is above this threshold, don't update the gyroscope +// bias estimation. This threshold is applied to the magnitude of gyroscope +// vectors in radians/s. +const float kGyroscopeForBiasThreshold = 0.30f; + +// Used to monitor if accelerometer and gyroscope have been static for a few +// frames. +const int kStaticFrameDetectionThreshold = 50; + +// Minimum time step between sensor updates. +const double kMinTimestep = 1; // std::chrono::nanoseconds(1); +} // namespace + +namespace cardboard { + +// A helper class to keep track of whether some signal can be considered static +// over specified number of frames. +class GyroscopeBiasEstimator::IsStaticCounter { +public: + // Initializes a counter with the number of consecutive frames we require + // the signal to be static before IsRecentlyStatic returns true. + // + // @param min_static_frames_threshold number of consecutive frames we + // require the signal to be static before IsRecentlyStatic returns true. + explicit IsStaticCounter(int min_static_frames_threshold) + : min_static_frames_threshold_(min_static_frames_threshold) + , consecutive_static_frames_(0) + { + } + + // Specifies whether the current frame is considered static. + // + // @param is_static static flag for current frame. + void AppendFrame(bool is_static) + { + if (is_static) { + ++consecutive_static_frames_; + } else { + consecutive_static_frames_ = 0; + } + } + + // Returns if static movement is assumed. + bool IsRecentlyStatic() const + { + return consecutive_static_frames_ >= min_static_frames_threshold_; + } + // Resets counter. + void Reset() { consecutive_static_frames_ = 0; } + +private: + const int min_static_frames_threshold_; + int consecutive_static_frames_; +}; + +GyroscopeBiasEstimator::GyroscopeBiasEstimator() + : accelerometer_lowpass_filter_(kAccelerometerLowPassCutOffFrequencyHz) + , simulated_gyroscope_from_accelerometer_lowpass_filter_( + kRotationVelocityBasedAccelerometerLowPassCutOffFrequencyHz) + , gyroscope_lowpass_filter_(kGyroscopeLowPassCutOffFrequencyHz) + , gyroscope_bias_lowpass_filter_(kGyroscopeBiasLowPassCutOffFrequencyHz) + , accelerometer_static_counter_(new IsStaticCounter(kStaticFrameDetectionThreshold)) + , gyroscope_static_counter_(new IsStaticCounter(kStaticFrameDetectionThreshold)) + , current_accumulated_weights_gyroscope_bias_(0.f) + , mean_filter_(kFilterWindowSize) + , median_filter_(kFilterWindowSize) + , last_mean_filtered_accelerometer_value_({ 0, 0, 0 }) +{ + Reset(); +} + +GyroscopeBiasEstimator::~GyroscopeBiasEstimator() { } + +void GyroscopeBiasEstimator::Reset() +{ + accelerometer_lowpass_filter_.Reset(); + gyroscope_lowpass_filter_.Reset(); + gyroscope_bias_lowpass_filter_.Reset(); + accelerometer_static_counter_->Reset(); + gyroscope_static_counter_->Reset(); +} + +void GyroscopeBiasEstimator::ProcessGyroscope( + const Vector3& gyroscope_sample, uint64_t timestamp_ns) +{ + // Update gyroscope and gyroscope delta low-pass filters. + gyroscope_lowpass_filter_.AddSample(gyroscope_sample, timestamp_ns); + + const auto smoothed_gyroscope_delta + = gyroscope_sample - gyroscope_lowpass_filter_.GetFilteredData(); + + gyroscope_static_counter_->AppendFrame( + Length(smoothed_gyroscope_delta) < kGyroscopeDeltaStaticThreshold); + + // Only update the bias if the gyroscope and accelerometer signals have been + // relatively static recently. + if (gyroscope_static_counter_->IsRecentlyStatic() + && accelerometer_static_counter_->IsRecentlyStatic()) { + // Reset static counter when updating the bias fails. + if (!UpdateGyroscopeBias(gyroscope_sample, timestamp_ns)) { + // Bias update fails because of large motion, thus reset the static + // counter. + gyroscope_static_counter_->AppendFrame(false); + } + } else { + // Reset weights, if not static. + current_accumulated_weights_gyroscope_bias_ = 0; + } +} + +void GyroscopeBiasEstimator::ProcessAccelerometer( + const Vector3& accelerometer_sample, uint64_t timestamp_ns) +{ + // Get current state of the filter. + const uint64_t previous_accel_timestamp_ns + = accelerometer_lowpass_filter_.GetMostRecentTimestampNs(); + const bool is_low_pass_filter_init = accelerometer_lowpass_filter_.IsInitialized(); + + // Update accel and accel delta low-pass filters. + accelerometer_lowpass_filter_.AddSample(accelerometer_sample, timestamp_ns); + + const auto smoothed_accelerometer_delta + = accelerometer_sample - accelerometer_lowpass_filter_.GetFilteredData(); + + accelerometer_static_counter_->AppendFrame( + Length(smoothed_accelerometer_delta) < kAccelerometerDeltaStaticThreshold); + + // Rotation from accel cannot be differentiated with only one sample. + if (!is_low_pass_filter_init) { + simulated_gyroscope_from_accelerometer_lowpass_filter_.AddSample({ 0, 0, 0 }, timestamp_ns); + return; + } + + // No need to update the simulated gyroscope at this point because the motion + // is too large. + if (!accelerometer_static_counter_->IsRecentlyStatic()) { + return; + } + + median_filter_.AddSample(accelerometer_lowpass_filter_.GetFilteredData()); + + // This processing can only be started if the buffer is fully initialized. + if (!median_filter_.IsValid()) { + mean_filter_.AddSample(accelerometer_lowpass_filter_.GetFilteredData()); + + // Update the last filtered accelerometer value. + last_mean_filtered_accelerometer_value_ = accelerometer_lowpass_filter_.GetFilteredData(); + return; + } + + mean_filter_.AddSample(median_filter_.GetFilteredData()); + + // Compute a mock gyroscope value from accelerometer. + const int64_t diff = timestamp_ns - previous_accel_timestamp_ns; + const double timestep = static_cast(diff); + + simulated_gyroscope_from_accelerometer_lowpass_filter_.AddSample( + ComputeAngularVelocityFromLatestAccelerometer(timestep), timestamp_ns); + last_mean_filtered_accelerometer_value_ = mean_filter_.GetFilteredData(); +} + +Vector3 GyroscopeBiasEstimator::ComputeAngularVelocityFromLatestAccelerometer(double timestep) const +{ + if (timestep < kMinTimestep) { + return { 0, 0, 0 }; + } + + const auto mean_of_median = mean_filter_.GetFilteredData(); + + // Compute an incremental rotation between the last state and the current + // state. + // + // Note that we switch to double precision here because of precision problem + // with small rotation. + const auto incremental_rotation = Rotation::RotateInto( + Vector3(last_mean_filtered_accelerometer_value_[0], + last_mean_filtered_accelerometer_value_[1], last_mean_filtered_accelerometer_value_[2]), + Vector3(mean_of_median[0], mean_of_median[1], mean_of_median[2])); + + // We use axis angle here because this is how gyroscope values are stored. + Vector3 incremental_rotation_axis; + double incremental_rotation_angle; + incremental_rotation.GetAxisAndAngle(&incremental_rotation_axis, &incremental_rotation_angle); + + incremental_rotation_axis *= incremental_rotation_angle / timestep; + + return { static_cast(incremental_rotation_axis[0]), + static_cast(incremental_rotation_axis[1]), + static_cast(incremental_rotation_axis[2]) }; +} + +bool GyroscopeBiasEstimator::UpdateGyroscopeBias( + const Vector3& gyroscope_sample, uint64_t timestamp_ns) +{ + // Gyroscope values that are too big are potentially dangerous as they could + // originate from slow and steady head rotations. + // + // Therefore we compute an update weight which: + // * favors gyroscope values that are closer to 0 + // * is set to zero if gyroscope values are greater than a threshold. + // + // This way, the gyroscope bias estimation converges faster if the phone is + // flat on a table, as opposed to held up somewhat stationary in the user's + // hands. + + // If magnitude is too big, don't update the filter at all so that we don't + // artificially increase the number of samples accumulated by the filter. + const float gyroscope_sample_norm2 = Length(gyroscope_sample); + if (gyroscope_sample_norm2 >= kGyroscopeForBiasThreshold) { + return false; + } + + float update_weight + = std::max(0.0f, 1 - gyroscope_sample_norm2 / kGyroscopeForBiasThreshold); + update_weight *= update_weight; + gyroscope_bias_lowpass_filter_.AddWeightedSample( + gyroscope_lowpass_filter_.GetFilteredData(), timestamp_ns, update_weight); + + // This counter is only partially valid as the low pass filter drops large + // samples. + current_accumulated_weights_gyroscope_bias_ += update_weight; + + return true; +} + +Vector3 GyroscopeBiasEstimator::GetGyroscopeBias() const +{ + return gyroscope_bias_lowpass_filter_.GetFilteredData(); +} + +bool GyroscopeBiasEstimator::IsCurrentEstimateValid() const +{ + // Remove any bias component along the gravity because they cannot be + // evaluated from accelerometer. + const auto current_gravity_dir = Normalized(last_mean_filtered_accelerometer_value_); + const auto gyro_bias_lowpass = gyroscope_bias_lowpass_filter_.GetFilteredData(); + + const auto off_gravity_gyro_bias + = gyro_bias_lowpass - current_gravity_dir * Dot(gyro_bias_lowpass, current_gravity_dir); + + // Checks that the current bias estimate is not correlated with the + // rotation computed from accelerometer. + const auto gyro_from_accel + = simulated_gyroscope_from_accelerometer_lowpass_filter_.GetFilteredData(); + const bool isGyroscopeBiasCorrelatedWithSimulatedGyro + = (Length(gyro_from_accel) * kRatioBetweenGyroBiasAndAccel + > (Length(off_gravity_gyro_bias) + kEpsilon)); + const bool hasEnoughSamples + = current_accumulated_weights_gyroscope_bias_ > kMinSumOfWeightsGyroBiasThreshold; + const bool areCountersStatic = gyroscope_static_counter_->IsRecentlyStatic() + && accelerometer_static_counter_->IsRecentlyStatic(); + + const bool isStatic + = hasEnoughSamples && areCountersStatic && !isGyroscopeBiasCorrelatedWithSimulatedGyro; + return isStatic; +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h b/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h new file mode 100644 index 000000000..1a46f96be --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h @@ -0,0 +1,134 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ +#define CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ + +#include // NOLINT +#include +#include +#include +#include + +#include "lowpass_filter.h" +#include "mean_filter.h" +#include "median_filter.h" +#include "../util/vector.h" + +namespace cardboard { + +// Class that attempts to estimate the gyroscope's bias. +// Its main idea is that it averages the gyroscope values when the phone is +// considered stationary. +// Usage: A client should call the ProcessGyroscope and ProcessAccelerometer +// methods for every accelerometer and gyroscope sensor sample. This class +// expects these calls to be frequent, i.e., at least at 10 Hz. The client can +// then call GetGyroBias to retrieve the current estimate of the gyroscope bias. +// For best results, the fastest available delay option should be used when +// registering to sensors. Note that this class is not thread-safe. +// +// The filtering applied to the accelerometer to estimate a rotation +// from it follows : +// Baptiste Delporte, Laurent Perroton, Thierry Grandpierre, Jacques Trichet. +// Accelerometer and Magnetometer Based Gyroscope Emulation on Smart Sensor +// for a Virtual Reality Application. Sensor and Transducers Journal, 2012. +// +// which is a combination of a IIR filter, a median and a mean filter. +class GyroscopeBiasEstimator { +public: + GyroscopeBiasEstimator(); + virtual ~GyroscopeBiasEstimator(); + + // Updates the estimator with a gyroscope event. + // + // @param gyroscope_sample the angular speed around the x, y, z axis in + // radians/sec. + // @param timestamp_ns the nanosecond at which the event occurred. Only + // guaranteed to be comparable with timestamps from other PocessGyroscope + // invocations. + virtual void ProcessGyroscope(const Vector3& gyroscope_sample, uint64_t timestamp_ns); + + // Processes accelerometer samples to estimate if device is + // stable or not. + // + // First we filter the accelerometer. This is done with 3 filters. + // - A IIR low-pass filter + // - A median filter + // - A mean filter. + // Then a rotation is computed between consecutive filtered accelerometer + // samples. + // Finally this is converted to a velocity to emulate a gyroscope. + // + // @param accelerometer_sample the acceleration (including gravity) on the x, + // y, z axis in meters/s^2. + // @param timestamp_ns the nanosecond at which the event occurred. Only + // guaranteed to be comparable with timestamps from other + // ProcessAccelerometer invocations. + virtual void ProcessAccelerometer(const Vector3& accelerometer_sample, uint64_t timestamp_ns); + + // Returns the estimated gyroscope bias. + // + // @return Estimated gyroscope bias. A vector with zeros is returned if no + // estimate has been computed. + virtual Vector3 GetGyroscopeBias() const; + + // Resets the estimator state. + void Reset(); + + // Returns true if the current estimate returned by GetGyroscopeBias is + // correct. The device (measured using the sensors) has to be static for this + // function to return true. + virtual bool IsCurrentEstimateValid() const; + +private: + // A helper class to keep track of whether some signal can be considered + // static over specified number of frames. + class IsStaticCounter; + + // Updates gyroscope bias estimation. + // + // @return false if the current sample is too large. + bool UpdateGyroscopeBias(const Vector3& gyroscope_sample, uint64_t timestamp_ns); + + // Returns device angular velocity (rad/s) from the latest accelerometer data. + // + // @param timestep in seconds between the last two samples. + // @return rotation velocity from latest accelerometer. This can be + // interpreted as an gyroscope. + Vector3 ComputeAngularVelocityFromLatestAccelerometer(double timestep) const; + + LowpassFilter accelerometer_lowpass_filter_; + LowpassFilter simulated_gyroscope_from_accelerometer_lowpass_filter_; + LowpassFilter gyroscope_lowpass_filter_; + LowpassFilter gyroscope_bias_lowpass_filter_; + + std::unique_ptr accelerometer_static_counter_; + std::unique_ptr gyroscope_static_counter_; + + // Sum of the weight of sample used for gyroscope filtering. + float current_accumulated_weights_gyroscope_bias_; + + // Set of filters for accelerometer data to estimate a rotation + // based only on accelerometer. + MeanFilter mean_filter_; + MedianFilter median_filter_; + + // Last computed filter accelerometer value used for finite differences. + Vector3 last_mean_filtered_accelerometer_value_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_data.h b/applications/plugins/airmouse/tracking/sensors/gyroscope_data.h new file mode 100644 index 000000000..085e85209 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/gyroscope_data.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ +#define CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ + +#include "../util/vector.h" + +namespace cardboard { + +struct GyroscopeData { + // System wall time. + uint64_t system_timestamp; + + // Sensor clock time in nanoseconds. + uint64_t sensor_timestamp_ns; + + // Rate of rotation around the x,y,z axes in rad/s. This follows android + // specification + // (https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords). + Vector3 data; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc b/applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc new file mode 100644 index 000000000..efadfbf4e --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc @@ -0,0 +1,84 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "lowpass_filter.h" + +#include + +namespace { + +const double kSecondsFromNanoseconds = 1.0e-9; + +// Minimum time step between sensor updates. This corresponds to 1000 Hz. +const double kMinTimestepS = 0.001f; + +// Maximum time step between sensor updates. This corresponds to 1 Hz. +const double kMaxTimestepS = 1.00f; + +} // namespace + +namespace cardboard { + +LowpassFilter::LowpassFilter(double cutoff_freq_hz) + : cutoff_time_constant_(1 / (2 * (double)M_PI * cutoff_freq_hz)) + , initialized_(false) +{ + Reset(); +} + +void LowpassFilter::AddSample(const Vector3& sample, uint64_t timestamp_ns) +{ + AddWeightedSample(sample, timestamp_ns, 1.0); +} + +void LowpassFilter::AddWeightedSample(const Vector3& sample, uint64_t timestamp_ns, double weight) +{ + if (!initialized_) { + // Initialize filter state + filtered_data_ = { sample[0], sample[1], sample[2] }; + timestamp_most_recent_update_ns_ = timestamp_ns; + initialized_ = true; + return; + } + + if (timestamp_ns < timestamp_most_recent_update_ns_) { + timestamp_most_recent_update_ns_ = timestamp_ns; + return; + } + + const double delta_s = static_cast(timestamp_ns - timestamp_most_recent_update_ns_) + * kSecondsFromNanoseconds; + if (delta_s <= kMinTimestepS || delta_s > kMaxTimestepS) { + timestamp_most_recent_update_ns_ = timestamp_ns; + return; + } + + const double weighted_delta_secs = weight * delta_s; + + const double alpha = weighted_delta_secs / (cutoff_time_constant_ + weighted_delta_secs); + + for (int i = 0; i < 3; ++i) { + filtered_data_[i] = (1 - alpha) * filtered_data_[i] + alpha * sample[i]; + } + timestamp_most_recent_update_ns_ = timestamp_ns; +} + +void LowpassFilter::Reset() +{ + initialized_ = false; + filtered_data_ = { 0, 0, 0 }; +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/lowpass_filter.h b/applications/plugins/airmouse/tracking/sensors/lowpass_filter.h new file mode 100644 index 000000000..c4994c425 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/lowpass_filter.h @@ -0,0 +1,81 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ + +#include +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Implements an IIR, first order, low pass filter over vectors of the given +// dimension = 3. +// See http://en.wikipedia.org/wiki/Low-pass_filter +class LowpassFilter { +public: + // Initializes a filter with the given cutoff frequency in Hz. + explicit LowpassFilter(double cutoff_freq_hz); + + // Updates the filter with the given sample. Note that samples with + // non-monotonic timestamps and successive samples with a time steps below 1 + // ms or above 1 s are ignored. + // + // @param sample current sample data. + // @param timestamp_ns timestamp associated to this sample in nanoseconds. + void AddSample(const Vector3& sample, uint64_t timestamp_ns); + + // Updates the filter with the given weighted sample. + // + // @param sample current sample data. + // @param timestamp_ns timestamp associated to this sample in nanoseconds. + // @param weight typically a [0, 1] weight factor used when applying a new + // sample. A weight of 1 corresponds to calling AddSample. A weight of 0 + // makes the update no-op. The first initial sample is not affected by + // this. + void AddWeightedSample(const Vector3& sample, uint64_t timestamp_ns, double weight); + + // Returns the filtered value. A vector with zeros is returned if no samples + // have been added. + Vector3 GetFilteredData() const { + return filtered_data_; + } + + // Returns the most recent update timestamp in ns. + uint64_t GetMostRecentTimestampNs() const { + return timestamp_most_recent_update_ns_; + } + + // Returns true when the filter is initialized. + bool IsInitialized() const { + return initialized_; + } + + // Resets filter state. + void Reset(); + +private: + const double cutoff_time_constant_; + uint64_t timestamp_most_recent_update_ns_; + bool initialized_; + + Vector3 filtered_data_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/mean_filter.cc b/applications/plugins/airmouse/tracking/sensors/mean_filter.cc new file mode 100644 index 000000000..02fb8034c --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/mean_filter.cc @@ -0,0 +1,46 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mean_filter.h" + +namespace cardboard { + +MeanFilter::MeanFilter(size_t filter_size) + : filter_size_(filter_size) +{ +} + +void MeanFilter::AddSample(const Vector3& sample) +{ + buffer_.push_back(sample); + if (buffer_.size() > filter_size_) { + buffer_.pop_front(); + } +} + +bool MeanFilter::IsValid() const { return buffer_.size() == filter_size_; } + +Vector3 MeanFilter::GetFilteredData() const +{ + // Compute mean of the samples stored in buffer_. + Vector3 mean = Vector3::Zero(); + for (auto sample : buffer_) { + mean += sample; + } + + return mean / static_cast(filter_size_); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/mean_filter.h b/applications/plugins/airmouse/tracking/sensors/mean_filter.h new file mode 100644 index 000000000..6b4956fef --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/mean_filter.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ + +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Fixed window FIFO mean filter for vectors of the given dimension. +class MeanFilter { +public: + // Create a mean filter of size filter_size. + // @param filter_size size of the internal filter. + explicit MeanFilter(size_t filter_size); + + // Add sample to buffer_ if buffer_ is full it drop the oldest sample. + void AddSample(const Vector3& sample); + + // Returns true if buffer has filter_size_ sample, false otherwise. + bool IsValid() const; + + // Returns the mean of values stored in the internal buffer. + Vector3 GetFilteredData() const; + +private: + const size_t filter_size_; + std::deque buffer_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/median_filter.cc b/applications/plugins/airmouse/tracking/sensors/median_filter.cc new file mode 100644 index 000000000..d27c19f47 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/median_filter.cc @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "median_filter.h" + +#include +#include + +#include "../util/vector.h" +#include "../util/vectorutils.h" + +namespace cardboard { + +MedianFilter::MedianFilter(size_t filter_size) + : filter_size_(filter_size) +{ +} + +void MedianFilter::AddSample(const Vector3& sample) +{ + buffer_.push_back(sample); + norms_.push_back(Length(sample)); + if (buffer_.size() > filter_size_) { + buffer_.pop_front(); + norms_.pop_front(); + } +} + +bool MedianFilter::IsValid() const { return buffer_.size() == filter_size_; } + +Vector3 MedianFilter::GetFilteredData() const +{ + std::vector norms(norms_.begin(), norms_.end()); + + // Get median of value of the norms. + std::nth_element(norms.begin(), norms.begin() + filter_size_ / 2, norms.end()); + const float median_norm = norms[filter_size_ / 2]; + + // Get median value based on their norm. + auto median_it = buffer_.begin(); + for (const auto norm : norms_) { + if (norm == median_norm) { + break; + } + ++median_it; + } + + return *median_it; +} + +void MedianFilter::Reset() +{ + buffer_.clear(); + norms_.clear(); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/median_filter.h b/applications/plugins/airmouse/tracking/sensors/median_filter.h new file mode 100644 index 000000000..9a8e7cfc7 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/median_filter.h @@ -0,0 +1,53 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ + +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Fixed window FIFO median filter for vectors of the given dimension = 3. +class MedianFilter { +public: + // Creates a median filter of size filter_size. + // @param filter_size size of the internal filter. + explicit MedianFilter(size_t filter_size); + + // Adds sample to buffer_ if buffer_ is full it drops the oldest sample. + void AddSample(const Vector3& sample); + + // Returns true if buffer has filter_size_ sample, false otherwise. + bool IsValid() const; + + // Returns the median of values store in the internal buffer. + Vector3 GetFilteredData() const; + + // Resets the filter, removing all samples that have been added. + void Reset(); + +private: + const size_t filter_size_; + std::deque buffer_; + // Contains norms of the elements stored in buffer_. + std::deque norms_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/pose_prediction.cc b/applications/plugins/airmouse/tracking/sensors/pose_prediction.cc new file mode 100644 index 000000000..baaf844dd --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/pose_prediction.cc @@ -0,0 +1,71 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "pose_prediction.h" + +#include // NOLINT + +#include "../util/logging.h" +#include "../util/vectorutils.h" + +namespace cardboard { + +namespace { + const double kEpsilon = 1.0e-15; +} // namespace + +namespace pose_prediction { + + Rotation GetRotationFromGyroscope(const Vector3& gyroscope_value, double timestep_s) + { + const double velocity = Length(gyroscope_value); + + // When there is no rotation data return an identity rotation. + if (velocity < kEpsilon) { + CARDBOARD_LOGI("PosePrediction::GetRotationFromGyroscope: Velocity really small, " + "returning identity rotation."); + return Rotation::Identity(); + } + // Since the gyroscope_value is a start from sensor transformation we need to + // invert it to have a sensor from start transformation, hence the minus sign. + // For more info: + // http://developer.android.com/guide/topics/sensors/sensors_motion.html#sensors-motion-gyro + return Rotation::FromAxisAndAngle(gyroscope_value / velocity, -timestep_s * velocity); + } + + Rotation PredictPose(int64_t requested_pose_timestamp, const PoseState& current_state) + { + // Subtracting unsigned numbers is bad when the result is negative. + const int64_t diff = requested_pose_timestamp - current_state.timestamp; + const double timestep_s = diff * 1.0e-9; + + const Rotation update = GetRotationFromGyroscope( + current_state.sensor_from_start_rotation_velocity, timestep_s); + return update * current_state.sensor_from_start_rotation; + } + + Rotation PredictPoseInv(int64_t requested_pose_timestamp, const PoseState& current_state) + { + // Subtracting unsigned numbers is bad when the result is negative. + const int64_t diff = requested_pose_timestamp - current_state.timestamp; + const double timestep_s = diff * 1.0e-9; + + const Rotation update = GetRotationFromGyroscope( + current_state.sensor_from_start_rotation_velocity, timestep_s); + return current_state.sensor_from_start_rotation * (-update); + } + +} // namespace pose_prediction +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/pose_prediction.h b/applications/plugins/airmouse/tracking/sensors/pose_prediction.h new file mode 100644 index 000000000..9ab311b33 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/pose_prediction.h @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ +#define CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ + +#include + +#include "pose_state.h" +#include "../util/rotation.h" + +namespace cardboard { +namespace pose_prediction { + +// Returns a rotation matrix based on the integration of the gyroscope_value +// over the timestep_s in seconds. +// TODO(pfg): Document the space better here. +// +// @param gyroscope_value gyroscope sensor values. +// @param timestep_s integration period in seconds. +// @return Integration of the gyroscope value the rotation is from Start to +// Sensor Space. +Rotation GetRotationFromGyroscope(const Vector3& gyroscope_value, double timestep_s); + +// Gets a predicted pose for a given time in the future (e.g. rendering time) +// based on a linear prediction model. This uses the system current state +// (position, velocity, etc) from the past to extrapolate a position in the +// future. +// +// @param requested_pose_timestamp time at which you want the pose. +// @param current_state current state that stores the pose and linear model at a +// given time prior to requested_pose_timestamp_ns. +// @return pose from Start to Sensor Space. +Rotation PredictPose(int64_t requested_pose_timestamp, const PoseState& current_state); + +// Equivalent to PredictPose, but for use with poses relative to Start Space +// rather than sensor space. +Rotation PredictPoseInv(int64_t requested_pose_timestamp, const PoseState& current_state); + +} // namespace pose_prediction +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/pose_state.h b/applications/plugins/airmouse/tracking/sensors/pose_state.h new file mode 100644 index 000000000..f7801c9f3 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/pose_state.h @@ -0,0 +1,56 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_POSE_STATE_H_ +#define CARDBOARD_SDK_SENSORS_POSE_STATE_H_ + +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace cardboard { + +enum { + kPoseStateFlagInvalid = 1U << 0, + kPoseStateFlagInitializing = 1U << 1, + kPoseStateFlagHas6DoF = 1U << 2, +}; + +// Stores a head pose pose plus derivatives. This can be used for prediction. +struct PoseState { + // System wall time. + int64_t timestamp; + + // Rotation from Sensor Space to Start Space. + Rotation sensor_from_start_rotation; + + // First derivative of the rotation. + Vector3 sensor_from_start_rotation_velocity; + + // Current gyroscope bias in rad/s. + Vector3 bias; + + // The position of the headset. + Vector3 position = Vector3(0, 0, 0); + + // In the same coordinate frame as the position. + Vector3 velocity = Vector3(0, 0, 0); + + // Flags indicating the status of the pose. + uint64_t flags = 0U; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_POSE_STATE_H_ diff --git a/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc b/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc new file mode 100644 index 000000000..575dde6f0 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc @@ -0,0 +1,333 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sensor_fusion_ekf.h" + +#include +#include + +#include "accelerometer_data.h" +#include "gyroscope_data.h" +#include "pose_prediction.h" +#include "../util/matrixutils.h" + +namespace cardboard { + +namespace { + + const double kFiniteDifferencingEpsilon = 1.0e-7; + const double kEpsilon = 1.0e-15; + // Default gyroscope frequency. This corresponds to 100 Hz. + const double kDefaultGyroscopeTimestep_s = 0.01f; + // Maximum time between gyroscope before we start limiting the integration. + const double kMaximumGyroscopeSampleDelay_s = 0.04f; + // Compute a first-order exponential moving average of changes in accel norm per + // frame. + const double kSmoothingFactor = 0.5; + // Minimum and maximum values used for accelerometer noise covariance matrix. + // The smaller the sigma value, the more weight is given to the accelerometer + // signal. + const double kMinAccelNoiseSigma = 0.75; + const double kMaxAccelNoiseSigma = 7.0; + // Initial value for the diagonal elements of the different covariance matrices. + const double kInitialStateCovarianceValue = 25.0; + const double kInitialProcessCovarianceValue = 1.0; + // Maximum accelerometer norm change allowed before capping it covariance to a + // large value. + const double kMaxAccelNormChange = 0.15; + // Timestep IIR filtering coefficient. + const double kTimestepFilterCoeff = 0.95; + // Minimum number of sample for timestep filtering. + const int kTimestepFilterMinSamples = 10; + + // Z direction in start space. + const Vector3 kCanonicalZDirection(0.0, 0.0, 1.0); + + // Computes an axis-angle rotation from the input vector. + // angle = norm(a) + // axis = a.normalized() + // If norm(a) == 0, it returns an identity rotation. + static inline Rotation RotationFromVector(const Vector3& a) + { + const double norm_a = Length(a); + if (norm_a < kEpsilon) { + return Rotation::Identity(); + } + return Rotation::FromAxisAndAngle(a / norm_a, norm_a); + } + +} // namespace + +SensorFusionEkf::SensorFusionEkf() + : execute_reset_with_next_accelerometer_sample_(false) + , bias_estimation_enabled_(true) + , gyroscope_bias_estimate_({ 0, 0, 0 }) +{ + ResetState(); +} + +void SensorFusionEkf::Reset() { execute_reset_with_next_accelerometer_sample_ = true; } + +void SensorFusionEkf::ResetState() +{ + current_state_.sensor_from_start_rotation = Rotation::Identity(); + current_state_.sensor_from_start_rotation_velocity = Vector3::Zero(); + + current_gyroscope_sensor_timestamp_ns_ = 0; + current_accelerometer_sensor_timestamp_ns_ = 0; + + state_covariance_ = Matrix3x3::Identity() * kInitialStateCovarianceValue; + process_covariance_ = Matrix3x3::Identity() * kInitialProcessCovarianceValue; + accelerometer_measurement_covariance_ + = Matrix3x3::Identity() * kMinAccelNoiseSigma * kMinAccelNoiseSigma; + innovation_covariance_ = Matrix3x3::Identity(); + + accelerometer_measurement_jacobian_ = Matrix3x3::Zero(); + kalman_gain_ = Matrix3x3::Zero(); + innovation_ = Vector3::Zero(); + accelerometer_measurement_ = Vector3::Zero(); + prediction_ = Vector3::Zero(); + control_input_ = Vector3::Zero(); + state_update_ = Vector3::Zero(); + + moving_average_accelerometer_norm_change_ = 0.0; + + is_timestep_filter_initialized_ = false; + is_gyroscope_filter_valid_ = false; + is_aligned_with_gravity_ = false; + + // Reset biases. + gyroscope_bias_estimator_.Reset(); + gyroscope_bias_estimate_ = { 0, 0, 0 }; +} + +// Here I am doing something wrong relative to time stamps. The state timestamps +// always correspond to the gyrostamps because it would require additional +// extrapolation if I wanted to do otherwise. +PoseState SensorFusionEkf::GetLatestPoseState() const { return current_state_; } + +void SensorFusionEkf::ProcessGyroscopeSample(const GyroscopeData& sample) +{ + // Don't accept gyroscope sample when waiting for a reset. + if (execute_reset_with_next_accelerometer_sample_) { + return; + } + + // Discard outdated samples. + if (current_gyroscope_sensor_timestamp_ns_ >= sample.sensor_timestamp_ns) { + current_gyroscope_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + return; + } + + // Checks that we received at least one gyroscope sample in the past. + if (current_gyroscope_sensor_timestamp_ns_ != 0) { + double current_timestep_s = std::chrono::duration_cast>( + std::chrono::nanoseconds( + sample.sensor_timestamp_ns - current_gyroscope_sensor_timestamp_ns_)) + .count(); + if (current_timestep_s > kMaximumGyroscopeSampleDelay_s) { + if (is_gyroscope_filter_valid_) { + // Replaces the delta timestamp by the filtered estimates of the delta time. + current_timestep_s = filtered_gyroscope_timestep_s_; + } else { + current_timestep_s = kDefaultGyroscopeTimestep_s; + } + } else { + FilterGyroscopeTimestep(current_timestep_s); + } + + if (bias_estimation_enabled_) { + gyroscope_bias_estimator_.ProcessGyroscope(sample.data, sample.sensor_timestamp_ns); + + if (gyroscope_bias_estimator_.IsCurrentEstimateValid()) { + // As soon as the device is considered to be static, the bias estimator + // should have a precise estimate of the gyroscope bias. + gyroscope_bias_estimate_ = gyroscope_bias_estimator_.GetGyroscopeBias(); + } + } + + // Only integrate after receiving an accelerometer sample. + if (is_aligned_with_gravity_) { + const Rotation rotation_from_gyroscope = pose_prediction::GetRotationFromGyroscope( + { sample.data[0] - gyroscope_bias_estimate_[0], + sample.data[1] - gyroscope_bias_estimate_[1], + sample.data[2] - gyroscope_bias_estimate_[2] }, + current_timestep_s); + current_state_.sensor_from_start_rotation + = rotation_from_gyroscope * current_state_.sensor_from_start_rotation; + UpdateStateCovariance(RotationMatrixNH(rotation_from_gyroscope)); + state_covariance_ = state_covariance_ + + ((current_timestep_s * current_timestep_s) * process_covariance_); + } + } + + // Saves gyroscope event for future prediction. + current_state_.timestamp = sample.system_timestamp; + current_gyroscope_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + current_state_.sensor_from_start_rotation_velocity.Set( + sample.data[0] - gyroscope_bias_estimate_[0], sample.data[1] - gyroscope_bias_estimate_[1], + sample.data[2] - gyroscope_bias_estimate_[2]); +} + +Vector3 SensorFusionEkf::ComputeInnovation(const Rotation& pose) +{ + const Vector3 predicted_down_direction = pose * kCanonicalZDirection; + + const Rotation rotation + = Rotation::RotateInto(predicted_down_direction, accelerometer_measurement_); + Vector3 axis; + double angle; + rotation.GetAxisAndAngle(&axis, &angle); + return axis * angle; +} + +void SensorFusionEkf::ComputeMeasurementJacobian() +{ + for (int dof = 0; dof < 3; dof++) { + Vector3 delta = Vector3::Zero(); + delta[dof] = kFiniteDifferencingEpsilon; + + const Rotation epsilon_rotation = RotationFromVector(delta); + const Vector3 delta_rotation + = ComputeInnovation(epsilon_rotation * current_state_.sensor_from_start_rotation); + + const Vector3 col = (innovation_ - delta_rotation) / kFiniteDifferencingEpsilon; + accelerometer_measurement_jacobian_(0, dof) = col[0]; + accelerometer_measurement_jacobian_(1, dof) = col[1]; + accelerometer_measurement_jacobian_(2, dof) = col[2]; + } +} + +void SensorFusionEkf::ProcessAccelerometerSample(const AccelerometerData& sample) +{ + // Discard outdated samples. + if (current_accelerometer_sensor_timestamp_ns_ >= sample.sensor_timestamp_ns) { + current_accelerometer_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + return; + } + + // Call reset state if required. + if (execute_reset_with_next_accelerometer_sample_.exchange(false)) { + ResetState(); + } + + accelerometer_measurement_.Set(sample.data[0], sample.data[1], sample.data[2]); + current_accelerometer_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + + if (bias_estimation_enabled_) { + gyroscope_bias_estimator_.ProcessAccelerometer(sample.data, sample.sensor_timestamp_ns); + } + + if (!is_aligned_with_gravity_) { + // This is the first accelerometer measurement so it initializes the + // orientation estimate. + current_state_.sensor_from_start_rotation + = Rotation::RotateInto(kCanonicalZDirection, accelerometer_measurement_); + is_aligned_with_gravity_ = true; + + previous_accelerometer_norm_ = Length(accelerometer_measurement_); + return; + } + + UpdateMeasurementCovariance(); + + innovation_ = ComputeInnovation(current_state_.sensor_from_start_rotation); + ComputeMeasurementJacobian(); + + // S = H * P * H' + R + innovation_covariance_ = accelerometer_measurement_jacobian_ * state_covariance_ + * Transpose(accelerometer_measurement_jacobian_) + + accelerometer_measurement_covariance_; + + // K = P * H' * S^-1 + kalman_gain_ = state_covariance_ * Transpose(accelerometer_measurement_jacobian_) + * Inverse(innovation_covariance_); + + // x_update = K*nu + state_update_ = kalman_gain_ * innovation_; + + // P = (I - K * H) * P; + state_covariance_ = (Matrix3x3::Identity() - kalman_gain_ * accelerometer_measurement_jacobian_) + * state_covariance_; + + // Updates pose and associate covariance matrix. + const Rotation rotation_from_state_update = RotationFromVector(state_update_); + + current_state_.sensor_from_start_rotation + = rotation_from_state_update * current_state_.sensor_from_start_rotation; + UpdateStateCovariance(RotationMatrixNH(rotation_from_state_update)); +} + +void SensorFusionEkf::UpdateStateCovariance(const Matrix3x3& motion_update) +{ + state_covariance_ = motion_update * state_covariance_ * Transpose(motion_update); +} + +void SensorFusionEkf::FilterGyroscopeTimestep(double gyroscope_timestep_s) +{ + if (!is_timestep_filter_initialized_) { + // Initializes the filter. + filtered_gyroscope_timestep_s_ = gyroscope_timestep_s; + num_gyroscope_timestep_samples_ = 1; + is_timestep_filter_initialized_ = true; + return; + } + + // Computes the IIR filter response. + filtered_gyroscope_timestep_s_ = kTimestepFilterCoeff * filtered_gyroscope_timestep_s_ + + (1 - kTimestepFilterCoeff) * gyroscope_timestep_s; + ++num_gyroscope_timestep_samples_; + + if (num_gyroscope_timestep_samples_ > kTimestepFilterMinSamples) { + is_gyroscope_filter_valid_ = true; + } +} + +void SensorFusionEkf::UpdateMeasurementCovariance() +{ + const double current_accelerometer_norm = Length(accelerometer_measurement_); + // Norm change between current and previous accel readings. + const double current_accelerometer_norm_change + = std::abs(current_accelerometer_norm - previous_accelerometer_norm_); + previous_accelerometer_norm_ = current_accelerometer_norm; + + moving_average_accelerometer_norm_change_ = kSmoothingFactor * current_accelerometer_norm_change + + (1 - kSmoothingFactor) * moving_average_accelerometer_norm_change_; + + // If we hit the accel norm change threshold, we use the maximum noise sigma + // for the accel covariance. For anything below that, we use a linear + // combination between min and max sigma values. + const double norm_change_ratio + = moving_average_accelerometer_norm_change_ / kMaxAccelNormChange; + const double accelerometer_noise_sigma = std::min(kMaxAccelNoiseSigma, + kMinAccelNoiseSigma + norm_change_ratio * (kMaxAccelNoiseSigma - kMinAccelNoiseSigma)); + + // Updates the accel covariance matrix with the new sigma value. + accelerometer_measurement_covariance_ + = Matrix3x3::Identity() * accelerometer_noise_sigma * accelerometer_noise_sigma; +} + +bool SensorFusionEkf::IsBiasEstimationEnabled() const { return bias_estimation_enabled_; } + +void SensorFusionEkf::SetBiasEstimationEnabled(bool enable) +{ + if (bias_estimation_enabled_ != enable) { + bias_estimation_enabled_ = enable; + gyroscope_bias_estimate_ = { 0, 0, 0 }; + gyroscope_bias_estimator_.Reset(); + } +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h b/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h new file mode 100644 index 000000000..a66fe33f4 --- /dev/null +++ b/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h @@ -0,0 +1,188 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ +#define CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ + +#include +#include +#include + +#include "accelerometer_data.h" +#include "gyroscope_bias_estimator.h" +#include "gyroscope_data.h" +#include "pose_state.h" +#include "../util/matrix_3x3.h" +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace cardboard { + +// Sensor fusion class that implements an Extended Kalman Filter (EKF) to +// estimate a 3D rotation from a gyroscope and an accelerometer. +// This system only has one state, the pose. It does not estimate any velocity +// or acceleration. +// +// To learn more about Kalman filtering one can read this article which is a +// good introduction: https://en.wikipedia.org/wiki/Kalman_filter +class SensorFusionEkf { +public: + SensorFusionEkf(); + + // Resets the state of the sensor fusion. It sets the velocity for + // prediction to zero. The reset will happen with the next + // accelerometer sample. Gyroscope sample will be discarded until a new + // accelerometer sample arrives. + void Reset(); + + // Gets the PoseState representing the latest pose and derivatives at a + // particular timestamp as estimated by SensorFusion. + PoseState GetLatestPoseState() const; + + // Processes one gyroscope sample event. This updates the pose of the system + // and the prediction model. The gyroscope data is assumed to be in axis angle + // form. Angle = ||v|| and Axis = v / ||v||, with v = [v_x, v_y, v_z]^T. + // + // @param sample gyroscope sample data. + void ProcessGyroscopeSample(const GyroscopeData& sample); + + // Processes one accelerometer sample event. This updates the pose of the + // system. If the Accelerometer norm changes too much between sample it is not + // trusted as much. + // + // @param sample accelerometer sample data. + void ProcessAccelerometerSample(const AccelerometerData& sample); + + // Enables or disables the drift correction by estimating the gyroscope bias. + // + // @param enable Enable drift correction. + void SetBiasEstimationEnabled(bool enable); + + // Returns a boolean that indicates if bias estimation is enabled or disabled. + // + // @return true if bias estimation is enabled, false otherwise. + bool IsBiasEstimationEnabled() const; + + // Returns the current gyroscope bias estimate from GyroscopeBiasEstimator. + Vector3 GetGyroscopeBias() const { + return { + gyroscope_bias_estimate_[0], gyroscope_bias_estimate_[1], gyroscope_bias_estimate_[2]}; + } + + // Returns true after receiving the first accelerometer measurement. + bool IsFullyInitialized() const { + return is_aligned_with_gravity_; + } + +private: + // Estimates the average timestep between gyroscope event. + void FilterGyroscopeTimestep(double gyroscope_timestep); + + // Updates the state covariance with an incremental motion. It changes the + // space of the quadric. + void UpdateStateCovariance(const Matrix3x3& motion_update); + + // Computes the innovation vector of the Kalman based on the input pose. + // It uses the latest measurement vector (i.e. accelerometer data), which must + // be set prior to calling this function. + Vector3 ComputeInnovation(const Rotation& pose); + + // This computes the measurement_jacobian_ via numerical differentiation based + // on the current value of sensor_from_start_rotation_. + void ComputeMeasurementJacobian(); + + // Updates the accelerometer covariance matrix. + // + // This looks at the norm of recent accelerometer readings. If it has changed + // significantly, it means the phone receives additional acceleration than + // just gravity, and so the down vector information gravity signal is noisier. + void UpdateMeasurementCovariance(); + + // Reset all internal states. This is not thread safe. Lock should be acquired + // outside of it. This function is called in ProcessAccelerometerSample. + void ResetState(); + + // Current transformation from Sensor Space to Start Space. + // x_sensor = sensor_from_start_rotation_ * x_start; + PoseState current_state_; + + // Filtering of the gyroscope timestep started? + bool is_timestep_filter_initialized_; + // Filtered gyroscope timestep valid? + bool is_gyroscope_filter_valid_; + // Sensor fusion currently aligned with gravity? After initialization + // it will requires a couple of accelerometer data for the system to get + // aligned. + std::atomic is_aligned_with_gravity_; + + // Covariance of Kalman filter state (P in common formulation). + Matrix3x3 state_covariance_; + // Covariance of the process noise (Q in common formulation). + Matrix3x3 process_covariance_; + // Covariance of the accelerometer measurement (R in common formulation). + Matrix3x3 accelerometer_measurement_covariance_; + // Covariance of innovation (S in common formulation). + Matrix3x3 innovation_covariance_; + // Jacobian of the measurements (H in common formulation). + Matrix3x3 accelerometer_measurement_jacobian_; + // Gain of the Kalman filter (K in common formulation). + Matrix3x3 kalman_gain_; + // Parameter update a.k.a. innovation vector. (\nu in common formulation). + Vector3 innovation_; + // Measurement vector (z in common formulation). + Vector3 accelerometer_measurement_; + // Current prediction vector (g in common formulation). + Vector3 prediction_; + // Control input, currently this is only the gyroscope data (\mu in common + // formulation). + Vector3 control_input_; + // Update of the state vector. (x in common formulation). + Vector3 state_update_; + + // Sensor time of the last gyroscope processed event. + uint64_t current_gyroscope_sensor_timestamp_ns_; + // Sensor time of the last accelerometer processed event. + uint64_t current_accelerometer_sensor_timestamp_ns_; + + // Estimates of the timestep between gyroscope event in seconds. + double filtered_gyroscope_timestep_s_; + // Number of timestep samples processed so far by the filter. + uint32_t num_gyroscope_timestep_samples_; + // Norm of the accelerometer for the previous measurement. + double previous_accelerometer_norm_; + // Moving average of the accelerometer norm changes. It is computed for every + // sensor datum. + double moving_average_accelerometer_norm_change_; + + // Flag indicating if a state reset should be executed with the next + // accelerometer sample. + std::atomic execute_reset_with_next_accelerometer_sample_; + + // Flag indicating if bias estimation is enabled (enabled by default). + std::atomic bias_estimation_enabled_; + + // Bias estimator and static device detector. + GyroscopeBiasEstimator gyroscope_bias_estimator_; + + // Current bias estimate_; + Vector3 gyroscope_bias_estimate_; + + SensorFusionEkf(const SensorFusionEkf&) = delete; + SensorFusionEkf& operator=(const SensorFusionEkf&) = delete; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ diff --git a/applications/plugins/airmouse/tracking/util/logging.h b/applications/plugins/airmouse/tracking/util/logging.h new file mode 100644 index 000000000..dee224b1c --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/logging.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_LOGGING_H_ +#define CARDBOARD_SDK_UTIL_LOGGING_H_ + +#include +#include + +#if defined(__ANDROID__) + +#include + +// Uncomment these to enable debug logging from native code + +#define CARDBOARD_LOGI(...) // __android_log_print(ANDROID_LOG_INFO, "CardboardSDK", __VA_ARGS__) +#define CARDBOARD_LOGE(...) // __android_log_print(ANDROID_LOG_ERROR, "CardboardSDK", __VA_ARGS__) + +#else + +#define CARDBOARD_LOGI(...) // FURI_LOG_I("CardboardSDK", __VA_ARGS__) +#define CARDBOARD_LOGE(...) // FURI_LOG_E("CardboardSDK", __VA_ARGS__) + +#endif + +#endif // CARDBOARD_SDK_UTIL_LOGGING_H_ diff --git a/applications/plugins/airmouse/tracking/util/matrix_3x3.cc b/applications/plugins/airmouse/tracking/util/matrix_3x3.cc new file mode 100644 index 000000000..9ddd847b0 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrix_3x3.cc @@ -0,0 +1,121 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrix_3x3.h" + +namespace cardboard { + +Matrix3x3::Matrix3x3(double m00, double m01, double m02, double m10, double m11, double m12, + double m20, double m21, double m22) + : elem_ { { { m00, m01, m02 }, { m10, m11, m12 }, { m20, m21, m22 } } } +{ +} + +Matrix3x3::Matrix3x3() +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + elem_[row][col] = 0; + } +} + +Matrix3x3 Matrix3x3::Zero() +{ + Matrix3x3 result; + return result; +} + +Matrix3x3 Matrix3x3::Identity() +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + result.elem_[row][row] = 1; + } + return result; +} + +void Matrix3x3::MultiplyScalar(double s) +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + elem_[row][col] *= s; + } +} + +Matrix3x3 Matrix3x3::Negation() const +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = -elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Scale(const Matrix3x3& m, double s) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = m.elem_[row][col] * s; + } + return result; +} + +Matrix3x3 Matrix3x3::Addition(const Matrix3x3& lhs, const Matrix3x3& rhs) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = lhs.elem_[row][col] + rhs.elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Subtraction(const Matrix3x3& lhs, const Matrix3x3& rhs) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = lhs.elem_[row][col] - rhs.elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Product(const Matrix3x3& m0, const Matrix3x3& m1) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + result.elem_[row][col] = 0; + for (int i = 0; i < 3; ++i) + result.elem_[row][col] += m0.elem_[row][i] * m1.elem_[i][col]; + } + } + return result; +} + +bool Matrix3x3::AreEqual(const Matrix3x3& m0, const Matrix3x3& m1) +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + if (m0.elem_[row][col] != m1.elem_[row][col]) + return false; + } + } + return true; +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/matrix_3x3.h b/applications/plugins/airmouse/tracking/util/matrix_3x3.h new file mode 100644 index 000000000..81e4f2158 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrix_3x3.h @@ -0,0 +1,138 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ +#define CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ + +#include +#include // For memcpy(). +#include // NOLINT +#include // NOLINT + +namespace cardboard { + +// The Matrix3x3 class defines a square 3-dimensional matrix. Elements are +// stored in row-major order. +// TODO(b/135461889): Make this class consistent with Matrix4x4. +class Matrix3x3 { +public: + // The default constructor zero-initializes all elements. + Matrix3x3(); + + // Dimension-specific constructors that are passed individual element values. + Matrix3x3( + double m00, + double m01, + double m02, + double m10, + double m11, + double m12, + double m20, + double m21, + double m22); + + // Constructor that reads elements from a linear array of the correct size. + explicit Matrix3x3(const double array[3 * 3]); + + // Returns a Matrix3x3 containing all zeroes. + static Matrix3x3 Zero(); + + // Returns an identity Matrix3x3. + static Matrix3x3 Identity(); + + // Mutable element accessors. + double& operator()(int row, int col) { + return elem_[row][col]; + } + std::array& operator[](int row) { + return elem_[row]; + } + + // Read-only element accessors. + const double& operator()(int row, int col) const { + return elem_[row][col]; + } + const std::array& operator[](int row) const { + return elem_[row]; + } + + // Return a pointer to the data for interfacing with libraries. + double* Data() { + return &elem_[0][0]; + } + const double* Data() const { + return &elem_[0][0]; + } + + // Self-modifying multiplication operators. + void operator*=(double s) { + MultiplyScalar(s); + } + void operator*=(const Matrix3x3& m) { + *this = Product(*this, m); + } + + // Unary operators. + Matrix3x3 operator-() const { + return Negation(); + } + + // Binary scale operators. + friend Matrix3x3 operator*(const Matrix3x3& m, double s) { + return Scale(m, s); + } + friend Matrix3x3 operator*(double s, const Matrix3x3& m) { + return Scale(m, s); + } + + // Binary matrix addition. + friend Matrix3x3 operator+(const Matrix3x3& lhs, const Matrix3x3& rhs) { + return Addition(lhs, rhs); + } + + // Binary matrix subtraction. + friend Matrix3x3 operator-(const Matrix3x3& lhs, const Matrix3x3& rhs) { + return Subtraction(lhs, rhs); + } + + // Binary multiplication operator. + friend Matrix3x3 operator*(const Matrix3x3& m0, const Matrix3x3& m1) { + return Product(m0, m1); + } + + // Exact equality and inequality comparisons. + friend bool operator==(const Matrix3x3& m0, const Matrix3x3& m1) { + return AreEqual(m0, m1); + } + friend bool operator!=(const Matrix3x3& m0, const Matrix3x3& m1) { + return !AreEqual(m0, m1); + } + +private: + // These private functions implement most of the operators. + void MultiplyScalar(double s); + Matrix3x3 Negation() const; + static Matrix3x3 Addition(const Matrix3x3& lhs, const Matrix3x3& rhs); + static Matrix3x3 Subtraction(const Matrix3x3& lhs, const Matrix3x3& rhs); + static Matrix3x3 Scale(const Matrix3x3& m, double s); + static Matrix3x3 Product(const Matrix3x3& m0, const Matrix3x3& m1); + static bool AreEqual(const Matrix3x3& m0, const Matrix3x3& m1); + + std::array, 3> elem_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ diff --git a/applications/plugins/airmouse/tracking/util/matrix_4x4.cc b/applications/plugins/airmouse/tracking/util/matrix_4x4.cc new file mode 100644 index 000000000..8db3cbc5b --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrix_4x4.cc @@ -0,0 +1,87 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrix_4x4.h" + +#include +#include +#include + +namespace cardboard { + +Matrix4x4 Matrix4x4::Identity() +{ + Matrix4x4 ret; + for (int j = 0; j < 4; ++j) { + for (int i = 0; i < 4; ++i) { + ret.m[j][i] = (i == j) ? 1 : 0; + } + } + + return ret; +} + +Matrix4x4 Matrix4x4::Zeros() +{ + Matrix4x4 ret; + for (int j = 0; j < 4; ++j) { + for (int i = 0; i < 4; ++i) { + ret.m[j][i] = 0; + } + } + + return ret; +} + +Matrix4x4 Matrix4x4::Translation(float x, float y, float z) +{ + Matrix4x4 ret = Matrix4x4::Identity(); + ret.m[3][0] = x; + ret.m[3][1] = y; + ret.m[3][2] = z; + + return ret; +} + +Matrix4x4 Matrix4x4::Perspective(const std::array& fov, float zNear, float zFar) +{ + Matrix4x4 ret = Matrix4x4::Zeros(); + + const float xLeft = -std::tan(fov[0] * M_PI / 180.0f) * zNear; + const float xRight = std::tan(fov[1] * M_PI / 180.0f) * zNear; + const float yBottom = -std::tan(fov[2] * M_PI / 180.0f) * zNear; + const float yTop = std::tan(fov[3] * M_PI / 180.0f) * zNear; + + const float X = (2 * zNear) / (xRight - xLeft); + const float Y = (2 * zNear) / (yTop - yBottom); + const float A = (xRight + xLeft) / (xRight - xLeft); + const float B = (yTop + yBottom) / (yTop - yBottom); + const float C = (zNear + zFar) / (zNear - zFar); + const float D = (2 * zNear * zFar) / (zNear - zFar); + + ret.m[0][0] = X; + ret.m[2][0] = A; + ret.m[1][1] = Y; + ret.m[2][1] = B; + ret.m[2][2] = C; + ret.m[3][2] = D; + ret.m[2][3] = -1; + + return ret; +} + +void Matrix4x4::ToArray(float* array) const { std::memcpy(array, &m[0][0], 16 * sizeof(float)); } + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/matrix_4x4.h b/applications/plugins/airmouse/tracking/util/matrix_4x4.h new file mode 100644 index 000000000..9934f6be0 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrix_4x4.h @@ -0,0 +1,37 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ +#define CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ + +#include + +namespace cardboard { + +class Matrix4x4 { +public: + static Matrix4x4 Identity(); + static Matrix4x4 Zeros(); + static Matrix4x4 Translation(float x, float y, float z); + static Matrix4x4 Perspective(const std::array& fov, float zNear, float zFar); + void ToArray(float* array) const; + +private: + std::array, 4> m; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_ diff --git a/applications/plugins/airmouse/tracking/util/matrixutils.cc b/applications/plugins/airmouse/tracking/util/matrixutils.cc new file mode 100644 index 000000000..12470beae --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrixutils.cc @@ -0,0 +1,148 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrixutils.h" + +#include "vectorutils.h" + +namespace cardboard { + +namespace { + + // Returns true if the cofactor for a given row and column should be negated. + static bool IsCofactorNegated(int row, int col) + { + // Negated iff (row + col) is odd. + return ((row + col) & 1) != 0; + } + + static double CofactorElement3(const Matrix3x3& m, int row, int col) + { + static const int index[3][2] = { { 1, 2 }, { 0, 2 }, { 0, 1 } }; + const int i0 = index[row][0]; + const int i1 = index[row][1]; + const int j0 = index[col][0]; + const int j1 = index[col][1]; + const double cofactor = m(i0, j0) * m(i1, j1) - m(i0, j1) * m(i1, j0); + return IsCofactorNegated(row, col) ? -cofactor : cofactor; + } + + // Multiplies a matrix and some type of column vector to + // produce another column vector of the same type. + Vector3 MultiplyMatrixAndVector(const Matrix3x3& m, const Vector3& v) + { + Vector3 result = Vector3::Zero(); + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result[row] += m(row, col) * v[col]; + } + return result; + } + + // Sets the upper 3x3 of a Matrix to represent a 3D rotation. + void RotationMatrix3x3(const Rotation& r, Matrix3x3* matrix) + { + // + // Given a quaternion (a,b,c,d) where d is the scalar part, the 3x3 rotation + // matrix is: + // + // a^2 - b^2 - c^2 + d^2 2ab - 2cd 2ac + 2bd + // 2ab + 2cd -a^2 + b^2 - c^2 + d^2 2bc - 2ad + // 2ac - 2bd 2bc + 2ad -a^2 - b^2 + c^2 + d^2 + // + const Vector<4>& quat = r.GetQuaternion(); + const double aa = quat[0] * quat[0]; + const double bb = quat[1] * quat[1]; + const double cc = quat[2] * quat[2]; + const double dd = quat[3] * quat[3]; + + const double ab = quat[0] * quat[1]; + const double ac = quat[0] * quat[2]; + const double bc = quat[1] * quat[2]; + + const double ad = quat[0] * quat[3]; + const double bd = quat[1] * quat[3]; + const double cd = quat[2] * quat[3]; + + Matrix3x3& m = *matrix; + m[0][0] = aa - bb - cc + dd; + m[0][1] = 2 * ab - 2 * cd; + m[0][2] = 2 * ac + 2 * bd; + m[1][0] = 2 * ab + 2 * cd; + m[1][1] = -aa + bb - cc + dd; + m[1][2] = 2 * bc - 2 * ad; + m[2][0] = 2 * ac - 2 * bd; + m[2][1] = 2 * bc + 2 * ad; + m[2][2] = -aa - bb + cc + dd; + } + +} // anonymous namespace + +Vector3 operator*(const Matrix3x3& m, const Vector3& v) { return MultiplyMatrixAndVector(m, v); } + +Matrix3x3 CofactorMatrix(const Matrix3x3& m) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result(row, col) = CofactorElement3(m, row, col); + } + return result; +} + +Matrix3x3 AdjugateWithDeterminant(const Matrix3x3& m, double* determinant) +{ + const Matrix3x3 cofactor_matrix = CofactorMatrix(m); + if (determinant) { + *determinant = m(0, 0) * cofactor_matrix(0, 0) + m(0, 1) * cofactor_matrix(0, 1) + + m(0, 2) * cofactor_matrix(0, 2); + } + return Transpose(cofactor_matrix); +} + +// Returns the transpose of a matrix. +Matrix3x3 Transpose(const Matrix3x3& m) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result(row, col) = m(col, row); + } + return result; +} + +Matrix3x3 InverseWithDeterminant(const Matrix3x3& m, double* determinant) +{ + // The inverse is the adjugate divided by the determinant. + double det; + Matrix3x3 adjugate = AdjugateWithDeterminant(m, &det); + if (determinant) + *determinant = det; + if (det == 0) + return Matrix3x3::Zero(); + else + return adjugate * (1 / det); +} + +Matrix3x3 Inverse(const Matrix3x3& m) { return InverseWithDeterminant(m, nullptr); } + +Matrix3x3 RotationMatrixNH(const Rotation& r) +{ + Matrix3x3 m; + RotationMatrix3x3(r, &m); + return m; +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/matrixutils.h b/applications/plugins/airmouse/tracking/util/matrixutils.h new file mode 100644 index 000000000..80f9b2168 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/matrixutils.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ +#define CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ + +// +// This file contains operators and free functions that define generic Matrix +// operations. +// + +#include "matrix_3x3.h" +#include "rotation.h" +#include "vector.h" + +namespace cardboard { + +// Returns the transpose of a matrix. +Matrix3x3 Transpose(const Matrix3x3& m); + +// Multiplies a Matrix and a column Vector of the same Dimension to produce +// another column Vector. +Vector3 operator*(const Matrix3x3& m, const Vector3& v); + +// Returns the determinant of the matrix. This function is defined for all the +// typedef'ed Matrix types. +double Determinant(const Matrix3x3& m); + +// Returns the adjugate of the matrix, which is defined as the transpose of the +// cofactor matrix. This function is defined for all the typedef'ed Matrix +// types. The determinant of the matrix is computed as a side effect, so it is +// returned in the determinant parameter if it is not null. +Matrix3x3 AdjugateWithDeterminant(const Matrix3x3& m, double* determinant); + +// Returns the inverse of the matrix. This function is defined for all the +// typedef'ed Matrix types. The determinant of the matrix is computed as a +// side effect, so it is returned in the determinant parameter if it is not +// null. If the determinant is 0, the returned matrix has all zeroes. +Matrix3x3 InverseWithDeterminant(const Matrix3x3& m, double* determinant); + +// Returns the inverse of the matrix. This function is defined for all the +// typedef'ed Matrix types. If the determinant of the matrix is 0, the returned +// matrix has all zeroes. +Matrix3x3 Inverse(const Matrix3x3& m); + +// Returns a 3x3 Matrix representing a 3D rotation. This creates a Matrix that +// does not work with homogeneous coordinates, so the function name ends in +// "NH". +Matrix3x3 RotationMatrixNH(const Rotation& r); + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ diff --git a/applications/plugins/airmouse/tracking/util/rotation.cc b/applications/plugins/airmouse/tracking/util/rotation.cc new file mode 100644 index 000000000..5c3d09a2b --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/rotation.cc @@ -0,0 +1,117 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rotation.h" + +#include +#include + +#include "vectorutils.h" + +namespace cardboard { + +void Rotation::SetAxisAndAngle(const VectorType& axis, double angle) +{ + VectorType unit_axis = axis; + if (!Normalize(&unit_axis)) { + *this = Identity(); + } else { + double a = angle / 2; + const double s = sin(a); + const VectorType v(unit_axis * s); + SetQuaternion(QuaternionType(v[0], v[1], v[2], cos(a))); + } +} + +Rotation Rotation::FromRotationMatrix(const Matrix3x3& mat) +{ + static const double kOne = 1.0; + static const double kFour = 4.0; + + const double d0 = mat(0, 0), d1 = mat(1, 1), d2 = mat(2, 2); + const double ww = kOne + d0 + d1 + d2; + const double xx = kOne + d0 - d1 - d2; + const double yy = kOne - d0 + d1 - d2; + const double zz = kOne - d0 - d1 + d2; + + const double max = std::max(ww, std::max(xx, std::max(yy, zz))); + if (ww == max) { + const double w4 = sqrt(ww * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(2, 1) - mat(1, 2)) / w4, + (mat(0, 2) - mat(2, 0)) / w4, (mat(1, 0) - mat(0, 1)) / w4, w4 / kFour)); + } + + if (xx == max) { + const double x4 = sqrt(xx * kFour); + return Rotation::FromQuaternion(QuaternionType(x4 / kFour, (mat(0, 1) + mat(1, 0)) / x4, + (mat(0, 2) + mat(2, 0)) / x4, (mat(2, 1) - mat(1, 2)) / x4)); + } + + if (yy == max) { + const double y4 = sqrt(yy * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(0, 1) + mat(1, 0)) / y4, y4 / kFour, + (mat(1, 2) + mat(2, 1)) / y4, (mat(0, 2) - mat(2, 0)) / y4)); + } + + // zz is the largest component. + const double z4 = sqrt(zz * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(0, 2) + mat(2, 0)) / z4, + (mat(1, 2) + mat(2, 1)) / z4, z4 / kFour, (mat(1, 0) - mat(0, 1)) / z4)); +} + +void Rotation::GetAxisAndAngle(VectorType* axis, double* angle) const +{ + VectorType vec(quat_[0], quat_[1], quat_[2]); + if (Normalize(&vec)) { + *angle = 2 * acos(quat_[3]); + *axis = vec; + } else { + *axis = VectorType(1, 0, 0); + *angle = 0.0; + } +} + +Rotation Rotation::RotateInto(const VectorType& from, const VectorType& to) +{ + static const double kTolerance = std::numeric_limits::epsilon() * 100; + + // Directly build the quaternion using the following technique: + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + const double norm_u_norm_v = sqrt(LengthSquared(from) * LengthSquared(to)); + double real_part = norm_u_norm_v + Dot(from, to); + VectorType w; + if (real_part < kTolerance * norm_u_norm_v) { + // If |from| and |to| are exactly opposite, rotate 180 degrees around an + // arbitrary orthogonal axis. Axis normalization can happen later, when we + // normalize the quaternion. + real_part = 0.0; + w = (abs(from[0]) > abs(from[2])) ? VectorType(-from[1], from[0], 0) + : VectorType(0, -from[2], from[1]); + } else { + // Otherwise, build the quaternion the standard way. + w = Cross(from, to); + } + + // Build and return a normalized quaternion. + // Note that Rotation::FromQuaternion automatically performs normalization. + return Rotation::FromQuaternion(QuaternionType(w[0], w[1], w[2], real_part)); +} + +Rotation::VectorType Rotation::operator*(const Rotation::VectorType& v) const +{ + return ApplyToVector(v); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/rotation.h b/applications/plugins/airmouse/tracking/util/rotation.h new file mode 100644 index 000000000..8730cb3b0 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/rotation.h @@ -0,0 +1,156 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_ROTATION_H_ +#define CARDBOARD_SDK_UTIL_ROTATION_H_ + +#include "matrix_3x3.h" +#include "vector.h" +#include "vectorutils.h" + +namespace cardboard { + +// The Rotation class represents a rotation around a 3-dimensional axis. It +// uses normalized quaternions internally to make the math robust. +class Rotation { +public: + // Convenience typedefs for vector of the correct type. + typedef Vector<3> VectorType; + typedef Vector<4> QuaternionType; + + // The default constructor creates an identity Rotation, which has no effect. + Rotation() { + quat_.Set(0, 0, 0, 1); + } + + // Returns an identity Rotation, which has no effect. + static Rotation Identity() { + return Rotation(); + } + + // Sets the Rotation from a quaternion (4D vector), which is first normalized. + void SetQuaternion(const QuaternionType& quaternion) { + quat_ = Normalized(quaternion); + } + + // Returns the Rotation as a normalized quaternion (4D vector). + const QuaternionType& GetQuaternion() const { + return quat_; + } + + // Sets the Rotation to rotate by the given angle around the given axis, + // following the right-hand rule. The axis does not need to be unit + // length. If it is zero length, this results in an identity Rotation. + void SetAxisAndAngle(const VectorType& axis, double angle); + + // Returns the right-hand rule axis and angle corresponding to the + // Rotation. If the Rotation is the identity rotation, this returns the +X + // axis and an angle of 0. + void GetAxisAndAngle(VectorType* axis, double* angle) const; + + // Convenience function that constructs and returns a Rotation given an axis + // and angle. + static Rotation FromAxisAndAngle(const VectorType& axis, double angle) { + Rotation r; + r.SetAxisAndAngle(axis, angle); + return r; + } + + // Convenience function that constructs and returns a Rotation given a + // quaternion. + static Rotation FromQuaternion(const QuaternionType& quat) { + Rotation r; + r.SetQuaternion(quat); + return r; + } + + // Convenience function that constructs and returns a Rotation given a + // rotation matrix R with $R^\top R = I && det(R) = 1$. + static Rotation FromRotationMatrix(const Matrix3x3& mat); + + // Convenience function that constructs and returns a Rotation given Euler + // angles that are applied in the order of rotate-Z by roll, rotate-X by + // pitch, rotate-Y by yaw (same as GetRollPitchYaw). + static Rotation FromRollPitchYaw(double roll, double pitch, double yaw) { + VectorType x(1, 0, 0), y(0, 1, 0), z(0, 0, 1); + return FromAxisAndAngle(z, roll) * (FromAxisAndAngle(x, pitch) * FromAxisAndAngle(y, yaw)); + } + + // Convenience function that constructs and returns a Rotation given Euler + // angles that are applied in the order of rotate-Y by yaw, rotate-X by + // pitch, rotate-Z by roll (same as GetYawPitchRoll). + static Rotation FromYawPitchRoll(double yaw, double pitch, double roll) { + VectorType x(1, 0, 0), y(0, 1, 0), z(0, 0, 1); + return FromAxisAndAngle(y, yaw) * (FromAxisAndAngle(x, pitch) * FromAxisAndAngle(z, roll)); + } + + // Constructs and returns a Rotation that rotates one vector to another along + // the shortest arc. This returns an identity rotation if either vector has + // zero length. + static Rotation RotateInto(const VectorType& from, const VectorType& to); + + // The negation operator returns the inverse rotation. + friend Rotation operator-(const Rotation& r) { + // Because we store normalized quaternions, the inverse is found by + // negating the vector part. + return Rotation(-r.quat_[0], -r.quat_[1], -r.quat_[2], r.quat_[3]); + } + + // Appends a rotation to this one. + Rotation& operator*=(const Rotation& r) { + const QuaternionType& qr = r.quat_; + QuaternionType& qt = quat_; + SetQuaternion(QuaternionType( + qr[3] * qt[0] + qr[0] * qt[3] + qr[2] * qt[1] - qr[1] * qt[2], + qr[3] * qt[1] + qr[1] * qt[3] + qr[0] * qt[2] - qr[2] * qt[0], + qr[3] * qt[2] + qr[2] * qt[3] + qr[1] * qt[0] - qr[0] * qt[1], + qr[3] * qt[3] - qr[0] * qt[0] - qr[1] * qt[1] - qr[2] * qt[2])); + return *this; + } + + // Binary multiplication operator - returns a composite Rotation. + friend const Rotation operator*(const Rotation& r0, const Rotation& r1) { + Rotation r = r0; + r *= r1; + return r; + } + + // Multiply a Rotation and a Vector to get a Vector. + VectorType operator*(const VectorType& v) const; + +private: + // Private constructor that builds a Rotation from quaternion components. + Rotation(double q0, double q1, double q2, double q3) + : quat_(q0, q1, q2, q3) { + } + + // Applies a Rotation to a Vector to rotate the Vector. Method borrowed from: + // http://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ + VectorType ApplyToVector(const VectorType& v) const { + VectorType im(quat_[0], quat_[1], quat_[2]); + VectorType temp = 2.0 * Cross(im, v); + return v + quat_[3] * temp + Cross(im, temp); + } + + // The rotation represented as a normalized quaternion. (Unit quaternions are + // required for constructing rotation matrices, so it makes sense to always + // store them that way.) The vector part is in the first 3 elements, and the + // scalar part is in the last element. + QuaternionType quat_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_ROTATION_H_ diff --git a/applications/plugins/airmouse/tracking/util/vector.h b/applications/plugins/airmouse/tracking/util/vector.h new file mode 100644 index 000000000..64c4f2546 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/vector.h @@ -0,0 +1,251 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_VECTOR_H_ +#define CARDBOARD_SDK_UTIL_VECTOR_H_ + +#include + +namespace cardboard { + +// Geometric N-dimensional Vector class. +template +class Vector { +public: + // The default constructor zero-initializes all elements. + Vector(); + + // Dimension-specific constructors that are passed individual element values. + constexpr Vector(double e0, double e1, double e2); + constexpr Vector(double e0, double e1, double e2, double e3); + + // Constructor for a Vector of dimension N from a Vector of dimension N-1 and + // a scalar of the correct type, assuming N is at least 2. + // constexpr Vector(const Vector& v, double s); + + void Set(double e0, double e1, double e2); // Only when Dimension == 3. + void Set(double e0, double e1, double e2, + double e3); // Only when Dimension == 4. + + // Mutable element accessor. + double& operator[](int index) { + return elem_[index]; + } + + // Element accessor. + double operator[](int index) const { + return elem_[index]; + } + + // Returns a Vector containing all zeroes. + static Vector Zero(); + + // Self-modifying operators. + void operator+=(const Vector& v) { + Add(v); + } + void operator-=(const Vector& v) { + Subtract(v); + } + void operator*=(double s) { + Multiply(s); + } + void operator/=(double s) { + Divide(s); + } + + // Unary negation operator. + Vector operator-() const { + return Negation(); + } + + // Binary operators. + friend Vector operator+(const Vector& v0, const Vector& v1) { + return Sum(v0, v1); + } + friend Vector operator-(const Vector& v0, const Vector& v1) { + return Difference(v0, v1); + } + friend Vector operator*(const Vector& v, double s) { + return Scale(v, s); + } + friend Vector operator*(double s, const Vector& v) { + return Scale(v, s); + } + friend Vector operator*(const Vector& v, const Vector& s) { + return Product(v, s); + } + friend Vector operator/(const Vector& v, double s) { + return Divide(v, s); + } + + // Self-modifying addition. + void Add(const Vector& v); + // Self-modifying subtraction. + void Subtract(const Vector& v); + // Self-modifying multiplication by a scalar. + void Multiply(double s); + // Self-modifying division by a scalar. + void Divide(double s); + + // Unary negation. + Vector Negation() const; + + // Binary component-wise multiplication. + static Vector Product(const Vector& v0, const Vector& v1); + // Binary component-wise addition. + static Vector Sum(const Vector& v0, const Vector& v1); + // Binary component-wise subtraction. + static Vector Difference(const Vector& v0, const Vector& v1); + // Binary multiplication by a scalar. + static Vector Scale(const Vector& v, double s); + // Binary division by a scalar. + static Vector Divide(const Vector& v, double s); + +private: + std::array elem_; +}; +//------------------------------------------------------------------------------ + +template +Vector::Vector() { + for(int i = 0; i < Dimension; i++) { + elem_[i] = 0; + } +} + +template +constexpr Vector::Vector(double e0, double e1, double e2) + : elem_{e0, e1, e2} { +} + +template +constexpr Vector::Vector(double e0, double e1, double e2, double e3) + : elem_{e0, e1, e2, e3} { +} +/* +template <> +constexpr Vector<4>::Vector(const Vector<3>& v, double s) + : elem_{v[0], v[1], v[2], s} {} +*/ +template +void Vector::Set(double e0, double e1, double e2) { + elem_[0] = e0; + elem_[1] = e1; + elem_[2] = e2; +} + +template +void Vector::Set(double e0, double e1, double e2, double e3) { + elem_[0] = e0; + elem_[1] = e1; + elem_[2] = e2; + elem_[3] = e3; +} + +template +Vector Vector::Zero() { + Vector v; + return v; +} + +template +void Vector::Add(const Vector& v) { + for(int i = 0; i < Dimension; i++) { + elem_[i] += v[i]; + } +} + +template +void Vector::Subtract(const Vector& v) { + for(int i = 0; i < Dimension; i++) { + elem_[i] -= v[i]; + } +} + +template +void Vector::Multiply(double s) { + for(int i = 0; i < Dimension; i++) { + elem_[i] *= s; + } +} + +template +void Vector::Divide(double s) { + for(int i = 0; i < Dimension; i++) { + elem_[i] /= s; + } +} + +template +Vector Vector::Negation() const { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = -elem_[i]; + } + return ret; +} + +template +Vector Vector::Product(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] * v1[i]; + } + return ret; +} + +template +Vector Vector::Sum(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] + v1[i]; + } + return ret; +} + +template +Vector Vector::Difference(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] - v1[i]; + } + return ret; +} + +template +Vector Vector::Scale(const Vector& v, double s) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v[i] * s; + } + return ret; +} + +template +Vector Vector::Divide(const Vector& v, double s) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v[i] / s; + } + return ret; +} + +typedef Vector<3> Vector3; +typedef Vector<4> Vector4; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_VECTOR_H_ diff --git a/applications/plugins/airmouse/tracking/util/vectorutils.cc b/applications/plugins/airmouse/tracking/util/vectorutils.cc new file mode 100644 index 000000000..b8f419c04 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/vectorutils.cc @@ -0,0 +1,40 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "vectorutils.h" + +namespace cardboard { + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<3>& v0, const Vector<3>& v1) +{ + return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2]; +} + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<4>& v0, const Vector<4>& v1) +{ + return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] + v0[3] * v1[3]; +} + +// Returns the 3-dimensional cross product of 2 Vectors. Note that this is +// defined only for 3-dimensional Vectors. +Vector<3> Cross(const Vector<3>& v0, const Vector<3>& v1) +{ + return Vector<3>(v0[1] * v1[2] - v0[2] * v1[1], v0[2] * v1[0] - v0[0] * v1[2], + v0[0] * v1[1] - v0[1] * v1[0]); +} + +} // namespace cardboard diff --git a/applications/plugins/airmouse/tracking/util/vectorutils.h b/applications/plugins/airmouse/tracking/util/vectorutils.h new file mode 100644 index 000000000..054236713 --- /dev/null +++ b/applications/plugins/airmouse/tracking/util/vectorutils.h @@ -0,0 +1,76 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_VECTORUTILS_H_ +#define CARDBOARD_SDK_UTIL_VECTORUTILS_H_ + +// +// This file contains free functions that operate on Vector instances. +// + +#include + +#include "vector.h" + +namespace cardboard { + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<3>& v0, const Vector<3>& v1); + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<4>& v0, const Vector<4>& v1); + +// Returns the 3-dimensional cross product of 2 Vectors. Note that this is +// defined only for 3-dimensional Vectors. +Vector<3> Cross(const Vector<3>& v0, const Vector<3>& v1); + +// Returns the square of the length of a Vector. +template +double LengthSquared(const Vector& v) { + return Dot(v, v); +} + +// Returns the geometric length of a Vector. +template +double Length(const Vector& v) { + return sqrt(LengthSquared(v)); +} + +// the Vector untouched and returns false. +template +bool Normalize(Vector* v) { + const double len = Length(*v); + if(len == 0) { + return false; + } else { + (*v) /= len; + return true; + } +} + +// Returns a unit-length version of a Vector. If the given Vector has no +// length, this returns a Zero() Vector. +template +Vector Normalized(const Vector& v) { + Vector result = v; + if(Normalize(&result)) + return result; + else + return Vector::Zero(); +} + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_VECTORUTILS_H_ diff --git a/applications/plugins/airmouse/views/bt_mouse.c b/applications/plugins/airmouse/views/bt_mouse.c new file mode 100644 index 000000000..1a6ea3020 --- /dev/null +++ b/applications/plugins/airmouse/views/bt_mouse.c @@ -0,0 +1,157 @@ +#include "bt_mouse.h" +#include "../tracking/main_loop.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +struct BtMouse { + View* view; + ViewDispatcher* view_dispatcher; + Bt* bt; + NotificationApp* notifications; +}; + +#define MOUSE_MOVE_SHORT 5 +#define MOUSE_MOVE_LONG 20 + +static void bt_mouse_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "Bluetooth Mouse mode"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +static void bt_mouse_process(BtMouse* bt_mouse, InputEvent* event) { + with_view_model( + bt_mouse->view, + void* model, + { + UNUSED(model); + if(event->key == InputKeyUp) { + if(event->type == InputTypePress) { + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); + } else if(event->type == InputTypeRelease) { + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); + } + } else if(event->key == InputKeyDown) { + if(event->type == InputTypePress) { + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_RIGHT); + } else if(event->type == InputTypeRelease) { + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_WHEEL); + } else if(event->type == InputTypeRelease) { + furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_WHEEL); + } + } + }, + true); +} + +static bool bt_mouse_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + furi_hal_bt_hid_mouse_release_all(); + } else { + bt_mouse_process(bt_mouse, event); + consumed = true; + } + + return consumed; +} + +void bt_mouse_connection_status_changed_callback(BtStatus status, void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + bool connected = (status == BtStatusConnected); + if(connected) { + notification_internal_message(bt_mouse->notifications, &sequence_set_blue_255); + } else { + notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); + } + + //with_view_model( + // bt_mouse->view, void * model, { model->connected = connected; }, true); +} + +void bt_mouse_enter_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + bt_mouse->bt = furi_record_open(RECORD_BT); + bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION); + bt_set_status_changed_callback( + bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse); + furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard)); + furi_hal_bt_start_advertising(); + + tracking_begin(); + + view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0); +} + +bool bt_mouse_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + BtMouse* bt_mouse = context; + + tracking_step(furi_hal_bt_hid_mouse_move); + + view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0); + return true; +} + +void bt_mouse_exit_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + tracking_end(); + + notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); + + furi_hal_bt_stop_advertising(); + bt_set_profile(bt_mouse->bt, BtProfileSerial); + + furi_record_close(RECORD_NOTIFICATION); + bt_mouse->notifications = NULL; + furi_record_close(RECORD_BT); + bt_mouse->bt = NULL; +} + +BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) { + BtMouse* bt_mouse = malloc(sizeof(BtMouse)); + bt_mouse->view = view_alloc(); + bt_mouse->view_dispatcher = view_dispatcher; + view_set_context(bt_mouse->view, bt_mouse); + view_set_draw_callback(bt_mouse->view, bt_mouse_draw_callback); + view_set_input_callback(bt_mouse->view, bt_mouse_input_callback); + view_set_enter_callback(bt_mouse->view, bt_mouse_enter_callback); + view_set_custom_callback(bt_mouse->view, bt_mouse_custom_callback); + view_set_exit_callback(bt_mouse->view, bt_mouse_exit_callback); + return bt_mouse; +} + +void bt_mouse_free(BtMouse* bt_mouse) { + furi_assert(bt_mouse); + view_free(bt_mouse->view); + free(bt_mouse); +} + +View* bt_mouse_get_view(BtMouse* bt_mouse) { + furi_assert(bt_mouse); + return bt_mouse->view; +} diff --git a/applications/plugins/airmouse/views/bt_mouse.h b/applications/plugins/airmouse/views/bt_mouse.h new file mode 100644 index 000000000..09153d8fa --- /dev/null +++ b/applications/plugins/airmouse/views/bt_mouse.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +typedef struct BtMouse BtMouse; + +BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher); + +void bt_mouse_free(BtMouse* bt_mouse); + +View* bt_mouse_get_view(BtMouse* bt_mouse); + +void bt_mouse_set_connected_status(BtMouse* bt_mouse, bool connected); diff --git a/applications/plugins/airmouse/views/calibration.c b/applications/plugins/airmouse/views/calibration.c new file mode 100644 index 000000000..a92f68be4 --- /dev/null +++ b/applications/plugins/airmouse/views/calibration.c @@ -0,0 +1,69 @@ +#include "calibration.h" +#include "../tracking/main_loop.h" +#include "../air_mouse.h" + +#include +#include + +struct Calibration { + View* view; + ViewDispatcher* view_dispatcher; +}; + +static void calibration_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "Calibrating..."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Please wait"); +} + +void calibration_enter_callback(void* context) { + furi_assert(context); + Calibration* calibration = context; + calibration_begin(); + view_dispatcher_send_custom_event(calibration->view_dispatcher, 0); +} + +bool calibration_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + Calibration* calibration = context; + + if(calibration_step()) { + view_dispatcher_switch_to_view(calibration->view_dispatcher, AirMouseViewSubmenu); + } else { + view_dispatcher_send_custom_event(calibration->view_dispatcher, 0); + } + + return true; +} + +void calibration_exit_callback(void* context) { + furi_assert(context); + calibration_end(); +} + +Calibration* calibration_alloc(ViewDispatcher* view_dispatcher) { + Calibration* calibration = malloc(sizeof(Calibration)); + calibration->view = view_alloc(); + calibration->view_dispatcher = view_dispatcher; + view_set_context(calibration->view, calibration); + view_set_draw_callback(calibration->view, calibration_draw_callback); + view_set_enter_callback(calibration->view, calibration_enter_callback); + view_set_custom_callback(calibration->view, calibration_custom_callback); + view_set_exit_callback(calibration->view, calibration_exit_callback); + return calibration; +} + +void calibration_free(Calibration* calibration) { + furi_assert(calibration); + view_free(calibration->view); + free(calibration); +} + +View* calibration_get_view(Calibration* calibration) { + furi_assert(calibration); + return calibration->view; +} diff --git a/applications/plugins/airmouse/views/calibration.h b/applications/plugins/airmouse/views/calibration.h new file mode 100644 index 000000000..da44ce0cd --- /dev/null +++ b/applications/plugins/airmouse/views/calibration.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +typedef struct Calibration Calibration; + +Calibration* calibration_alloc(ViewDispatcher* view_dispatcher); + +void calibration_free(Calibration* calibration); + +View* calibration_get_view(Calibration* calibration); diff --git a/applications/plugins/airmouse/views/usb_mouse.c b/applications/plugins/airmouse/views/usb_mouse.c new file mode 100644 index 000000000..2d5f1b0a9 --- /dev/null +++ b/applications/plugins/airmouse/views/usb_mouse.c @@ -0,0 +1,124 @@ +#include "usb_mouse.h" +#include "../tracking/main_loop.h" + +#include +#include +#include +#include + +struct UsbMouse { + View* view; + ViewDispatcher* view_dispatcher; + FuriHalUsbInterface* usb_mode_prev; +}; + +static void usb_mouse_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "USB Mouse mode"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) { + with_view_model( + usb_mouse->view, + void* model, + { + UNUSED(model); + if(event->key == InputKeyUp) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + } + } else if(event->key == InputKeyDown) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_WHEEL); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_WHEEL); + } + } + }, + true); +} + +static bool usb_mouse_input_callback(InputEvent* event, void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + // furi_hal_hid_mouse_release_all(); + } else { + usb_mouse_process(usb_mouse, event); + consumed = true; + } + + return consumed; +} + +void usb_mouse_enter_callback(void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + + usb_mouse->usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); + + tracking_begin(); + + view_dispatcher_send_custom_event(usb_mouse->view_dispatcher, 0); +} + +bool usb_mouse_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + UsbMouse* usb_mouse = context; + + tracking_step(furi_hal_hid_mouse_move); + furi_delay_ms(1); // Magic! Removing this will break the buttons + + view_dispatcher_send_custom_event(usb_mouse->view_dispatcher, 0); + return true; +} + +void usb_mouse_exit_callback(void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + + tracking_end(); + + furi_hal_usb_set_config(usb_mouse->usb_mode_prev, NULL); +} + +UsbMouse* usb_mouse_alloc(ViewDispatcher* view_dispatcher) { + UsbMouse* usb_mouse = malloc(sizeof(UsbMouse)); + usb_mouse->view = view_alloc(); + usb_mouse->view_dispatcher = view_dispatcher; + view_set_context(usb_mouse->view, usb_mouse); + view_set_draw_callback(usb_mouse->view, usb_mouse_draw_callback); + view_set_input_callback(usb_mouse->view, usb_mouse_input_callback); + view_set_enter_callback(usb_mouse->view, usb_mouse_enter_callback); + view_set_custom_callback(usb_mouse->view, usb_mouse_custom_callback); + view_set_exit_callback(usb_mouse->view, usb_mouse_exit_callback); + return usb_mouse; +} + +void usb_mouse_free(UsbMouse* usb_mouse) { + furi_assert(usb_mouse); + view_free(usb_mouse->view); + free(usb_mouse); +} + +View* usb_mouse_get_view(UsbMouse* usb_mouse) { + furi_assert(usb_mouse); + return usb_mouse->view; +} diff --git a/applications/plugins/airmouse/views/usb_mouse.h b/applications/plugins/airmouse/views/usb_mouse.h new file mode 100644 index 000000000..5ce589a69 --- /dev/null +++ b/applications/plugins/airmouse/views/usb_mouse.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +typedef struct UsbMouse UsbMouse; + +UsbMouse* usb_mouse_alloc(ViewDispatcher* view_dispatcher); + +void usb_mouse_free(UsbMouse* usb_mouse); + +View* usb_mouse_get_view(UsbMouse* usb_mouse); diff --git a/applications/plugins/am2320_temp_sensor/application.fam b/applications/plugins/am2320_temp_sensor/application.fam new file mode 100644 index 000000000..9a17539dd --- /dev/null +++ b/applications/plugins/am2320_temp_sensor/application.fam @@ -0,0 +1,14 @@ +App( + appid="am2320_temp_sensor", + name="[AM2320] Temp. Sensor", + apptype=FlipperAppType.EXTERNAL, + entry_point="am_temperature_sensor_app", + cdefines=["APP_AM_TEMPERATURE_SENSOR"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=90, + fap_icon="temperature_sensor.png", + fap_category="GPIO", +) diff --git a/applications/plugins/am2320_temp_sensor/temperature_sensor.c b/applications/plugins/am2320_temp_sensor/temperature_sensor.c new file mode 100644 index 000000000..212014e22 --- /dev/null +++ b/applications/plugins/am2320_temp_sensor/temperature_sensor.c @@ -0,0 +1,336 @@ +/* Flipper Plugin to read the values from a AM2320/AM2321 Sensor */ +/* Created by @xMasterX, original app (was used as template) by Mywk - https://github.com/Mywk */ +/* Lib used as reference: https://github.com/Gozem/am2320/blob/master/am2321.c*/ +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#define TS_DEFAULT_VALUE 0xFFFF + +#define AM2320_ADDRESS (0x5C << 1) + +#define DATA_BUFFER_SIZE 8 + +// External I2C BUS +#define I2C_BUS &furi_hal_i2c_handle_external + +typedef enum { + TSSInitializing, + TSSNoSensor, + TSSPendingUpdate, +} TSStatus; + +typedef enum { + TSEventTypeTick, + TSEventTypeInput, +} TSEventType; + +typedef struct { + TSEventType type; + InputEvent input; +} TSEvent; + +extern const NotificationSequence sequence_blink_red_100; +extern const NotificationSequence sequence_blink_blue_100; + +static TSStatus temperature_sensor_current_status = TSSInitializing; + +// Temperature and Humidity data buffers, ready to print +char ts_data_buffer_temperature_c[DATA_BUFFER_SIZE]; +char ts_data_buffer_temperature_f[DATA_BUFFER_SIZE]; +char ts_data_buffer_relative_humidity[DATA_BUFFER_SIZE]; +char ts_data_buffer_absolute_humidity[DATA_BUFFER_SIZE]; + +// CRC16 calculation +static uint16_t get_crc16(const uint8_t* buf, size_t len) { + uint16_t crc = 0xFFFF; + + while(len--) { + crc ^= (uint16_t)*buf++; + for(unsigned i = 0; i < 8; i++) { + if(crc & 0x0001) { + crc >>= 1; + crc ^= 0xA001; + } else { + crc >>= 1; + } + } + } + + return crc; +} +// Combine bytes +static uint16_t combine_bytes(uint8_t msb, uint8_t lsb) { + return ((uint16_t)msb << 8) | (uint16_t)lsb; +} + +// Executes an I2C wake up, sends command and reads result +// true if fetch was successful, false otherwise +static bool temperature_sensor_get_data(uint8_t* buffer, uint8_t size) { + uint32_t timeout = furi_ms_to_ticks(100); + uint8_t cmdbuffer[3] = {0, 0, 0}; + bool ret = false; + + // Aquire I2C bus + furi_hal_i2c_acquire(I2C_BUS); + + // Wake UP AM2320 (sensor goes to sleep to not warm up and affect the humidity sensor) + furi_hal_i2c_is_device_ready(I2C_BUS, (uint8_t)AM2320_ADDRESS, timeout); + // Check if device woken up then we do next stuff + if(furi_hal_i2c_is_device_ready(I2C_BUS, (uint8_t)AM2320_ADDRESS, timeout)) { + // Wait a bit + furi_delay_us(1000); + + // Prepare command: Addr 0x03, start register = 0x00, number of registers to read = 0x04 + cmdbuffer[0] = 0x03; + cmdbuffer[1] = 0x00; + cmdbuffer[2] = 0x04; + + // Transmit command to read registers + ret = furi_hal_i2c_tx(I2C_BUS, (uint8_t)AM2320_ADDRESS, cmdbuffer, 3, timeout); + + // Wait a bit + furi_delay_us(1600); + if(ret) { + /* + * Read out 8 bytes of data + * Byte 0: Should be Modbus function code 0x03 + * Byte 1: Should be number of registers to read (0x04) + * Byte 2: Humidity msb + * Byte 3: Humidity lsb + * Byte 4: Temperature msb + * Byte 5: Temperature lsb + * Byte 6: CRC lsb byte + * Byte 7: CRC msb byte + */ + ret = furi_hal_i2c_rx(I2C_BUS, (uint8_t)AM2320_ADDRESS, buffer, size, timeout); + } + } + // Release i2c bus + furi_hal_i2c_release(I2C_BUS); + + return ret; +} + +// Fetches temperature and humidity from sensor +// Temperature and humidity must be preallocated +// true if fetch was successful, false otherwise +static bool temperature_sensor_fetch_info(double* temperature, double* humidity) { + *humidity = (float)0; + bool ret = false; + + uint8_t buffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + // Fetch data from sensor + ret = temperature_sensor_get_data(buffer, 8); + + // If we got no result + if(!ret) return false; + + if(buffer[0] != 0x03) return false; // must be 0x03 modbus reply + if(buffer[1] != 0x04) return false; // must be 0x04 number of registers reply + + // Check CRC16 sum, if not correct - return false + uint16_t crcdata = get_crc16(buffer, 6); + uint16_t crcread = combine_bytes(buffer[7], buffer[6]); + if(crcdata != crcread) return false; + + // Combine bytes for temp and humidity + uint16_t temp16 = combine_bytes(buffer[4], buffer[5]); + uint16_t humi16 = combine_bytes(buffer[2], buffer[3]); + + /* Temperature resolution is 16Bit, + * temperature highest bit (Bit15) is equal to 1 indicates a + * negative temperature, the temperature highest bit (Bit15) + * is equal to 0 indicates a positive temperature; + * temperature in addition to the most significant bit (Bit14 ~ Bit0) + * indicates the temperature sensor string value. + * Temperature sensor value is a string of 10 times the + * actual temperature value. + */ + if(temp16 & 0x8000) { + temp16 = -(temp16 & 0x7FFF); + } + + // Prepare output data + *temperature = (float)temp16 / 10.0; + *humidity = (float)humi16 / 10.0; + + return true; +} + +// Draw callback + +static void temperature_sensor_draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "AM2320/AM2321 Sensor"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 62, "Press back to exit."); + + switch(temperature_sensor_current_status) { + case TSSInitializing: + canvas_draw_str(canvas, 2, 30, "Initializing.."); + break; + case TSSNoSensor: + canvas_draw_str(canvas, 2, 30, "No sensor found!"); + break; + case TSSPendingUpdate: { + canvas_draw_str(canvas, 3, 24, "Temperature"); + canvas_draw_str(canvas, 68, 24, "Humidity"); + + // Draw vertical lines + canvas_draw_line(canvas, 61, 16, 61, 50); + canvas_draw_line(canvas, 62, 16, 62, 50); + + // Draw horizontal line + canvas_draw_line(canvas, 2, 27, 122, 27); + + // Draw temperature and humidity values + canvas_draw_str(canvas, 8, 38, ts_data_buffer_temperature_c); + canvas_draw_str(canvas, 42, 38, "C"); + canvas_draw_str(canvas, 8, 48, ts_data_buffer_temperature_f); + canvas_draw_str(canvas, 42, 48, "F"); + canvas_draw_str(canvas, 68, 38, ts_data_buffer_relative_humidity); + canvas_draw_str(canvas, 100, 38, "%"); + canvas_draw_str(canvas, 68, 48, ts_data_buffer_absolute_humidity); + canvas_draw_str(canvas, 100, 48, "g/m3"); + + } break; + default: + break; + } +} + +// Input callback + +static void temperature_sensor_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + TSEvent event = {.type = TSEventTypeInput, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +// Timer callback + +static void temperature_sensor_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + TSEvent event = {.type = TSEventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +// App entry point + +int32_t am_temperature_sensor_app(void* p) { + UNUSED(p); + + furi_hal_power_suppress_charge_enter(); + // Declare our variables and assign variables a default value + TSEvent tsEvent; + bool sensorFound = false; + double celsius, fahrenheit, rel_humidity, abs_humidity = TS_DEFAULT_VALUE; + + // Used for absolute humidity calculation + double vapour_pressure = 0; + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TSEvent)); + + // Register callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, temperature_sensor_draw_callback, NULL); + view_port_input_callback_set(view_port, temperature_sensor_input_callback, event_queue); + + // Create timer and register its callback + FuriTimer* timer = + furi_timer_alloc(temperature_sensor_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency()); + + // Register viewport + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // Used to notify the user by blinking red (error) or blue (fetch successful) + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); + + while(1) { + furi_check(furi_message_queue_get(event_queue, &tsEvent, FuriWaitForever) == FuriStatusOk); + + // Handle events + if(tsEvent.type == TSEventTypeInput) { + // Exit on back key + if(tsEvent.input.key == + InputKeyBack) // We dont check for type here, we can check the type of keypress like: (event.input.type == InputTypeShort) + break; + + } else if(tsEvent.type == TSEventTypeTick) { + // Update sensor data + // Fetch data and set the sensor current status accordingly + sensorFound = temperature_sensor_fetch_info(&celsius, &rel_humidity); + temperature_sensor_current_status = (sensorFound ? TSSPendingUpdate : TSSNoSensor); + + if(sensorFound) { + // Blink blue + notification_message(notifications, &sequence_blink_blue_100); + + if(celsius != TS_DEFAULT_VALUE && rel_humidity != TS_DEFAULT_VALUE) { + // Convert celsius to fahrenheit + fahrenheit = (celsius * 9 / 5) + 32; + + // Calculate absolute humidity - For more info refer to https://github.com/Mywk/FlipperTemperatureSensor/issues/1 + // Calculate saturation vapour pressure first + vapour_pressure = + (double)6.11 * + pow(10, (double)(((double)7.5 * celsius) / ((double)237.3 + celsius))); + // Then the vapour pressure in Pa + vapour_pressure = vapour_pressure * rel_humidity; + // Calculate absolute humidity + abs_humidity = + (double)2.16679 * (double)(vapour_pressure / ((double)273.15 + celsius)); + + // Fill our buffers here, not on the canvas draw callback + snprintf(ts_data_buffer_temperature_c, DATA_BUFFER_SIZE, "%.2f", celsius); + snprintf(ts_data_buffer_temperature_f, DATA_BUFFER_SIZE, "%.2f", fahrenheit); + snprintf( + ts_data_buffer_relative_humidity, DATA_BUFFER_SIZE, "%.2f", rel_humidity); + snprintf( + ts_data_buffer_absolute_humidity, DATA_BUFFER_SIZE, "%.2f", abs_humidity); + } + + } else { + // Reset our variables to their default values + celsius = fahrenheit = rel_humidity = abs_humidity = TS_DEFAULT_VALUE; + + // Blink red + notification_message(notifications, &sequence_blink_red_100); + } + } + + uint32_t wait_ticks = furi_ms_to_ticks(!sensorFound ? 100 : 500); + furi_delay_tick(wait_ticks); + } + + furi_hal_power_suppress_charge_exit(); + // Dobby is freee (free our variables, Flipper will crash if we don't do this!) + furi_timer_free(timer); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + return 0; +} diff --git a/applications/plugins/am2320_temp_sensor/temperature_sensor.png b/applications/plugins/am2320_temp_sensor/temperature_sensor.png new file mode 100644 index 000000000..b6fe6d7fe Binary files /dev/null and b/applications/plugins/am2320_temp_sensor/temperature_sensor.png differ diff --git a/applications/plugins/arkanoid/arkanoid_game.c b/applications/plugins/arkanoid/arkanoid_game.c index 91ae8e736..0b0458424 100644 --- a/applications/plugins/arkanoid/arkanoid_game.c +++ b/applications/plugins/arkanoid/arkanoid_game.c @@ -370,8 +370,6 @@ static void arkanoid_update_timer_callback(FuriMessageQueue* event_queue) { int32_t arkanoid_game_app(void* p) { UNUSED(p); int32_t return_code = 0; - // Set random seed from interrupts - srand(DWT->CYCCNT); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); @@ -448,14 +446,13 @@ int32_t arkanoid_game_app(void* p) { arkanoid_state->ball_state.dy = -1; //start the game flag arkanoid_state->gameStarted = true; - break; } + break; + default: + break; } } } - } else { - // Event timeout - FURI_LOG_D(TAG, "furi_message_queue: Event timeout"); } view_port_update(view_port); diff --git a/applications/plugins/badapple/application.fam b/applications/plugins/badapple/application.fam new file mode 100644 index 000000000..7b8b97e05 --- /dev/null +++ b/applications/plugins/badapple/application.fam @@ -0,0 +1,9 @@ +App( + appid="badapple", + name="Bad Apple", + apptype=FlipperAppType.EXTERNAL, + entry_point="bad_apple_main", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Misc" +) diff --git a/applications/plugins/badapple/bad_apple.c b/applications/plugins/badapple/bad_apple.c new file mode 100644 index 000000000..325d6ea55 --- /dev/null +++ b/applications/plugins/badapple/bad_apple.c @@ -0,0 +1,183 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bad_apple.h" +#include "video_player.h" + +#define TAG "badapple" + +typedef enum { + BadAppleEventTypeInput, + BadAppleEventTypeTick, +} BadAppleEventType; + +typedef struct { + BadAppleEventType type; + InputEvent* input; +} BadAppleEvent; + +// Screen is 128x64 px +static void app_draw_callback(Canvas* canvas, void* ctx) { + BadAppleCtx* inst = ctx; + + canvas_clear(canvas); + canvas_draw_xbm(canvas, VIDEO_X, VIDEO_Y, VIDEO_WIDTH, SCREEN_HEIGHT, inst->framebuffer); + canvas_draw_box(canvas, 0, 0, VIDEO_X, SCREEN_HEIGHT); + canvas_draw_box( + canvas, VIDEO_X + VIDEO_WIDTH, 0, SCREEN_WIDTH - VIDEO_WIDTH - VIDEO_X, SCREEN_HEIGHT); +} + +static void app_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + + FuriMessageQueue* event_queue = ctx; + BadAppleEvent event = {.type = BadAppleEventTypeInput, .input = input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +BadAppleCtx* bad_apple_ctx_alloc(void) { + BadAppleCtx* inst = malloc(sizeof(BadAppleCtx)); + memset(inst, 0, sizeof(BadAppleCtx)); + + if(inst) { + inst->storage = furi_record_open(RECORD_STORAGE); + inst->video_file = storage_file_alloc(inst->storage); + inst->file_buffer_offset = sizeof(inst->file_buffer); + } + + return inst; +} + +void bad_apple_ctx_free(BadAppleCtx* inst) { + if(inst) { + storage_file_free(inst->video_file); + furi_record_close(RECORD_STORAGE); + free(inst); + } +} + +void bad_apple_load_next_video_chunk(BadAppleCtx* inst) { + size_t bytes_to_read = sizeof(inst->file_buffer); + uint8_t* buf_ptr = inst->file_buffer; + while(bytes_to_read > 0) { + uint16_t curr_bytes_to_read = bytes_to_read > (UINT16_MAX / 2 + 1) ? (UINT16_MAX / 2 + 1) : + bytes_to_read; + uint16_t read = storage_file_read(inst->video_file, buf_ptr, curr_bytes_to_read); + bytes_to_read -= read; + buf_ptr += read; + if(read == 0) break; + } + inst->file_buffer_offset = 0; +} + +uint8_t bad_apple_read_byte(BadAppleCtx* inst) { + if(inst->file_buffer_offset >= sizeof(inst->file_buffer)) { + bad_apple_load_next_video_chunk(inst); + } + return inst->file_buffer[inst->file_buffer_offset++]; +} + +void bad_apple_timer_isr(void* ctx) { + FuriMessageQueue* event_queue = ctx; + BadAppleEvent event = {.type = BadAppleEventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); + LL_TIM_ClearFlag_UPDATE(TIM2); +} + +void bad_apple_timer_setup(BadAppleCtx* inst, void* ctx) { + UNUSED(inst); + + LL_TIM_InitTypeDef tim_init = { + .Prescaler = 63999, + .CounterMode = LL_TIM_COUNTERMODE_UP, + .Autoreload = 30, + }; + + LL_TIM_Init(TIM2, &tim_init); + LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, bad_apple_timer_isr, ctx); + LL_TIM_EnableIT_UPDATE(TIM2); +} + +void bad_apple_timer_deinit(void) { + LL_TIM_DisableCounter(TIM2); + LL_TIM_DisableIT_UPDATE(TIM2); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + LL_TIM_DeInit(TIM2); +} + +int32_t bad_apple_main(void* p) { + UNUSED(p); + BadAppleCtx* ctx = bad_apple_ctx_alloc(); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(BadAppleEvent)); + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, app_draw_callback, ctx); + view_port_input_callback_set(view_port, app_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + // Frame rate: 32 FPS + bad_apple_timer_setup(ctx, event_queue); + + bool is_opened = storage_file_open(ctx->video_file, VIDEO_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + if(is_opened) { + BadAppleEvent event; + notification_message(notification, &sequence_display_backlight_enforce_on); + + bool running = true; + LL_TIM_EnableCounter(TIM2); + while(running) { + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + if(event.type == BadAppleEventTypeInput) { + InputEvent* input_event = event.input; + if(input_event->type == InputTypeLong) { + if(input_event->key == InputKeyBack) { + running = false; + } + } + } else if(event.type == BadAppleEventTypeTick) { + // FURI_LOG_D(TAG, "Update frame"); + if(!vp_play_frame(ctx)) { + running = false; + } + } + } + view_port_update(view_port); + } + LL_TIM_DisableCounter(TIM2); + notification_message(notification, &sequence_display_backlight_enforce_auto); + storage_file_close(ctx->video_file); + } + + furi_record_close(RECORD_NOTIFICATION); + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + furi_record_close(RECORD_GUI); + bad_apple_timer_deinit(); + bad_apple_ctx_free(ctx); + + return 0; +} diff --git a/applications/plugins/badapple/bad_apple.h b/applications/plugins/badapple/bad_apple.h new file mode 100644 index 000000000..4b7a92cd1 --- /dev/null +++ b/applications/plugins/badapple/bad_apple.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define VIDEO_WIDTH 104 +#define VIDEO_HEIGHT 80 +#define FILE_BUFFER_SIZE (1024 * 64) +#define VIDEO_PATH EXT_PATH("bad_apple/video.bin") +#define VIDEO_X ((SCREEN_WIDTH - VIDEO_WIDTH) / 2) +#define VIDEO_Y 0 + +typedef struct { + uint8_t framebuffer[VIDEO_HEIGHT * VIDEO_WIDTH / 8]; + uint8_t file_buffer[FILE_BUFFER_SIZE]; + uint32_t file_buffer_offset; + uint32_t frame_write_offset; // bit index + Storage* storage; + File* video_file; +} BadAppleCtx; + +uint8_t bad_apple_read_byte(BadAppleCtx* inst); diff --git a/applications/plugins/badapple/video_player.c b/applications/plugins/badapple/video_player.c new file mode 100644 index 000000000..945837d6b --- /dev/null +++ b/applications/plugins/badapple/video_player.c @@ -0,0 +1,137 @@ +#include + +#include + +#include "bad_apple.h" +#include "video_player.h" + +#define TAG "video_player" + +static void vp_decode_rle(BadAppleCtx* ctx); +static void vp_decode_delta(BadAppleCtx* ctx); + +int vp_play_frame(BadAppleCtx* ctx) { + // Check frame type + // FURI_LOG_D(TAG, "Buffer offset: %04lx", ctx->file_buffer_offset); + uint8_t b = bad_apple_read_byte(ctx); + // FURI_LOG_D(TAG, "Frame byte: %02x", b); + switch(b) { + case 1: // PFrame (delta) + vp_decode_delta(ctx); + break; + case 2: // IFrame (rle) + vp_decode_rle(ctx); + break; + case 3: // DFrame (duplicate) + break; + case 4: // next page (not supported) + case 5: // end + default: + return 0; + } + + return 1; +} + +static inline void vp_write_pixel(BadAppleCtx* ctx, bool color) { + if(color) { + // White + ctx->framebuffer[ctx->frame_write_offset / 8] &= ~(1 << (ctx->frame_write_offset % 8)); + } else { + // Black + ctx->framebuffer[ctx->frame_write_offset / 8] |= (1 << (ctx->frame_write_offset % 8)); + } + ++ctx->frame_write_offset; +} + +static inline void vp_set_rect_fast(BadAppleCtx* ctx, int x, int y) { + ctx->frame_write_offset = y * VIDEO_WIDTH + x; +} + +static void vp_decode_rle(BadAppleCtx* ctx) { + int i = 0; + int repeat_byte = bad_apple_read_byte(ctx); + + // Set rect to video area + vp_set_rect_fast(ctx, 0, 0); + + while(i < VIDEO_WIDTH * VIDEO_HEIGHT) { + int count; + unsigned pixels; + int j; + int b = bad_apple_read_byte(ctx); + + if(b == repeat_byte) { + count = bad_apple_read_byte(ctx); + if(count == 0) count = 256; + pixels = bad_apple_read_byte(ctx); + } else { + count = 1; + pixels = (unsigned)b; + } + + for(j = 0; j < count; ++j) { + bool out_color; + int k; + unsigned loop_pixels = pixels; + + for(k = 0; k < 8; ++k) { + if(loop_pixels & 0x80) + out_color = COLOR_WHITE; + else + out_color = COLOR_BLACK; + + vp_write_pixel(ctx, out_color); + loop_pixels <<= 1; + ++i; + } + } + } +} + +static void vp_decode_delta(BadAppleCtx* ctx) { + int frame_header[(VIDEO_HEIGHT + 7) / 8]; + uint i; + int fh_byte = 0; + int fh_index = 0; + + for(i = 0; i < sizeof(frame_header) / sizeof(frame_header[0]); ++i) { + frame_header[i] = bad_apple_read_byte(ctx); + } + + for(i = 0; i < VIDEO_HEIGHT; ++i) { + if(i % 8 == 0) fh_byte = frame_header[fh_index++]; + + if(fh_byte & 0x80) { + int j; + int sl_byte = 0; + + for(j = 0; j < VIDEO_WIDTH;) { + if(j % (8 * 8) == 0) sl_byte = bad_apple_read_byte(ctx); + + if(sl_byte & 0x80) { + unsigned out_color; + int k; + unsigned pixel_group = bad_apple_read_byte(ctx); + + // Note: this needs to be revised for screen width not multiple of 8 + vp_set_rect_fast(ctx, j, i); + + for(k = 0; k < 8 && j < VIDEO_WIDTH; ++k, ++j) { + if(pixel_group & 0x80) + out_color = COLOR_WHITE; + else + out_color = COLOR_BLACK; + + vp_write_pixel(ctx, out_color); + pixel_group <<= 1; + } + } else { + j += 8; + } + sl_byte <<= 1; + } + } + fh_byte <<= 1; + } +} diff --git a/applications/plugins/badapple/video_player.h b/applications/plugins/badapple/video_player.h new file mode 100644 index 000000000..c81a6c2e8 --- /dev/null +++ b/applications/plugins/badapple/video_player.h @@ -0,0 +1,6 @@ +#pragma once + +#define COLOR_WHITE true +#define COLOR_BLACK false + +int vp_play_frame(BadAppleCtx* ctx); diff --git a/applications/plugins/barcode_generator/application.fam b/applications/plugins/barcode_generator/application.fam index 043d653b1..97dc9acef 100644 --- a/applications/plugins/barcode_generator/application.fam +++ b/applications/plugins/barcode_generator/application.fam @@ -1,8 +1,8 @@ App( - appid="UPC_Generator", - name="UPC-A Generator", + appid="Barcode_Generator", + name="Barcode Generator", apptype=FlipperAppType.EXTERNAL, - entry_point="barcode_UPCA_generator_app", + entry_point="barcode_generator_app", cdefines=["APP_BARCODE_GEN"], requires=[ "gui", diff --git a/applications/plugins/barcode_generator/barcode_generator.c b/applications/plugins/barcode_generator/barcode_generator.c index d03d45495..776862531 100644 --- a/applications/plugins/barcode_generator/barcode_generator.c +++ b/applications/plugins/barcode_generator/barcode_generator.c @@ -3,229 +3,75 @@ #include #include -#define BARCODE_STARTING_POS 16 -#define BARCODE_HEIGHT 50 -#define BARCODE_Y_START 3 -#define BARCODE_TEXT_OFFSET 9 +#include "barcode_generator.h" -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; +static BarcodeType* barcodeTypes[NUMBER_OF_BARCODE_TYPES]; -typedef struct { - EventType type; - InputEvent input; -} PluginEvent; +void init_types() { + BarcodeType* upcA = malloc(sizeof(BarcodeType)); + upcA->name = "UPC-A"; + upcA->numberOfDigits = 12; + upcA->startPos = 19; + barcodeTypes[0] = upcA; -typedef struct { - int barcodeNumeral[12]; //The current barcode number - int editingIndex; //The index of the editing symbol - int menuIndex; //The index of the menu cursor - int modeIndex; //Set to 0 for view, 1 for edit, 2 for menu - bool doParityCalculation; //Should do parity check? -} PluginState; + BarcodeType* ean8 = malloc(sizeof(BarcodeType)); + ean8->name = "EAN-8"; + ean8->numberOfDigits = 8; + ean8->startPos = 33; + barcodeTypes[1] = ean8; +} -void number_0( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #0 on left is OOOIIOI +void draw_digit(Canvas* canvas, int digit, bool rightHand, int startingPosition) { + char digitStr[2]; + snprintf(digitStr, 2, "%u", digit); canvas_set_color(canvas, ColorBlack); canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "0"); + canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, digitStr); if(rightHand) { canvas_set_color(canvas, ColorBlack); } else { canvas_set_color(canvas, ColorWhite); } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 3, BARCODE_HEIGHT); //OOO - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 6, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I -} -void number_1( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #1 on left is OOIIOOI - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "1"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); + int count = 0; + for(int i = 0; i < 4; i++) { + canvas_draw_box( + canvas, startingPosition + count, BARCODE_Y_START, DIGITS[digit][i], BARCODE_HEIGHT); + canvas_invert_color(canvas); + count += DIGITS[digit][i]; } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 2, BARCODE_HEIGHT); //OO - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 2, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 4, BARCODE_Y_START, 2, BARCODE_HEIGHT); //OO - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 6, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I } -void number_2( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #2 on left is OOIOOII - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "2"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); + +int get_digit_position(int index, BarcodeType* type) { + int pos = type->startPos + index * 7; + if(index >= type->numberOfDigits / 2) { + pos += 5; } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 2, BARCODE_HEIGHT); //OO - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 2, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 2, BARCODE_HEIGHT); //OO - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II + return pos; } -void number_3( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #3 on left is OIIIIOI - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "3"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 4, BARCODE_HEIGHT); //IIII - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 6, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I + +int get_menu_text_location(int index) { + return 20 + 10 * index; } -void number_4( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #4 on left is OIOOOII - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "4"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); + +int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) { + int checkDigit = 0; + //add all odd positions. Confusing because 0index + for(int i = 0; i < type->numberOfDigits - 1; i += 2) { + checkDigit += plugin_state->barcodeNumeral[i]; } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 2, BARCODE_Y_START, 3, BARCODE_HEIGHT); //OOO - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II -} -void number_5( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #5 on left is OIIOOOI - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "5"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); + + checkDigit = checkDigit * 3; //times 3 + + //add all even positions to above. Confusing because 0index + for(int i = 1; i < type->numberOfDigits - 1; i += 2) { + checkDigit += plugin_state->barcodeNumeral[i]; } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 3, BARCODE_HEIGHT); //OOO - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 6, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I -} -void number_6( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #6 on left is OIOIIII - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "6"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 2, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 4, BARCODE_HEIGHT); //IIII -} -void number_7( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #7 on left is OIIIOII - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "7"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 3, BARCODE_HEIGHT); //III - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 4, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II -} -void number_8( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #8 on left is OIIOIII - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "8"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 4, BARCODE_Y_START, 3, BARCODE_HEIGHT); //III -} -void number_9( - Canvas* canvas, - bool rightHand, - int startingPosition) { //UPC Code for #9 on left is OOOIOII - canvas_set_color(canvas, ColorBlack); - canvas_draw_str( - canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "9"); - if(rightHand) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 3, BARCODE_HEIGHT); //OOO - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 4, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O - canvas_invert_color(canvas); - canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II + + checkDigit = checkDigit % 10; //mod 10 + + //if m = 0 then x12 = 0, otherwise x12 is 10 - m + return (10 - checkDigit) % 10; } static void render_callback(Canvas* const canvas, void* ctx) { @@ -234,144 +80,79 @@ static void render_callback(Canvas* const canvas, void* ctx) { return; } - //I originally had all of these values being generated at runtime by math, but that kept giving me trouble. - int editingMarkerPosition[12] = { - 19, - 26, - 33, - 40, - 47, - 54, - 66, - 73, - 80, - 87, - 94, - 101, - }; - int menuTextLocations[6] = { - 20, - 30, - 40, - 50, - 60, - 70, - }; - - if(plugin_state->modeIndex == 2) { //if in the menu + if(plugin_state->mode == MenuMode) { canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignCenter, "MENU"); canvas_draw_frame(canvas, 50, 0, 29, 11); //box around Menu canvas_draw_str_aligned( - canvas, 64, menuTextLocations[0], AlignCenter, AlignCenter, "View"); + canvas, 64, get_menu_text_location(0), AlignCenter, AlignCenter, "View"); canvas_draw_str_aligned( - canvas, 64, menuTextLocations[1], AlignCenter, AlignCenter, "Edit"); + canvas, 64, get_menu_text_location(1), AlignCenter, AlignCenter, "Edit"); canvas_draw_str_aligned( - canvas, 64, menuTextLocations[2], AlignCenter, AlignCenter, "Parity?"); + canvas, 64, get_menu_text_location(2), AlignCenter, AlignCenter, "Parity?"); - canvas_draw_frame(canvas, 81, menuTextLocations[2] - 2, 6, 6); + canvas_draw_frame(canvas, 83, get_menu_text_location(2) - 3, 6, 6); if(plugin_state->doParityCalculation == true) { - canvas_draw_box(canvas, 83, menuTextLocations[2], 2, 2); + canvas_draw_box(canvas, 85, get_menu_text_location(2) - 1, 2, 2); } canvas_draw_str_aligned( - canvas, 64, menuTextLocations[3], AlignCenter, AlignCenter, "TODO"); + canvas, + 64, + get_menu_text_location(3), + AlignCenter, + AlignCenter, + (barcodeTypes[plugin_state->barcodeTypeIndex])->name); canvas_draw_disc( - canvas, 40, menuTextLocations[plugin_state->menuIndex], 2); //draw menu cursor - } + canvas, 40, get_menu_text_location(plugin_state->menuIndex) - 1, 2); //draw menu cursor + } else { + BarcodeType* type = barcodeTypes[plugin_state->barcodeTypeIndex]; - if(plugin_state->modeIndex != 2) { //if not in the menu canvas_set_color(canvas, ColorBlack); - //canvas_draw_glyph(canvas, 115, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, 'M'); - canvas_draw_box(canvas, BARCODE_STARTING_POS, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); - //canvas_draw_box(canvas, BARCODE_STARTING_POS + 1, 1, 1, 50); //left blank on purpose + canvas_draw_box(canvas, type->startPos - 3, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); canvas_draw_box( canvas, - (BARCODE_STARTING_POS + 2), + (type->startPos - 1), BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); //start saftey - for(int index = 0; index < 12; index++) { + + for(int index = 0; index < type->numberOfDigits; index++) { bool isOnRight = false; - if(index >= 6) { + if(index >= type->numberOfDigits / 2) { isOnRight = true; } - if((index == 11) && (plugin_state->doParityCalculation)) { //calculate the check digit - int checkDigit = - plugin_state->barcodeNumeral[0] + plugin_state->barcodeNumeral[2] + - plugin_state->barcodeNumeral[4] + plugin_state->barcodeNumeral[6] + - plugin_state->barcodeNumeral[8] + plugin_state->barcodeNumeral[10]; - //add all odd positions Confusing because 0index - checkDigit = checkDigit * 3; //times 3 - checkDigit += plugin_state->barcodeNumeral[1] + plugin_state->barcodeNumeral[3] + - plugin_state->barcodeNumeral[5] + plugin_state->barcodeNumeral[7] + - plugin_state->barcodeNumeral[9]; - //add all even positions to above. Confusing because 0index - checkDigit = checkDigit % 10; //mod 10 - //if m - 0 then x12 = 0, otherwise x12 is 10 - m - if(checkDigit == 0) { - plugin_state->barcodeNumeral[11] = 0; - } else { - checkDigit = 10 - checkDigit; - plugin_state->barcodeNumeral[11] = checkDigit; - } - } - switch(plugin_state->barcodeNumeral[index]) { - case 0: - number_0(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 1: - number_1(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 2: - number_2(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 3: - number_3(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 4: - number_4(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 5: - number_5(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 6: - number_6(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 7: - number_7(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 8: - number_8(canvas, isOnRight, editingMarkerPosition[index]); - break; - case 9: - number_9(canvas, isOnRight, editingMarkerPosition[index]); - break; + if((index == type->numberOfDigits - 1) && + (plugin_state->doParityCalculation)) { //calculate the check digit + int checkDigit = calculate_check_digit(plugin_state, type); + plugin_state->barcodeNumeral[type->numberOfDigits - 1] = checkDigit; } + int digitPosition = + get_digit_position(index, barcodeTypes[plugin_state->barcodeTypeIndex]); + draw_digit(canvas, plugin_state->barcodeNumeral[index], isOnRight, digitPosition); } canvas_set_color(canvas, ColorBlack); - //canvas_draw_box(canvas, BARCODE_STARTING_POS + 45, BARCODE_Y_START, 1, BARCODE_HEIGHT); - canvas_draw_box(canvas, BARCODE_STARTING_POS + 46, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); - //canvas_draw_box(canvas, BARCODE_STARTING_POS + 47, BARCODE_Y_START, 1, BARCODE_HEIGHT); - canvas_draw_box(canvas, BARCODE_STARTING_POS + 48, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); - //canvas_draw_box(canvas, BARCODE_STARTING_POS + 49, BARCODE_Y_START, 1, BARCODE_HEIGHT); + canvas_draw_box(canvas, 62, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); + canvas_draw_box(canvas, 64, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); - if(plugin_state->modeIndex == 1) { + if(plugin_state->mode == EditMode) { canvas_set_color(canvas, ColorBlack); canvas_draw_box( canvas, - editingMarkerPosition[plugin_state->editingIndex], + get_digit_position( + plugin_state->editingIndex, barcodeTypes[plugin_state->barcodeTypeIndex]) - + 1, 63, 7, 1); //draw editing cursor } + int endSafetyPosition = get_digit_position(type->numberOfDigits - 1, type) + 7; canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, BARCODE_STARTING_POS + 92, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); - //canvas_draw_box(canvas, 14, 1, 1, 50); //left blank on purpose + canvas_draw_box(canvas, endSafetyPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); canvas_draw_box( canvas, - (BARCODE_STARTING_POS + 2) + 92, + (endSafetyPosition + 2), BARCODE_Y_START, 1, BARCODE_HEIGHT + 2); //end safety @@ -387,31 +168,140 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu furi_message_queue_put(event_queue, &event, FuriWaitForever); } -static void barcode_UPCA_generator_state_init(PluginState* const plugin_state) { - int i; - for(i = 0; i < 12; ++i) { - if(i > 9) { - plugin_state->barcodeNumeral[i] = i - 10; - } else if(i < 10) { - plugin_state->barcodeNumeral[i] = i; - } +static void barcode_generator_state_init(PluginState* const plugin_state) { + for(int i = 0; i < 12; ++i) { + plugin_state->barcodeNumeral[i] = i % 10; } plugin_state->editingIndex = 0; - plugin_state->modeIndex = 0; + plugin_state->mode = ViewMode; plugin_state->doParityCalculation = true; - plugin_state->menuIndex = 0; + plugin_state->menuIndex = MENU_INDEX_VIEW; + plugin_state->barcodeTypeIndex = 0; } -int32_t barcode_UPCA_generator_app(void* p) { +static bool handle_key_press_view(InputKey key, PluginState* plugin_state) { + switch(key) { + case InputKeyOk: + case InputKeyBack: + plugin_state->mode = MenuMode; + break; + + default: + break; + } + + return true; +} + +static bool handle_key_press_edit(InputKey key, PluginState* plugin_state) { + int barcodeMaxIndex = plugin_state->doParityCalculation ? + barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits - 1 : + barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits; + + switch(key) { + case InputKeyUp: + plugin_state->barcodeNumeral[plugin_state->editingIndex] = + (plugin_state->barcodeNumeral[plugin_state->editingIndex] + 1) % 10; + break; + + case InputKeyDown: + plugin_state->barcodeNumeral[plugin_state->editingIndex] = + (plugin_state->barcodeNumeral[plugin_state->editingIndex] == 0) ? + 9 : + plugin_state->barcodeNumeral[plugin_state->editingIndex] - 1; + break; + + case InputKeyRight: + plugin_state->editingIndex = (plugin_state->editingIndex + 1) % barcodeMaxIndex; + break; + + case InputKeyLeft: + plugin_state->editingIndex = (plugin_state->editingIndex == 0) ? + barcodeMaxIndex - 1 : + plugin_state->editingIndex - 1; + break; + + case InputKeyOk: + case InputKeyBack: + plugin_state->mode = MenuMode; + break; + + default: + break; + } + + return true; +} + +static bool handle_key_press_menu(InputKey key, PluginState* plugin_state) { + switch(key) { + case InputKeyUp: + plugin_state->menuIndex = (plugin_state->menuIndex == MENU_INDEX_VIEW) ? + MENU_INDEX_TYPE : + plugin_state->menuIndex - 1; + break; + + case InputKeyDown: + plugin_state->menuIndex = (plugin_state->menuIndex + 1) % 4; + break; + + case InputKeyRight: + if(plugin_state->menuIndex == MENU_INDEX_TYPE) { + plugin_state->barcodeTypeIndex = + (plugin_state->barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ? + 0 : + plugin_state->barcodeTypeIndex + 1; + } else if(plugin_state->menuIndex == MENU_INDEX_PARITY) { + plugin_state->doParityCalculation = !plugin_state->doParityCalculation; + } + break; + case InputKeyLeft: + if(plugin_state->menuIndex == MENU_INDEX_TYPE) { + plugin_state->barcodeTypeIndex = (plugin_state->barcodeTypeIndex == 0) ? + NUMBER_OF_BARCODE_TYPES - 1 : + plugin_state->barcodeTypeIndex - 1; + } else if(plugin_state->menuIndex == MENU_INDEX_PARITY) { + plugin_state->doParityCalculation = !plugin_state->doParityCalculation; + } + break; + + case InputKeyOk: + if(plugin_state->menuIndex == MENU_INDEX_VIEW) { + plugin_state->mode = ViewMode; + } else if(plugin_state->menuIndex == MENU_INDEX_EDIT) { + plugin_state->mode = EditMode; + } else if(plugin_state->menuIndex == MENU_INDEX_PARITY) { + plugin_state->doParityCalculation = !plugin_state->doParityCalculation; + } else if(plugin_state->menuIndex == MENU_INDEX_TYPE) { + plugin_state->barcodeTypeIndex = + (plugin_state->barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ? + 0 : + plugin_state->barcodeTypeIndex + 1; + } + break; + + case InputKeyBack: + return false; + + default: + break; + } + + return true; +} + +int32_t barcode_generator_app(void* p) { UNUSED(p); - //testing + + init_types(); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); - barcode_UPCA_generator_state_init(plugin_state); + barcode_generator_state_init(plugin_state); ValueMutex state_mutex; if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { - FURI_LOG_E("barcode_UPCA_generator", "cannot create mutex\r\n"); + FURI_LOG_E("barcode_generator", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); free(plugin_state); return 255; @@ -430,105 +320,25 @@ int32_t barcode_UPCA_generator_app(void* p) { for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); - int barcodeMaxIndex; - if(plugin_state->doParityCalculation == true) { - barcodeMaxIndex = 11; - } - if(plugin_state->doParityCalculation == false) { - barcodeMaxIndex = 12; - } if(event_status == FuriStatusOk) { // press events - if(event.type == EventTypeKey) { - if((event.input.type == InputTypePress) || (event.input.type == InputTypeRepeat)) { - switch(event.input.key) { - case InputKeyUp: - if(plugin_state->modeIndex == 1) { //if edit mode - plugin_state->barcodeNumeral[plugin_state->editingIndex]++; - } - if(plugin_state->barcodeNumeral[plugin_state->editingIndex] > 9) { - plugin_state->barcodeNumeral[plugin_state->editingIndex] = 0; - } - if(plugin_state->modeIndex == 2) { //if menu mode - plugin_state->menuIndex--; - } - if(plugin_state->menuIndex < 0) { - plugin_state->menuIndex = 3; - } - break; - case InputKeyDown: - if(plugin_state->modeIndex == 1) { - plugin_state->barcodeNumeral[plugin_state->editingIndex]--; - } - if(plugin_state->barcodeNumeral[plugin_state->editingIndex] < 0) { - plugin_state->barcodeNumeral[plugin_state->editingIndex] = 9; - } - if(plugin_state->modeIndex == 2) { //if menu mode - plugin_state->menuIndex++; - } - if(plugin_state->menuIndex > 3) { - plugin_state->menuIndex = 0; - } - break; - case InputKeyRight: - if(plugin_state->modeIndex == 1) { - plugin_state->editingIndex++; - } - if(plugin_state->editingIndex >= barcodeMaxIndex) { - plugin_state->editingIndex = 0; - } - break; - case InputKeyLeft: - if(plugin_state->modeIndex == 1) { - plugin_state->editingIndex--; - } - if(plugin_state->editingIndex < 0) { - plugin_state->editingIndex = barcodeMaxIndex - 1; - } - break; - case InputKeyOk: - if((plugin_state->modeIndex == 0) || - (plugin_state->modeIndex == 1)) { //if normal or edit more, open menu - plugin_state->modeIndex = 2; - break; - } else if( - (plugin_state->modeIndex == 2) && - (plugin_state->menuIndex == - 1)) { //if hits select in menu, while index is 1. edit mode - plugin_state->modeIndex = 1; - break; - } else if( - (plugin_state->modeIndex == 2) && - (plugin_state->menuIndex == - 0)) { //if hits select in menu, while index is 0. view mode - plugin_state->modeIndex = 0; - break; - } else if( - (plugin_state->modeIndex == 2) && - (plugin_state->menuIndex == - 2)) { //if hits select in menu, while index is 2. Parity switch - plugin_state->doParityCalculation = - !plugin_state->doParityCalculation; //invert bool - break; - } else { - break; - } - - case InputKeyBack: - if(plugin_state->modeIndex == 0) { - processing = false; - } - if(plugin_state->modeIndex == 2) { - plugin_state->modeIndex = 0; - } - break; - } + if(event.type == EventTypeKey && + ((event.input.type == InputTypePress) || (event.input.type == InputTypeRepeat))) { + switch(plugin_state->mode) { + case ViewMode: + processing = handle_key_press_view(event.input.key, plugin_state); + break; + case EditMode: + processing = handle_key_press_edit(event.input.key, plugin_state); + break; + case MenuMode: + processing = handle_key_press_menu(event.input.key, plugin_state); + break; + default: + break; } } - } else { - FURI_LOG_D("barcode_UPCA_generator", "osMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); diff --git a/applications/plugins/barcode_generator/barcode_generator.h b/applications/plugins/barcode_generator/barcode_generator.h new file mode 100644 index 000000000..7e1a078e8 --- /dev/null +++ b/applications/plugins/barcode_generator/barcode_generator.h @@ -0,0 +1,52 @@ +#define BARCODE_HEIGHT 50 +#define BARCODE_Y_START 3 +#define BARCODE_TEXT_OFFSET 9 +#define NUMBER_OF_BARCODE_TYPES 2 +#define MENU_INDEX_VIEW 0 +#define MENU_INDEX_EDIT 1 +#define MENU_INDEX_PARITY 2 +#define MENU_INDEX_TYPE 3 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef enum { + ViewMode, + EditMode, + MenuMode, +} Mode; + +typedef struct { + char* name; + int numberOfDigits; + int startPos; +} BarcodeType; + +typedef struct { + int barcodeNumeral[12]; //The current barcode number + int editingIndex; //The index of the editing symbol + int menuIndex; //The index of the menu cursor + Mode mode; //View, edit or menu + bool doParityCalculation; //Should do parity check? + int barcodeTypeIndex; +} PluginState; + +static const int DIGITS[10][4] = { + {3, 2, 1, 1}, + {2, 2, 2, 1}, + {2, 1, 2, 2}, + {1, 4, 1, 1}, + {1, 1, 3, 2}, + {1, 2, 3, 1}, + {1, 1, 1, 4}, + {1, 3, 1, 2}, + {1, 2, 1, 3}, + {3, 1, 1, 2}, +}; diff --git a/applications/plugins/blackjack/application.fam b/applications/plugins/blackjack/application.fam index 798e240e3..8230cd047 100644 --- a/applications/plugins/blackjack/application.fam +++ b/applications/plugins/blackjack/application.fam @@ -1,12 +1,13 @@ App( - appid="blackjack", - name="Blackjack", + appid="BlackJack", + name="BlackJack", apptype=FlipperAppType.EXTERNAL, entry_point="blackjack_app", cdefines=["APP_BLACKJACK"], - requires=["gui"], - stack_size=1 * 1024, + requires=["gui","storage","canvas"], + stack_size=2 * 1024, order=30, fap_icon="blackjack_10px.png", fap_category="Games", + fap_icon_assets="assets" ) \ No newline at end of file diff --git a/applications/plugins/blackjack/assets/blackjack.png b/applications/plugins/blackjack/assets/blackjack.png new file mode 100644 index 000000000..bb367f28e Binary files /dev/null and b/applications/plugins/blackjack/assets/blackjack.png differ diff --git a/applications/plugins/blackjack/assets/card_graphics.png b/applications/plugins/blackjack/assets/card_graphics.png new file mode 100644 index 000000000..8b00e351f Binary files /dev/null and b/applications/plugins/blackjack/assets/card_graphics.png differ diff --git a/applications/plugins/blackjack/assets/endscreen.png b/applications/plugins/blackjack/assets/endscreen.png new file mode 100644 index 000000000..7a3abc927 Binary files /dev/null and b/applications/plugins/blackjack/assets/endscreen.png differ diff --git a/applications/plugins/blackjack/blackjack.c b/applications/plugins/blackjack/blackjack.c index 798fa6e5a..73d393f8b 100644 --- a/applications/plugins/blackjack/blackjack.c +++ b/applications/plugins/blackjack/blackjack.c @@ -2,360 +2,620 @@ #include #include #include -#include +#include +#include +#include +#include "util.h" #include "defines.h" -#include "card.h" +#include "common/card.h" +#include "common/dml.h" +#include "common/queue.h" #include "util.h" #include "ui.h" -#define APP_NAME "Blackjack" -#define STARTING_MONEY 200 +#include "BlackJack_icons.h" + #define DEALER_MAX 17 +void start_round(GameState* game_state); -void start_round(GameState *game_state); - -static void draw_ui(Canvas *const canvas, const GameState *game_state) { +void init(GameState* game_state); +static void draw_ui(Canvas* const canvas, const GameState* game_state) { draw_money(canvas, game_state->player_score); - draw_score(canvas, true, handCount(game_state->player_cards, game_state->player_card_count)); + draw_score(canvas, true, hand_count(game_state->player_cards, game_state->player_card_count)); - if (!game_state->animating && game_state->state == GameStatePlay) { - draw_play_menu(canvas, game_state); + if(!game_state->queue_state.running && game_state->state == GameStatePlay) { + render_menu(game_state->menu, canvas, 2, 47); } } +static void render_callback(Canvas* const canvas, void* ctx) { + const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); -static void render_callback(Canvas *const canvas, void *ctx) { - const GameState *game_state = acquire_mutex((ValueMutex *) ctx, 25); - - if (game_state == NULL) { + if(game_state == NULL) { return; } + canvas_set_color(canvas, ColorBlack); canvas_draw_frame(canvas, 0, 0, 128, 64); - switch (game_state->state) { - case GameStateStart: - case GameStateGameOver: - draw_message_scene(canvas, game_state); - break; - case GameStatePlay: - draw_player_scene(canvas, game_state); - break; - case GameStateDealer: - draw_dealer_scene(canvas, game_state); - break; + if(game_state->state == GameStateStart) { + canvas_draw_icon(canvas, 0, 0, &I_blackjack); } - if (game_state->state != GameStateStart && game_state->state != GameStateGameOver) { - animateQueue(game_state, canvas); - draw_ui(canvas, game_state); + if(game_state->state == GameStateGameOver) { + canvas_draw_icon(canvas, 0, 0, &I_endscreen); } - release_mutex((ValueMutex *) ctx, game_state); + if(game_state->state == GameStatePlay || game_state->state == GameStateDealer) { + if(game_state->state == GameStatePlay) + draw_player_scene(canvas, game_state); + else + draw_dealer_scene(canvas, game_state); + render_queue(&(game_state->queue_state), game_state, canvas); + draw_ui(canvas, game_state); + } else if(game_state->state == GameStateSettings) { + settings_page(canvas, game_state); + } + + release_mutex((ValueMutex*)ctx, game_state); } //region card draw -Card draw_card(GameState *game_state) { +Card draw_card(GameState* game_state) { Card c = game_state->deck.cards[game_state->deck.index]; game_state->deck.index++; return c; } -char *letters[4] = {"spade", "hearth", "diamond", "club"}; - -void drawPlayerCard(GameState *game_state) { +void drawPlayerCard(void* ctx) { + GameState* game_state = ctx; Card c = draw_card(game_state); game_state->player_cards[game_state->player_card_count] = c; game_state->player_card_count++; + if(game_state->player_score < game_state->settings.round_price || game_state->doubled) { + set_menu_state(game_state->menu, 0, false); + } } -void drawDealerCard(GameState *game_state) { +void drawDealerCard(void* ctx) { + GameState* game_state = ctx; Card c = draw_card(game_state); game_state->dealer_cards[game_state->dealer_card_count] = c; game_state->dealer_card_count++; - FURI_LOG_D(APP_NAME, "drawing dealer %s %i", letters[c.pip], c.character + 2); } //endregion //region queue callbacks -void to_lose_state(const GameState *game_state, Canvas *const canvas) { - UNUSED(game_state); - popupFrame(canvas); +void to_lose_state(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + if(game_state->settings.message_duration == 0) return; + popup_frame(canvas); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You lost"); } -void to_bust_state(const GameState *game_state, Canvas *const canvas) { - UNUSED(game_state); - popupFrame(canvas); +void to_bust_state(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + if(game_state->settings.message_duration == 0) return; + popup_frame(canvas); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Busted!"); } -void to_draw_state(const GameState *game_state, Canvas *const canvas) { - UNUSED(game_state); - popupFrame(canvas); +void to_draw_state(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + if(game_state->settings.message_duration == 0) return; + popup_frame(canvas); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Draw"); } -void to_dealer_turn(const GameState *game_state, Canvas *const canvas) { - UNUSED(game_state); - popupFrame(canvas); +void to_dealer_turn(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + if(game_state->settings.message_duration == 0) return; + popup_frame(canvas); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Dealers turn"); } -void to_win_state(const GameState *game_state, Canvas *const canvas) { - UNUSED(game_state); - popupFrame(canvas); +void to_win_state(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + if(game_state->settings.message_duration == 0) return; + popup_frame(canvas); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You win"); } -void to_start(const GameState *game_state, Canvas *const canvas) { - UNUSED(game_state); - popupFrame(canvas); +void to_start(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + if(game_state->settings.message_duration == 0) return; + popup_frame(canvas); elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Round started"); } -void before_start(GameState *gameState) { - gameState->dealer_card_count = 0; - gameState->player_card_count = 0; +void before_start(void* ctx) { + GameState* game_state = ctx; + game_state->dealer_card_count = 0; + game_state->player_card_count = 0; } - -void start(GameState *game_state) { +void start(void* ctx) { + GameState* game_state = ctx; start_round(game_state); } -void draw(GameState *game_state) { +void draw(void* ctx) { + GameState* game_state = ctx; game_state->player_score += game_state->bet; game_state->bet = 0; - queue(game_state, start, before_start, to_start); + enqueue( + &(game_state->queue_state), + game_state, + start, + before_start, + to_start, + game_state->settings.message_duration); } -void game_over(GameState *game_state) { +void game_over(void* ctx) { + GameState* game_state = ctx; game_state->state = GameStateGameOver; } -void lose(GameState *game_state) { +void lose(void* ctx) { + GameState* game_state = ctx; game_state->state = GameStatePlay; game_state->bet = 0; - if (game_state->player_score >= ROUND_PRICE) - queue(game_state, start, before_start, to_start); - else - queue(game_state, game_over, NULL, NULL); + if(game_state->player_score >= game_state->settings.round_price) { + enqueue( + &(game_state->queue_state), + game_state, + start, + before_start, + to_start, + game_state->settings.message_duration); + } else { + enqueue(&(game_state->queue_state), game_state, game_over, NULL, NULL, 0); + } } -void win(GameState *game_state) { +void win(void* ctx) { + GameState* game_state = ctx; game_state->state = GameStatePlay; game_state->player_score += game_state->bet * 2; game_state->bet = 0; - queue(game_state, start, before_start, to_start); + enqueue( + &(game_state->queue_state), + game_state, + start, + before_start, + to_start, + game_state->settings.message_duration); } - -void dealerTurn(GameState *game_state) { +void dealerTurn(void* ctx) { + GameState* game_state = ctx; game_state->state = GameStateDealer; } + +float animationTime(const GameState* game_state) { + return (float)(furi_get_tick() - game_state->queue_state.start) / + (float)(game_state->settings.animation_duration); +} + +void dealer_card_animation(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + float t = animationTime(game_state); + + Card animatingCard = game_state->deck.cards[game_state->deck.index]; + if(game_state->dealer_card_count > 1) { + Vector end = card_pos_at_index(game_state->dealer_card_count); + draw_card_animation(animatingCard, (Vector){0, 64}, (Vector){0, 32}, end, t, true, canvas); + } else { + draw_card_animation( + animatingCard, + (Vector){32, -CARD_HEIGHT}, + (Vector){64, 32}, + (Vector){2, 2}, + t, + false, + canvas); + } +} + +void dealer_back_card_animation(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + float t = animationTime(game_state); + + Vector currentPos = + quadratic_2d((Vector){32, -CARD_HEIGHT}, (Vector){64, 32}, (Vector){13, 5}, t); + draw_card_back_at(currentPos.x, currentPos.y, canvas); +} + +void player_card_animation(const void* ctx, Canvas* const canvas) { + const GameState* game_state = ctx; + float t = animationTime(game_state); + + Card animatingCard = game_state->deck.cards[game_state->deck.index]; + Vector end = card_pos_at_index(game_state->player_card_count); + + draw_card_animation( + animatingCard, (Vector){32, -CARD_HEIGHT}, (Vector){0, 32}, end, t, true, canvas); +} //endregion -void player_tick(GameState *game_state) { - uint8_t score = handCount(game_state->player_cards, game_state->player_card_count); - if ((game_state->doubled && score <= 21) || score == 21) { - queue(game_state, dealerTurn, NULL, NULL); - } else if (score > 21) { - queue(game_state, lose, NULL, to_bust_state); +void player_tick(GameState* game_state) { + uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count); + if((game_state->doubled && score <= 21) || score == 21) { + enqueue( + &(game_state->queue_state), + game_state, + dealerTurn, + NULL, + to_dealer_turn, + game_state->settings.message_duration); + } else if(score > 21) { + enqueue( + &(game_state->queue_state), + game_state, + lose, + NULL, + to_bust_state, + game_state->settings.message_duration); } else { - if (game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) { - game_state->selectedMenu--; + if(game_state->selectDirection == DirectionUp || + game_state->selectDirection == DirectionDown) { + move_menu(game_state->menu, game_state->selectDirection == DirectionUp ? -1 : 1); } - if (game_state->selectDirection == DirectionDown && game_state->selectedMenu < 2) { - game_state->selectedMenu++; - } - if (game_state->selectDirection == Select) { - //double - if (!game_state->doubled && game_state->selectedMenu == 0 && - game_state->player_score >= ROUND_PRICE) { - game_state->player_score -= ROUND_PRICE; - game_state->bet += ROUND_PRICE; - game_state->doubled = true; - game_state->selectedMenu = 1; - queue(game_state, drawPlayerCard, NULL, draw_card_animation); - game_state->player_cards[game_state->player_card_count] = game_state->deck.cards[game_state->deck.index]; - score = handCount(game_state->player_cards, game_state->player_card_count + 1); - if (score > 21) - queue(game_state, lose, NULL, to_bust_state); - else - queue(game_state, dealerTurn, NULL, to_dealer_turn); - - } //hit - else if (game_state->selectedMenu == 1) { - queue(game_state, drawPlayerCard, NULL, draw_card_animation); - } //stay - else if (game_state->selectedMenu == 2) { - queue(game_state, dealerTurn, NULL, to_dealer_turn); - } + if(game_state->selectDirection == Select) { + activate_menu(game_state->menu, game_state); } } } +void dealer_tick(GameState* game_state) { + uint8_t dealer_score = hand_count(game_state->dealer_cards, game_state->dealer_card_count); + uint8_t player_score = hand_count(game_state->player_cards, game_state->player_card_count); -void dealer_tick(GameState *game_state) { - uint8_t dealer_score = handCount(game_state->dealer_cards, game_state->dealer_card_count); - uint8_t player_score = handCount(game_state->player_cards, game_state->player_card_count); - - if (dealer_score >= DEALER_MAX) { - if (dealer_score > 21 || dealer_score < player_score) - queue(game_state, win, NULL, to_win_state); - else if (dealer_score > player_score) - queue(game_state, lose, NULL, to_lose_state); - else if (dealer_score == player_score) - queue(game_state, draw, NULL, to_draw_state); + if(dealer_score >= DEALER_MAX) { + if(dealer_score > 21 || dealer_score < player_score) { + enqueue( + &(game_state->queue_state), + game_state, + win, + NULL, + to_win_state, + game_state->settings.message_duration); + } else if(dealer_score > player_score) { + enqueue( + &(game_state->queue_state), + game_state, + lose, + NULL, + to_lose_state, + game_state->settings.message_duration); + } else if(dealer_score == player_score) { + enqueue( + &(game_state->queue_state), + game_state, + draw, + NULL, + to_draw_state, + game_state->settings.message_duration); + } } else { - queue(game_state, drawDealerCard, NULL, draw_card_animation); + enqueue( + &(game_state->queue_state), + game_state, + drawDealerCard, + NULL, + dealer_card_animation, + game_state->settings.animation_duration); } } -void tick(GameState *game_state) { +void settings_tick(GameState* game_state) { + if(game_state->selectDirection == DirectionDown && game_state->selectedMenu < 4) { + game_state->selectedMenu++; + } + if(game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) { + game_state->selectedMenu--; + } + + if(game_state->selectDirection == DirectionLeft || + game_state->selectDirection == DirectionRight) { + int nextScore = 0; + switch(game_state->selectedMenu) { + case 0: + nextScore = game_state->settings.starting_money; + if(game_state->selectDirection == DirectionLeft) + nextScore -= 10; + else + nextScore += 10; + if(nextScore >= (int)game_state->settings.round_price && nextScore < 400) + game_state->settings.starting_money = nextScore; + break; + case 1: + nextScore = game_state->settings.round_price; + if(game_state->selectDirection == DirectionLeft) + nextScore -= 10; + else + nextScore += 10; + if(nextScore >= 5 && nextScore <= (int)game_state->settings.starting_money) + game_state->settings.round_price = nextScore; + break; + case 2: + nextScore = game_state->settings.animation_duration; + if(game_state->selectDirection == DirectionLeft) + nextScore -= 100; + else + nextScore += 100; + if(nextScore >= 0 && nextScore < 2000) + game_state->settings.animation_duration = nextScore; + break; + case 3: + nextScore = game_state->settings.message_duration; + if(game_state->selectDirection == DirectionLeft) + nextScore -= 100; + else + nextScore += 100; + if(nextScore >= 0 && nextScore < 2000) + game_state->settings.message_duration = nextScore; + break; + case 4: + game_state->settings.sound_effects = !game_state->settings.sound_effects; + default: + break; + } + } +} + +void tick(GameState* game_state) { game_state->last_tick = furi_get_tick(); + bool queue_ran = run_queue(&(game_state->queue_state), game_state); - if (!game_state->started && game_state->state == GameStatePlay) { - game_state->started = true; - drawDealerCard(game_state); - queue(game_state, drawPlayerCard, NULL, draw_card_animation); - queue(game_state, drawDealerCard, NULL, draw_card_animation); - queue(game_state, drawPlayerCard, NULL, draw_card_animation); - } - - if (!run_queue(game_state)) { - if (game_state->state == GameStatePlay) { - player_tick(game_state); - } else if (game_state->state == GameStateDealer) { - dealer_tick(game_state); + switch(game_state->state) { + case GameStateGameOver: + case GameStateStart: + if(game_state->selectDirection == Select) + init(game_state); + else if(game_state->selectDirection == DirectionRight) { + game_state->selectedMenu = 0; + game_state->state = GameStateSettings; } + break; + case GameStatePlay: + if(!game_state->started) { + game_state->selectedMenu = 0; + game_state->started = true; + enqueue( + &(game_state->queue_state), + game_state, + drawDealerCard, + NULL, + dealer_back_card_animation, + game_state->settings.animation_duration); + enqueue( + &(game_state->queue_state), + game_state, + drawPlayerCard, + NULL, + player_card_animation, + game_state->settings.animation_duration); + enqueue( + &(game_state->queue_state), + game_state, + drawDealerCard, + NULL, + dealer_card_animation, + game_state->settings.animation_duration); + enqueue( + &(game_state->queue_state), + game_state, + drawPlayerCard, + NULL, + player_card_animation, + game_state->settings.animation_duration); + } + if(!queue_ran) player_tick(game_state); + break; + case GameStateDealer: + if(!queue_ran) dealer_tick(game_state); + break; + case GameStateSettings: + settings_tick(game_state); + break; + default: + break; } game_state->selectDirection = None; - } -void start_round(GameState *game_state) { +void start_round(GameState* game_state) { + game_state->menu->current_menu = 1; game_state->player_card_count = 0; game_state->dealer_card_count = 0; - game_state->selectedMenu = 0; + set_menu_state(game_state->menu, 0, true); + game_state->menu->enabled = true; game_state->started = false; game_state->doubled = false; - game_state->animating = true; - game_state->animationStart = 0; - shuffleDeck(&(game_state->deck)); + game_state->queue_state.running = true; + shuffle_deck(&(game_state->deck)); game_state->doubled = false; - game_state->bet = ROUND_PRICE; - if (game_state->player_score < ROUND_PRICE) { + game_state->bet = game_state->settings.round_price; + if(game_state->player_score < game_state->settings.round_price) { game_state->state = GameStateGameOver; } else { - game_state->player_score -= ROUND_PRICE; + game_state->player_score -= game_state->settings.round_price; } game_state->state = GameStatePlay; } -void init(GameState *game_state) { +void init(GameState* game_state) { + set_menu_state(game_state->menu, 0, true); + game_state->menu->enabled = true; + game_state->menu->current_menu = 1; + game_state->settings = load_settings(); game_state->last_tick = 0; - game_state->player_score = STARTING_MONEY; - generateDeck(&(game_state->deck)); + game_state->processing = true; + game_state->selectedMenu = 0; + game_state->player_score = game_state->settings.starting_money; + generate_deck(&(game_state->deck), 6); start_round(game_state); } -static void input_callback(InputEvent *input_event, FuriMessageQueue *event_queue) { +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { furi_assert(event_queue); AppEvent event = {.type = EventTypeKey, .input = *input_event}; furi_message_queue_put(event_queue, &event, FuriWaitForever); } -static void update_timer_callback(FuriMessageQueue *event_queue) { +static void update_timer_callback(FuriMessageQueue* event_queue) { furi_assert(event_queue); AppEvent event = {.type = EventTypeTick}; furi_message_queue_put(event_queue, &event, 0); } -int32_t blackjack_app(void *p) { +void doubleAction(void* state) { + GameState* game_state = state; + if(!game_state->doubled && game_state->player_score >= game_state->settings.round_price) { + game_state->player_score -= game_state->settings.round_price; + game_state->bet += game_state->settings.round_price; + game_state->doubled = true; + enqueue( + &(game_state->queue_state), + game_state, + drawPlayerCard, + NULL, + player_card_animation, + game_state->settings.animation_duration); + game_state->player_cards[game_state->player_card_count] = + game_state->deck.cards[game_state->deck.index]; + uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count + 1); + if(score > 21) { + enqueue( + &(game_state->queue_state), + game_state, + lose, + NULL, + to_bust_state, + game_state->settings.message_duration); + } else { + enqueue( + &(game_state->queue_state), + game_state, + dealerTurn, + NULL, + to_dealer_turn, + game_state->settings.message_duration); + } + set_menu_state(game_state->menu, 0, false); + } +} + +void hitAction(void* state) { + GameState* game_state = state; + enqueue( + &(game_state->queue_state), + game_state, + drawPlayerCard, + NULL, + player_card_animation, + game_state->settings.animation_duration); +} +void stayAction(void* state) { + GameState* game_state = state; + enqueue( + &(game_state->queue_state), + game_state, + dealerTurn, + NULL, + to_dealer_turn, + game_state->settings.message_duration); +} + +int32_t blackjack_app(void* p) { UNUSED(p); int32_t return_code = 0; - FuriMessageQueue *event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); - GameState *game_state = malloc(sizeof(GameState)); + GameState* game_state = malloc(sizeof(GameState)); + game_state->menu = malloc(sizeof(Menu)); + game_state->menu->menu_width = 40; init(game_state); + add_menu(game_state->menu, "Double", doubleAction); + add_menu(game_state->menu, "Hit", hitAction); + add_menu(game_state->menu, "Stay", stayAction); + set_card_graphics(&I_card_graphics); + game_state->state = GameStateStart; ValueMutex state_mutex; - if (!init_mutex(&state_mutex, game_state, sizeof(GameState))) { + if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); return_code = 255; goto free_and_exit; } - ViewPort *view_port = view_port_alloc(); + ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, render_callback, &state_mutex); view_port_input_callback_set(view_port, input_callback, event_queue); - FuriTimer *timer = - furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue); + FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25); - Gui *gui = furi_record_open("gui"); + Gui* gui = furi_record_open("gui"); gui_add_view_port(gui, view_port, GuiLayerFullscreen); AppEvent event; - for (bool processing = true; processing;) { + for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - GameState *game_state = (GameState *) acquire_mutex_block(&state_mutex); - - if (event_status == FuriStatusOk) { - if (event.type == EventTypeKey) { - - if (event.input.type == InputTypePress) { - switch (event.input.key) { - - case InputKeyUp: - game_state->selectDirection = DirectionUp; - break; - case InputKeyDown: - game_state->selectDirection = DirectionDown; - break; - case InputKeyRight: - game_state->selectDirection = DirectionRight; - break; - case InputKeyLeft: - game_state->selectDirection = DirectionLeft; - break; - case InputKeyBack: + GameState* localstate = (GameState*)acquire_mutex_block(&state_mutex); + if(event_status == FuriStatusOk) { + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + localstate->selectDirection = DirectionUp; + break; + case InputKeyDown: + localstate->selectDirection = DirectionDown; + break; + case InputKeyRight: + localstate->selectDirection = DirectionRight; + break; + case InputKeyLeft: + localstate->selectDirection = DirectionLeft; + break; + case InputKeyBack: + if(localstate->state == GameStateSettings) { + localstate->state = GameStateStart; + save_settings(localstate->settings); + } else processing = false; - break; - - case InputKeyOk: - if (game_state->state == GameStateGameOver || game_state->state == GameStateStart) { - init(game_state); - } else { - game_state->selectDirection = Select; - } - break; + break; + case InputKeyOk: + localstate->selectDirection = Select; + break; + default: + break; } } - } else if (event.type == EventTypeTick) { - tick(game_state); + } else if(event.type == EventTypeTick) { + tick(localstate); + processing = localstate->processing; } } else { - FURI_LOG_D(APP_NAME, "osMessageQueue: event timeout"); + //FURI_LOG_D(APP_NAME, "osMessageQueue: event timeout"); // event timeout } view_port_update(view_port); - release_mutex(&state_mutex, game_state); + release_mutex(&state_mutex, localstate); } - furi_timer_free(timer); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); @@ -363,8 +623,10 @@ int32_t blackjack_app(void *p) { view_port_free(view_port); delete_mutex(&state_mutex); - free_and_exit: - queue_clear(); +free_and_exit: + free(game_state->deck.cards); + free_menu(game_state->menu); + queue_clear(&(game_state->queue_state)); free(game_state); furi_message_queue_free(event_queue); diff --git a/applications/plugins/blackjack/card.c b/applications/plugins/blackjack/card.c deleted file mode 100644 index 2f43a33f7..000000000 --- a/applications/plugins/blackjack/card.c +++ /dev/null @@ -1,239 +0,0 @@ -#include "card.h" -#include - -//region CardDesign -bool pips[4][49] = - { - { - //spades - 0, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 1, 0, 1, 1, - 0, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0 - }, - { - //hearts - 0, 1, 0, 0, 0, 1, 0, - 1, 1, 1, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0, - }, - { - //diamonds - 0, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 0, - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 0 - }, - { - //clubs - 0, 0, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 0, 0, - 1, 1, 0, 1, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 1, 0, 1, 1, - 0, 0, 0, 1, 0, 0, 0, - 0, 0, 1, 1, 1, 0, 0 - } - }; - -bool backDesign[4] = { - 0, 1, - 1, 0 -}; -//endregion - -uint8_t characters[13] = - { - 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A' - }; - - -//region Player card positions -uint8_t playerCardPositions[22][4] = { - //first row - {108, 38, 0, 0}, - {98, 38, 0, 1}, - {88, 38, 0, 1}, - {78, 38, 0, 1}, - {68, 38, 0, 1}, - {58, 38, 0, 1}, - {48, 38, 0, 1}, - {38, 38, 0, 1}, - //second row - {104, 26, 1, 0}, - {94, 26, 1, 1}, - {84, 26, 1, 1}, - {74, 26, 1, 1}, - {64, 26, 1, 1}, - {54, 26, 1, 1}, - {44, 26, 1, 1}, - //third row - {99, 14, 1, 0}, - {89, 14, 1, 1}, - {79, 14, 1, 1}, - {69, 14, 1, 1}, - {59, 14, 1, 1}, - {49, 14, 1, 1}, -}; -//endregion - - -void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas *const canvas) { - for (uint8_t i = 0; i < count; i++) { - CardState state = Normal; - if (playerCardPositions[i][2] == 1 && playerCardPositions[i][3] == 1) - state = BottomAndRightCut; - else if (playerCardPositions[i][3] == 1) - state = RightCut; - else if (playerCardPositions[i][2] == 1) - state = BottomCut; - drawCardAt(playerCardPositions[i][0], playerCardPositions[i][1], cards[i].pip, cards[i].character, state, - canvas); - } -} - -void drawCardAt(uint8_t pos_x, uint8_t pos_y, uint8_t pip, uint8_t character, CardState state, Canvas *const canvas) { - if (state == Normal) { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT); - - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT); - } else { - if (state == BottomCut || state == BottomAndRightCut) - canvas_draw_line(canvas, pos_x, pos_y, pos_x, pos_y + CARD_HALF_HEIGHT - 1); //half height line - - if (state == BottomCut) { - canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_WIDHT - 1, pos_y); //full width line - canvas_draw_line(canvas, pos_x + CARD_WIDHT - 1, pos_y, pos_x + CARD_WIDHT - 1, - pos_y + CARD_HALF_HEIGHT - 1); //half height line - } - - if (state == BottomAndRightCut) { - canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDHT - 1, pos_y); //half width - } - - if (state == RightCut) { - canvas_draw_line(canvas, pos_x, pos_y, pos_x + CARD_HALF_WIDHT - 1, pos_y); //half width - canvas_draw_line(canvas, pos_x, pos_y, pos_x, pos_y + CARD_HEIGHT - 1); //full height line - canvas_draw_line(canvas, pos_x, pos_y + CARD_HEIGHT - 1, pos_x + CARD_HALF_WIDHT - 1, - pos_y + CARD_HEIGHT - 1); //full height line - } - - } - - uint8_t left = pos_x + CORNER_MARGIN; - uint8_t right = (pos_x + CARD_WIDHT - CORNER_MARGIN - 7); - uint8_t top = pos_y + CORNER_MARGIN; - uint8_t bottom = (pos_y + CARD_HEIGHT - CORNER_MARGIN - 7); - - for (uint8_t x = 0; x < 7; x++) { - for (uint8_t y = 0; y < 7; y++) { - if (pips[pip][x + y * 7]) { - if (state == Normal || state == BottomCut) - canvas_draw_dot(canvas, right + x + 1, top + y); - if (state == Normal || state == RightCut) - canvas_draw_dot(canvas, left + x - 1, bottom + y); - } - } - } - - canvas_set_font(canvas, FontSecondary); - char drawChar[3]; - if (character < 9) - snprintf(drawChar, sizeof(drawChar), "%i", character + 2); - else { - snprintf(drawChar, sizeof(drawChar), "%c", characters[character]); - } - - canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); - canvas_draw_str_aligned(canvas, left + 2, top + 3, AlignCenter, AlignCenter, drawChar); - - canvas_set_font_direction(canvas, CanvasDirectionRightToLeft); - if (state == Normal) { //flipper crashes on non center aligned text when upside down - uint8_t margin = 9; - if (character == 8) //10 needs bigger margin - margin = 12; - canvas_draw_str_aligned(canvas, right + margin, bottom - 3, AlignCenter, AlignCenter, drawChar); - } - - canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); - //canvas_draw_str(canvas, left, top, drawChar ); -} - -void drawCardBackAt(uint8_t pos_x, uint8_t pos_y, Canvas *const canvas) { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT); - - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, pos_x, pos_y, CARD_WIDHT, CARD_HEIGHT); - for (uint8_t x = 0; x < CARD_WIDHT - 2; x++) { - for (uint8_t y = 0; y < CARD_HEIGHT - 2; y++) { - uint8_t _x = x; - uint8_t _y = y * 2; - if (backDesign[(_x + _y) % 4]) { - canvas_draw_dot(canvas, pos_x + x + 1, pos_y + y + 1); - } - } - } -} - -void generateDeck(Deck *deck_ptr) { - uint16_t counter = 0; - for (uint8_t deck = 0; deck < DECK_COUNT; deck++) { - for (uint8_t pip = 0; pip < 4; pip++) { - for (uint8_t label = 0; label < 13; label++) { - deck_ptr->cards[counter] = (Card) - { - pip, label - }; - counter++; - } - } - } -} - -void shuffleDeck(Deck *deck_ptr) { - srand(DWT->CYCCNT); - deck_ptr->index = 0; - int max = DECK_COUNT * 52; - for (int i = 0; i < max; i++) { - int r = i + (rand() % (max - i)); - Card tmp = deck_ptr->cards[i]; - deck_ptr->cards[i] = deck_ptr->cards[r]; - deck_ptr->cards[r] = tmp; - } -} - -uint8_t handCount(const Card cards[21], uint8_t count) { - uint8_t aceCount = 0; - uint8_t score = 0; - - for (uint8_t i = 0; i < count; i++) { - if (cards[i].character == 12) - aceCount++; - else { - if (cards[i].character > 8) - score += 10; - else - score += cards[i].character + 2; - } - } - - for (uint8_t i = 0; i < aceCount; i++) { - if ((score + 11) <= 21) score += 11; - else score++; - } - - return score; -} \ No newline at end of file diff --git a/applications/plugins/blackjack/card.h b/applications/plugins/blackjack/card.h deleted file mode 100644 index aa99784bb..000000000 --- a/applications/plugins/blackjack/card.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _card_h -#define _card_h - -#include - -#define DECK_COUNT 6 -#define CARD_HEIGHT 24 -#define CARD_HALF_HEIGHT CARD_HEIGHT/2 -#define CARD_WIDHT 18 -#define CARD_HALF_WIDHT CARD_WIDHT/2 -#define CORNER_MARGIN 3 -#define LEGEND_SIZE 10 - -typedef enum { - Normal, BottomCut, RightCut, BottomAndRightCut, TopCut, LeftCut, TopAndLeftCut -} CardState; - -typedef struct { - uint8_t pip; - uint8_t character; -} Card; - -typedef struct { - Card cards[52 * DECK_COUNT]; - int index; -} Deck; - -void drawPlayerDeck(const Card cards[21], uint8_t count, Canvas *const canvas); - -void drawCardAt(uint8_t pos_x, uint8_t pos_y, uint8_t pip, uint8_t character, CardState state, Canvas *const canvas); - -void drawCardBackAt(uint8_t pos_x, uint8_t pos_y, Canvas *const canvas); - -void generateDeck(Deck *deck_ptr); - -void shuffleDeck(Deck *deck_ptr); - -uint8_t handCount(const Card cards[21], uint8_t count); - -#endif \ No newline at end of file diff --git a/applications/plugins/blackjack/common/card.c b/applications/plugins/blackjack/common/card.c new file mode 100644 index 000000000..199135bb5 --- /dev/null +++ b/applications/plugins/blackjack/common/card.c @@ -0,0 +1,353 @@ +#include "card.h" +#include "dml.h" +#include "ui.h" + +#define CARD_DRAW_X_START 108 +#define CARD_DRAW_Y_START 38 +#define CARD_DRAW_X_SPACE 10 +#define CARD_DRAW_Y_SPACE 8 +#define CARD_DRAW_X_OFFSET 4 +#define CARD_DRAW_FIRST_ROW_LENGTH 7 + +uint8_t pips[4][3] = { + {21, 10, 7}, //spades + {7, 10, 7}, //hearts + {0, 10, 7}, //diamonds + {14, 10, 7}, //clubs +}; +uint8_t letters[13][3] = { + {0, 0, 5}, + {5, 0, 5}, + {10, 0, 5}, + {15, 0, 5}, + {20, 0, 5}, + {25, 0, 5}, + {30, 0, 5}, + {0, 5, 5}, + {5, 5, 5}, + {10, 5, 5}, + {15, 5, 5}, + {20, 5, 5}, + {25, 5, 5}, +}; + +//region Player card positions +uint8_t playerCardPositions[22][4] = { + //first row + {108, 38}, + {98, 38}, + {88, 38}, + {78, 38}, + {68, 38}, + {58, 38}, + {48, 38}, + {38, 38}, + //second row + {104, 26}, + {94, 26}, + {84, 26}, + {74, 26}, + {64, 26}, + {54, 26}, + {44, 26}, + //third row + {99, 14}, + {89, 14}, + {79, 14}, + {69, 14}, + {59, 14}, + {49, 14}, +}; +//endregion +Icon* card_graphics = NULL; + +void set_card_graphics(const Icon* graphics) { + card_graphics = (Icon*)graphics; +} + +void draw_card_at_colored( + int8_t pos_x, + int8_t pos_y, + uint8_t pip, + uint8_t character, + bool inverted, + Canvas* const canvas) { + DrawMode primary = inverted ? Black : White; + DrawMode secondary = inverted ? White : Black; + draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, primary); + draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); + + uint8_t* drawInfo = pips[pip]; + uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; + + uint8_t left = pos_x + 2; + uint8_t right = (pos_x + CARD_WIDTH - s - 2); + uint8_t top = pos_y + 2; + uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2); + + draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s, secondary); + draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s, secondary); + + drawInfo = letters[character]; + px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; + left = pos_x + 2; + right = (pos_x + CARD_WIDTH - s - 2); + top = pos_y + 2; + bottom = (pos_y + CARD_HEIGHT - s - 2); + + draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s, secondary); + draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s, secondary); +} + +void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas) { + draw_card_at_colored(pos_x, pos_y, pip, character, false, canvas); +} + +void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas) { + for(int i = count - 1; i >= 0; i--) { + draw_card_at( + playerCardPositions[i][0], + playerCardPositions[i][1], + cards[i].pip, + cards[i].character, + canvas); + } +} + +Vector card_pos_at_index(uint8_t index) { + return (Vector){playerCardPositions[index][0], playerCardPositions[index][1]}; +} + +void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas) { + draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White); + draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); + + draw_icon_clip(canvas, card_graphics, pos_x + 1, pos_y + 1, 35, 0, 15, 21, Black); +} + +void generate_deck(Deck* deck_ptr, uint8_t deck_count) { + uint16_t counter = 0; + if(deck_ptr->cards != NULL) { + free(deck_ptr->cards); + } + + deck_ptr->deck_count = deck_count; + deck_ptr->card_count = deck_count * 52; + deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count); + + for(uint8_t deck = 0; deck < deck_count; deck++) { + for(uint8_t pip = 0; pip < 4; pip++) { + for(uint8_t label = 0; label < 13; label++) { + deck_ptr->cards[counter] = (Card){pip, label, false, false}; + counter++; + } + } + } +} + +void shuffle_deck(Deck* deck_ptr) { + srand(DWT->CYCCNT); + deck_ptr->index = 0; + int max = deck_ptr->deck_count * 52; + for(int i = 0; i < max; i++) { + int r = i + (rand() % (max - i)); + Card tmp = deck_ptr->cards[i]; + deck_ptr->cards[i] = deck_ptr->cards[r]; + deck_ptr->cards[r] = tmp; + } +} + +uint8_t hand_count(const Card* cards, uint8_t count) { + uint8_t aceCount = 0; + uint8_t score = 0; + + for(uint8_t i = 0; i < count; i++) { + if(cards[i].character == 12) + aceCount++; + else { + if(cards[i].character > 8) + score += 10; + else + score += cards[i].character + 2; + } + } + + for(uint8_t i = 0; i < aceCount; i++) { + if((score + 11) <= 21) + score += 11; + else + score++; + } + + return score; +} + +void draw_card_animation( + Card animatingCard, + Vector from, + Vector control, + Vector to, + float t, + bool extra_margin, + Canvas* const canvas) { + float time = t; + if(extra_margin) { + time += 0.2; + } + + Vector currentPos = quadratic_2d(from, control, to, time); + if(t > 1) { + draw_card_at( + currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas); + } else { + if(t < 0.5) + draw_card_back_at(currentPos.x, currentPos.y, canvas); + else + draw_card_at( + currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas); + } +} + +void init_hand(Hand* hand_ptr, uint8_t count) { + hand_ptr->cards = malloc(sizeof(Card) * count); + hand_ptr->index = 0; + hand_ptr->max = count; +} + +void free_hand(Hand* hand_ptr) { + FURI_LOG_D("CARD", "Freeing hand"); + free(hand_ptr->cards); +} + +void add_to_hand(Hand* hand_ptr, Card card) { + FURI_LOG_D("CARD", "Adding to hand"); + if(hand_ptr->index < hand_ptr->max) { + hand_ptr->cards[hand_ptr->index] = card; + hand_ptr->index++; + } +} + +void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas) { + if(highlighted) { + draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); + draw_rounded_box_frame( + canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White); + } else { + draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); + draw_rounded_box_frame( + canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White); + } +} + +int first_non_flipped_card(Hand hand) { + for(int i = 0; i < hand.index; i++) { + if(!hand.cards[i].flipped) { + return i; + } + } + return hand.index; +} + +void draw_hand_column( + Hand hand, + int16_t pos_x, + int16_t pos_y, + int8_t highlight, + Canvas* const canvas) { + if(hand.index == 0) { + draw_card_space(pos_x, pos_y, highlight > 0, canvas); + if(highlight == 0) + draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Inverse); + return; + } + + int loopEnd = hand.index; + int hStart = max(loopEnd - 4, 0); + int pos = 0; + int first = first_non_flipped_card(hand); + bool wastop = false; + if(first >= 0 && first <= hStart && highlight != first) { + if(first > 0) { + draw_card_back_at(pos_x, pos_y + pos, canvas); + pos += 4; + hStart++; + wastop = true; + } + draw_card_at_colored( + pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character, false, canvas); + pos += 8; + hStart++; + } + if(hStart > highlight && highlight >= 0) { + if(!wastop && first > 0) { + draw_card_back_at(pos_x, pos_y + pos, canvas); + pos += 4; + hStart++; + } + draw_card_at_colored( + pos_x, + pos_y + pos, + hand.cards[highlight].pip, + hand.cards[highlight].character, + true, + canvas); + pos += 8; + hStart++; + } + for(int i = hStart; i < loopEnd; i++, pos += 4) { + if(hand.cards[i].flipped) { + draw_card_back_at(pos_x, pos_y + pos, canvas); + if(i == highlight) + draw_rounded_box( + canvas, pos_x + 1, pos_y + pos + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2, Inverse); + } else { + draw_card_at_colored( + pos_x, + pos_y + pos, + hand.cards[i].pip, + hand.cards[i].character, + (i == highlight), + canvas); + if(i == highlight || i == first) pos += 4; + } + } +} + +Card remove_from_deck(uint16_t index, Deck* deck) { + FURI_LOG_D("CARD", "Removing from deck"); + Card result = {0, 0, true, false}; + if(deck->card_count > 0) { + deck->card_count--; + for(int i = 0, curr_index = 0; i <= deck->card_count; i++) { + if(i != index) { + deck->cards[curr_index] = deck->cards[i]; + curr_index++; + } else { + result = deck->cards[i]; + } + } + if(deck->index >= 0) { + deck->index--; + } + } + return result; +} + +void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index) { + FURI_LOG_D("CARD", "Extracting hand region"); + if(start_index >= hand->index) return; + + for(uint8_t i = start_index; i < hand->index; i++) { + add_to_hand(to, hand->cards[i]); + } + hand->index = start_index; +} + +void add_hand_region(Hand* to, Hand* from) { + FURI_LOG_D("CARD", "Adding hand region"); + if((to->index + from->index) <= to->max) { + for(int i = 0; i < from->index; i++) { + add_to_hand(to, from->cards[i]); + } + } +} \ No newline at end of file diff --git a/applications/plugins/blackjack/common/card.h b/applications/plugins/blackjack/common/card.h new file mode 100644 index 000000000..8e5e23bbf --- /dev/null +++ b/applications/plugins/blackjack/common/card.h @@ -0,0 +1,192 @@ +#pragma once + +#include +#include +#include +#include "dml.h" + +#define CARD_HEIGHT 23 +#define CARD_HALF_HEIGHT 11 +#define CARD_WIDTH 17 +#define CARD_HALF_WIDTH 8 + +//region types +typedef struct { + uint8_t pip; //Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs + uint8_t character; //Card letter [0-12], 0 means 2, 12 is Ace + bool disabled; + bool flipped; +} Card; + +typedef struct { + uint8_t deck_count; //Number of decks used + Card* cards; //Cards in the deck + int card_count; + int index; //Card index (to know where we at in the deck) +} Deck; + +typedef struct { + Card* cards; //Cards in the deck + uint8_t index; //Current index + uint8_t max; //How many cards we want to store +} Hand; +//endregion + +void set_card_graphics(const Icon* graphics); + +/** + * Gets card coordinates at the index (range: 0-20). + * + * @param index Index to check 0-20 + * @return Position of the card + */ +Vector card_pos_at_index(uint8_t index); + +/** + * Draws card at a given coordinate (top-left corner) + * + * @param pos_x X position + * @param pos_y Y position + * @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs + * @param character Letter [0-12] 0 is 2, 12 is A + * @param canvas Pointer to Flipper's canvas object + */ +void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas); + +/** + * Draws card at a given coordinate (top-left corner) + * + * @param pos_x X position + * @param pos_y Y position + * @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs + * @param character Letter [0-12] 0 is 2, 12 is A + * @param inverted Invert colors + * @param canvas Pointer to Flipper's canvas object + */ +void draw_card_at_colored( + int8_t pos_x, + int8_t pos_y, + uint8_t pip, + uint8_t character, + bool inverted, + Canvas* const canvas); + +/** + * Draws 'count' cards at the bottom right corner + * + * @param cards List of cards + * @param count Count of cards + * @param canvas Pointer to Flipper's canvas object + */ +void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas); + +/** + * Draws card back at a given coordinate (top-left corner) + * + * @param pos_x X coordinate + * @param pos_y Y coordinate + * @param canvas Pointer to Flipper's canvas object + */ +void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas); + +/** + * Generates the deck + * + * @param deck_ptr Pointer to the deck + * @param deck_count Number of decks + */ +void generate_deck(Deck* deck_ptr, uint8_t deck_count); + +/** + * Shuffles the deck + * + * @param deck_ptr Pointer to the deck + */ +void shuffle_deck(Deck* deck_ptr); + +/** + * Calculates the hand count for blackjack + * + * @param cards List of cards + * @param count Count of cards + * @return Hand value + */ +uint8_t hand_count(const Card* cards, uint8_t count); + +/** + * Draws card animation + * + * @param animatingCard Card to animate + * @param from Starting position + * @param control Quadratic lerp control point + * @param to End point + * @param t Current time (0-1) + * @param extra_margin Use extra margin at the end (arrives 0.2 unit before the end so it can stay there a bit) + * @param canvas Pointer to Flipper's canvas object + */ +void draw_card_animation( + Card animatingCard, + Vector from, + Vector control, + Vector to, + float t, + bool extra_margin, + Canvas* const canvas); + +/** + * Init hand pointer + * @param hand_ptr Pointer to hand + * @param count Number of cards we want to store + */ +void init_hand(Hand* hand_ptr, uint8_t count); + +/** + * Free hand resources + * @param hand_ptr Pointer to hand + */ +void free_hand(Hand* hand_ptr); + +/** + * Add card to hand + * @param hand_ptr Pointer to hand + * @param card Card to add + */ +void add_to_hand(Hand* hand_ptr, Card card); + +/** + * Draw card placement position at coordinate + * @param pos_x X coordinate + * @param pos_y Y coordinate + * @param highlighted Apply highlight effect + * @param canvas Canvas object + */ +void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas); + +/** + * Draws a column of card, displaying the last [max_cards] cards on the list + * @param hand Hand object + * @param pos_x X coordinate to draw + * @param pos_y Y coordinate to draw + * @param highlight Index to highlight, negative means no highlight + * @param canvas Canvas object + */ +void draw_hand_column( + Hand hand, + int16_t pos_x, + int16_t pos_y, + int8_t highlight, + Canvas* const canvas); + +/** + * Removes a card from the deck (Be aware, if you remove the first item, the deck index will be at -1 so you have to handle that) + * @param index Index to remove + * @param deck Deck reference + * @return The removed card + */ +Card remove_from_deck(uint16_t index, Deck* deck); + +int first_non_flipped_card(Hand hand); + +void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index); + +void add_hand_region(Hand* to, Hand* from); \ No newline at end of file diff --git a/applications/plugins/blackjack/common/dml.c b/applications/plugins/blackjack/common/dml.c new file mode 100644 index 000000000..b9a0e395f --- /dev/null +++ b/applications/plugins/blackjack/common/dml.c @@ -0,0 +1,53 @@ +#include "dml.h" +#include + +float lerp(float v0, float v1, float t) { + if(t > 1) return v1; + return (1 - t) * v0 + t * v1; +} + +Vector lerp_2d(Vector start, Vector end, float t) { + return (Vector){ + lerp(start.x, end.x, t), + lerp(start.y, end.y, t), + }; +} + +Vector quadratic_2d(Vector start, Vector control, Vector end, float t) { + return lerp_2d(lerp_2d(start, control, t), lerp_2d(control, end, t), t); +} + +Vector vector_add(Vector a, Vector b) { + return (Vector){a.x + b.x, a.y + b.y}; +} + +Vector vector_sub(Vector a, Vector b) { + return (Vector){a.x - b.x, a.y - b.y}; +} + +Vector vector_mul_components(Vector a, Vector b) { + return (Vector){a.x * b.x, a.y * b.y}; +} + +Vector vector_div_components(Vector a, Vector b) { + return (Vector){a.x / b.x, a.y / b.y}; +} + +Vector vector_normalized(Vector a) { + float length = vector_magnitude(a); + return (Vector){a.x / length, a.y / length}; +} + +float vector_magnitude(Vector a) { + return sqrt(a.x * a.x + a.y * a.y); +} + +float vector_distance(Vector a, Vector b) { + return vector_magnitude(vector_sub(a, b)); +} + +float vector_dot(Vector a, Vector b) { + Vector _a = vector_normalized(a); + Vector _b = vector_normalized(b); + return _a.x * _b.x + _a.y * _b.y; +} \ No newline at end of file diff --git a/applications/plugins/blackjack/common/dml.h b/applications/plugins/blackjack/common/dml.h new file mode 100644 index 000000000..0e1a23e23 --- /dev/null +++ b/applications/plugins/blackjack/common/dml.h @@ -0,0 +1,116 @@ +// +// Doofy's Math library +// + +#pragma once + +typedef struct { + float x; + float y; +} Vector; + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define abs(x) ((x) > 0 ? (x) : -(x)) + +/** + * Lerp function + * + * @param v0 Start value + * @param v1 End value + * @param t Time (0-1 range) + * @return Point between v0-v1 at a given time + */ +float lerp(float v0, float v1, float t); + +/** + * 2D lerp function + * + * @param start Start vector + * @param end End vector + * @param t Time (0-1 range) + * @return 2d Vector between start and end at time + */ +Vector lerp_2d(Vector start, Vector end, float t); + +/** + * Quadratic lerp function + * + * @param start Start vector + * @param control Control point + * @param end End vector + * @param t Time (0-1 range) + * @return 2d Vector at time + */ +Vector quadratic_2d(Vector start, Vector control, Vector end, float t); + +/** + * Add vector components together + * + * @param a First vector + * @param b Second vector + * @return Resulting vector + */ +Vector vector_add(Vector a, Vector b); + +/** + * Subtract vector components together + * + * @param a First vector + * @param b Second vector + * @return Resulting vector + */ +Vector vector_sub(Vector a, Vector b); + +/** + * Multiplying vector components together + * + * @param a First vector + * @param b Second vector + * @return Resulting vector + */ +Vector vector_mul_components(Vector a, Vector b); + +/** + * Dividing vector components + * + * @param a First vector + * @param b Second vector + * @return Resulting vector + */ +Vector vector_div_components(Vector a, Vector b); + +/** + * Calculating Vector length + * + * @param a Direction vector + * @return Length of the vector + */ +float vector_magnitude(Vector a); + +/** + * Get a normalized vector (length of 1) + * + * @param a Direction vector + * @return Normalized vector + */ +Vector vector_normalized(Vector a); + +/** + * Calculate two vector's distance + * + * @param a First vector + * @param b Second vector + * @return Distance between vectors + */ +float vector_distance(Vector a, Vector b); + +/** + * Calculate the dot product of the vectors. + * No need to normalize, it will do it + * + * @param a First vector + * @param b Second vector + * @return value from -1 to 1 + */ +float vector_dot(Vector a, Vector b); diff --git a/applications/plugins/blackjack/common/menu.c b/applications/plugins/blackjack/common/menu.c new file mode 100644 index 000000000..ffc3921b7 --- /dev/null +++ b/applications/plugins/blackjack/common/menu.c @@ -0,0 +1,103 @@ +#include "menu.h" + +void add_menu(Menu* menu, const char* name, void (*callback)(void*)) { + MenuItem* items = menu->items; + + menu->items = malloc(sizeof(MenuItem) * (menu->menu_count + 1)); + for(uint8_t i = 0; i < menu->menu_count; i++) { + menu->items[i] = items[i]; + } + free(items); + + menu->items[menu->menu_count] = (MenuItem){name, true, callback}; + menu->menu_count++; +} + +void free_menu(Menu* menu) { + free(menu->items); + free(menu); +} + +void set_menu_state(Menu* menu, uint8_t index, bool state) { + if(menu->menu_count > index) { + menu->items[index].enabled = state; + } + if(!state && menu->current_menu == index) move_menu(menu, 1); +} + +void move_menu(Menu* menu, int8_t direction) { + if(!menu->enabled) return; + int max = menu->menu_count; + for(int8_t i = 0; i < max; i++) { + FURI_LOG_D( + "MENU", + "Iteration %i, current %i, direction %i, state %i", + i, + menu->current_menu, + direction, + menu->items[menu->current_menu].enabled ? 1 : 0); + if(direction < 0 && menu->current_menu == 0) { + menu->current_menu = menu->menu_count - 1; + } else { + menu->current_menu = (menu->current_menu + direction) % menu->menu_count; + } + FURI_LOG_D( + "MENU", + "After process current %i, direction %i, state %i", + menu->current_menu, + direction, + menu->items[menu->current_menu].enabled ? 1 : 0); + if(menu->items[menu->current_menu].enabled) { + FURI_LOG_D("MENU", "Next menu %i", menu->current_menu); + return; + } + } + FURI_LOG_D("MENU", "Not found, setting false"); + menu->enabled = false; +} + +void activate_menu(Menu* menu, void* state) { + if(!menu->enabled) return; + menu->items[menu->current_menu].callback(state); +} + +void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y) { + if(!menu->enabled) return; + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2); + + uint8_t w = pos_x + menu->menu_width; + uint8_t h = pos_y + 10; + uint8_t p1x = pos_x + 2; + uint8_t p2x = pos_x + menu->menu_width - 2; + uint8_t p1y = pos_y + 2; + uint8_t p2y = pos_y + 8; + + canvas_set_color(canvas, ColorBlack); + canvas_draw_line(canvas, p1x, pos_y, p2x, pos_y); + canvas_draw_line(canvas, p1x, h, p2x, h); + canvas_draw_line(canvas, pos_x, p1y, pos_x, p2y); + canvas_draw_line(canvas, w, p1y, w, p2y); + canvas_draw_dot(canvas, pos_x + 1, pos_y + 1); + canvas_draw_dot(canvas, w - 1, pos_y + 1); + canvas_draw_dot(canvas, w - 1, h - 1); + canvas_draw_dot(canvas, pos_x + 1, h - 1); + + // canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + pos_x + menu->menu_width / 2, + pos_y + 6, + AlignCenter, + AlignCenter, + menu->items[menu->current_menu].name); + //9*5 + int center = pos_x + menu->menu_width / 2; + for(uint8_t i = 0; i < 4; i++) { + for(int8_t j = -i; j <= i; j++) { + canvas_draw_dot(canvas, center + j, pos_y - 4 + i); + canvas_draw_dot(canvas, center + j, pos_y + 14 - i); + } + } +} \ No newline at end of file diff --git a/applications/plugins/blackjack/common/menu.h b/applications/plugins/blackjack/common/menu.h new file mode 100644 index 000000000..9f2852522 --- /dev/null +++ b/applications/plugins/blackjack/common/menu.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +typedef struct { + const char* name; //Name of the menu + bool enabled; //Is the menu item enabled (it will not render, you cannot select it) + + void (*callback)( + void* state); //Callback for when the activate_menu is called while this menu is selected +} MenuItem; + +typedef struct { + MenuItem* items; //list of menu items + uint8_t menu_count; //count of menu items (do not change) + uint8_t current_menu; //currently selected menu item + uint8_t menu_width; //width of the menu + bool enabled; //is the menu enabled (it will not render and accept events when disabled) +} Menu; + +/** + * Cleans up the pointers used by the menu + * + * @param menu Pointer of the menu to clean up + */ +void free_menu(Menu* menu); + +/** + * Add a new menu item + * + * @param menu Pointer of the menu + * @param name Name of the menu item + * @param callback Callback called on activation + */ +void add_menu(Menu* menu, const char* name, void (*callback)(void*)); + +/** + * Setting menu item to be enabled/disabled + * + * @param menu Pointer of the menu + * @param index Menu index to set + * @param state Enabled (true), Disabled(false) + */ +void set_menu_state(Menu* menu, uint8_t index, bool state); + +/** + * Moves selection up or down + * + * @param menu Pointer of the menu + * @param direction Direction to move -1 down, 1 up + */ +void move_menu(Menu* menu, int8_t direction); + +/** + * Triggers the current menu callback + * + * @param menu Pointer of the menu + * @param state Usually your application state + */ +void activate_menu(Menu* menu, void* state); + +/** + * Renders the menu at a coordinate (call it in your render function). + * + * Keep in mind that Flipper has a 128x64 pixel screen resolution and the coordinate + * you give is the menu's rectangle top-left corner (arrows not included). + * The rectangle height is 10 px, the arrows have a 4 pixel height. Space needed is 18px. + * The width of the menu can be configured in the menu object. + * + * + * @param menu Pointer of the menu + * @param canvas Flippers Canvas pointer + * @param pos_x X position to draw + * @param pos_y Y position to draw + */ +void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y); \ No newline at end of file diff --git a/applications/plugins/blackjack/common/queue.c b/applications/plugins/blackjack/common/queue.c new file mode 100644 index 000000000..a80373460 --- /dev/null +++ b/applications/plugins/blackjack/common/queue.c @@ -0,0 +1,69 @@ +#include "queue.h" + +void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas) { + if(queue_state->current != NULL && queue_state->current->render != NULL) + ((QueueItem*)queue_state->current)->render(app_state, canvas); +} + +bool run_queue(QueueState* queue_state, void* app_state) { + if(queue_state->current != NULL) { + queue_state->running = true; + if((furi_get_tick() - queue_state->start) >= queue_state->current->duration) + dequeue(queue_state, app_state); + + return true; + } + return false; +} + +void dequeue(QueueState* queue_state, void* app_state) { + ((QueueItem*)queue_state->current)->callback(app_state); + QueueItem* f = queue_state->current; + queue_state->current = f->next; + free(f); + if(queue_state->current != NULL) { + if(queue_state->current->start != NULL) queue_state->current->start(app_state); + queue_state->start = furi_get_tick(); + } else { + queue_state->running = false; + } +} + +void queue_clear(QueueState* queue_state) { + queue_state->running = false; + QueueItem* curr = queue_state->current; + while(curr != NULL) { + QueueItem* f = curr; + curr = curr->next; + free(f); + } +} + +void enqueue( + QueueState* queue_state, + void* app_state, + void (*done)(void* state), + void (*start)(void* state), + void (*render)(const void* state, Canvas* const canvas), + uint32_t duration) { + QueueItem* next; + if(queue_state->current == NULL) { + queue_state->start = furi_get_tick(); + queue_state->current = malloc(sizeof(QueueItem)); + next = queue_state->current; + if(next->start != NULL) next->start(app_state); + + } else { + next = queue_state->current; + while(next->next != NULL) { + next = (QueueItem*)(next->next); + } + next->next = malloc(sizeof(QueueItem)); + next = next->next; + } + next->callback = done; + next->render = render; + next->start = start; + next->duration = duration; + next->next = NULL; +} \ No newline at end of file diff --git a/applications/plugins/blackjack/common/queue.h b/applications/plugins/blackjack/common/queue.h new file mode 100644 index 000000000..dcfe0c091 --- /dev/null +++ b/applications/plugins/blackjack/common/queue.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +typedef struct { + void (*callback)(void* state); //Callback for when the item is dequeued + void (*render)( + const void* state, + Canvas* const canvas); //Callback for the rendering loop while this item is running + void (*start)(void* state); //Callback when this item is started running + void* next; //Pointer to the next item + uint32_t duration; //duration of the item +} QueueItem; + +typedef struct { + unsigned int start; //current queue item start time + QueueItem* current; //current queue item + bool running; //is the queue running +} QueueState; + +/** + * Enqueue a new item. + * + * @param queue_state The queue state pointer + * @param app_state Your app state + * @param done Callback for dequeue event + * @param start Callback for when the item is activated + * @param render Callback to render loop if needed + * @param duration Length of the item + */ +void enqueue( + QueueState* queue_state, + void* app_state, + void (*done)(void* state), + void (*start)(void* state), + void (*render)(const void* state, Canvas* const canvas), + uint32_t duration); +/** + * Clears all queue items + * + * @param queue_state The queue state pointer + */ +void queue_clear(QueueState* queue_state); + +/** + * Dequeues the active queue item. Usually you don't need to call it directly. + * + * @param queue_state The queue state pointer + * @param app_state Your application state + */ +void dequeue(QueueState* queue_state, void* app_state); + +/** + * Runs the queue logic (place it in your tick function) + * + * @param queue_state The queue state pointer + * @param app_state Your application state + * @return FALSE when there is nothing to run, TRUE otherwise + */ +bool run_queue(QueueState* queue_state, void* app_state); + +/** + * Calls the currently active queue items render callback (if there is any) + * + * @param queue_state The queue state pointer + * @param app_state Your application state + * @param canvas Pointer to Flipper's canvas object + */ +void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas); \ No newline at end of file diff --git a/applications/plugins/blackjack/common/ui.c b/applications/plugins/blackjack/common/ui.c new file mode 100644 index 000000000..032877a9e --- /dev/null +++ b/applications/plugins/blackjack/common/ui.c @@ -0,0 +1,257 @@ +#include "ui.h" +#include +#include +#include +#include +#include +#include + +TileMap* tileMap; +uint8_t tileMapCount = 0; + +void ui_cleanup() { + if(tileMap != NULL) { + for(uint8_t i = 0; i < tileMapCount; i++) { + if(tileMap[i].data != NULL) free(tileMap[i].data); + } + free(tileMap); + } +} + +void add_new_tilemap(uint8_t* data, unsigned long iconId) { + TileMap* old = tileMap; + tileMapCount++; + tileMap = malloc(sizeof(TileMap) * tileMapCount); + if(tileMapCount > 1) { + for(uint8_t i = 0; i < tileMapCount; i++) tileMap[i] = old[i]; + } + tileMap[tileMapCount - 1] = (TileMap){data, iconId}; +} + +uint8_t* get_tilemap(unsigned long icon_id) { + for(uint8_t i = 0; i < tileMapCount; i++) { + if(tileMap[i].iconId == icon_id) return tileMap[i].data; + } + + return NULL; +} + +uint32_t pixel_index(uint8_t x, uint8_t y) { + return y * SCREEN_WIDTH + x; +} + +bool in_screen(int16_t x, int16_t y) { + return x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT; +} + +unsigned flipBit(uint8_t x, uint8_t bit) { + return x ^ (1 << bit); +} + +unsigned setBit(uint8_t x, uint8_t bit) { + return x | (1 << bit); +} + +unsigned unsetBit(uint8_t x, uint8_t bit) { + return x & ~(1 << bit); +} + +bool test_pixel(uint8_t* data, uint8_t x, uint8_t y, uint8_t w) { + uint8_t current_bit = (y % 8); + uint8_t current_row = ((y - current_bit) / 8); + uint8_t current_value = data[current_row * w + x]; + return current_value & (1 << current_bit); +} + +uint8_t* get_buffer(Canvas* const canvas) { + return canvas->fb.tile_buf_ptr; + // return canvas_get_buffer(canvas); +} +uint8_t* make_buffer() { + return malloc(sizeof(uint8_t) * 8 * 128); +} +void clone_buffer(uint8_t* canvas, uint8_t* data) { + for(int i = 0; i < 1024; i++) { + data[i] = canvas[i]; + } +} + +bool read_pixel(Canvas* const canvas, int16_t x, int16_t y) { + if(in_screen(x, y)) { + return test_pixel(get_buffer(canvas), x, y, SCREEN_WIDTH); + } + return false; +} + +void set_pixel(Canvas* const canvas, int16_t x, int16_t y, DrawMode draw_mode) { + if(in_screen(x, y)) { + uint8_t current_bit = (y % 8); + uint8_t current_row = ((y - current_bit) / 8); + uint32_t i = pixel_index(x, current_row); + uint8_t* buffer = get_buffer(canvas); + + uint8_t current_value = buffer[i]; + if(draw_mode == Inverse) { + buffer[i] = flipBit(current_value, current_bit); + } else { + if(draw_mode == White) { + buffer[i] = unsetBit(current_value, current_bit); + } else { + buffer[i] = setBit(current_value, current_bit); + } + } + } +} + +void draw_line( + Canvas* const canvas, + int16_t x1, + int16_t y1, + int16_t x2, + int16_t y2, + DrawMode draw_mode) { + for(int16_t x = x2; x >= x1; x--) { + for(int16_t y = y2; y >= y1; y--) { + set_pixel(canvas, x, y, draw_mode); + } + } +} + +void draw_rounded_box_frame( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode draw_mode) { + int16_t xMinCorner = x + 1; + int16_t xMax = x + w - 1; + int16_t xMaxCorner = x + w - 2; + int16_t yMinCorner = y + 1; + int16_t yMax = y + h - 1; + int16_t yMaxCorner = y + h - 2; + draw_line(canvas, xMinCorner, y, xMaxCorner, y, draw_mode); + draw_line(canvas, xMinCorner, yMax, xMaxCorner, yMax, draw_mode); + draw_line(canvas, x, yMinCorner, x, yMaxCorner, draw_mode); + draw_line(canvas, xMax, yMinCorner, xMax, yMaxCorner, draw_mode); +} + +void draw_rounded_box( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode draw_mode) { + for(int16_t o = w - 2; o >= 1; o--) { + for(int16_t p = h - 2; p >= 1; p--) { + set_pixel(canvas, x + o, y + p, draw_mode); + } + } + draw_rounded_box_frame(canvas, x, y, w, h, draw_mode); +} + +void invert_shape(Canvas* const canvas, uint8_t* data, int16_t x, int16_t y, uint8_t w, uint8_t h) { + draw_pixels(canvas, data, x, y, w, h, Inverse); +} + +void draw_pixels( + Canvas* const canvas, + uint8_t* data, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode) { + for(int8_t o = 0; o < w; o++) { + for(int8_t p = 0; p < h; p++) { + if(in_screen(o + x, p + y) && data[p * w + o] == 1) + set_pixel(canvas, o + x, p + y, drawMode); + } + } +} + +void draw_rectangle( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode) { + for(int8_t o = 0; o < w; o++) { + for(int8_t p = 0; p < h; p++) { + if(in_screen(o + x, p + y)) { + set_pixel(canvas, o + x, p + y, drawMode); + } + } + } +} + +void invert_rectangle(Canvas* const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h) { + draw_rectangle(canvas, x, y, w, h, Inverse); +} + +uint8_t* image_data(Canvas* const canvas, const Icon* icon) { + uint8_t* data = malloc(sizeof(uint8_t) * 8 * 128); + uint8_t* screen = canvas->fb.tile_buf_ptr; + canvas->fb.tile_buf_ptr = data; + canvas_draw_icon(canvas, 0, 0, icon); + canvas->fb.tile_buf_ptr = screen; + return data; +} + +uint8_t* getOrAddIconData(Canvas* const canvas, const Icon* icon) { + uint8_t* icon_data = get_tilemap((unsigned long)icon); + if(icon_data == NULL) { + icon_data = image_data(canvas, icon); + add_new_tilemap(icon_data, (unsigned long)icon); + } + return icon_data; +} + +void draw_icon_clip( + Canvas* const canvas, + const Icon* icon, + int16_t x, + int16_t y, + uint8_t left, + uint8_t top, + uint8_t w, + uint8_t h, + DrawMode drawMode) { + uint8_t* icon_data = getOrAddIconData(canvas, icon); + + for(int i = 0; i < w; i++) { + for(int j = 0; j < h; j++) { + bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH); + if(drawMode == Filled) { + set_pixel(canvas, x + i, y + j, on ? Black : White); + } else if(on) + set_pixel(canvas, x + i, y + j, drawMode); + } + } +} + +void draw_icon_clip_flipped( + Canvas* const canvas, + const Icon* icon, + int16_t x, + int16_t y, + uint8_t left, + uint8_t top, + uint8_t w, + uint8_t h, + DrawMode drawMode) { + uint8_t* icon_data = getOrAddIconData(canvas, icon); + + for(int i = 0; i < w; i++) { + for(int j = 0; j < h; j++) { + bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH); + + if(drawMode == Filled) { + set_pixel(canvas, x + w - i - 1, y + h - j - 1, on ? Black : White); + } else if(on) + set_pixel(canvas, x + w - i - 1, y + h - j - 1, drawMode); + } + } +} \ No newline at end of file diff --git a/applications/plugins/blackjack/common/ui.h b/applications/plugins/blackjack/common/ui.h new file mode 100644 index 000000000..13d8da257 --- /dev/null +++ b/applications/plugins/blackjack/common/ui.h @@ -0,0 +1,105 @@ +#pragma once + +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 + +typedef enum { + Black, + White, + Inverse, + Filled //Currently only for Icon clip drawing +} DrawMode; + +// size is the screen size + +typedef struct { + uint8_t* data; + unsigned long iconId; +} TileMap; + +bool test_pixel(uint8_t* data, uint8_t x, uint8_t y, uint8_t w); + +uint8_t* image_data(Canvas* const canvas, const Icon* icon); + +uint32_t pixel_index(uint8_t x, uint8_t y); + +void draw_icon_clip( + Canvas* const canvas, + const Icon* icon, + int16_t x, + int16_t y, + uint8_t left, + uint8_t top, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void draw_icon_clip_flipped( + Canvas* const canvas, + const Icon* icon, + int16_t x, + int16_t y, + uint8_t left, + uint8_t top, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void draw_rounded_box( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void draw_rounded_box_frame( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void draw_rectangle( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void invert_rectangle(Canvas* const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h); + +void invert_shape(Canvas* const canvas, uint8_t* data, int16_t x, int16_t y, uint8_t w, uint8_t h); + +void draw_pixels( + Canvas* const canvas, + uint8_t* data, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +bool read_pixel(Canvas* const canvas, int16_t x, int16_t y); + +void set_pixel(Canvas* const canvas, int16_t x, int16_t y, DrawMode draw_mode); + +void draw_line( + Canvas* const canvas, + int16_t x1, + int16_t y1, + int16_t x2, + int16_t y2, + DrawMode draw_mode); + +bool in_screen(int16_t x, int16_t y); + +void ui_cleanup(); +uint8_t* get_buffer(Canvas* const canvas); +uint8_t* make_buffer(); +void clone_buffer(uint8_t* canvas, uint8_t* data); \ No newline at end of file diff --git a/applications/plugins/blackjack/defines.h b/applications/plugins/blackjack/defines.h index 36e547eb3..b400badfb 100644 --- a/applications/plugins/blackjack/defines.h +++ b/applications/plugins/blackjack/defines.h @@ -3,17 +3,33 @@ #include #include #include -#include "card.h" +#include +#include +#include "common/card.h" +#include "common/queue.h" +#include "common/menu.h" -#define ANIMATION_TIME furi_ms_to_ticks(1500) -#define ANIMATION_END_MARGIN furi_ms_to_ticks(200) -#define ROUND_PRICE 10 +#define APP_NAME "Blackjack" + +#define CONF_ANIMATION_DURATION "AnimationDuration" +#define CONF_MESSAGE_DURATION "MessageDuration" +#define CONF_STARTING_MONEY "StartingMoney" +#define CONF_ROUND_PRICE "RoundPrice" +#define CONF_SOUND_EFFECTS "SoundEffects" typedef enum { EventTypeTick, EventTypeKey, } EventType; +typedef struct { + uint32_t animation_duration; + uint32_t message_duration; + uint32_t starting_money; + uint32_t round_price; + bool sound_effects; +} Settings; + typedef struct { EventType type; InputEvent input; @@ -23,15 +39,17 @@ typedef enum { GameStateGameOver, GameStateStart, GameStatePlay, + GameStateSettings, GameStateDealer, } PlayState; typedef enum { DirectionUp, - DirectionRight, DirectionDown, + DirectionRight, DirectionLeft, Select, + Back, None } Direction; @@ -42,20 +60,17 @@ typedef struct { uint8_t dealer_card_count; Direction selectDirection; + Settings settings; uint32_t player_score; - uint8_t multiplier; uint32_t bet; - uint8_t player_time; - bool doubled; - bool animating; - bool started; uint8_t selectedMenu; + bool doubled; + bool started; + bool processing; Deck deck; PlayState state; + QueueState queue_state; + Menu* menu; unsigned int last_tick; - unsigned int animationStart; - bool dealer_animating; - unsigned int delay_tick; } GameState; - diff --git a/applications/plugins/blackjack/ui.c b/applications/plugins/blackjack/ui.c index ab6462473..d4ee82191 100644 --- a/applications/plugins/blackjack/ui.c +++ b/applications/plugins/blackjack/ui.c @@ -1,49 +1,33 @@ -#include "ui.h" -#include "card.h" #include -#include "util.h" +#include -const char MoneyMul[4] = { - 'K', 'B', 'T', 'S' -}; +#include "ui.h" -void draw_player_scene(Canvas *const canvas, const GameState *game_state) { +#define LINE_HEIGHT 16 +#define ITEM_PADDING 4 + +const char MoneyMul[4] = {'K', 'B', 'T', 'S'}; + +void draw_player_scene(Canvas* const canvas, const GameState* game_state) { int max_card = game_state->player_card_count; - if (max_card > 0) - drawPlayerDeck((game_state->player_cards), max_card, canvas); + if(max_card > 0) draw_deck((game_state->player_cards), max_card, canvas); - drawCardBackAt(13, 5, canvas); + if(game_state->dealer_card_count > 0) draw_card_back_at(13, 5, canvas); max_card = game_state->dealer_card_count; - if (max_card > 1) { - drawCardAt(2, 2, game_state->dealer_cards[1].pip, game_state->dealer_cards[1].character, Normal, - canvas); + if(max_card > 1) { + draw_card_at( + 2, 2, game_state->dealer_cards[1].pip, game_state->dealer_cards[1].character, canvas); } } -void draw_dealer_scene(Canvas *const canvas, const GameState *game_state) { +void draw_dealer_scene(Canvas* const canvas, const GameState* game_state) { uint8_t max_card = game_state->dealer_card_count; - drawPlayerDeck((game_state->dealer_cards), max_card, canvas); + draw_deck((game_state->dealer_cards), max_card, canvas); } -void draw_card_animation(const GameState *game_state, Canvas *const canvas) { - float t = (float) (furi_get_tick() - game_state->animationStart) / (ANIMATION_TIME - ANIMATION_END_MARGIN); - t *= 2; - Card animatingCard = game_state->deck.cards[game_state->deck.index]; - if (t > 1) { - int cardY = round(lerp(-CARD_HEIGHT, 10, 1)); - drawCardAt(64 - CARD_HALF_WIDHT, cardY, animatingCard.pip, - animatingCard.character, Normal, canvas); - } else { - int cardY = round(lerp(-CARD_HEIGHT, 10, t)); - drawCardAt(64 - CARD_HALF_WIDHT, cardY, animatingCard.pip, - animatingCard.character, Normal, canvas); -// drawCardBackAt(64 - CARD_HALF_WIDHT, cardY, canvas); - } -} - -void popupFrame(Canvas *const canvas) { +void popup_frame(Canvas* const canvas) { canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 32, 15, 66, 13); canvas_set_color(canvas, ColorBlack); @@ -51,34 +35,16 @@ void popupFrame(Canvas *const canvas) { canvas_set_font(canvas, FontSecondary); } -void draw_message_scene(Canvas *const canvas, const GameState *game_state) { - switch (game_state->state) { - case GameStateStart: - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Blackjack"); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 64, 24, AlignCenter, AlignTop, "Made by Doofy"); - elements_multiline_text_aligned(canvas, 64, 38, AlignCenter, AlignTop, "Press center button\nto start"); - break; - case GameStateGameOver: - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Game Over"); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 64, 24, AlignCenter, AlignTop, "Press center button\nto start"); - break; - default: - break; - } -} - -void draw_play_menu(Canvas *const canvas, const GameState *game_state) { - const char *menus[3] = {"Double", "Hit", "Stay"}; - for (uint8_t m = 0; m < 3; m++) { - if (m == 0 && (game_state->doubled || game_state->player_score < ROUND_PRICE)) continue; +void draw_play_menu(Canvas* const canvas, const GameState* game_state) { + const char* menus[3] = {"Double", "Hit", "Stay"}; + for(uint8_t m = 0; m < 3; m++) { + if(m == 0 && + (game_state->doubled || game_state->player_score < game_state->settings.round_price)) + continue; int y = m * 13 + 25; canvas_set_color(canvas, ColorBlack); - if (game_state->selectedMenu == m) { + if(game_state->selectedMenu == m) { canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 1, y, 31, 12); } else { @@ -88,7 +54,7 @@ void draw_play_menu(Canvas *const canvas, const GameState *game_state) { canvas_draw_frame(canvas, 1, y, 31, 12); } - if (game_state->selectedMenu == m) + if(game_state->selectedMenu == m) canvas_set_color(canvas, ColorWhite); else canvas_set_color(canvas, ColorBlack); @@ -96,26 +62,34 @@ void draw_play_menu(Canvas *const canvas, const GameState *game_state) { } } -void draw_score(Canvas *const canvas, bool top, uint8_t amount) { +void draw_screen(Canvas* const canvas, const bool* points) { + for(uint8_t x = 0; x < 128; x++) { + for(uint8_t y = 0; y < 64; y++) { + if(points[y * 128 + x]) canvas_draw_dot(canvas, x, y); + } + } +} + +void draw_score(Canvas* const canvas, bool top, uint8_t amount) { char drawChar[20]; snprintf(drawChar, sizeof(drawChar), "Player score: %i", amount); - if (top) + if(top) canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, drawChar); else canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, drawChar); } -void draw_money(Canvas *const canvas, uint32_t score) { +void draw_money(Canvas* const canvas, uint32_t score) { canvas_set_font(canvas, FontSecondary); - char drawChar[10]; + char drawChar[11]; uint32_t currAmount = score; - if (currAmount < 1000) { + if(currAmount < 1000) { snprintf(drawChar, sizeof(drawChar), "$%lu", currAmount); } else { char c = 'K'; - for (uint8_t i = 0; i < 4; i++) { + for(uint8_t i = 0; i < 4; i++) { currAmount = currAmount / 1000; - if (currAmount < 1000) { + if(currAmount < 1000) { c = MoneyMul[i]; break; } @@ -124,4 +98,89 @@ void draw_money(Canvas *const canvas, uint32_t score) { snprintf(drawChar, sizeof(drawChar), "$%lu %c", currAmount, c); } canvas_draw_str_aligned(canvas, 126, 2, AlignRight, AlignTop, drawChar); +} + +void draw_menu( + Canvas* const canvas, + const char* text, + const char* value, + int8_t y, + bool left_caret, + bool right_caret, + bool selected) { + UNUSED(selected); + if(y < 0 || y >= 64) return; + + if(selected) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, y, 122, LINE_HEIGHT); + canvas_set_color(canvas, ColorWhite); + } + + canvas_draw_str_aligned(canvas, 4, y + ITEM_PADDING, AlignLeft, AlignTop, text); + if(left_caret) canvas_draw_str_aligned(canvas, 80, y + ITEM_PADDING, AlignLeft, AlignTop, "<"); + canvas_draw_str_aligned(canvas, 100, y + ITEM_PADDING, AlignCenter, AlignTop, value); + if(right_caret) + canvas_draw_str_aligned(canvas, 120, y + ITEM_PADDING, AlignRight, AlignTop, ">"); + + canvas_set_color(canvas, ColorBlack); +} + +void settings_page(Canvas* const canvas, const GameState* gameState) { + char drawChar[10]; + int startY = 0; + if(LINE_HEIGHT * (gameState->selectedMenu + 1) >= 64) { + startY -= (LINE_HEIGHT * (gameState->selectedMenu + 1)) - 64; + } + + int scrollHeight = round(64 / 6.0) + ITEM_PADDING * 2; + int scrollPos = 64 / (6.0 / (gameState->selectedMenu + 1)) - ITEM_PADDING * 2; + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 123, scrollPos, 4, scrollHeight); + canvas_draw_box(canvas, 125, 0, 1, 64); + + snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.starting_money); + draw_menu( + canvas, + "Start money", + drawChar, + 0 * LINE_HEIGHT + startY, + gameState->settings.starting_money > gameState->settings.round_price, + gameState->settings.starting_money < 400, + gameState->selectedMenu == 0); + snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.round_price); + draw_menu( + canvas, + "Round price", + drawChar, + 1 * LINE_HEIGHT + startY, + gameState->settings.round_price > 10, + gameState->settings.round_price < gameState->settings.starting_money, + gameState->selectedMenu == 1); + + snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.animation_duration); + draw_menu( + canvas, + "Anim. length", + drawChar, + 2 * LINE_HEIGHT + startY, + gameState->settings.animation_duration > 0, + gameState->settings.animation_duration < 2000, + gameState->selectedMenu == 2); + snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.message_duration); + draw_menu( + canvas, + "Popup time", + drawChar, + 3 * LINE_HEIGHT + startY, + gameState->settings.message_duration > 0, + gameState->settings.message_duration < 2000, + gameState->selectedMenu == 3); + // draw_menu(canvas, "Sound", gameState->settings.sound_effects ? "Yes" : "No", + // 5 * LINE_HEIGHT + startY, + // true, + // true, + // gameState->selectedMenu == 5 + // ); } \ No newline at end of file diff --git a/applications/plugins/blackjack/ui.h b/applications/plugins/blackjack/ui.h index 7a9499f99..51b388010 100644 --- a/applications/plugins/blackjack/ui.h +++ b/applications/plugins/blackjack/ui.h @@ -3,18 +3,16 @@ #include "defines.h" #include -void draw_player_scene(Canvas *const canvas, const GameState *game_state); +void draw_player_scene(Canvas* const canvas, const GameState* game_state); -void draw_dealer_scene(Canvas *const canvas, const GameState *game_state); +void draw_dealer_scene(Canvas* const canvas, const GameState* game_state); -void draw_message_scene(Canvas *const canvas, const GameState *game_state); +void draw_play_menu(Canvas* const canvas, const GameState* game_state); -void draw_play_menu(Canvas *const canvas, const GameState *game_state); +void draw_score(Canvas* const canvas, bool top, uint8_t amount); -void draw_score(Canvas *const canvas, bool top, uint8_t amount); +void draw_money(Canvas* const canvas, uint32_t score); +void settings_page(Canvas* const canvas, const GameState* gameState); -void draw_money(Canvas *const canvas, uint32_t score); - -void draw_card_animation(const GameState *game_state, Canvas *const canvas); - -void popupFrame(Canvas *const canvas); \ No newline at end of file +void popup_frame(Canvas* const canvas); +void draw_screen(Canvas* const canvas, const bool* points); diff --git a/applications/plugins/blackjack/util.c b/applications/plugins/blackjack/util.c index bc260eb54..8e88c2231 100644 --- a/applications/plugins/blackjack/util.c +++ b/applications/plugins/blackjack/util.c @@ -1,65 +1,123 @@ +#include #include "util.h" -static List *afterDelay; +const char* CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings"); -float lerp(float v0, float v1, float t) { - return (1 - t) * v0 + t * v1; -} +void save_settings(Settings settings) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + FURI_LOG_D(APP_NAME, "Saving config"); + if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { + FURI_LOG_D( + APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration); + flipper_format_update_uint32( + file, CONF_ANIMATION_DURATION, &(settings.animation_duration), 1); -void queue(GameState *game_state, - void (*callback)(GameState *game_state), - void (*start)(GameState *game_state), - void (*processing)(const GameState *gameState, Canvas *const canvas) -) { - if (afterDelay == NULL) { - game_state->animationStart = game_state->last_tick; - afterDelay = malloc(sizeof(List)); - afterDelay->callback = callback; - afterDelay->processing = processing; - afterDelay->start = start; - afterDelay->next = NULL; + FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_MESSAGE_DURATION, settings.message_duration); + flipper_format_update_uint32(file, CONF_MESSAGE_DURATION, &(settings.message_duration), 1); + + FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_STARTING_MONEY, settings.starting_money); + flipper_format_update_uint32(file, CONF_STARTING_MONEY, &(settings.starting_money), 1); + + FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ROUND_PRICE, settings.round_price); + flipper_format_update_uint32(file, CONF_ROUND_PRICE, &(settings.round_price), 1); + + FURI_LOG_D(APP_NAME, "Saving %s: %i", CONF_SOUND_EFFECTS, settings.sound_effects ? 1 : 0); + flipper_format_update_bool(file, CONF_SOUND_EFFECTS, &(settings.sound_effects), 1); + FURI_LOG_D(APP_NAME, "Config saved"); } else { - List *next = afterDelay; - while (next->next != NULL) { next = (List *) (next->next); } - next->next = malloc(sizeof(List)); - ((List *) next->next)->callback = callback; - ((List *) next->next)->processing = processing; - ((List *) next->next)->start = start; - ((List *) next->next)->next = NULL; + FURI_LOG_E(APP_NAME, "Save error"); } + flipper_format_file_close(file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); } -void queue_clear() { - while (afterDelay != NULL) { - List *f = afterDelay; - free(f); - afterDelay = f->next; - } +void save_settings_file(FlipperFormat* file, Settings* settings) { + flipper_format_write_header_cstr(file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION); + flipper_format_write_comment_cstr(file, "Card animation duration in ms"); + flipper_format_write_uint32(file, CONF_ANIMATION_DURATION, &(settings->animation_duration), 1); + flipper_format_write_comment_cstr(file, "Popup message duration in ms"); + flipper_format_write_uint32(file, CONF_MESSAGE_DURATION, &(settings->message_duration), 1); + flipper_format_write_comment_cstr(file, "Player's starting money"); + flipper_format_write_uint32(file, CONF_STARTING_MONEY, &(settings->starting_money), 1); + flipper_format_write_comment_cstr(file, "Round price"); + flipper_format_write_uint32(file, CONF_ROUND_PRICE, &(settings->round_price), 1); + flipper_format_write_comment_cstr(file, "Enable sound effects"); + flipper_format_write_bool(file, CONF_SOUND_EFFECTS, &(settings->sound_effects), 1); } -void dequeue(GameState *game_state) { - ((List *) afterDelay)->callback(game_state); - List *f = afterDelay; - afterDelay = (List *) afterDelay->next; - free(f); - if (afterDelay != NULL && afterDelay->start != NULL)afterDelay->start(game_state); - game_state->animationStart = game_state->last_tick; -} +Settings load_settings() { + Settings settings; -void animateQueue(const GameState *game_state, Canvas *const canvas) { - if (afterDelay != NULL && ((List *) afterDelay)->processing != NULL) { - ((List *) afterDelay)->processing(game_state, canvas); - } -} + FURI_LOG_D(APP_NAME, "Loading default settings"); + settings.animation_duration = 800; + settings.message_duration = 1500; + settings.starting_money = 200; + settings.round_price = 10; + settings.sound_effects = true; -bool run_queue(GameState *game_state) { - if (afterDelay != NULL) { - game_state->animating = true; - if ((game_state->last_tick - game_state->animationStart) > ANIMATION_TIME) { - dequeue(game_state); + FURI_LOG_D(APP_NAME, "Opening storage"); + Storage* storage = furi_record_open(RECORD_STORAGE); + FURI_LOG_D(APP_NAME, "Allocating file"); + FlipperFormat* file = flipper_format_file_alloc(storage); + + FURI_LOG_D(APP_NAME, "Allocating string"); + FuriString* string_value; + string_value = furi_string_alloc(); + + if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) { + FURI_LOG_D(APP_NAME, "Config file %s not found, creating new one...", CONFIG_FILE_PATH); + if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { + FURI_LOG_E(APP_NAME, "Error creating new file %s", CONFIG_FILE_PATH); + flipper_format_file_close(file); + } else { + save_settings_file(file, &settings); + } + } else { + if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { + FURI_LOG_E(APP_NAME, "Error opening existing file %s", CONFIG_FILE_PATH); + flipper_format_file_close(file); + } else { + uint32_t value; + bool valueBool; + FURI_LOG_D(APP_NAME, "Checking version"); + if(!flipper_format_read_header(file, string_value, &value)) { + FURI_LOG_E(APP_NAME, "Config file mismatch"); + } else { + FURI_LOG_D(APP_NAME, "Loading %s", CONF_ANIMATION_DURATION); + if(flipper_format_read_uint32(file, CONF_ANIMATION_DURATION, &value, 1)) { + settings.animation_duration = value; + FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ANIMATION_DURATION, value); + } + FURI_LOG_D(APP_NAME, "Loading %s", CONF_MESSAGE_DURATION); + if(flipper_format_read_uint32(file, CONF_MESSAGE_DURATION, &value, 1)) { + settings.message_duration = value; + FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_MESSAGE_DURATION, value); + } + FURI_LOG_D(APP_NAME, "Loading %s", CONF_STARTING_MONEY); + if(flipper_format_read_uint32(file, CONF_STARTING_MONEY, &value, 1)) { + settings.starting_money = value; + FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_STARTING_MONEY, value); + } + FURI_LOG_D(APP_NAME, "Loading %s", CONF_ROUND_PRICE); + if(flipper_format_read_uint32(file, CONF_ROUND_PRICE, &value, 1)) { + settings.round_price = value; + FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ROUND_PRICE, value); + } + FURI_LOG_D(APP_NAME, "Loading %s", CONF_SOUND_EFFECTS); + if(flipper_format_read_bool(file, CONF_SOUND_EFFECTS, &valueBool, 1)) { + settings.sound_effects = valueBool; + FURI_LOG_D(APP_NAME, "Loaded %s: %i", CONF_ROUND_PRICE, valueBool ? 1 : 0); + } + } + flipper_format_file_close(file); } - return true; } - game_state->animating = false; - return false; -} + + furi_string_free(string_value); + // flipper_format_file_close(file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + return settings; +} \ No newline at end of file diff --git a/applications/plugins/blackjack/util.h b/applications/plugins/blackjack/util.h index 9bd966740..4bcc4d890 100644 --- a/applications/plugins/blackjack/util.h +++ b/applications/plugins/blackjack/util.h @@ -1,18 +1,7 @@ #pragma once #include "defines.h" +#define CONFIG_FILE_HEADER "Blackjack config file" +#define CONFIG_FILE_VERSION 1 -typedef struct{ - void (*callback)(GameState *game_state); - void (*processing)(const GameState *game_state, Canvas *const canvas); - void (*start)(GameState *game_state); - void *next; -} List; - -float lerp(float v0, float v1, float t); -void queue(GameState *game_state, - void (*callback)(GameState *game_state), - void (*start)(GameState *game_state), - void (*processing)(const GameState *gameState, Canvas *const canvas)); -bool run_queue(GameState *gameState); -void animateQueue(const GameState *gameState, Canvas *const canvas); -void queue_clear(); \ No newline at end of file +void save_settings(Settings settings); +Settings load_settings(); \ No newline at end of file diff --git a/applications/plugins/bpmtapper/application.fam b/applications/plugins/bpmtapper/application.fam index 70cd3da84..93c4179c5 100644 --- a/applications/plugins/bpmtapper/application.fam +++ b/applications/plugins/bpmtapper/application.fam @@ -8,5 +8,6 @@ App( stack_size=2 * 1024, fap_icon="bpm_10px.png", fap_category="Music", + fap_icon_assets="icons", order=15, ) diff --git a/applications/plugins/bpmtapper/bpm.c b/applications/plugins/bpmtapper/bpm.c index 6bf658528..cee83a6a4 100644 --- a/applications/plugins/bpmtapper/bpm.c +++ b/applications/plugins/bpmtapper/bpm.c @@ -4,6 +4,7 @@ #include #include #include +#include "BPM_Tapper_icons.h" typedef enum { EventTypeTick, @@ -145,7 +146,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); furi_string_reset(tempStr); - furi_string_printf(tempStr, "Interval: %dms", bpm_state->interval); + furi_string_printf(tempStr, "Interval: %ldms", bpm_state->interval); canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); furi_string_reset(tempStr); @@ -235,6 +236,8 @@ int32_t bpm_tapper_app(void* p) { // Exit the plugin processing = false; break; + default: + break; } } } diff --git a/applications/plugins/bpmtapper/icons/DolphinCommon_56x48.png b/applications/plugins/bpmtapper/icons/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/applications/plugins/bpmtapper/icons/DolphinCommon_56x48.png differ 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 000000000..2954bb6a6 Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/ButtonDown_7x4.png differ 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 000000000..0b4655d43 Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/ButtonLeft_4x7.png differ 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 000000000..8e1c74c1c Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/ButtonRight_4x7.png differ 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 000000000..1be79328b Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/ButtonUp_7x4.png differ 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 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/Ok_btn_9x9.png differ diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png b/applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png new file mode 100644 index 000000000..9687397af Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/Pin_arrow_down_7x9.png differ 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 000000000..fb4ded78f Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/Pin_arrow_left_9x7.png differ diff --git a/applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png b/applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png new file mode 100644 index 000000000..97648d176 Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/Pin_arrow_right_9x7.png differ 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 000000000..a91a6fd5e Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/Pin_arrow_up_7x9.png differ diff --git a/applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png b/applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/applications/plugins/bt_hid_app/assets/Pin_back_arrow_10x8.png differ 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 d39034069..f751c53d2 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); } diff --git a/applications/plugins/caesarcipher/application.fam b/applications/plugins/caesarcipher/application.fam index bfe75623f..652585de2 100644 --- a/applications/plugins/caesarcipher/application.fam +++ b/applications/plugins/caesarcipher/application.fam @@ -1,5 +1,5 @@ App( - appid="caesar_cipher", + appid="Caesar_Cipher", name="Caesar Cipher", apptype=FlipperAppType.PLUGIN, entry_point="caesar_cipher_app", diff --git a/applications/plugins/chess/fast_chess.c b/applications/plugins/chess/fast_chess.c index c09b912b0..d9d42e42c 100644 --- a/applications/plugins/chess/fast_chess.c +++ b/applications/plugins/chess/fast_chess.c @@ -269,7 +269,7 @@ int toMinFen(char* fen, Position* position) { emptyCount++; } else { if(emptyCount != 0) { - snprintf(&fen[charCount++], 2, "%d", emptyCount); + snprintf(&fen[charCount++], 2, "%ls", emptyCount); emptyCount = 0; } fen[charCount++] = bb2char(bb, &(position->board)); @@ -278,7 +278,7 @@ int toMinFen(char* fen, Position* position) { file++; if(file > 7) { if(emptyCount != 0) { - snprintf(&fen[charCount++], 2, "%d", emptyCount); + snprintf(&fen[charCount++], 2, "%ls", emptyCount); emptyCount = 0; } file = 0; @@ -2357,7 +2357,7 @@ Node iterativeDeepeningAlphaBeta(Position* position, char depth, int alpha, int int score = iterativeDeepeningAlphaBeta(&newPosition, depth - 1, alpha, beta, FALSE).score; if(verbose) { - printf("%.2f\n", score / 100.00); + printf("%.2f\n", (double)score / (double)100.00); fflush(stdout); } @@ -2850,8 +2850,8 @@ Move getAIMove(Game* game, int depth) { printf( " in %d seconds [%+.2f, %+.2f]\n", (int)(endTime - startTime), - staticEvaluation(&game->position) / 100.0, - node.score / 100.0); + (double)staticEvaluation(&game->position) / (double)100.0, + (double)node.score / (double)100.0); fflush(stdout); return node.move; diff --git a/applications/plugins/clock_app/application.fam b/applications/plugins/clock_app/application.fam deleted file mode 100644 index 90334bc4a..000000000 --- a/applications/plugins/clock_app/application.fam +++ /dev/null @@ -1,13 +0,0 @@ -App( - appid="Clock", - name="Clock", - apptype=FlipperAppType.EXTERNAL, - entry_point="clock_app", - cdefines=["APP_CLOCK"], - requires=["gui"], - # icon="A_Clock_14", - stack_size=2 * 1024, - order=9, - fap_icon="ClockIcon.png", - fap_category="Main", -) \ No newline at end of file diff --git a/applications/plugins/counter/README.md b/applications/plugins/counter/README.md new file mode 100644 index 000000000..803c68634 --- /dev/null +++ b/applications/plugins/counter/README.md @@ -0,0 +1,8 @@ +# Dolphin counter +This is a simple plugin for the [Flipper Zero](https://www.flipperzero.one). +It gives you access to a counter which you can increment and decrement using the up and down buttons respectively. + +![preview](https://github.com/Krulknul/dolphin-counter/blob/main/media/preview.gif) + +# How to install this? +I'd recommend using [flipperzero-ufbt](https://github.com/flipperdevices/flipperzero-ufbt), which is a lightweight tool for quickly testing Flipper Zero applications. The app will stay present on your device so it is not necessary to flash the entire firmware. diff --git a/applications/plugins/counter/application.fam b/applications/plugins/counter/application.fam new file mode 100644 index 000000000..8f7eef884 --- /dev/null +++ b/applications/plugins/counter/application.fam @@ -0,0 +1,12 @@ +App( + appid="counter", + name="Counter", + apptype=FlipperAppType.PLUGIN, + entry_point="counterapp", + requires=[ + "gui", + ], + fap_category="Misc", + fap_icon="icons/counter_icon.png", + fap_icon_assets="icons", +) \ No newline at end of file diff --git a/applications/plugins/counter/counter.c b/applications/plugins/counter/counter.c new file mode 100644 index 000000000..c76ed7293 --- /dev/null +++ b/applications/plugins/counter/counter.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include + +#define MAX_COUNT 99 +#define BOXTIME 2 +#define BOXWIDTH 30 +#define MIDDLE_X 64 - BOXWIDTH / 2 +#define MIDDLE_Y 32 - BOXWIDTH / 2 +#define OFFSET_Y 9 + +typedef struct { + FuriMessageQueue* input_queue; + ViewPort* view_port; + Gui* gui; + FuriMutex** mutex; + + int count; + bool pressed; + int boxtimer; +} Counter; + +void state_free(Counter* c) { + gui_remove_view_port(c->gui, c->view_port); + furi_record_close(RECORD_GUI); + view_port_free(c->view_port); + furi_message_queue_free(c->input_queue); + furi_mutex_free(c->mutex); + free(c); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + Counter* c = ctx; + if(input_event->type == InputTypeShort) { + furi_message_queue_put(c->input_queue, input_event, 0); + } +} + +static void render_callback(Canvas* canvas, void* ctx) { + Counter* c = ctx; + furi_check(furi_mutex_acquire(c->mutex, FuriWaitForever) == FuriStatusOk); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignCenter, "Counter :)"); + canvas_set_font(canvas, FontBigNumbers); + + char scount[5]; + if(c->pressed == true || c->boxtimer > 0) { + canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y + OFFSET_Y, BOXWIDTH, BOXWIDTH, 5); + canvas_draw_rframe( + canvas, MIDDLE_X - 1, MIDDLE_Y + OFFSET_Y - 1, BOXWIDTH + 2, BOXWIDTH + 2, 5); + canvas_draw_rframe( + canvas, MIDDLE_X - 2, MIDDLE_Y + OFFSET_Y - 2, BOXWIDTH + 4, BOXWIDTH + 4, 5); + c->pressed = false; + c->boxtimer--; + } else { + canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y + OFFSET_Y, BOXWIDTH, BOXWIDTH, 5); + } + snprintf(scount, sizeof(scount), "%d", c->count); + canvas_draw_str_aligned(canvas, 64, 32 + OFFSET_Y, AlignCenter, AlignCenter, scount); + furi_mutex_release(c->mutex); +} + +Counter* state_init() { + Counter* c = malloc(sizeof(Counter)); + c->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + c->view_port = view_port_alloc(); + c->gui = furi_record_open(RECORD_GUI); + c->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + c->count = 0; + c->boxtimer = 0; + view_port_input_callback_set(c->view_port, input_callback, c); + view_port_draw_callback_set(c->view_port, render_callback, c); + gui_add_view_port(c->gui, c->view_port, GuiLayerFullscreen); + return c; +} + +int32_t counterapp(void) { + Counter* c = state_init(); + + while(1) { + InputEvent input; + while(furi_message_queue_get(c->input_queue, &input, FuriWaitForever) == FuriStatusOk) { + furi_check(furi_mutex_acquire(c->mutex, FuriWaitForever) == FuriStatusOk); + + if(input.key == InputKeyBack) { + furi_mutex_release(c->mutex); + state_free(c); + return 0; + } else if(input.key == InputKeyUp && c->count < MAX_COUNT) { + c->pressed = true; + c->boxtimer = BOXTIME; + c->count++; + } else if(input.key == InputKeyDown && c->count != 0) { + c->pressed = true; + c->boxtimer = BOXTIME; + c->count--; + } + furi_mutex_release(c->mutex); + view_port_update(c->view_port); + } + } + state_free(c); + return 0; +} diff --git a/applications/plugins/counter/icons/counter_icon.png b/applications/plugins/counter/icons/counter_icon.png new file mode 100644 index 000000000..4b8358b42 Binary files /dev/null and b/applications/plugins/counter/icons/counter_icon.png differ diff --git a/applications/plugins/counter/media/preview.gif b/applications/plugins/counter/media/preview.gif new file mode 100644 index 000000000..87098b733 Binary files /dev/null and b/applications/plugins/counter/media/preview.gif differ diff --git a/applications/plugins/dap_link/README.md b/applications/plugins/dap_link/README.md new file mode 100644 index 000000000..aead0a60a --- /dev/null +++ b/applications/plugins/dap_link/README.md @@ -0,0 +1,105 @@ +# Flipper Zero as CMSIS DAP/DAP Link +Flipper Zero as a [Free-DAP](https://github.com/ataradov/free-dap) based SWD\JTAG debugger. Free-DAP is a free and open source firmware implementation of the [CMSIS-DAP](https://www.keil.com/pack/doc/CMSIS_Dev/DAP/html/index.html) debugger. + +## Protocols +SWD, JTAG , CMSIS-DAP v1 (18 KiB/s), CMSIS-DAP v2 (46 KiB/s), VCP (USB-UART). + +WinUSB for driverless installation for Windows 8 and above. + +## Usage + +### VSCode + Cortex-Debug + Set `"device": "cmsis-dap"` + +
+ BluePill configuration example + + ```json +{ + "name": "Attach (DAP)", + "cwd": "${workspaceFolder}", + "executable": "./build/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/stm32f1x.cfg", + ], +}, + ``` +
+ +
+ Flipper Zero configuration example + + ```json +{ + "name": "Attach (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", + ], +}, + ``` +
+ +### OpenOCD +Use `interface/cmsis-dap.cfg`. You will need OpenOCD v0.11.0. + +Additional commands: +* `cmsis_dap_backend hid` for CMSIS-DAP v1 protocol. +* `cmsis_dap_backend usb_bulk` for CMSIS-DAP v2 protocol. +* `cmsis_dap_serial DAP_Oyevoxo` use DAP-Link running on Flipper named `Oyevoxo`. +* `cmsis-dap cmd 81` - reboot connected DAP-Link. + +
+ Flash BluePill + + ``` +openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c init -c "program build/firmware.bin reset exit 0x8000000" + ``` +
+ +
+ Flash Flipper Zero using DAP v2 protocol + + ``` +openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_backend usb_bulk" -f debug/stm32wbx.cfg -c init -c "program build/latest/firmware.bin reset exit 0x8000000" + ``` +
+ +
+ Reboot connected DAP-Link on Flipper named Oyevoxo + + ``` +openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_serial DAP_Oyevoxo" -c "transport select swd" -c "adapter speed 4000000" -c init -c "cmsis-dap cmd 81" -c "exit" + ``` +
+ +### PlatformIO +Use `debug_tool = cmsis-dap` and `upload_protocol = cmsis-dap`. [Documentation](https://docs.platformio.org/en/latest/plus/debug-tools/cmsis-dap.html#debugging-tool-cmsis-dap). Remember that Windows 8 and above do not require drivers. + +
+ BluePill platformio.ini example + + ``` +[env:bluepill_f103c8] +platform = ststm32 +board = bluepill_f103c8 +debug_tool = cmsis-dap +upload_protocol = cmsis-dap + ``` +
diff --git a/applications/plugins/dap_link/application.fam b/applications/plugins/dap_link/application.fam new file mode 100644 index 000000000..3b99d5ef3 --- /dev/null +++ b/applications/plugins/dap_link/application.fam @@ -0,0 +1,24 @@ +App( + appid="dap_link", + name="DAP Link", + apptype=FlipperAppType.PLUGIN, + entry_point="dap_link_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + order=20, + fap_icon="dap_link.png", + fap_category="Tools", + fap_private_libs=[ + Lib( + name="free-dap", + cincludes=["."], + sources=[ + "dap.c", + ], + ), + ], + fap_icon_assets="icons", +) diff --git a/applications/plugins/dap_link/dap_config.h b/applications/plugins/dap_link/dap_config.h new file mode 100644 index 000000000..88b90bd34 --- /dev/null +++ b/applications/plugins/dap_link/dap_config.h @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022, Alex Taradov . All rights reserved. + +#ifndef _DAP_CONFIG_H_ +#define _DAP_CONFIG_H_ + +/*- Includes ----------------------------------------------------------------*/ +#include + +/*- Definitions -------------------------------------------------------------*/ +#define DAP_CONFIG_ENABLE_JTAG + +#define DAP_CONFIG_DEFAULT_PORT DAP_PORT_SWD +#define DAP_CONFIG_DEFAULT_CLOCK 4200000 // Hz + +#define DAP_CONFIG_PACKET_SIZE 64 +#define DAP_CONFIG_PACKET_COUNT 1 + +#define DAP_CONFIG_JTAG_DEV_COUNT 8 + +// DAP_CONFIG_PRODUCT_STR must contain "CMSIS-DAP" to be compatible with the standard +#define DAP_CONFIG_VENDOR_STR "Flipper Zero" +#define DAP_CONFIG_PRODUCT_STR "Generic CMSIS-DAP Adapter" +#define DAP_CONFIG_SER_NUM_STR usb_serial_number +#define DAP_CONFIG_CMSIS_DAP_VER_STR "2.0.0" + +#define DAP_CONFIG_RESET_TARGET_FN dap_app_target_reset +#define DAP_CONFIG_VENDOR_FN dap_app_vendor_cmd + +// Attribute to use for performance-critical functions +#define DAP_CONFIG_PERFORMANCE_ATTR + +// A value at which dap_clock_test() produces 1 kHz output on the SWCLK pin +// #define DAP_CONFIG_DELAY_CONSTANT 19000 +#define DAP_CONFIG_DELAY_CONSTANT 6290 + +// A threshold for switching to fast clock (no added delays) +// This is the frequency produced by dap_clock_test(1) on the SWCLK pin +#define DAP_CONFIG_FAST_CLOCK 2400000 // Hz + +/*- Prototypes --------------------------------------------------------------*/ +extern char usb_serial_number[16]; + +/*- Implementations ---------------------------------------------------------*/ +extern GpioPin flipper_dap_swclk_pin; +extern GpioPin flipper_dap_swdio_pin; +extern GpioPin flipper_dap_reset_pin; +extern GpioPin flipper_dap_tdo_pin; +extern GpioPin flipper_dap_tdi_pin; + +extern void dap_app_vendor_cmd(uint8_t cmd); +extern void dap_app_target_reset(); +extern void dap_app_disconnect(); +extern void dap_app_connect_swd(); +extern void dap_app_connect_jtag(); + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_write(int value) { + furi_hal_gpio_write(&flipper_dap_swclk_pin, value); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_write(int value) { + furi_hal_gpio_write(&flipper_dap_swdio_pin, value); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_TDI_write(int value) { +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_write(&flipper_dap_tdi_pin, value); +#else + (void)value; +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_TDO_write(int value) { +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_write(&flipper_dap_tdo_pin, value); +#else + (void)value; +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_nTRST_write(int value) { + (void)value; +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_nRESET_write(int value) { + furi_hal_gpio_write(&flipper_dap_reset_pin, value); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_SWCLK_TCK_read(void) { + return furi_hal_gpio_read(&flipper_dap_swclk_pin); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_SWDIO_TMS_read(void) { + return furi_hal_gpio_read(&flipper_dap_swdio_pin); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_TDO_read(void) { +#ifdef DAP_CONFIG_ENABLE_JTAG + return furi_hal_gpio_read(&flipper_dap_tdo_pin); +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_TDI_read(void) { +#ifdef DAP_CONFIG_ENABLE_JTAG + return furi_hal_gpio_read(&flipper_dap_tdi_pin); +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_nTRST_read(void) { + return 0; +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_nRESET_read(void) { + return furi_hal_gpio_read(&flipper_dap_reset_pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_set(void) { + LL_GPIO_SetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_clr(void) { + LL_GPIO_ResetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_in(void) { + LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_INPUT); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_out(void) { + LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_OUTPUT); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SETUP(void) { + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_DISCONNECT(void) { + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif + dap_app_disconnect(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_CONNECT_SWD(void) { + furi_hal_gpio_init( + &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swdio_pin, true); + + furi_hal_gpio_init( + &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swclk_pin, true); + + furi_hal_gpio_init( + &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_reset_pin, true); + +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif + dap_app_connect_swd(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_CONNECT_JTAG(void) { + furi_hal_gpio_init( + &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swdio_pin, true); + + furi_hal_gpio_init( + &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swclk_pin, true); + + furi_hal_gpio_init( + &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_reset_pin, true); + +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + + furi_hal_gpio_init( + &flipper_dap_tdi_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_tdi_pin, true); +#endif + dap_app_connect_jtag(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_LED(int index, int state) { + (void)index; + (void)state; +} + +//----------------------------------------------------------------------------- +__attribute__((always_inline)) static inline void DAP_CONFIG_DELAY(uint32_t cycles) { + asm volatile("1: subs %[cycles], %[cycles], #1 \n" + " bne 1b \n" + : [cycles] "+l"(cycles)); +} + +#endif // _DAP_CONFIG_H_ diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c new file mode 100644 index 000000000..5c30e85c5 --- /dev/null +++ b/applications/plugins/dap_link/dap_link.c @@ -0,0 +1,528 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dap_link.h" +#include "dap_config.h" +#include "gui/dap_gui.h" +#include "usb/dap_v2_usb.h" +#include +#include "dap_link_icons.h" + +/***************************************************************************/ +/****************************** DAP COMMON *********************************/ +/***************************************************************************/ + +struct DapApp { + FuriThread* dap_thread; + FuriThread* cdc_thread; + FuriThread* gui_thread; + + DapState state; + DapConfig config; +}; + +void dap_app_get_state(DapApp* app, DapState* state) { + *state = app->state; +} + +#define DAP_PROCESS_THREAD_TICK 500 + +typedef enum { + DapThreadEventStop = (1 << 0), +} DapThreadEvent; + +void dap_thread_send_stop(FuriThread* thread) { + furi_thread_flags_set(furi_thread_get_id(thread), DapThreadEventStop); +} + +GpioPin flipper_dap_swclk_pin; +GpioPin flipper_dap_swdio_pin; +GpioPin flipper_dap_reset_pin; +GpioPin flipper_dap_tdo_pin; +GpioPin flipper_dap_tdi_pin; + +/***************************************************************************/ +/****************************** DAP PROCESS ********************************/ +/***************************************************************************/ + +typedef struct { + uint8_t data[DAP_CONFIG_PACKET_SIZE]; + uint8_t size; +} DapPacket; + +typedef enum { + DAPThreadEventStop = DapThreadEventStop, + DAPThreadEventRxV1 = (1 << 1), + DAPThreadEventRxV2 = (1 << 2), + DAPThreadEventUSBConnect = (1 << 3), + DAPThreadEventUSBDisconnect = (1 << 4), + DAPThreadEventApplyConfig = (1 << 5), + DAPThreadEventAll = DAPThreadEventStop | DAPThreadEventRxV1 | DAPThreadEventRxV2 | + DAPThreadEventUSBConnect | DAPThreadEventUSBDisconnect | + DAPThreadEventApplyConfig, +} DAPThreadEvent; + +#define USB_SERIAL_NUMBER_LEN 16 +char usb_serial_number[USB_SERIAL_NUMBER_LEN] = {0}; + +const char* dap_app_get_serial(DapApp* app) { + UNUSED(app); + return usb_serial_number; +} + +static void dap_app_rx1_callback(void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + furi_thread_flags_set(thread_id, DAPThreadEventRxV1); +} + +static void dap_app_rx2_callback(void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + furi_thread_flags_set(thread_id, DAPThreadEventRxV2); +} + +static void dap_app_usb_state_callback(bool state, void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + if(state) { + furi_thread_flags_set(thread_id, DAPThreadEventUSBConnect); + } else { + furi_thread_flags_set(thread_id, DAPThreadEventUSBDisconnect); + } +} + +static void dap_app_process_v1() { + DapPacket tx_packet; + DapPacket rx_packet; + memset(&tx_packet, 0, sizeof(DapPacket)); + rx_packet.size = dap_v1_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_process_request(rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_v1_usb_tx(tx_packet.data, DAP_CONFIG_PACKET_SIZE); +} + +static void dap_app_process_v2() { + DapPacket tx_packet; + DapPacket rx_packet; + memset(&tx_packet, 0, sizeof(DapPacket)); + rx_packet.size = dap_v2_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); + size_t len = dap_process_request( + rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_v2_usb_tx(tx_packet.data, len); +} + +void dap_app_vendor_cmd(uint8_t cmd) { + // openocd -c "cmsis-dap cmd 81" + if(cmd == 0x01) { + furi_hal_power_reset(); + } +} + +void dap_app_target_reset() { + FURI_LOG_I("DAP", "Target reset"); +} + +static void dap_init_gpio(DapSwdPins swd_pins) { + switch(swd_pins) { + case DapSwdPinsPA7PA6: + flipper_dap_swclk_pin = gpio_ext_pa7; + flipper_dap_swdio_pin = gpio_ext_pa6; + break; + case DapSwdPinsPA14PA13: + flipper_dap_swclk_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_14}; + flipper_dap_swdio_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_13}; + break; + } + + flipper_dap_reset_pin = gpio_ext_pa4; + flipper_dap_tdo_pin = gpio_ext_pb3; + flipper_dap_tdi_pin = gpio_ext_pb2; +} + +static void dap_deinit_gpio(DapSwdPins swd_pins) { + // setup gpio pins to default state + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + if(DapSwdPinsPA14PA13 == swd_pins) { + // PA14 and PA13 are used by SWD + furi_hal_gpio_init_ex( + &flipper_dap_swclk_pin, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedLow, + GpioAltFn0JTCK_SWCLK); + furi_hal_gpio_init_ex( + &flipper_dap_swdio_pin, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn0JTMS_SWDIO); + } else { + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } +} + +static int32_t dap_process(void* p) { + DapApp* app = p; + DapState* dap_state = &(app->state); + + // allocate resources + FuriHalUsbInterface* usb_config_prev; + app->config.swd_pins = DapSwdPinsPA7PA6; + DapSwdPins swd_pins_prev = app->config.swd_pins; + + // init pins + dap_init_gpio(swd_pins_prev); + + // init dap + dap_init(); + + // get name + const char* name = furi_hal_version_get_name_ptr(); + if(!name) { + name = "Flipper"; + } + snprintf(usb_serial_number, USB_SERIAL_NUMBER_LEN, "DAP_%s", name); + + // init usb + usb_config_prev = furi_hal_usb_get_config(); + dap_common_usb_alloc_name(usb_serial_number); + dap_common_usb_set_context(furi_thread_get_id(furi_thread_get_current())); + dap_v1_usb_set_rx_callback(dap_app_rx1_callback); + dap_v2_usb_set_rx_callback(dap_app_rx2_callback); + dap_common_usb_set_state_callback(dap_app_usb_state_callback); + furi_hal_usb_set_config(&dap_v2_usb_hid, NULL); + + // work + uint32_t events; + while(1) { + events = furi_thread_flags_wait(DAPThreadEventAll, FuriFlagWaitAny, FuriWaitForever); + + if(!(events & FuriFlagError)) { + if(events & DAPThreadEventRxV1) { + dap_app_process_v1(); + dap_state->dap_counter++; + dap_state->dap_version = DapVersionV1; + } + + if(events & DAPThreadEventRxV2) { + dap_app_process_v2(); + dap_state->dap_counter++; + dap_state->dap_version = DapVersionV2; + } + + if(events & DAPThreadEventUSBConnect) { + dap_state->usb_connected = true; + } + + if(events & DAPThreadEventUSBDisconnect) { + dap_state->usb_connected = false; + dap_state->dap_version = DapVersionUnknown; + } + + if(events & DAPThreadEventApplyConfig) { + if(swd_pins_prev != app->config.swd_pins) { + dap_deinit_gpio(swd_pins_prev); + swd_pins_prev = app->config.swd_pins; + dap_init_gpio(swd_pins_prev); + } + } + + if(events & DAPThreadEventStop) { + break; + } + } + } + + // deinit usb + furi_hal_usb_set_config(usb_config_prev, NULL); + dap_common_wait_for_deinit(); + dap_common_usb_free_name(); + dap_deinit_gpio(swd_pins_prev); + return 0; +} + +/***************************************************************************/ +/****************************** CDC PROCESS ********************************/ +/***************************************************************************/ + +typedef enum { + CDCThreadEventStop = DapThreadEventStop, + CDCThreadEventUARTRx = (1 << 1), + CDCThreadEventCDCRx = (1 << 2), + CDCThreadEventCDCConfig = (1 << 3), + CDCThreadEventApplyConfig = (1 << 4), + CDCThreadEventAll = CDCThreadEventStop | CDCThreadEventUARTRx | CDCThreadEventCDCRx | + CDCThreadEventCDCConfig | CDCThreadEventApplyConfig, +} CDCThreadEvent; + +typedef struct { + FuriStreamBuffer* rx_stream; + FuriThreadId thread_id; + FuriHalUartId uart_id; + struct usb_cdc_line_coding line_coding; +} CDCProcess; + +static void cdc_uart_irq_cb(UartIrqEvent ev, uint8_t data, void* ctx) { + CDCProcess* app = ctx; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(app->rx_stream, &data, 1, 0); + furi_thread_flags_set(app->thread_id, CDCThreadEventUARTRx); + } +} + +static void cdc_usb_rx_callback(void* context) { + CDCProcess* app = context; + furi_thread_flags_set(app->thread_id, CDCThreadEventCDCRx); +} + +static void cdc_usb_control_line_callback(uint8_t state, void* context) { + UNUSED(context); + UNUSED(state); +} + +static void cdc_usb_config_callback(struct usb_cdc_line_coding* config, void* context) { + CDCProcess* app = context; + app->line_coding = *config; + furi_thread_flags_set(app->thread_id, CDCThreadEventCDCConfig); +} + +static FuriHalUartId cdc_init_uart( + DapUartType type, + DapUartTXRX swap, + uint32_t baudrate, + void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), + void* ctx) { + FuriHalUartId uart_id = FuriHalUartIdUSART1; + if(baudrate == 0) baudrate = 115200; + + switch(type) { + case DapUartTypeUSART1: + uart_id = FuriHalUartIdUSART1; + furi_hal_console_disable(); + furi_hal_uart_deinit(uart_id); + if(swap == DapUartTXRXSwap) { + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_SWAPPED); + } else { + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); + } + furi_hal_uart_init(uart_id, baudrate); + furi_hal_uart_set_irq_cb(uart_id, cb, ctx); + break; + case DapUartTypeLPUART1: + uart_id = FuriHalUartIdLPUART1; + furi_hal_uart_deinit(uart_id); + if(swap == DapUartTXRXSwap) { + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_SWAPPED); + } else { + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); + } + furi_hal_uart_init(uart_id, baudrate); + furi_hal_uart_set_irq_cb(uart_id, cb, ctx); + break; + } + + return uart_id; +} + +static void cdc_deinit_uart(DapUartType type) { + switch(type) { + case DapUartTypeUSART1: + furi_hal_uart_deinit(FuriHalUartIdUSART1); + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); + furi_hal_console_init(); + break; + case DapUartTypeLPUART1: + furi_hal_uart_deinit(FuriHalUartIdLPUART1); + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); + break; + } +} + +static int32_t cdc_process(void* p) { + DapApp* dap_app = p; + DapState* dap_state = &(dap_app->state); + + dap_app->config.uart_pins = DapUartTypeLPUART1; + dap_app->config.uart_swap = DapUartTXRXNormal; + + DapUartType uart_pins_prev = dap_app->config.uart_pins; + DapUartTXRX uart_swap_prev = dap_app->config.uart_swap; + + CDCProcess* app = malloc(sizeof(CDCProcess)); + app->thread_id = furi_thread_get_id(furi_thread_get_current()); + app->rx_stream = furi_stream_buffer_alloc(512, 1); + + const uint8_t rx_buffer_size = 64; + uint8_t* rx_buffer = malloc(rx_buffer_size); + + app->uart_id = cdc_init_uart( + uart_pins_prev, uart_swap_prev, dap_state->cdc_baudrate, cdc_uart_irq_cb, app); + + dap_cdc_usb_set_context(app); + dap_cdc_usb_set_rx_callback(cdc_usb_rx_callback); + dap_cdc_usb_set_control_line_callback(cdc_usb_control_line_callback); + dap_cdc_usb_set_config_callback(cdc_usb_config_callback); + + uint32_t events; + while(1) { + events = furi_thread_flags_wait(CDCThreadEventAll, FuriFlagWaitAny, FuriWaitForever); + + if(!(events & FuriFlagError)) { + if(events & CDCThreadEventCDCConfig) { + if(dap_state->cdc_baudrate != app->line_coding.dwDTERate) { + dap_state->cdc_baudrate = app->line_coding.dwDTERate; + if(dap_state->cdc_baudrate > 0) { + furi_hal_uart_set_br(app->uart_id, dap_state->cdc_baudrate); + } + } + } + + if(events & CDCThreadEventUARTRx) { + size_t len = + furi_stream_buffer_receive(app->rx_stream, rx_buffer, rx_buffer_size, 0); + + if(len > 0) { + dap_cdc_usb_tx(rx_buffer, len); + } + dap_state->cdc_rx_counter += len; + } + + if(events & CDCThreadEventCDCRx) { + size_t len = dap_cdc_usb_rx(rx_buffer, rx_buffer_size); + if(len > 0) { + furi_hal_uart_tx(app->uart_id, rx_buffer, len); + } + dap_state->cdc_tx_counter += len; + } + + if(events & CDCThreadEventApplyConfig) { + if(uart_pins_prev != dap_app->config.uart_pins || + uart_swap_prev != dap_app->config.uart_swap) { + cdc_deinit_uart(uart_pins_prev); + uart_pins_prev = dap_app->config.uart_pins; + uart_swap_prev = dap_app->config.uart_swap; + app->uart_id = cdc_init_uart( + uart_pins_prev, + uart_swap_prev, + dap_state->cdc_baudrate, + cdc_uart_irq_cb, + app); + } + } + + if(events & CDCThreadEventStop) { + break; + } + } + } + + cdc_deinit_uart(uart_pins_prev); + free(rx_buffer); + furi_stream_buffer_free(app->rx_stream); + free(app); + + return 0; +} + +/***************************************************************************/ +/******************************* MAIN APP **********************************/ +/***************************************************************************/ + +static DapApp* dap_app_alloc() { + DapApp* dap_app = malloc(sizeof(DapApp)); + dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app); + dap_app->cdc_thread = furi_thread_alloc_ex("DAP CDC", 1024, cdc_process, dap_app); + dap_app->gui_thread = furi_thread_alloc_ex("DAP GUI", 1024, dap_gui_thread, dap_app); + return dap_app; +} + +static void dap_app_free(DapApp* dap_app) { + furi_assert(dap_app); + furi_thread_free(dap_app->dap_thread); + furi_thread_free(dap_app->cdc_thread); + furi_thread_free(dap_app->gui_thread); + free(dap_app); +} + +static DapApp* app_handle = NULL; + +void dap_app_disconnect() { + app_handle->state.dap_mode = DapModeDisconnected; +} + +void dap_app_connect_swd() { + app_handle->state.dap_mode = DapModeSWD; +} + +void dap_app_connect_jtag() { + app_handle->state.dap_mode = DapModeJTAG; +} + +void dap_app_set_config(DapApp* app, DapConfig* config) { + app->config = *config; + furi_thread_flags_set(furi_thread_get_id(app->dap_thread), DAPThreadEventApplyConfig); + furi_thread_flags_set(furi_thread_get_id(app->cdc_thread), CDCThreadEventApplyConfig); +} + +DapConfig* dap_app_get_config(DapApp* app) { + return &app->config; +} + +int32_t dap_link_app(void* p) { + UNUSED(p); + + if(furi_hal_usb_is_locked()) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); + dialog_message_set_text( + message, + "Disconnect from\nPC or phone to\nuse this function.", + 3, + 30, + AlignLeft, + AlignTop); + dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + return -1; + } + + // alloc app + DapApp* app = dap_app_alloc(); + app_handle = app; + + furi_thread_start(app->dap_thread); + furi_thread_start(app->cdc_thread); + furi_thread_start(app->gui_thread); + + // wait until gui thread is finished + furi_thread_join(app->gui_thread); + + // send stop event to threads + dap_thread_send_stop(app->dap_thread); + dap_thread_send_stop(app->cdc_thread); + + // wait for threads to stop + furi_thread_join(app->dap_thread); + furi_thread_join(app->cdc_thread); + + // free app + dap_app_free(app); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/dap_link/dap_link.h b/applications/plugins/dap_link/dap_link.h new file mode 100644 index 000000000..d51726c45 --- /dev/null +++ b/applications/plugins/dap_link/dap_link.h @@ -0,0 +1,55 @@ +#pragma once +#include + +typedef enum { + DapModeDisconnected, + DapModeSWD, + DapModeJTAG, +} DapMode; + +typedef enum { + DapVersionUnknown, + DapVersionV1, + DapVersionV2, +} DapVersion; + +typedef struct { + bool usb_connected; + DapMode dap_mode; + DapVersion dap_version; + uint32_t dap_counter; + uint32_t cdc_baudrate; + uint32_t cdc_tx_counter; + uint32_t cdc_rx_counter; +} DapState; + +typedef enum { + DapSwdPinsPA7PA6, // Pins 2, 3 + DapSwdPinsPA14PA13, // Pins 10, 12 +} DapSwdPins; + +typedef enum { + DapUartTypeUSART1, // Pins 13, 14 + DapUartTypeLPUART1, // Pins 15, 16 +} DapUartType; + +typedef enum { + DapUartTXRXNormal, + DapUartTXRXSwap, +} DapUartTXRX; + +typedef struct { + DapSwdPins swd_pins; + DapUartType uart_pins; + DapUartTXRX uart_swap; +} DapConfig; + +typedef struct DapApp DapApp; + +void dap_app_get_state(DapApp* app, DapState* state); + +const char* dap_app_get_serial(DapApp* app); + +void dap_app_set_config(DapApp* app, DapConfig* config); + +DapConfig* dap_app_get_config(DapApp* app); \ No newline at end of file diff --git a/applications/plugins/dap_link/dap_link.png b/applications/plugins/dap_link/dap_link.png new file mode 100644 index 000000000..2278ce2b6 Binary files /dev/null and b/applications/plugins/dap_link/dap_link.png differ diff --git a/applications/plugins/dap_link/gui/dap_gui.c b/applications/plugins/dap_link/gui/dap_gui.c new file mode 100644 index 000000000..4dd986153 --- /dev/null +++ b/applications/plugins/dap_link/gui/dap_gui.c @@ -0,0 +1,92 @@ +#include "dap_gui.h" +#include "dap_gui_i.h" + +#define DAP_GUI_TICK 250 + +static bool dap_gui_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + DapGuiApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool dap_gui_back_event_callback(void* context) { + furi_assert(context); + DapGuiApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void dap_gui_tick_event_callback(void* context) { + furi_assert(context); + DapGuiApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +DapGuiApp* dap_gui_alloc() { + DapGuiApp* app = malloc(sizeof(DapGuiApp)); + app->gui = furi_record_open(RECORD_GUI); + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&dap_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback(app->view_dispatcher, dap_gui_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, dap_gui_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, dap_gui_tick_event_callback, DAP_GUI_TICK); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + DapGuiAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + app->main_view = dap_main_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, DapGuiAppViewMainView, dap_main_view_get_view(app->main_view)); + + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, DapGuiAppViewWidget, widget_get_view(app->widget)); + + scene_manager_next_scene(app->scene_manager, DapSceneMain); + + return app; +} + +void dap_gui_free(DapGuiApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewVarItemList); + variable_item_list_free(app->var_item_list); + + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewMainView); + dap_main_view_free(app->main_view); + + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewWidget); + widget_free(app->widget); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + + free(app); +} + +int32_t dap_gui_thread(void* arg) { + DapGuiApp* app = dap_gui_alloc(); + app->dap_app = arg; + + notification_message_block(app->notifications, &sequence_display_backlight_enforce_on); + view_dispatcher_run(app->view_dispatcher); + notification_message_block(app->notifications, &sequence_display_backlight_enforce_auto); + + dap_gui_free(app); + return 0; +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/dap_gui.h b/applications/plugins/dap_link/gui/dap_gui.h new file mode 100644 index 000000000..3d8e6bdf9 --- /dev/null +++ b/applications/plugins/dap_link/gui/dap_gui.h @@ -0,0 +1,4 @@ +#pragma once +#include + +int32_t dap_gui_thread(void* arg); \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/dap_gui_custom_event.h b/applications/plugins/dap_link/gui/dap_gui_custom_event.h new file mode 100644 index 000000000..8b127c9d4 --- /dev/null +++ b/applications/plugins/dap_link/gui/dap_gui_custom_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + DapAppCustomEventConfig, + DapAppCustomEventHelp, + DapAppCustomEventAbout, +} DapAppCustomEvent; diff --git a/applications/plugins/dap_link/gui/dap_gui_i.h b/applications/plugins/dap_link/gui/dap_gui_i.h new file mode 100644 index 000000000..59411e78c --- /dev/null +++ b/applications/plugins/dap_link/gui/dap_gui_i.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "dap_gui.h" +#include "../dap_link.h" +#include "scenes/config/dap_scene.h" +#include "dap_gui_custom_event.h" +#include "views/dap_main_view.h" + +typedef struct { + DapApp* dap_app; + + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + VariableItemList* var_item_list; + DapMainView* main_view; + Widget* widget; +} DapGuiApp; + +typedef enum { + DapGuiAppViewVarItemList, + DapGuiAppViewMainView, + DapGuiAppViewWidget, +} DapGuiAppView; diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene.c b/applications/plugins/dap_link/gui/scenes/config/dap_scene.c new file mode 100644 index 000000000..37e235540 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/config/dap_scene.c @@ -0,0 +1,30 @@ +#include "dap_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const dap_scene_on_enter_handlers[])(void*) = { +#include "dap_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const dap_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "dap_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const dap_scene_on_exit_handlers[])(void* context) = { +#include "dap_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers dap_scene_handlers = { + .on_enter_handlers = dap_scene_on_enter_handlers, + .on_event_handlers = dap_scene_on_event_handlers, + .on_exit_handlers = dap_scene_on_exit_handlers, + .scene_num = DapSceneNum, +}; diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene.h b/applications/plugins/dap_link/gui/scenes/config/dap_scene.h new file mode 100644 index 000000000..6fb38da4a --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/config/dap_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) DapScene##id, +typedef enum { +#include "dap_scene_config.h" + DapSceneNum, +} DapScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers dap_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "dap_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "dap_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "dap_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h b/applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h new file mode 100644 index 000000000..8957aca06 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h @@ -0,0 +1,4 @@ +ADD_SCENE(dap, main, Main) +ADD_SCENE(dap, config, Config) +ADD_SCENE(dap, help, Help) +ADD_SCENE(dap, about, About) \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_about.c b/applications/plugins/dap_link/gui/scenes/dap_scene_about.c new file mode 100644 index 000000000..0974e60a7 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_about.c @@ -0,0 +1,68 @@ +#include "../dap_gui_i.h" + +#define DAP_VERSION_APP "0.1.0" +#define DAP_DEVELOPED "Dr_Zlo" +#define DAP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +void dap_scene_about_on_enter(void* context) { + DapGuiApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", DAP_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", DAP_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", DAP_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, "CMSIS-DAP debugger\nbased on Free-DAP\nThanks to Alex Taradov\n\n"); + + furi_string_cat_printf( + temp_str, + "Supported protocols:\n" + "SWD, JTAG, UART\n" + "DAP v1 (cmsis_backend hid), DAP v2 (cmsis_backend usb_bulk), VCP\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! DAP Link \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); +} + +bool dap_scene_about_on_event(void* context, SceneManagerEvent event) { + DapGuiApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void dap_scene_about_on_exit(void* context) { + DapGuiApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_config.c b/applications/plugins/dap_link/gui/scenes/dap_scene_config.c new file mode 100644 index 000000000..48d5fedcd --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_config.c @@ -0,0 +1,107 @@ +#include "../dap_gui_i.h" + +static const char* swd_pins[] = {[DapSwdPinsPA7PA6] = "2,3", [DapSwdPinsPA14PA13] = "10,12"}; +static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"}; +static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"}; + +static void swd_pins_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, swd_pins[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->swd_pins = index; + dap_app_set_config(app->dap_app, config); +} + +static void uart_pins_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_pins[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->uart_pins = index; + dap_app_set_config(app->dap_app, config); +} + +static void uart_swap_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_swap[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->uart_swap = index; + dap_app_set_config(app->dap_app, config); +} + +static void ok_cb(void* context, uint32_t index) { + DapGuiApp* app = context; + switch(index) { + case 3: + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventHelp); + break; + case 4: + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventAbout); + break; + default: + break; + } +} + +void dap_scene_config_on_enter(void* context) { + DapGuiApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + DapConfig* config = dap_app_get_config(app->dap_app); + + item = variable_item_list_add( + var_item_list, "SWC SWD Pins", COUNT_OF(swd_pins), swd_pins_cb, app); + variable_item_set_current_value_index(item, config->swd_pins); + variable_item_set_current_value_text(item, swd_pins[config->swd_pins]); + + item = + variable_item_list_add(var_item_list, "UART Pins", COUNT_OF(uart_pins), uart_pins_cb, app); + variable_item_set_current_value_index(item, config->uart_pins); + variable_item_set_current_value_text(item, uart_pins[config->uart_pins]); + + item = variable_item_list_add( + var_item_list, "Swap TX RX", COUNT_OF(uart_swap), uart_swap_cb, app); + variable_item_set_current_value_index(item, config->uart_swap); + variable_item_set_current_value_text(item, uart_swap[config->uart_swap]); + + variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); + variable_item_list_add(var_item_list, "About", 0, NULL, NULL); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig)); + + variable_item_list_set_enter_callback(var_item_list, ok_cb, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewVarItemList); +} + +bool dap_scene_config_on_event(void* context, SceneManagerEvent event) { + DapGuiApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DapAppCustomEventHelp) { + scene_manager_next_scene(app->scene_manager, DapSceneHelp); + return true; + } else if(event.event == DapAppCustomEventAbout) { + scene_manager_next_scene(app->scene_manager, DapSceneAbout); + return true; + } + } + return false; +} + +void dap_scene_config_on_exit(void* context) { + DapGuiApp* app = context; + scene_manager_set_scene_state( + app->scene_manager, + DapSceneConfig, + variable_item_list_get_selected_item_index(app->var_item_list)); + variable_item_list_reset(app->var_item_list); +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_help.c b/applications/plugins/dap_link/gui/scenes/dap_scene_help.c new file mode 100644 index 000000000..d8d70e7ff --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_help.c @@ -0,0 +1,102 @@ +#include "../dap_gui_i.h" + +void dap_scene_help_on_enter(void* context) { + DapGuiApp* app = context; + DapConfig* config = dap_app_get_config(app->dap_app); + FuriString* string = furi_string_alloc(); + + furi_string_cat(string, "CMSIS DAP/DAP Link v2\r\n"); + furi_string_cat_printf(string, "Serial: %s\r\n", dap_app_get_serial(app->dap_app)); + furi_string_cat( + string, + "Pinout:\r\n" + "\e#SWD:\r\n"); + + switch(config->swd_pins) { + case DapSwdPinsPA7PA6: + furi_string_cat( + string, + " SWC: 2 [A7]\r\n" + " SWD: 3 [A6]\r\n"); + break; + case DapSwdPinsPA14PA13: + furi_string_cat( + string, + " SWC: 10 [SWC]\r\n" + " SWD: 12 [SIO]\r\n"); + break; + default: + break; + } + + furi_string_cat(string, "\e#JTAG:\r\n"); + switch(config->swd_pins) { + case DapSwdPinsPA7PA6: + furi_string_cat( + string, + " TCK: 2 [A7]\r\n" + " TMS: 3 [A6]\r\n" + " RST: 4 [A4]\r\n" + " TDO: 5 [B3]\r\n" + " TDI: 6 [B2]\r\n"); + break; + case DapSwdPinsPA14PA13: + furi_string_cat( + string, + " RST: 4 [A4]\r\n" + " TDO: 5 [B3]\r\n" + " TDI: 6 [B2]\r\n" + " TCK: 10 [SWC]\r\n" + " TMS: 12 [SIO]\r\n"); + break; + default: + break; + } + + furi_string_cat(string, "\e#UART:\r\n"); + switch(config->uart_pins) { + case DapUartTypeUSART1: + if(config->uart_swap == DapUartTXRXNormal) { + furi_string_cat( + string, + " TX: 13 [TX]\r\n" + " RX: 14 [RX]\r\n"); + } else { + furi_string_cat( + string, + " RX: 13 [TX]\r\n" + " TX: 14 [RX]\r\n"); + } + break; + case DapUartTypeLPUART1: + if(config->uart_swap == DapUartTXRXNormal) { + furi_string_cat( + string, + " TX: 15 [C1]\r\n" + " RX: 16 [C0]\r\n"); + } else { + furi_string_cat( + string, + " RX: 15 [C1]\r\n" + " TX: 16 [C0]\r\n"); + } + break; + default: + break; + } + + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(string)); + furi_string_free(string); + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); +} + +bool dap_scene_help_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void dap_scene_help_on_exit(void* context) { + DapGuiApp* app = context; + widget_reset(app->widget); +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_main.c b/applications/plugins/dap_link/gui/scenes/dap_scene_main.c new file mode 100644 index 000000000..8c19bd6a5 --- /dev/null +++ b/applications/plugins/dap_link/gui/scenes/dap_scene_main.c @@ -0,0 +1,154 @@ +#include "../dap_gui_i.h" +#include "../../dap_link.h" + +typedef struct { + DapState dap_state; + bool dap_active; + bool tx_active; + bool rx_active; +} DapSceneMainState; + +static bool process_dap_state(DapGuiApp* app) { + DapSceneMainState* state = + (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); + if(state == NULL) return true; + + DapState* prev_state = &state->dap_state; + DapState next_state; + dap_app_get_state(app->dap_app, &next_state); + bool need_to_update = false; + + if(prev_state->dap_mode != next_state.dap_mode) { + switch(next_state.dap_mode) { + case DapModeDisconnected: + dap_main_view_set_mode(app->main_view, DapMainViewModeDisconnected); + notification_message(app->notifications, &sequence_blink_stop); + break; + case DapModeSWD: + dap_main_view_set_mode(app->main_view, DapMainViewModeSWD); + notification_message(app->notifications, &sequence_blink_start_blue); + break; + case DapModeJTAG: + dap_main_view_set_mode(app->main_view, DapMainViewModeJTAG); + notification_message(app->notifications, &sequence_blink_start_magenta); + break; + } + need_to_update = true; + } + + if(prev_state->dap_version != next_state.dap_version) { + switch(next_state.dap_version) { + case DapVersionUnknown: + dap_main_view_set_version(app->main_view, DapMainViewVersionUnknown); + break; + case DapVersionV1: + dap_main_view_set_version(app->main_view, DapMainViewVersionV1); + break; + case DapVersionV2: + dap_main_view_set_version(app->main_view, DapMainViewVersionV2); + break; + } + need_to_update = true; + } + + if(prev_state->usb_connected != next_state.usb_connected) { + dap_main_view_set_usb_connected(app->main_view, next_state.usb_connected); + need_to_update = true; + } + + if(prev_state->dap_counter != next_state.dap_counter) { + if(!state->dap_active) { + state->dap_active = true; + dap_main_view_set_dap(app->main_view, state->dap_active); + need_to_update = true; + } + } else { + if(state->dap_active) { + state->dap_active = false; + dap_main_view_set_dap(app->main_view, state->dap_active); + need_to_update = true; + } + } + + if(prev_state->cdc_baudrate != next_state.cdc_baudrate) { + dap_main_view_set_baudrate(app->main_view, next_state.cdc_baudrate); + need_to_update = true; + } + + if(prev_state->cdc_tx_counter != next_state.cdc_tx_counter) { + if(!state->tx_active) { + state->tx_active = true; + dap_main_view_set_tx(app->main_view, state->tx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_start_red); + } + } else { + if(state->tx_active) { + state->tx_active = false; + dap_main_view_set_tx(app->main_view, state->tx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_stop); + } + } + + if(prev_state->cdc_rx_counter != next_state.cdc_rx_counter) { + if(!state->rx_active) { + state->rx_active = true; + dap_main_view_set_rx(app->main_view, state->rx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_start_green); + } + } else { + if(state->rx_active) { + state->rx_active = false; + dap_main_view_set_rx(app->main_view, state->rx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_stop); + } + } + + if(need_to_update) { + dap_main_view_update(app->main_view); + } + + *prev_state = next_state; + return true; +} + +static void dap_scene_main_on_left(void* context) { + DapGuiApp* app = (DapGuiApp*)context; + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventConfig); +} + +void dap_scene_main_on_enter(void* context) { + DapGuiApp* app = context; + DapSceneMainState* state = malloc(sizeof(DapSceneMainState)); + dap_main_view_set_left_callback(app->main_view, dap_scene_main_on_left, app); + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewMainView); + scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)state); +} + +bool dap_scene_main_on_event(void* context, SceneManagerEvent event) { + DapGuiApp* app = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DapAppCustomEventConfig) { + scene_manager_next_scene(app->scene_manager, DapSceneConfig); + return true; + } + } else if(event.type == SceneManagerEventTypeTick) { + return process_dap_state(app); + } + + return false; +} + +void dap_scene_main_on_exit(void* context) { + DapGuiApp* app = context; + DapSceneMainState* state = + (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); + scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)NULL); + FURI_SW_MEMBARRIER(); + free(state); + notification_message(app->notifications, &sequence_blink_stop); +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.c b/applications/plugins/dap_link/gui/views/dap_main_view.c new file mode 100644 index 000000000..c5c8f9dff --- /dev/null +++ b/applications/plugins/dap_link/gui/views/dap_main_view.c @@ -0,0 +1,189 @@ +#include "dap_main_view.h" +#include "dap_link_icons.h" +#include + +// extern const Icon I_ArrowDownEmpty_12x18; +// extern const Icon I_ArrowDownFilled_12x18; +// extern const Icon I_ArrowUpEmpty_12x18; +// extern const Icon I_ArrowUpFilled_12x18; + +struct DapMainView { + View* view; + DapMainViewButtonCallback cb_left; + void* cb_context; +}; + +typedef struct { + DapMainViewMode mode; + DapMainViewVersion version; + bool usb_connected; + uint32_t baudrate; + bool dap_active; + bool tx_active; + bool rx_active; +} DapMainViewModel; + +static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { + DapMainViewModel* model = _model; + UNUSED(model); + canvas_clear(canvas); + elements_button_left(canvas, "Config"); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0, 127, 11); + canvas_set_color(canvas, ColorWhite); + + const char* header_string; + if(model->usb_connected) { + if(model->version == DapMainViewVersionV1) { + header_string = "DAP Link V1 Connected"; + } else if(model->version == DapMainViewVersionV2) { + header_string = "DAP Link V2 Connected"; + } else { + header_string = "DAP Link Connected"; + } + } else { + header_string = "DAP Link"; + } + + canvas_draw_str_aligned(canvas, 64, 9, AlignCenter, AlignBottom, header_string); + + canvas_set_color(canvas, ColorBlack); + if(model->dap_active) { + canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18); + canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18); + } else { + canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18); + canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18); + } + + switch(model->mode) { + case DapMainViewModeDisconnected: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "----"); + break; + case DapMainViewModeSWD: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "SWD"); + break; + case DapMainViewModeJTAG: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "JTAG"); + break; + } + + if(model->tx_active) { + canvas_draw_icon(canvas, 87, 16, &I_ArrowUpFilled_12x18); + } else { + canvas_draw_icon(canvas, 87, 16, &I_ArrowUpEmpty_12x18); + } + + if(model->rx_active) { + canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18); + } else { + canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18); + } + + canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART"); + + canvas_draw_line(canvas, 44, 52, 123, 52); + if(model->baudrate == 0) { + canvas_draw_str(canvas, 45, 62, "Baud: ????"); + } else { + char baudrate_str[18]; + snprintf(baudrate_str, 18, "Baud: %lu", model->baudrate); + canvas_draw_str(canvas, 45, 62, baudrate_str); + } +} + +static bool dap_main_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); + DapMainView* dap_main_view = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + if(dap_main_view->cb_left) { + dap_main_view->cb_left(dap_main_view->cb_context); + } + consumed = true; + } + } + + return consumed; +} + +DapMainView* dap_main_view_alloc() { + DapMainView* dap_main_view = malloc(sizeof(DapMainView)); + + dap_main_view->view = view_alloc(); + view_allocate_model(dap_main_view->view, ViewModelTypeLocking, sizeof(DapMainViewModel)); + view_set_context(dap_main_view->view, dap_main_view); + view_set_draw_callback(dap_main_view->view, dap_main_view_draw_callback); + view_set_input_callback(dap_main_view->view, dap_main_view_input_callback); + return dap_main_view; +} + +void dap_main_view_free(DapMainView* dap_main_view) { + view_free(dap_main_view->view); + free(dap_main_view); +} + +View* dap_main_view_get_view(DapMainView* dap_main_view) { + return dap_main_view->view; +} + +void dap_main_view_set_left_callback( + DapMainView* dap_main_view, + DapMainViewButtonCallback callback, + void* context) { + with_view_model( + dap_main_view->view, + DapMainViewModel * model, + { + UNUSED(model); + dap_main_view->cb_left = callback; + dap_main_view->cb_context = context; + }, + true); +} + +void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->mode = mode; }, false); +} + +void dap_main_view_set_dap(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->dap_active = active; }, false); +} + +void dap_main_view_set_tx(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->tx_active = active; }, false); +} + +void dap_main_view_set_rx(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->rx_active = active; }, false); +} + +void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->baudrate = baudrate; }, false); +} + +void dap_main_view_update(DapMainView* dap_main_view) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { UNUSED(model); }, true); +} + +void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->version = version; }, false); +} + +void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected) { + with_view_model( + dap_main_view->view, + DapMainViewModel * model, + { model->usb_connected = connected; }, + false); +} \ No newline at end of file diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.h b/applications/plugins/dap_link/gui/views/dap_main_view.h new file mode 100644 index 000000000..1fd900452 --- /dev/null +++ b/applications/plugins/dap_link/gui/views/dap_main_view.h @@ -0,0 +1,45 @@ +#pragma once +#include + +typedef struct DapMainView DapMainView; + +typedef void (*DapMainViewButtonCallback)(void* context); + +typedef enum { + DapMainViewVersionUnknown, + DapMainViewVersionV1, + DapMainViewVersionV2, +} DapMainViewVersion; + +typedef enum { + DapMainViewModeDisconnected, + DapMainViewModeSWD, + DapMainViewModeJTAG, +} DapMainViewMode; + +DapMainView* dap_main_view_alloc(); + +void dap_main_view_free(DapMainView* dap_main_view); + +View* dap_main_view_get_view(DapMainView* dap_main_view); + +void dap_main_view_set_left_callback( + DapMainView* dap_main_view, + DapMainViewButtonCallback callback, + void* context); + +void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode); + +void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version); + +void dap_main_view_set_dap(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_tx(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_rx(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected); + +void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate); + +void dap_main_view_update(DapMainView* dap_main_view); \ No newline at end of file diff --git a/applications/plugins/dap_link/icons/ActiveConnection_50x64.png b/applications/plugins/dap_link/icons/ActiveConnection_50x64.png new file mode 100644 index 000000000..1d7686ddd Binary files /dev/null and b/applications/plugins/dap_link/icons/ActiveConnection_50x64.png differ diff --git a/applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png b/applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png new file mode 100644 index 000000000..6007f74ab Binary files /dev/null and b/applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png differ diff --git a/applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png b/applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png new file mode 100644 index 000000000..5541e7723 Binary files /dev/null and b/applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png differ diff --git a/applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png b/applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png new file mode 100644 index 000000000..c9365a67d Binary files /dev/null and b/applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png differ diff --git a/applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png b/applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png new file mode 100644 index 000000000..dc481517e Binary files /dev/null and b/applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png differ diff --git a/applications/plugins/dap_link/lib/free-dap b/applications/plugins/dap_link/lib/free-dap new file mode 160000 index 000000000..e7752beb5 --- /dev/null +++ b/applications/plugins/dap_link/lib/free-dap @@ -0,0 +1 @@ +Subproject commit e7752beb5e8a69119af67b70b9179cb3c90f3ac5 diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.c b/applications/plugins/dap_link/usb/dap_v2_usb.c new file mode 100644 index 000000000..0c303a3ba --- /dev/null +++ b/applications/plugins/dap_link/usb/dap_v2_usb.c @@ -0,0 +1,994 @@ +#include +#include +#include +#include +#include +#include + +#include "dap_v2_usb.h" + +// #define DAP_USB_LOG + +#define HID_EP_IN 0x80 +#define HID_EP_OUT 0x00 + +#define DAP_HID_EP_SEND 1 +#define DAP_HID_EP_RECV 2 +#define DAP_HID_EP_BULK_RECV 3 +#define DAP_HID_EP_BULK_SEND 4 +#define DAP_CDC_EP_COMM 5 +#define DAP_CDC_EP_SEND 6 +#define DAP_CDC_EP_RECV 7 + +#define DAP_HID_EP_IN (HID_EP_IN | DAP_HID_EP_SEND) +#define DAP_HID_EP_OUT (HID_EP_OUT | DAP_HID_EP_RECV) +#define DAP_HID_EP_BULK_IN (HID_EP_IN | DAP_HID_EP_BULK_SEND) +#define DAP_HID_EP_BULK_OUT (HID_EP_OUT | DAP_HID_EP_BULK_RECV) + +#define DAP_HID_EP_SIZE 64 +#define DAP_CDC_COMM_EP_SIZE 8 +#define DAP_CDC_EP_SIZE 64 + +#define DAP_BULK_INTERVAL 0 +#define DAP_HID_INTERVAL 1 +#define DAP_CDC_INTERVAL 0 +#define DAP_CDC_COMM_INTERVAL 1 + +#define DAP_HID_VID 0x0483 +#define DAP_HID_PID 0x5740 + +#define DAP_USB_EP0_SIZE 8 + +#define EP_CFG_DECONFIGURE 0 +#define EP_CFG_CONFIGURE 1 + +enum { + USB_INTF_HID, + USB_INTF_BULK, + USB_INTF_CDC_COMM, + USB_INTF_CDC_DATA, + USB_INTF_COUNT, +}; + +enum { + USB_STR_ZERO, + USB_STR_MANUFACTURER, + USB_STR_PRODUCT, + USB_STR_SERIAL_NUMBER, + USB_STR_CMSIS_DAP_V1, + USB_STR_CMSIS_DAP_V2, + USB_STR_COM_PORT, + USB_STR_COUNT, +}; + +// static const char* usb_str[] = { +// [USB_STR_MANUFACTURER] = "Flipper Devices Inc.", +// [USB_STR_PRODUCT] = "Combined VCP and CMSIS-DAP Adapter", +// [USB_STR_COM_PORT] = "Virtual COM-Port", +// [USB_STR_CMSIS_DAP_V1] = "CMSIS-DAP v1 Adapter", +// [USB_STR_CMSIS_DAP_V2] = "CMSIS-DAP v2 Adapter", +// [USB_STR_SERIAL_NUMBER] = "01234567890ABCDEF", +// }; + +static const struct usb_string_descriptor dev_manuf_descr = + USB_STRING_DESC("Flipper Devices Inc."); + +static const struct usb_string_descriptor dev_prod_descr = + USB_STRING_DESC("Combined VCP and CMSIS-DAP Adapter"); + +static struct usb_string_descriptor* dev_serial_descr = NULL; + +static const struct usb_string_descriptor dev_dap_v1_descr = + USB_STRING_DESC("CMSIS-DAP v1 Adapter"); + +static const struct usb_string_descriptor dev_dap_v2_descr = + USB_STRING_DESC("CMSIS-DAP v2 Adapter"); + +static const struct usb_string_descriptor dev_com_descr = USB_STRING_DESC("Virtual COM-Port"); + +struct HidConfigDescriptor { + struct usb_config_descriptor configuration; + + // CMSIS-DAP v1 + struct usb_interface_descriptor hid_interface; + struct usb_hid_descriptor hid; + struct usb_endpoint_descriptor hid_ep_in; + struct usb_endpoint_descriptor hid_ep_out; + + // CMSIS-DAP v2 + struct usb_interface_descriptor bulk_interface; + struct usb_endpoint_descriptor bulk_ep_out; + struct usb_endpoint_descriptor bulk_ep_in; + + // CDC + struct usb_iad_descriptor iad; + struct usb_interface_descriptor interface_comm; + struct usb_cdc_header_desc cdc_header; + struct usb_cdc_call_mgmt_desc cdc_acm; + struct usb_cdc_acm_desc cdc_call_mgmt; + struct usb_cdc_union_desc cdc_union; + struct usb_endpoint_descriptor ep_comm; + struct usb_interface_descriptor interface_data; + struct usb_endpoint_descriptor ep_in; + struct usb_endpoint_descriptor ep_out; + +} __attribute__((packed)); + +static const struct usb_device_descriptor hid_device_desc = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 1, 0), + .bDeviceClass = USB_CLASS_MISC, + .bDeviceSubClass = USB_SUBCLASS_IAD, + .bDeviceProtocol = USB_PROTO_IAD, + .bMaxPacketSize0 = DAP_USB_EP0_SIZE, + .idVendor = DAP_HID_VID, + .idProduct = DAP_HID_PID, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = USB_STR_MANUFACTURER, + .iProduct = USB_STR_PRODUCT, + .iSerialNumber = USB_STR_SERIAL_NUMBER, + .bNumConfigurations = 1, +}; + +static const uint8_t hid_report_desc[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x00, // Usage (Undefined) + 0xa1, 0x01, // Collection (Application) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x40, // Report Count (64) + 0x09, 0x00, // Usage (Undefined) + 0x81, 0x82, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x40, // Report Count (64) + 0x09, 0x00, // Usage (Undefined) + 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) + 0xc0, // End Collection +}; + +static const struct HidConfigDescriptor hid_cfg_desc = { + .configuration = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct HidConfigDescriptor), + .bNumInterfaces = USB_INTF_COUNT, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED, + .bMaxPower = USB_CFG_POWER_MA(500), + }, + + // CMSIS-DAP v1 + .hid_interface = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = USB_INTF_HID, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT, + .bInterfaceProtocol = USB_HID_PROTO_NONBOOT, + .iInterface = USB_STR_CMSIS_DAP_V1, + }, + + .hid = + { + .bLength = sizeof(struct usb_hid_descriptor), + .bDescriptorType = USB_DTYPE_HID, + .bcdHID = VERSION_BCD(1, 1, 1), + .bCountryCode = USB_HID_COUNTRY_NONE, + .bNumDescriptors = 1, + .bDescriptorType0 = USB_DTYPE_HID_REPORT, + .wDescriptorLength0 = sizeof(hid_report_desc), + }, + + .hid_ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = DAP_HID_EP_IN, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = DAP_HID_EP_SIZE, + .bInterval = DAP_HID_INTERVAL, + }, + + .hid_ep_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = DAP_HID_EP_OUT, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = DAP_HID_EP_SIZE, + .bInterval = DAP_HID_INTERVAL, + }, + + // CMSIS-DAP v2 + .bulk_interface = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = USB_INTF_BULK, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = USB_STR_CMSIS_DAP_V2, + }, + + .bulk_ep_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = DAP_HID_EP_BULK_OUT, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = DAP_HID_EP_SIZE, + .bInterval = DAP_BULK_INTERVAL, + }, + + .bulk_ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = DAP_HID_EP_BULK_IN, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = DAP_HID_EP_SIZE, + .bInterval = DAP_BULK_INTERVAL, + }, + + // CDC + .iad = + { + .bLength = sizeof(struct usb_iad_descriptor), + .bDescriptorType = USB_DTYPE_INTERFASEASSOC, + .bFirstInterface = USB_INTF_CDC_COMM, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_CDC, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_PROTO_NONE, + .iFunction = USB_STR_COM_PORT, + }, + .interface_comm = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = USB_INTF_CDC_COMM, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = 0, + }, + + .cdc_header = + { + .bFunctionLength = sizeof(struct usb_cdc_header_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_HEADER, + .bcdCDC = VERSION_BCD(1, 1, 0), + }, + + .cdc_acm = + { + .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, + // .bmCapabilities = USB_CDC_CAP_LINE | USB_CDC_CAP_BRK, + .bmCapabilities = 0, + }, + + .cdc_call_mgmt = + { + .bFunctionLength = sizeof(struct usb_cdc_acm_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_ACM, + .bmCapabilities = USB_CDC_CALL_MGMT_CAP_DATA_INTF, + // .bDataInterface = USB_INTF_CDC_DATA, + }, + + .cdc_union = + { + .bFunctionLength = sizeof(struct usb_cdc_union_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_UNION, + .bMasterInterface0 = USB_INTF_CDC_COMM, + .bSlaveInterface0 = USB_INTF_CDC_DATA, + }, + + .ep_comm = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_COMM, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = DAP_CDC_COMM_EP_SIZE, + .bInterval = DAP_CDC_COMM_INTERVAL, + }, + + .interface_data = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = USB_INTF_CDC_DATA, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = USB_SUBCLASS_NONE, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = NO_DESCRIPTOR, + }, + + .ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_SEND, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = DAP_CDC_EP_SIZE, + .bInterval = DAP_CDC_INTERVAL, + }, + + .ep_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = HID_EP_OUT | DAP_CDC_EP_RECV, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = DAP_CDC_EP_SIZE, + .bInterval = DAP_CDC_INTERVAL, + }, +}; + +// WinUSB +#include "usb_winusb.h" + +typedef struct USB_PACK { + usb_binary_object_store_descriptor_t bos; + usb_winusb_capability_descriptor_t winusb; +} usb_bos_hierarchy_t; + +typedef struct USB_PACK { + usb_winusb_subset_header_function_t header; + usb_winusb_feature_compatble_id_t comp_id; + usb_winusb_feature_reg_property_guids_t property; +} usb_msos_descriptor_subset_t; + +typedef struct USB_PACK { + usb_winusb_set_header_descriptor_t header; + usb_msos_descriptor_subset_t subset; +} usb_msos_descriptor_set_t; + +#define USB_DTYPE_BINARY_OBJECT_STORE 15 +#define USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR 16 +#define USB_DC_TYPE_PLATFORM 5 + +const usb_bos_hierarchy_t usb_bos_hierarchy = { + .bos = + { + .bLength = sizeof(usb_binary_object_store_descriptor_t), + .bDescriptorType = USB_DTYPE_BINARY_OBJECT_STORE, + .wTotalLength = sizeof(usb_bos_hierarchy_t), + .bNumDeviceCaps = 1, + }, + .winusb = + { + .bLength = sizeof(usb_winusb_capability_descriptor_t), + .bDescriptorType = USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR, + .bDevCapabilityType = USB_DC_TYPE_PLATFORM, + .bReserved = 0, + .PlatformCapabilityUUID = USB_WINUSB_PLATFORM_CAPABILITY_ID, + .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, + .wMSOSDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), + .bMS_VendorCode = USB_WINUSB_VENDOR_CODE, + .bAltEnumCode = 0, + }, +}; + +const usb_msos_descriptor_set_t usb_msos_descriptor_set = { + .header = + { + .wLength = sizeof(usb_winusb_set_header_descriptor_t), + .wDescriptorType = USB_WINUSB_SET_HEADER_DESCRIPTOR, + .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, + .wDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), + }, + + .subset = + { + .header = + { + .wLength = sizeof(usb_winusb_subset_header_function_t), + .wDescriptorType = USB_WINUSB_SUBSET_HEADER_FUNCTION, + .bFirstInterface = USB_INTF_BULK, + .bReserved = 0, + .wSubsetLength = sizeof(usb_msos_descriptor_subset_t), + }, + + .comp_id = + { + .wLength = sizeof(usb_winusb_feature_compatble_id_t), + .wDescriptorType = USB_WINUSB_FEATURE_COMPATBLE_ID, + .CompatibleID = "WINUSB\0\0", + .SubCompatibleID = {0}, + }, + + .property = + { + .wLength = sizeof(usb_winusb_feature_reg_property_guids_t), + .wDescriptorType = USB_WINUSB_FEATURE_REG_PROPERTY, + .wPropertyDataType = USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ, + .wPropertyNameLength = + sizeof(usb_msos_descriptor_set.subset.property.PropertyName), + .PropertyName = {'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 'I', 0, + 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, + 'e', 0, 'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0, 0}, + .wPropertyDataLength = + sizeof(usb_msos_descriptor_set.subset.property.PropertyData), + .PropertyData = {'{', 0, 'C', 0, 'D', 0, 'B', 0, '3', 0, 'B', 0, '5', 0, + 'A', 0, 'D', 0, '-', 0, '2', 0, '9', 0, '3', 0, 'B', 0, + '-', 0, '4', 0, '6', 0, '6', 0, '3', 0, '-', 0, 'A', 0, + 'A', 0, '3', 0, '6', 0, '-', 0, '1', 0, 'A', 0, 'A', 0, + 'E', 0, '4', 0, '6', 0, '4', 0, '6', 0, '3', 0, '7', 0, + '7', 0, '6', 0, '}', 0, 0, 0, 0, 0}, + }, + }, +}; + +typedef struct { + FuriSemaphore* semaphore_v1; + FuriSemaphore* semaphore_v2; + FuriSemaphore* semaphore_cdc; + bool connected; + usbd_device* usb_dev; + DapStateCallback state_callback; + DapRxCallback rx_callback_v1; + DapRxCallback rx_callback_v2; + DapRxCallback rx_callback_cdc; + DapCDCControlLineCallback control_line_callback_cdc; + DapCDCConfigCallback config_callback_cdc; + void* context; + void* context_cdc; +} DAPState; + +static DAPState dap_state = { + .semaphore_v1 = NULL, + .semaphore_v2 = NULL, + .semaphore_cdc = NULL, + .connected = false, + .usb_dev = NULL, + .state_callback = NULL, + .rx_callback_v1 = NULL, + .rx_callback_v2 = NULL, + .rx_callback_cdc = NULL, + .control_line_callback_cdc = NULL, + .config_callback_cdc = NULL, + .context = NULL, + .context_cdc = NULL, +}; + +static struct usb_cdc_line_coding cdc_config = {0}; +static uint8_t cdc_ctrl_line_state = 0; + +#ifdef DAP_USB_LOG +void furi_console_log_printf(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); + +void furi_console_log_printf(const char* format, ...) { + char buffer[256]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + furi_hal_console_puts(buffer); + furi_hal_console_puts("\r\n"); + UNUSED(format); +} +#else +#define furi_console_log_printf(...) +#endif + +int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size) { + if((dap_state.semaphore_v1 == NULL) || (dap_state.connected == false)) return 0; + + furi_check(furi_semaphore_acquire(dap_state.semaphore_v1, FuriWaitForever) == FuriStatusOk); + + if(dap_state.connected) { + int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_IN, buffer, size); + furi_console_log_printf("v1 tx %ld", len); + return len; + } else { + return 0; + } +} + +int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size) { + if((dap_state.semaphore_v2 == NULL) || (dap_state.connected == false)) return 0; + + furi_check(furi_semaphore_acquire(dap_state.semaphore_v2, FuriWaitForever) == FuriStatusOk); + + if(dap_state.connected) { + int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_BULK_IN, buffer, size); + furi_console_log_printf("v2 tx %ld", len); + return len; + } else { + return 0; + } +} + +int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size) { + if((dap_state.semaphore_cdc == NULL) || (dap_state.connected == false)) return 0; + + furi_check(furi_semaphore_acquire(dap_state.semaphore_cdc, FuriWaitForever) == FuriStatusOk); + + if(dap_state.connected) { + int32_t len = usbd_ep_write(dap_state.usb_dev, HID_EP_IN | DAP_CDC_EP_SEND, buffer, size); + furi_console_log_printf("cdc tx %ld", len); + return len; + } else { + return 0; + } +} + +void dap_v1_usb_set_rx_callback(DapRxCallback callback) { + dap_state.rx_callback_v1 = callback; +} + +void dap_v2_usb_set_rx_callback(DapRxCallback callback) { + dap_state.rx_callback_v2 = callback; +} + +void dap_cdc_usb_set_rx_callback(DapRxCallback callback) { + dap_state.rx_callback_cdc = callback; +} + +void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback) { + dap_state.control_line_callback_cdc = callback; +} + +void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback) { + dap_state.config_callback_cdc = callback; +} + +void dap_cdc_usb_set_context(void* context) { + dap_state.context_cdc = context; +} + +void dap_common_usb_set_context(void* context) { + dap_state.context = context; +} + +void dap_common_usb_set_state_callback(DapStateCallback callback) { + dap_state.state_callback = callback; +} + +static void* dap_usb_alloc_string_descr(const char* str) { + furi_assert(str); + + uint8_t len = strlen(str); + uint8_t wlen = (len + 1) * sizeof(uint16_t); + struct usb_string_descriptor* dev_str_desc = malloc(wlen); + dev_str_desc->bLength = wlen; + dev_str_desc->bDescriptorType = USB_DTYPE_STRING; + for(uint8_t i = 0; i < len; i++) { + dev_str_desc->wString[i] = str[i]; + } + + return dev_str_desc; +} + +void dap_common_usb_alloc_name(const char* name) { + dev_serial_descr = dap_usb_alloc_string_descr(name); +} + +void dap_common_usb_free_name() { + free(dev_serial_descr); +} + +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); +static void hid_deinit(usbd_device* dev); +static void hid_on_wakeup(usbd_device* dev); +static void hid_on_suspend(usbd_device* dev); + +static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); + +FuriHalUsbInterface dap_v2_usb_hid = { + .init = hid_init, + .deinit = hid_deinit, + .wakeup = hid_on_wakeup, + .suspend = hid_on_suspend, + .dev_descr = (struct usb_device_descriptor*)&hid_device_desc, + .cfg_descr = (void*)&hid_cfg_desc, +}; + +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + UNUSED(ctx); + + dap_v2_usb_hid.str_manuf_descr = (void*)&dev_manuf_descr; + dap_v2_usb_hid.str_prod_descr = (void*)&dev_prod_descr; + dap_v2_usb_hid.str_serial_descr = (void*)dev_serial_descr; + + dap_state.usb_dev = dev; + if(dap_state.semaphore_v1 == NULL) dap_state.semaphore_v1 = furi_semaphore_alloc(1, 1); + if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1); + if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1); + + usb_hid.dev_descr->idVendor = DAP_HID_VID; + usb_hid.dev_descr->idProduct = DAP_HID_PID; + + usbd_reg_config(dev, hid_ep_config); + usbd_reg_control(dev, hid_control); + + usbd_connect(dev, true); +} + +static bool deinit_flag = false; + +void dap_common_wait_for_deinit() { + while(!deinit_flag) { + furi_delay_ms(50); + } +} + +static void hid_deinit(usbd_device* dev) { + dap_state.usb_dev = NULL; + + furi_semaphore_free(dap_state.semaphore_v1); + furi_semaphore_free(dap_state.semaphore_v2); + furi_semaphore_free(dap_state.semaphore_cdc); + dap_state.semaphore_v1 = NULL; + dap_state.semaphore_v2 = NULL; + dap_state.semaphore_cdc = NULL; + + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + free(usb_hid.str_manuf_descr); + free(usb_hid.str_prod_descr); + + FURI_SW_MEMBARRIER(); + deinit_flag = true; +} + +static void hid_on_wakeup(usbd_device* dev) { + UNUSED(dev); + if(!dap_state.connected) { + dap_state.connected = true; + if(dap_state.state_callback != NULL) { + dap_state.state_callback(dap_state.connected, dap_state.context); + } + } +} + +static void hid_on_suspend(usbd_device* dev) { + UNUSED(dev); + if(dap_state.connected) { + dap_state.connected = false; + if(dap_state.state_callback != NULL) { + dap_state.state_callback(dap_state.connected, dap_state.context); + } + } +} + +size_t dap_v1_usb_rx(uint8_t* buffer, size_t size) { + size_t len = 0; + + if(dap_state.connected) { + len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_OUT, buffer, size); + } + + return len; +} + +size_t dap_v2_usb_rx(uint8_t* buffer, size_t size) { + size_t len = 0; + + if(dap_state.connected) { + len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_BULK_OUT, buffer, size); + } + + return len; +} + +size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size) { + size_t len = 0; + + if(dap_state.connected) { + len = usbd_ep_read(dap_state.usb_dev, HID_EP_OUT | DAP_CDC_EP_RECV, buffer, size); + } + + return len; +} + +static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(ep); + + switch(event) { + case usbd_evt_eptx: + furi_semaphore_release(dap_state.semaphore_v1); + furi_console_log_printf("hid tx complete"); + break; + case usbd_evt_eprx: + if(dap_state.rx_callback_v1 != NULL) { + dap_state.rx_callback_v1(dap_state.context); + } + break; + default: + furi_console_log_printf("hid %d, %d", event, ep); + break; + } +} + +static void hid_txrx_ep_bulk_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(ep); + + switch(event) { + case usbd_evt_eptx: + furi_semaphore_release(dap_state.semaphore_v2); + furi_console_log_printf("bulk tx complete"); + break; + case usbd_evt_eprx: + if(dap_state.rx_callback_v2 != NULL) { + dap_state.rx_callback_v2(dap_state.context); + } + break; + default: + furi_console_log_printf("bulk %d, %d", event, ep); + break; + } +} + +static void cdc_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(ep); + + switch(event) { + case usbd_evt_eptx: + furi_semaphore_release(dap_state.semaphore_cdc); + furi_console_log_printf("cdc tx complete"); + break; + case usbd_evt_eprx: + if(dap_state.rx_callback_cdc != NULL) { + dap_state.rx_callback_cdc(dap_state.context_cdc); + } + break; + default: + furi_console_log_printf("cdc %d, %d", event, ep); + break; + } +} + +static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case EP_CFG_DECONFIGURE: + usbd_ep_deconfig(dev, DAP_HID_EP_OUT); + usbd_ep_deconfig(dev, DAP_HID_EP_IN); + usbd_ep_deconfig(dev, DAP_HID_EP_BULK_IN); + usbd_ep_deconfig(dev, DAP_HID_EP_BULK_OUT); + usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_COMM); + usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_SEND); + usbd_ep_deconfig(dev, HID_EP_OUT | DAP_CDC_EP_RECV); + usbd_reg_endpoint(dev, DAP_HID_EP_OUT, NULL); + usbd_reg_endpoint(dev, DAP_HID_EP_IN, NULL); + usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, NULL); + usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, NULL); + usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, 0); + usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, 0); + return usbd_ack; + case EP_CFG_CONFIGURE: + usbd_ep_config(dev, DAP_HID_EP_IN, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); + usbd_ep_config(dev, DAP_HID_EP_OUT, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); + usbd_ep_config(dev, DAP_HID_EP_BULK_OUT, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); + usbd_ep_config(dev, DAP_HID_EP_BULK_IN, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); + usbd_ep_config(dev, HID_EP_OUT | DAP_CDC_EP_RECV, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); + usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_SEND, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); + usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_COMM, USB_EPTYPE_INTERRUPT, DAP_CDC_EP_SIZE); + usbd_reg_endpoint(dev, DAP_HID_EP_IN, hid_txrx_ep_callback); + usbd_reg_endpoint(dev, DAP_HID_EP_OUT, hid_txrx_ep_callback); + usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, hid_txrx_ep_bulk_callback); + usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, hid_txrx_ep_bulk_callback); + usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, cdc_txrx_ep_callback); + usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, cdc_txrx_ep_callback); + // usbd_ep_write(dev, DAP_HID_EP_IN, NULL, 0); + // usbd_ep_write(dev, DAP_HID_EP_BULK_IN, NULL, 0); + // usbd_ep_write(dev, HID_EP_IN | DAP_CDC_EP_SEND, NULL, 0); + return usbd_ack; + default: + return usbd_fail; + } +} + +#ifdef DAP_USB_LOG +static void dump_request_type(uint8_t type) { + switch(type & USB_REQ_DIRECTION) { + case USB_REQ_HOSTTODEV: + furi_hal_console_puts("host to dev, "); + break; + case USB_REQ_DEVTOHOST: + furi_hal_console_puts("dev to host, "); + break; + } + + switch(type & USB_REQ_TYPE) { + case USB_REQ_STANDARD: + furi_hal_console_puts("standard, "); + break; + case USB_REQ_CLASS: + furi_hal_console_puts("class, "); + break; + case USB_REQ_VENDOR: + furi_hal_console_puts("vendor, "); + break; + } + + switch(type & USB_REQ_RECIPIENT) { + case USB_REQ_DEVICE: + furi_hal_console_puts("device"); + break; + case USB_REQ_INTERFACE: + furi_hal_console_puts("interface"); + break; + case USB_REQ_ENDPOINT: + furi_hal_console_puts("endpoint"); + break; + case USB_REQ_OTHER: + furi_hal_console_puts("other"); + break; + } + + furi_hal_console_puts("\r\n"); +} +#else +#define dump_request_type(...) +#endif + +static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + + dump_request_type(req->bmRequestType); + furi_console_log_printf( + "control: RT %02x, R %02x, V %04x, I %04x, L %04x", + req->bmRequestType, + req->bRequest, + req->wValue, + req->wIndex, + req->wLength); + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE | USB_REQ_DIRECTION) & req->bmRequestType) == + (USB_REQ_STANDARD | USB_REQ_VENDOR | USB_REQ_DEVTOHOST)) { + // vendor request, device to host + furi_console_log_printf("vendor request"); + if(USB_WINUSB_VENDOR_CODE == req->bRequest) { + // WINUSB request + if(USB_WINUSB_DESCRIPTOR_INDEX == req->wIndex) { + furi_console_log_printf("WINUSB descriptor"); + uint16_t length = req->wLength; + if(length > sizeof(usb_msos_descriptor_set_t)) { + length = sizeof(usb_msos_descriptor_set_t); + } + + dev->status.data_ptr = (uint8_t*)&usb_msos_descriptor_set; + dev->status.data_count = length; + return usbd_ack; + } + } + } + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_STANDARD | USB_REQ_DEVICE)) { + // device request + if(req->bRequest == USB_STD_GET_DESCRIPTOR) { + const uint8_t dtype = req->wValue >> 8; + const uint8_t dnumber = req->wValue & 0xFF; + // get string descriptor + if(USB_DTYPE_STRING == dtype) { + if(dnumber == USB_STR_CMSIS_DAP_V1) { + furi_console_log_printf("str CMSIS-DAP v1"); + dev->status.data_ptr = (uint8_t*)&dev_dap_v1_descr; + dev->status.data_count = dev_dap_v1_descr.bLength; + return usbd_ack; + } else if(dnumber == USB_STR_CMSIS_DAP_V2) { + furi_console_log_printf("str CMSIS-DAP v2"); + dev->status.data_ptr = (uint8_t*)&dev_dap_v2_descr; + dev->status.data_count = dev_dap_v2_descr.bLength; + return usbd_ack; + } else if(dnumber == USB_STR_COM_PORT) { + furi_console_log_printf("str COM port"); + dev->status.data_ptr = (uint8_t*)&dev_com_descr; + dev->status.data_count = dev_com_descr.bLength; + return usbd_ack; + } + } else if(USB_DTYPE_BINARY_OBJECT_STORE == dtype) { + furi_console_log_printf("BOS descriptor"); + uint16_t length = req->wLength; + if(length > sizeof(usb_bos_hierarchy_t)) { + length = sizeof(usb_bos_hierarchy_t); + } + dev->status.data_ptr = (uint8_t*)&usb_bos_hierarchy; + dev->status.data_count = length; + return usbd_ack; + } + } + } + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + req->wIndex == 0) { + // class request + switch(req->bRequest) { + // get hid descriptor + case USB_HID_GETREPORT: + furi_console_log_printf("get report"); + return usbd_fail; + // set hid idle + case USB_HID_SETIDLE: + furi_console_log_printf("set idle"); + return usbd_ack; + default: + break; + } + } + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + req->wIndex == 2) { + // class request + switch(req->bRequest) { + // control line state + case USB_CDC_SET_CONTROL_LINE_STATE: + furi_console_log_printf("set control line state"); + cdc_ctrl_line_state = req->wValue; + if(dap_state.control_line_callback_cdc != NULL) { + dap_state.control_line_callback_cdc(cdc_ctrl_line_state, dap_state.context_cdc); + } + return usbd_ack; + // set cdc line coding + case USB_CDC_SET_LINE_CODING: + furi_console_log_printf("set line coding"); + memcpy(&cdc_config, req->data, sizeof(cdc_config)); + if(dap_state.config_callback_cdc != NULL) { + dap_state.config_callback_cdc(&cdc_config, dap_state.context_cdc); + } + return usbd_ack; + // get cdc line coding + case USB_CDC_GET_LINE_CODING: + furi_console_log_printf("get line coding"); + dev->status.data_ptr = &cdc_config; + dev->status.data_count = sizeof(cdc_config); + return usbd_ack; + default: + break; + } + } + + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_STANDARD) && + req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) { + // standard request + switch(req->wValue >> 8) { + // get hid descriptor + case USB_DTYPE_HID: + furi_console_log_printf("get hid descriptor"); + dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.hid); + dev->status.data_count = sizeof(hid_cfg_desc.hid); + return usbd_ack; + // get hid report descriptor + case USB_DTYPE_HID_REPORT: + furi_console_log_printf("get hid report descriptor"); + dev->status.data_ptr = (uint8_t*)hid_report_desc; + dev->status.data_count = sizeof(hid_report_desc); + return usbd_ack; + default: + break; + } + } + + return usbd_fail; +} \ No newline at end of file diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.h b/applications/plugins/dap_link/usb/dap_v2_usb.h new file mode 100644 index 000000000..2a0e86056 --- /dev/null +++ b/applications/plugins/dap_link/usb/dap_v2_usb.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include + +extern FuriHalUsbInterface dap_v2_usb_hid; + +// receive callback type +typedef void (*DapRxCallback)(void* context); + +typedef void (*DapStateCallback)(bool state, void* context); + +/************************************ V1 ***************************************/ + +int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size); + +size_t dap_v1_usb_rx(uint8_t* buffer, size_t size); + +void dap_v1_usb_set_rx_callback(DapRxCallback callback); + +/************************************ V2 ***************************************/ + +int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size); + +size_t dap_v2_usb_rx(uint8_t* buffer, size_t size); + +void dap_v2_usb_set_rx_callback(DapRxCallback callback); + +/************************************ CDC **************************************/ + +typedef void (*DapCDCControlLineCallback)(uint8_t state, void* context); +typedef void (*DapCDCConfigCallback)(struct usb_cdc_line_coding* config, void* context); + +int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size); + +size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size); + +void dap_cdc_usb_set_rx_callback(DapRxCallback callback); + +void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback); + +void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback); + +void dap_cdc_usb_set_context(void* context); + +/*********************************** Common ************************************/ + +void dap_common_usb_set_context(void* context); + +void dap_common_usb_set_state_callback(DapStateCallback callback); + +void dap_common_usb_alloc_name(const char* name); + +void dap_common_usb_free_name(); + +void dap_common_wait_for_deinit(); \ No newline at end of file diff --git a/applications/plugins/dap_link/usb/usb_winusb.h b/applications/plugins/dap_link/usb/usb_winusb.h new file mode 100644 index 000000000..9c3a172dc --- /dev/null +++ b/applications/plugins/dap_link/usb/usb_winusb.h @@ -0,0 +1,143 @@ +#pragma once +#include + +/*- Definitions -------------------------------------------------------------*/ + +#define USB_PACK __attribute__((packed)) + +#define USB_WINUSB_VENDOR_CODE 0x20 + +#define USB_WINUSB_WINDOWS_VERSION 0x06030000 // Windows 8.1 + +#define USB_WINUSB_PLATFORM_CAPABILITY_ID \ + { \ + 0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, 0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, \ + 0x9f \ + } + +enum // WinUSB Microsoft OS 2.0 descriptor request codes +{ + USB_WINUSB_DESCRIPTOR_INDEX = 0x07, + USB_WINUSB_SET_ALT_ENUMERATION = 0x08, +}; + +enum // wDescriptorType +{ + USB_WINUSB_SET_HEADER_DESCRIPTOR = 0x00, + USB_WINUSB_SUBSET_HEADER_CONFIGURATION = 0x01, + USB_WINUSB_SUBSET_HEADER_FUNCTION = 0x02, + USB_WINUSB_FEATURE_COMPATBLE_ID = 0x03, + USB_WINUSB_FEATURE_REG_PROPERTY = 0x04, + USB_WINUSB_FEATURE_MIN_RESUME_TIME = 0x05, + USB_WINUSB_FEATURE_MODEL_ID = 0x06, + USB_WINUSB_FEATURE_CCGP_DEVICE = 0x07, + USB_WINUSB_FEATURE_VENDOR_REVISION = 0x08, +}; + +enum // wPropertyDataType +{ + USB_WINUSB_PROPERTY_DATA_TYPE_SZ = 1, + USB_WINUSB_PROPERTY_DATA_TYPE_EXPAND_SZ = 2, + USB_WINUSB_PROPERTY_DATA_TYPE_BINARY = 3, + USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_LITTLE_ENDIAN = 4, + USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_BIG_ENDIAN = 5, + USB_WINUSB_PROPERTY_DATA_TYPE_LINK = 6, + USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ = 7, +}; + +/*- Types BOS -------------------------------------------------------------------*/ + +typedef struct USB_PACK { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} usb_binary_object_store_descriptor_t; + +/*- Types WinUSB -------------------------------------------------------------------*/ + +typedef struct USB_PACK { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t PlatformCapabilityUUID[16]; + uint32_t dwWindowsVersion; + uint16_t wMSOSDescriptorSetTotalLength; + uint8_t bMS_VendorCode; + uint8_t bAltEnumCode; +} usb_winusb_capability_descriptor_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint32_t dwWindowsVersion; + uint16_t wDescriptorSetTotalLength; +} usb_winusb_set_header_descriptor_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t bConfigurationValue; + uint8_t bReserved; + uint16_t wTotalLength; +} usb_winusb_subset_header_configuration_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t bFirstInterface; + uint8_t bReserved; + uint16_t wSubsetLength; +} usb_winusb_subset_header_function_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t CompatibleID[8]; + uint8_t SubCompatibleID[8]; +} usb_winusb_feature_compatble_id_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint16_t wPropertyDataType; + //uint16_t wPropertyNameLength; + //uint8_t PropertyName[...]; + //uint16_t wPropertyDataLength + //uint8_t PropertyData[...]; +} usb_winusb_feature_reg_property_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint16_t wPropertyDataType; + uint16_t wPropertyNameLength; + uint8_t PropertyName[42]; + uint16_t wPropertyDataLength; + uint8_t PropertyData[80]; +} usb_winusb_feature_reg_property_guids_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t bResumeRecoveryTime; + uint8_t bResumeSignalingTime; +} usb_winusb_feature_min_resume_time_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t ModelID[16]; +} usb_winusb_feature_model_id_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; +} usb_winusb_feature_ccgp_device_t; + +typedef struct USB_PACK { + uint16_t wLength; + uint16_t wDescriptorType; + uint16_t VendorRevision; +} usb_winusb_feature_vendor_revision_t; \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/DHT.c b/applications/plugins/dht_temp_sensor/DHT.c new file mode 100644 index 000000000..63a189ce1 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/DHT.c @@ -0,0 +1,169 @@ +#include "DHT.h" + +#define lineDown() furi_hal_gpio_write(sensor->GPIO, false) +#define lineUp() furi_hal_gpio_write(sensor->GPIO, true) +#define getLine() furi_hal_gpio_read(sensor->GPIO) +#define Delay(d) furi_delay_ms(d) + +DHT_data DHT_getData(DHT_sensor* sensor) { + DHT_data data = {-128.0f, -128.0f}; + +#if DHT_POLLING_CONTROL == 1 + /* ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ частотС опроса Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° */ + //ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π»Π° опроса Π² зависимости ΠΎΡ‚ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + uint16_t pollingInterval; + if(sensor->type == DHT11) { + pollingInterval = DHT_POLLING_INTERVAL_DHT11; + } else { + pollingInterval = DHT_POLLING_INTERVAL_DHT22; + } + + //Если ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π» малСнький, Ρ‚ΠΎ Π²ΠΎΠ·Π²Ρ€Π°Ρ‚ послСднСго ΡƒΠ΄Π°Ρ‡Π½ΠΎΠ³ΠΎ значСния + if((furi_get_tick() - sensor->lastPollingTime < pollingInterval) && + sensor->lastPollingTime != 0) { + data.hum = sensor->lastHum; + data.temp = sensor->lastTemp; + return data; + } + sensor->lastPollingTime = furi_get_tick() + 1; +#endif + + //ΠžΠΏΡƒΡΠΊΠ°Π½ΠΈΠ΅ Π»ΠΈΠ½ΠΈΠΈ Π΄Π°Π½Π½Ρ‹Ρ… Π½Π° 18 мс + lineDown(); +#ifdef DHT_IRQ_CONTROL + //Π’Ρ‹ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΏΡ€Π΅Ρ€Ρ‹Π²Π°Π½ΠΈΠΉ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½ΠΈΡ‡Ρ‚ΠΎ Π½Π΅ мСшало ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… + __disable_irq(); +#endif + Delay(18); + + //ΠŸΠΎΠ΄ΡŠΡ‘ΠΌ Π»ΠΈΠ½ΠΈΠΈ + lineUp(); + + /* ОТиданиС ΠΎΡ‚Π²Π΅Ρ‚Π° ΠΎΡ‚ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° */ + uint16_t timeout = 0; + while(!getLine()) { + timeout++; + if(timeout > DHT_TIMEOUT) { +#ifdef DHT_IRQ_CONTROL + __enable_irq(); +#endif + //Если Π΄Π°Ρ‚Ρ‡ΠΈΠΊ Π½Π΅ отозвался, Π·Π½Π°Ρ‡ΠΈΡ‚ Π΅Π³ΠΎ Ρ‚ΠΎΡ‡Π½ΠΎ Π½Π΅Ρ‚ + //ΠžΠ±Π½ΡƒΠ»Π΅Π½ΠΈΠ΅ послСднСго ΡƒΠ΄Π°Ρ‡Π½ΠΎΠ³ΠΎ значСния, Ρ‡Ρ‚ΠΎΠ±Ρ‹ + //Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ Ρ„Π°Π½Ρ‚ΠΎΠΌΠ½Ρ‹Π΅ значСния + sensor->lastHum = -128.0f; + sensor->lastTemp = -128.0f; + + return data; + } + } + //ОТиданиС спада + while(getLine()) { + timeout++; + if(timeout > DHT_TIMEOUT) { +#ifdef DHT_IRQ_CONTROL + __enable_irq(); +#endif + //Если Π΄Π°Ρ‚Ρ‡ΠΈΠΊ Π½Π΅ отозвался, Π·Π½Π°Ρ‡ΠΈΡ‚ Π΅Π³ΠΎ Ρ‚ΠΎΡ‡Π½ΠΎ Π½Π΅Ρ‚ + //ΠžΠ±Π½ΡƒΠ»Π΅Π½ΠΈΠ΅ послСднСго ΡƒΠ΄Π°Ρ‡Π½ΠΎΠ³ΠΎ значСния, Ρ‡Ρ‚ΠΎΠ±Ρ‹ + //Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ Ρ„Π°Π½Ρ‚ΠΎΠΌΠ½Ρ‹Π΅ значСния + sensor->lastHum = -128.0f; + sensor->lastTemp = -128.0f; + + return data; + } + } + timeout = 0; + //ОТиданиС ΠΏΠΎΠ΄ΡŠΡ‘ΠΌΠ° + while(!getLine()) { + timeout++; + if(timeout > DHT_TIMEOUT) { + if(timeout > DHT_TIMEOUT) { +#ifdef DHT_IRQ_CONTROL + __enable_irq(); +#endif + //Если Π΄Π°Ρ‚Ρ‡ΠΈΠΊ Π½Π΅ отозвался, Π·Π½Π°Ρ‡ΠΈΡ‚ Π΅Π³ΠΎ Ρ‚ΠΎΡ‡Π½ΠΎ Π½Π΅Ρ‚ + //ΠžΠ±Π½ΡƒΠ»Π΅Π½ΠΈΠ΅ послСднСго ΡƒΠ΄Π°Ρ‡Π½ΠΎΠ³ΠΎ значСния, Ρ‡Ρ‚ΠΎΠ±Ρ‹ + //Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ Ρ„Π°Π½Ρ‚ΠΎΠΌΠ½Ρ‹Π΅ значСния + sensor->lastHum = -128.0f; + sensor->lastTemp = -128.0f; + + return data; + } + } + } + timeout = 0; + //ОТиданиС спада + while(getLine()) { + timeout++; + if(timeout > DHT_TIMEOUT) { +#ifdef DHT_IRQ_CONTROL + __enable_irq(); +#endif + //Если Π΄Π°Ρ‚Ρ‡ΠΈΠΊ Π½Π΅ отозвался, Π·Π½Π°Ρ‡ΠΈΡ‚ Π΅Π³ΠΎ Ρ‚ΠΎΡ‡Π½ΠΎ Π½Π΅Ρ‚ + //ΠžΠ±Π½ΡƒΠ»Π΅Π½ΠΈΠ΅ послСднСго ΡƒΠ΄Π°Ρ‡Π½ΠΎΠ³ΠΎ значСния, Ρ‡Ρ‚ΠΎΠ±Ρ‹ + //Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ Ρ„Π°Π½Ρ‚ΠΎΠΌΠ½Ρ‹Π΅ значСния + sensor->lastHum = -128.0f; + sensor->lastTemp = -128.0f; + return data; + } + } + + /* Π§Ρ‚Π΅Π½ΠΈΠ΅ ΠΎΡ‚Π²Π΅Ρ‚Π° ΠΎΡ‚ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° */ + uint8_t rawData[5] = {0, 0, 0, 0, 0}; + for(uint8_t a = 0; a < 5; a++) { + for(uint8_t b = 7; b != 255; b--) { + uint16_t hT = 0, lT = 0; + //Пока линия Π² Π½ΠΈΠ·ΠΊΠΎΠΌ ΡƒΡ€ΠΎΠ²Π½Π΅, ΠΈΠ½ΠΊΡ€Π΅ΠΌΠ΅Π½Ρ‚ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ lT + while(!getLine() && lT != 65535) lT++; + //Пока линия Π² высоком ΡƒΡ€ΠΎΠ²Π½Π΅, ΠΈΠ½ΠΊΡ€Π΅ΠΌΠ΅Π½Ρ‚ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ hT + timeout = 0; + while(getLine() && hT != 65535) hT++; + //Если hT большС lT, Ρ‚ΠΎ ΠΏΡ€ΠΈΡˆΠ»Π° Π΅Π΄ΠΈΠ½ΠΈΡ†Π° + if(hT > lT) rawData[a] |= (1 << b); + } + } +#ifdef DHT_IRQ_CONTROL + //Π’ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΏΡ€Π΅Ρ€Ρ‹Π²Π°Π½ΠΈΠΉ послС ΠΏΡ€ΠΈΡ‘ΠΌΠ° Π΄Π°Π½Π½Ρ‹Ρ… + __enable_irq(); +#endif + /* ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° цСлостности Π΄Π°Π½Π½Ρ‹Ρ… */ + if((uint8_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4]) { + //Если ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½Π°Ρ сумма совпадаСт, Ρ‚ΠΎ конвСртация ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Ρ… Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ + if(sensor->type == DHT22) { + data.hum = (float)(((uint16_t)rawData[0] << 8) | rawData[1]) * 0.1f; + //ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° ΠΎΡ‚Ρ€ΠΈΡ†Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ Ρ‚Π΅ΠΌΠΏΠ΅Ρ€Π°Ρ‚ΡƒΡ€Ρ‹ + if(!(rawData[2] & (1 << 7))) { + data.temp = (float)(((uint16_t)rawData[2] << 8) | rawData[3]) * 0.1f; + } else { + rawData[2] &= ~(1 << 7); + data.temp = (float)(((uint16_t)rawData[2] << 8) | rawData[3]) * -0.1f; + } + } + if(sensor->type == DHT11) { + data.hum = (float)rawData[0]; + data.temp = (float)rawData[2]; + //DHT11 производства ASAIR ΠΈΠΌΠ΅ΡŽΡ‚ Π΄Ρ€ΠΎΠ±Π½ΡƒΡŽ Ρ‡Π°ΡΡ‚ΡŒ Π² Ρ‚Π΅ΠΌΠΏΠ΅Ρ€Π°Ρ‚ΡƒΡ€Π΅ + //А Π΅Ρ‰Ρ‘ Ρ‚Π΅ΠΌΠΏΠ΅Ρ€Π°Ρ‚ΡƒΡ€Ρƒ измСряСт ΠΎΡ‚ -20 Π΄ΠΎ +60 *Π‘ + //Π’ΠΎΡ‚ ΠΏΡ€ΠΈΠΊΠΎΠ», Π΄Π°? + if(rawData[3] != 0) { + //ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π·Π½Π°ΠΊΠ° + if(!(rawData[3] & (1 << 7))) { + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ Π΄Ρ€ΠΎΠ±Π½ΠΎΠΉ части + data.temp += rawData[3] * 0.1f; + } else { + //А Ρ‚ΡƒΡ‚ Π΄Π΅Π»Π°Π΅ΠΌ ΠΎΡ‚Ρ€ΠΈΡ†Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ + rawData[3] &= ~(1 << 7); + data.temp += rawData[3] * 0.1f; + data.temp *= -1; + } + } + } + } + +#if DHT_POLLING_CONTROL == 1 + sensor->lastHum = data.hum; + sensor->lastTemp = data.temp; +#endif + + return data; +} \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/DHT.h b/applications/plugins/dht_temp_sensor/DHT.h new file mode 100644 index 000000000..409847d8b --- /dev/null +++ b/applications/plugins/dht_temp_sensor/DHT.h @@ -0,0 +1,40 @@ +#ifndef DHT_H_ +#define DHT_H_ + +#include + +/* Настройки */ +#define DHT_TIMEOUT 65534 //ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠΉ, послС ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… функция Π²Π΅Ρ€Π½Ρ‘Ρ‚ пустыС значСния +#define DHT_POLLING_CONTROL 1 //Π’ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ частоты опроса Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° +#define DHT_POLLING_INTERVAL_DHT11 \ + 2000 //Π˜Π½Ρ‚Π΅Ρ€Π²Π°Π» опроса DHT11 (0.5 Π“Ρ† ΠΏΠΎ Π΄Π°Ρ‚Π°ΡˆΠΈΡ‚Ρƒ). МоТно ΠΏΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ 1500, Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ +//ΠšΠΎΡΡ‚Ρ‹Π»ΡŒ, Π²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ 2 сСкунды для Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° AM2302 +#define DHT_POLLING_INTERVAL_DHT22 2000 //Π˜Π½Ρ‚Π΅Ρ€Π²Π°Π» опроса DHT22 (1 Π“Ρ† ΠΏΠΎ Π΄Π°Ρ‚Π°ΡˆΠΈΡ‚Ρƒ) +#define DHT_IRQ_CONTROL //Π’Ρ‹ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ прСрывания Π²ΠΎ врСмя ΠΎΠ±ΠΌΠ΅Π½Π° Π΄Π°Π½Π½Ρ‹Ρ… с Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠΌ +/* Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΡ‹Ρ… Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠΌ Π΄Π°Π½Π½Ρ‹Ρ… */ +typedef struct { + float hum; + float temp; +} DHT_data; + +/* Π’ΠΈΠΏ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠΎΠ³ΠΎ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° */ +typedef enum { DHT11, DHT22 } DHT_type; + +/* Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° */ +typedef struct { + char name[11]; + const GpioPin* GPIO; //Пин Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + DHT_type type; //Π’ΠΈΠΏ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° (DHT11 ΠΈΠ»ΠΈ DHT22) + +//ΠšΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒ частоты опроса Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ°. ЗначСния Π½Π΅ Π·Π°ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ! +#if DHT_POLLING_CONTROL == 1 + uint32_t lastPollingTime; //ВрСмя послСднСго опроса Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + float lastTemp; //ПослСднСС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Ρ‚Π΅ΠΌΠΏΠ΅Ρ€Π°Ρ‚ΡƒΡ€Ρ‹ + float lastHum; //ПослСднСС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ влаТности +#endif +} DHT_sensor; + +/* ΠŸΡ€ΠΎΡ‚ΠΎΡ‚ΠΈΠΏΡ‹ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ */ +DHT_data DHT_getData(DHT_sensor* sensor); //ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ с Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + +#endif diff --git a/applications/plugins/dht_temp_sensor/application.fam b/applications/plugins/dht_temp_sensor/application.fam new file mode 100644 index 000000000..fe91415b3 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/application.fam @@ -0,0 +1,13 @@ +App( + appid="DHT_Monitor", + name="[DHT] Temp. Monitor", + apptype=FlipperAppType.EXTERNAL, + entry_point="quenon_dht_mon_app", + cdefines=["QUENON_DHT_MON"], + requires=[ + "gui", + ], + fap_category="GPIO", + fap_icon="icon.png", + stack_size=2 * 1024, +) \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/icon.png b/applications/plugins/dht_temp_sensor/icon.png new file mode 100644 index 000000000..0e87c26c2 Binary files /dev/null and b/applications/plugins/dht_temp_sensor/icon.png differ diff --git a/applications/plugins/dht_temp_sensor/quenon_dht_mon.c b/applications/plugins/dht_temp_sensor/quenon_dht_mon.c new file mode 100644 index 000000000..e2a1aba8b --- /dev/null +++ b/applications/plugins/dht_temp_sensor/quenon_dht_mon.c @@ -0,0 +1,469 @@ +#include "quenon_dht_mon.h" +#include + +//ΠŸΠΎΡ€Ρ‚Ρ‹ Π²Π²ΠΎΠ΄Π°/Π²Ρ‹Π²ΠΎΠ΄Π°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½Π΅ Π±Ρ‹Π»ΠΈ ΠΎΠ±ΠΎΠ·Π½Π°Ρ‡Π΅Π½Ρ‹ Π² ΠΎΠ±Ρ‰Π΅ΠΌ спискС +const GpioPin SWC_10 = {.pin = LL_GPIO_PIN_14, .port = GPIOA}; +const GpioPin SIO_12 = {.pin = LL_GPIO_PIN_13, .port = GPIOA}; +const GpioPin TX_13 = {.pin = LL_GPIO_PIN_6, .port = GPIOB}; +const GpioPin RX_14 = {.pin = LL_GPIO_PIN_7, .port = GPIOB}; + +//ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ доступных ΠΏΠΎΡ€Ρ‚ΠΎΠ² Π²Π²ΠΎΠ΄Π°/Π²Ρ‹Π²ΠΎΠ΄Π° +#define GPIO_ITEMS (sizeof(gpio_item) / sizeof(GpioItem)) + +//ΠŸΠ΅Ρ€Π΅Ρ‡Π΅Π½ΡŒ достуных ΠΏΠΎΡ€Ρ‚ΠΎΠ² Π²Π²ΠΎΠ΄Π°/Π²Ρ‹Π²ΠΎΠ΄Π° +static const GpioItem gpio_item[] = { + {2, "2 (A7)", &gpio_ext_pa7}, + {3, "3 (A6)", &gpio_ext_pa6}, + {4, "4 (A4)", &gpio_ext_pa4}, + {5, "5 (B3)", &gpio_ext_pb3}, + {6, "6 (B2)", &gpio_ext_pb2}, + {7, "7 (C3)", &gpio_ext_pc3}, + {10, " 10(SWC) ", &SWC_10}, + {12, "12 (SIO)", &SIO_12}, + {13, "13 (TX)", &TX_13}, + {14, "14 (RX)", &RX_14}, + {15, "15 (C1)", &gpio_ext_pc1}, + {16, "16 (C0)", &gpio_ext_pc0}, + {17, "17 (1W)", &ibutton_gpio}}; + +//Π”Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° +static PluginData* app; + +uint8_t DHTMon_GPIO_to_int(const GpioPin* gpio) { + if(gpio == NULL) return 255; + for(uint8_t i = 0; i < GPIO_ITEMS; i++) { + if(gpio_item[i].pin->pin == gpio->pin && gpio_item[i].pin->port == gpio->port) { + return gpio_item[i].num; + } + } + return 255; +} + +const GpioPin* DHTMon_GPIO_form_int(uint8_t name) { + for(uint8_t i = 0; i < GPIO_ITEMS; i++) { + if(gpio_item[i].num == name) { + return gpio_item[i].pin; + } + } + return NULL; +} + +const GpioPin* DHTMon_GPIO_from_index(uint8_t index) { + if(index > GPIO_ITEMS) return NULL; + return gpio_item[index].pin; +} + +uint8_t DHTMon_GPIO_to_index(const GpioPin* gpio) { + if(gpio == NULL) return 255; + for(uint8_t i = 0; i < GPIO_ITEMS; i++) { + if(gpio_item[i].pin->pin == gpio->pin && gpio_item[i].pin->port == gpio->port) { + return i; + } + } + return 255; +} + +const char* DHTMon_GPIO_getName(const GpioPin* gpio) { + if(gpio == NULL) return NULL; + for(uint8_t i = 0; i < GPIO_ITEMS; i++) { + if(gpio_item[i].pin->pin == gpio->pin && gpio_item[i].pin->port == gpio->port) { + return gpio_item[i].name; + } + } + return NULL; +} + +void DHTMon_sensors_init(void) { + //Π’ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ 5V Ссли Π½Π° ΠΏΠΎΡ€Ρ‚Ρƒ 1 FZ Π΅Π³ΠΎ Π½Π΅Ρ‚ + if(furi_hal_power_is_otg_enabled() != true) { + furi_hal_power_enable_otg(); + } + + //Настройка GPIO Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹Ρ… Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + for(uint8_t i = 0; i < app->sensors_count; i++) { + //Высокий ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ + furi_hal_gpio_write(app->sensors[i].GPIO, true); + //Π Π΅ΠΆΠΈΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹ - OpenDrain, подтяТка Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π½Π° всякий случай + furi_hal_gpio_init( + app->sensors[i].GPIO, //ΠŸΠΎΡ€Ρ‚ FZ + GpioModeOutputOpenDrain, //Π Π΅ΠΆΠΈΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹ - ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΉ сток + GpioPullUp, //ΠŸΡ€ΠΈΠ½ΡƒΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Π°Ρ подтяТка Π»ΠΈΠ½ΠΈΠΈ Π΄Π°Π½Π½Ρ‹Ρ… ΠΊ ΠΏΠΈΡ‚Π°Π½ΠΈΡŽ + GpioSpeedVeryHigh); //Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρ‹ - максимальная + } +} + +void DHTMon_sensors_deinit(void) { + //Π’ΠΎΠ·Π²Ρ€Π°Ρ‚ исходного состояния 5V + if(app->last_OTG_State != true) { + furi_hal_power_disable_otg(); + } + + //ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄ ΠΏΠΎΡ€Ρ‚ΠΎΠ² GPIO Π² состояниС ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ + for(uint8_t i = 0; i < app->sensors_count; i++) { + furi_hal_gpio_init( + app->sensors[i].GPIO, //ΠŸΠΎΡ€Ρ‚ FZ + GpioModeAnalog, //Π Π΅ΠΆΠΈΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹ - Π°Π½Π°Π»ΠΎΠ³ + GpioPullNo, //ΠžΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ подтяТки + GpioSpeedLow); //Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρ‹ - низкая + //Установка Π½ΠΈΠ·ΠΊΠΎΠ³ΠΎ уровня + furi_hal_gpio_write(app->sensors[i].GPIO, false); + } +} + +bool DHTMon_sensor_check(DHT_sensor* sensor) { + /* ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΈΠΌΠ΅Π½ΠΈ */ + //1) Π‘Ρ‚Ρ€ΠΎΠΊΠ° Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ Π΄Π»ΠΈΠ½ΠΎΠΉ ΠΎΡ‚ 1 Π΄ΠΎ 10 символов + //2) ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ символ строки Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ 0-9, A-Z, a-z ΠΈ _ + if(strlen(sensor->name) == 0 || strlen(sensor->name) > 10 || + (!(sensor->name[0] >= '0' && sensor->name[0] <= '9') && + !(sensor->name[0] >= 'A' && sensor->name[0] <= 'Z') && + !(sensor->name[0] >= 'a' && sensor->name[0] <= 'z') && !(sensor->name[0] == '_'))) { + FURI_LOG_D(APP_NAME, "Sensor [%s] name check failed\r\n", sensor->name); + return false; + } + //ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° GPIO + if(DHTMon_GPIO_to_int(sensor->GPIO) == 255) { + FURI_LOG_D( + APP_NAME, + "Sensor [%s] GPIO check failed: %d\r\n", + sensor->name, + DHTMon_GPIO_to_int(sensor->GPIO)); + return false; + } + //ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ‚ΠΈΠΏΠ° Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + if(sensor->type != DHT11 && sensor->type != DHT22) { + FURI_LOG_D(APP_NAME, "Sensor [%s] type check failed: %d\r\n", sensor->name, sensor->type); + return false; + } + + //Π’ΠΎΠ·Π²Ρ€Π°Ρ‚ истины Ссли всё ΠΎΠΊ + FURI_LOG_D(APP_NAME, "Sensor [%s] all checks passed\r\n", sensor->name); + return true; +} + +void DHTMon_sensor_delete(DHT_sensor* sensor) { + if(sensor == NULL) return; + //Π”Π΅Π»Π°Π΅ΠΌ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° Π½Π΅Π²Π΅Ρ€Π½Ρ‹ΠΌΠΈ + sensor->name[0] = '\0'; + sensor->type = 255; + //Π’Π΅ΠΏΠ΅Ρ€ΡŒ сохраняСм Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠ΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΈ. Бохранятор Π½Π΅ сохранит нСисправный Π΄Π°Ρ‚Ρ‡ΠΈΠΊ + DHTMon_sensors_save(); + //ΠŸΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌΡΡ с SD-ΠΊΠ°Ρ€Ρ‚Ρ‹ + DHTMon_sensors_reload(); +} + +uint8_t DHTMon_sensors_save(void) { + //Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ памяти для ΠΏΠΎΡ‚ΠΎΠΊΠ° + app->file_stream = file_stream_alloc(app->storage); + uint8_t savedSensorsCount = 0; + //ΠŸΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Π°Ρ ΠΏΡƒΡ‚ΠΈ ΠΊ Ρ„Π°ΠΉΠ»Ρƒ + FuriString* filepath = furi_string_alloc(); + //БоставлСниС ΠΏΡƒΡ‚ΠΈ ΠΊ Ρ„Π°ΠΉΠ»Ρƒ + furi_string_printf(filepath, "%s/%s", APP_PATH_FOLDER, APP_FILENAME); + + //ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ ΠΏΠΎΡ‚ΠΎΠΊΠ°. Если ΠΏΠΎΡ‚ΠΎΠΊ открылся, Ρ‚ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ сохранСния Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + if(file_stream_open( + app->file_stream, furi_string_get_cstr(filepath), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { + const char template[] = + "#DHT monitor sensors file\n#Name - name of sensor. Up to 10 sumbols\n#Type - type of sensor. DHT11 - 0, DHT22 - 1\n#GPIO - connection port. May being 2-7, 10, 12-17\n#Name Type GPIO\n"; + stream_write(app->file_stream, (uint8_t*)template, strlen(template)); + //Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + for(uint8_t i = 0; i < app->sensors_count; i++) { + //Если ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° Π²Π΅Ρ€Π½Ρ‹, Ρ‚ΠΎ сохраняСмся + if(DHTMon_sensor_check(&app->sensors[i])) { + stream_write_format( + app->file_stream, + "%s %d %d\n", + app->sensors[i].name, + app->sensors[i].type, + DHTMon_GPIO_to_int(app->sensors[i].GPIO)); + savedSensorsCount++; + } + } + } else { + //TODO: ΠΏΠ΅Ρ‡Π°Ρ‚ΡŒ ошибки Π½Π° экран + FURI_LOG_E(APP_NAME, "cannot create sensors file\r\n"); + } + stream_free(app->file_stream); + + return savedSensorsCount; +} + +bool DHTMon_sensors_load(void) { + //ΠžΠ±Π½ΡƒΠ»Π΅Π½ΠΈΠ΅ количСства Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + app->sensors_count = -1; + //ΠžΡ‡ΠΈΡΡ‚ΠΊΠ° ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰ΠΈΡ… Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + memset(app->sensors, 0, sizeof(app->sensors)); + + //ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ Ρ„Π°ΠΉΠ»Π° Π½Π° SD-ΠΊΠ°Ρ€Ρ‚Π΅ + //Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ памяти для ΠΏΠΎΡ‚ΠΎΠΊΠ° + app->file_stream = file_stream_alloc(app->storage); + //ΠŸΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Π°Ρ ΠΏΡƒΡ‚ΠΈ ΠΊ Ρ„Π°ΠΉΠ»Ρƒ + FuriString* filepath = furi_string_alloc(); + //БоставлСниС ΠΏΡƒΡ‚ΠΈ ΠΊ Ρ„Π°ΠΉΠ»Ρƒ + furi_string_printf(filepath, "%s/%s", APP_PATH_FOLDER, APP_FILENAME); + //ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ ΠΏΠΎΡ‚ΠΎΠΊΠ° ΠΊ Ρ„Π°ΠΉΠ»Ρƒ + if(!file_stream_open( + app->file_stream, furi_string_get_cstr(filepath), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { + //Если Ρ„Π°ΠΉΠ» отсутствуСт, Ρ‚ΠΎ созданиС Π±ΠΎΠ»Π²Π°Π½ΠΊΠΈ + FURI_LOG_W(APP_NAME, "Missing sensors file. Creating new file\r\n"); + app->sensors_count = 0; + stream_free(app->file_stream); + DHTMon_sensors_save(); + return false; + } + //ВычислСниС Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Ρ„Π°ΠΉΠ»Π° + size_t file_size = stream_size(app->file_stream); + if(file_size == (size_t)0) { + //Π’Ρ‹Ρ…ΠΎΠ΄ Ссли Ρ„Π°ΠΉΠ» пустой + FURI_LOG_W(APP_NAME, "Sensors file is empty\r\n"); + app->sensors_count = 0; + stream_free(app->file_stream); + return false; + } + + //Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ памяти ΠΏΠΎΠ΄ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ Ρ„Π°ΠΉΠ»Π° + uint8_t* file_buf = malloc(file_size); + //ΠžΠΏΡƒΡΡ‚ΠΎΡˆΠ΅Π½ΠΈΠ΅ Π±ΡƒΡ„Π΅Ρ€Π° Ρ„Π°ΠΉΠ»Π° + memset(file_buf, 0, file_size); + //Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Ρ„Π°ΠΉΠ»Π° + if(stream_read(app->file_stream, file_buf, file_size) != file_size) { + //Π’Ρ‹Ρ…ΠΎΠ΄ ΠΏΡ€ΠΈ ошибкС чтСния + FURI_LOG_E(APP_NAME, "Error reading sensor file\r\n"); + app->sensors_count = 0; + stream_free(app->file_stream); + return false; + } + //ΠŸΠΎΡΡ‚Ρ€ΠΎΡ‡Π½ΠΎΠ΅ Ρ‡Ρ‚Π΅Π½ΠΈΠ΅ Ρ„Π°ΠΉΠ»Π° + //Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π½Π°Ρ‡Π°Π»ΠΎ строки + FuriString* file = furi_string_alloc_set_str((char*)file_buf); + //Бколько Π±Π°ΠΉΡ‚ Π΄ΠΎ ΠΊΠΎΠ½Ρ†Π° строки + size_t line_end = 0; + while(line_end != STRING_FAILURE && app->sensors_count < MAX_SENSORS) { + if(((char*)(file_buf + line_end))[1] != '#') { + DHT_sensor s = {0}; + int type, port; + char name[11] = {0}; + sscanf(((char*)(file_buf + line_end)), "%s %d %d", name, &type, &port); + s.type = type; + s.GPIO = DHTMon_GPIO_form_int(port); + + name[10] = '\0'; + strcpy(s.name, name); + //Если Π΄Π°Π½Π½Ρ‹Π΅ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹, Ρ‚ΠΎ + if(DHTMon_sensor_check(&s) == true) { + //Установка нуля ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π²ΠΎΠΌ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ΅ + if(app->sensors_count == -1) app->sensors_count = 0; + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° Π² ΠΎΠ±Ρ‰ΠΈΠΉ список + app->sensors[app->sensors_count] = s; + //Π£Π²Π΅Π»ΠΈΡ‡Π΅Π½ΠΈΠ΅ количСства Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹Ρ… Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + app->sensors_count++; + } + } + line_end = furi_string_search_char(file, '\n', line_end + 1); + } + stream_free(app->file_stream); + free(file_buf); + + //ΠžΠ±Π½ΡƒΠ»Π΅Π½ΠΈΠ΅ количСства Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² Ссли Π½ΠΈ ΠΎΠ΄ΠΈΠ½ ΠΈΠ· Π½ΠΈΡ… Π½Π΅ Π±Ρ‹Π» Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½ + if(app->sensors_count == -1) app->sensors_count = 0; + + //Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ ΠΏΠΎΡ€Ρ‚ΠΎΠ² Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² Ссли Ρ‚Π°ΠΊΠΎΠ²Ρ‹Π΅ Π΅ΡΡ‚ΡŒ + if(app->sensors_count > 0) { + DHTMon_sensors_init(); + return true; + } else { + return false; + } + return false; +} + +bool DHTMon_sensors_reload(void) { + DHTMon_sensors_deinit(); + return DHTMon_sensors_load(); +} + +/** + * @brief ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ отрисовки экрана + * + * @param canvas Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° холст + * @param ctx Π”Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + */ +static void render_callback(Canvas* const canvas, void* ctx) { + PluginData* app = acquire_mutex((ValueMutex*)ctx, 25); + if(app == NULL) { + return; + } + //Π’Ρ‹Π·ΠΎΠ² отрисовки Π³Π»Π°Π²Π½ΠΎΠ³ΠΎ экрана + scene_main(canvas, app); + + release_mutex((ValueMutex*)ctx, app); +} + +/** + * @brief ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ наТатия ΠΊΠ½ΠΎΠΏΠΎΠΊ Π³Π»Π°Π²Π½ΠΎΠ³ΠΎ экрана + * + * @param input_event Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° событиС + * @param event_queue Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ событий + */ +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +/** + * @brief Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ мСста ΠΏΠΎΠ΄ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + * + * @return true Если всё ΠΏΡ€ΠΎΡˆΠ»ΠΎ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ + * @return false Если Π² процСссС Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка + */ +static bool DHTMon_alloc(void) { + //Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ мСста ΠΏΠΎΠ΄ Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + app = malloc(sizeof(PluginData)); + //Π’Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ мСста ΠΏΠΎΠ΄ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ событий + app->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + //ΠžΠ±Π½ΡƒΠ»Π΅Π½ΠΈΠ΅ количСства Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + app->sensors_count = -1; + + //Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ мутСкса + if(!init_mutex(&app->state_mutex, app, sizeof(PluginData))) { + FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); + return false; + } + + // Set system callbacks + app->view_port = view_port_alloc(); + view_port_draw_callback_set(app->view_port, render_callback, &app->state_mutex); + view_port_input_callback_set(app->view_port, input_callback, app->event_queue); + + // Open GUI and register view_port + app->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + app->view_dispatcher = view_dispatcher_alloc(); + + sensorActions_sceneCreate(app); + sensorEdit_sceneCreate(app); + + app->widget = widget_alloc(); + view_dispatcher_add_view(app->view_dispatcher, WIDGET_VIEW, widget_get_view(app->widget)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, TEXTINPUT_VIEW, text_input_get_view(app->text_input)); + + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + //УвСдомлСния + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + //ΠŸΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π° + app->storage = furi_record_open(RECORD_STORAGE); + storage_common_mkdir(app->storage, APP_PATH_FOLDER); + app->file_stream = file_stream_alloc(app->storage); + + return true; +} + +/** + * @brief ΠžΡΠ²Ρ‹Π±ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ памяти послС Ρ€Π°Π±ΠΎΡ‚Ρ‹ прилоТСния + */ +static void DHTMon_free(void) { + //АвтоматичСскоС ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ подсвСткой + notification_message(app->notifications, &sequence_display_backlight_enforce_auto); + + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); + + text_input_free(app->text_input); + widget_free(app->widget); + sensorEdit_sceneRemove(); + sensorActions_screneRemove(); + view_dispatcher_free(app->view_dispatcher); + + furi_record_close(RECORD_GUI); + + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(app->gui, app->view_port); + + view_port_free(app->view_port); + furi_message_queue_free(app->event_queue); + delete_mutex(&app->state_mutex); + + free(app); +} + +/** + * @brief Π’ΠΎΡ‡ΠΊΠ° Π²Ρ…ΠΎΠ΄Π° Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ + * + * @return Код ошибки + */ +int32_t quenon_dht_mon_app() { + if(!DHTMon_alloc()) { + DHTMon_free(); + return 255; + } + //ΠŸΠΎΡΡ‚ΠΎΡΠ½Π½ΠΎΠ΅ свСчСниС подсвСтки + notification_message(app->notifications, &sequence_display_backlight_enforce_on); + //Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ состояния наличия 5V Π½Π° ΠΏΠΎΡ€Ρ‚Ρƒ 1 FZ + app->last_OTG_State = furi_hal_power_is_otg_enabled(); + + //Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² с SD-ΠΊΠ°Ρ€Ρ‚Ρ‹ + DHTMon_sensors_load(); + + app->currentSensorEdit = &app->sensors[0]; + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(app->event_queue, &event, 100); + + acquire_mutex_block(&app->state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + break; + case InputKeyLeft: + break; + case InputKeyMAX: + break; + case InputKeyOk: + view_port_update(app->view_port); + release_mutex(&app->state_mutex, app); + mainMenu_scene(app); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } + } else { + FURI_LOG_D(APP_NAME, "FuriMessageQueue: event timeout"); + // event timeout + } + + view_port_update(app->view_port); + release_mutex(&app->state_mutex, app); + } + //ОсвобоТдСниС памяти ΠΈ дСинициализация + DHTMon_sensors_deinit(); + DHTMon_free(); + + return 0; +} +//TODO: ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок +//TODO: ΠŸΡ€ΠΎΠΏΡƒΡΠΊ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½Π½Ρ‹Ρ… ΠΏΠΎΡ€Ρ‚ΠΎΠ² Π² мСню добавлСния Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/quenon_dht_mon.h b/applications/plugins/dht_temp_sensor/quenon_dht_mon.h new file mode 100644 index 000000000..4e888f6c1 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/quenon_dht_mon.h @@ -0,0 +1,176 @@ +#ifndef QUENON_DHT_MON +#define QUENON_DHT_MON + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "DHT.h" + +#define APP_NAME "DHT_monitor" +#define APP_PATH_FOLDER "/ext/dht_monitor" +#define APP_FILENAME "sensors.txt" +#define MAX_SENSORS 5 + +// //Π’ΠΈΠ΄Ρ‹ мСнюшСк +typedef enum { + MAIN_MENU_VIEW, + ADDSENSOR_MENU_VIEW, + TEXTINPUT_VIEW, + SENSOR_ACTIONS_VIEW, + WIDGET_VIEW, +} MENU_VIEWS; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + const uint8_t num; + const char* name; + const GpioPin* pin; +} GpioItem; + +//Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° с Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΠΏΠ»Π°Π³ΠΈΠ½Π° +typedef struct { + //ΠžΡ‡Π΅Ρ€Π΅Π΄ΡŒ сообщСний + FuriMessageQueue* event_queue; + //ΠœΡƒΡ‚Π΅ΠΊΡ + ValueMutex state_mutex; + //Π’ΡŒΡŽΠΏΠΎΡ€Ρ‚ + ViewPort* view_port; + //GUI + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + View* view; + TextInput* text_input; + VariableItem* item; + Widget* widget; + + char txtbuff[30]; //Π‘ΡƒΡ„Ρ„Π΅Ρ€ для ΠΏΠ΅Ρ‡Π°Ρ‚ΠΈ строк Π½Π° экранС + bool last_OTG_State; //БостояниС OTG Π΄ΠΎ запуска прилоТСния + Storage* storage; //Π₯Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + Stream* file_stream; //ΠŸΠΎΡ‚ΠΎΠΊ Ρ„Π°ΠΉΠ»Π° с Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ°ΠΌΠΈ + int8_t sensors_count; // ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹Ρ… Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + DHT_sensor sensors[MAX_SENSORS]; //Π‘ΠΎΡ…Ρ€Π°Π½Ρ‘Π½Π½Ρ‹Π΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΈ + DHT_data data; //Π˜Π½Ρ„Π° ΠΈΠ· Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + DHT_sensor* currentSensorEdit; //Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΡƒΠ΅ΠΌΡ‹ΠΉ Π΄Π°Ρ‚Ρ‡ΠΈΠΊ + +} PluginData; + +/* ================== Π Π°Π±ΠΎΡ‚Π° с GPIO ================== */ +/** + * @brief ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡ GPIO Π² Π΅Π³ΠΎ Π½ΠΎΠΌΠ΅Ρ€ Π½Π° корпусС FZ + * + * @param gpio Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Ρ‹Π²Π°Π΅ΠΌΡ‹ΠΉ GPIO + * @return НомСр ΠΏΠΎΡ€Ρ‚Π° Π½Π° корпусС FZ + */ +uint8_t DHTMon_GPIO_to_int(const GpioPin* gpio); +/** + * @brief ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡ Π½ΠΎΠΌΠ΅Ρ€Π° ΠΏΠΎΡ€Ρ‚Π° Π½Π° корпусС FZ Π² GPIO + * + * @param name НомСр ΠΏΠΎΡ€Ρ‚Π° Π½Π° корпусС FZ + * @return Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° GPIO ΠΏΡ€ΠΈ успСхС, NULL ΠΏΡ€ΠΈ ошибкС + */ +const GpioPin* DHTMon_GPIO_form_int(uint8_t name); +/** + * @brief ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ порядкового Π½ΠΎΠΌΠ΅Ρ€Π° ΠΏΠΎΡ€Ρ‚Π° Π² GPIO + * + * @param index ИндСкс ΠΏΠΎΡ€Ρ‚Π° ΠΎΡ‚ 0 Π΄ΠΎ GPIO_ITEMS-1 + * @return Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° GPIO ΠΏΡ€ΠΈ успСхС, NULL ΠΏΡ€ΠΈ ошибкС + */ +const GpioPin* DHTMon_GPIO_from_index(uint8_t index); +/** + * @brief ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ GPIO Π² порядковый Π½ΠΎΠΌΠ΅Ρ€ ΠΏΠΎΡ€Ρ‚Π° + * + * @param gpio Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° GPIO + * @return index ΠΏΡ€ΠΈ успСхС, 255 ΠΏΡ€ΠΈ ошибкС + */ +uint8_t DHTMon_GPIO_to_index(const GpioPin* gpio); + +/** + * @brief ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ имя GPIO Π² Π²ΠΈΠ΄Π΅ строки + * + * @param gpio Π˜ΡΠΊΠΎΠΌΡ‹ΠΉ ΠΏΠΎΡ€Ρ‚ + * @return char* Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° строку с ΠΈΠΌΠ΅Π½Π΅ΠΌ ΠΏΠΎΡ€Ρ‚Π° + */ +const char* DHTMon_GPIO_getName(const GpioPin* gpio); + +/* ================== Π Π°Π±ΠΎΡ‚Π° с Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ°ΠΌΠΈ ================== */ +/** + * @brief Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ ΠΏΠΎΡ€Ρ‚ΠΎΠ² Π²Π²ΠΎΠ΄Π°/Π²Ρ‹Π²ΠΎΠ΄Π° Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + */ +void DHTMon_sensors_init(void); +/** + * @brief Ѐункция Π΄Π΅ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΏΠΎΡ€Ρ‚ΠΎΠ² Π²Π²ΠΎΠ΄Π°/Π²Ρ‹Π²ΠΎΠ΄Π° Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + */ +void DHTMon_sensors_deinit(void); +/** + * @brief ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° коррСктности ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + * + * @param sensor Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° провСряСмый Π΄Π°Ρ‚Ρ‡ΠΈΠΊ + * @return true ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹Π΅ + * @return false ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° Π½Π΅ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹Π΅ + */ +bool DHTMon_sensor_check(DHT_sensor* sensor); +/** + * @brief Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° ΠΈΠ· списка ΠΈ ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° + * + * @param sensor Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° удаляСмый Π΄Π°Ρ‚Ρ‡ΠΈΠΊ + */ +void DHTMon_sensor_delete(DHT_sensor* sensor); +/** + * @brief Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² Π½Π° SD-ΠΊΠ°Ρ€Ρ‚Ρƒ + * + * @return ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ сохранённых Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² + */ +uint8_t DHTMon_sensors_save(void); +/** + * @brief Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² с SD-ΠΊΠ°Ρ€Ρ‚Ρ‹ + * + * @return true Π‘Ρ‹Π» Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½ хотя Π±Ρ‹ 1 Π΄Π°Ρ‚Ρ‡ΠΈΠΊ + * @return false Π”Π°Ρ‚Ρ‡ΠΈΠΊΠΈ ΠΎΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΡƒΡŽΡ‚ + */ +bool DHTMon_sensors_load(void); +/** + * @brief ΠŸΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² с SD-ΠΊΠ°Ρ€Ρ‚Ρ‹ + * + * @return true Когда Π±Ρ‹Π» Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½ хотя Π±Ρ‹ 1 Π΄Π°Ρ‚Ρ‡ΠΈΠΊ + * @return false Ни ΠΎΠ΄ΠΈΠ½ ΠΈΠ· Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² Π½Π΅ Π±Ρ‹Π» Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½ + */ +bool DHTMon_sensors_reload(void); + +void scene_main(Canvas* const canvas, PluginData* app); +void mainMenu_scene(PluginData* app); + +void sensorEdit_sceneCreate(PluginData* app); +void sensorEdit_scene(PluginData* app); +void sensorEdit_sceneRemove(void); + +void sensorActions_sceneCreate(PluginData* app); +void sensorActions_scene(PluginData* app); +void sensorActions_screneRemove(void); +#endif \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/scenes/DHTMon_mainMenu_scene.c b/applications/plugins/dht_temp_sensor/scenes/DHTMon_mainMenu_scene.c new file mode 100644 index 000000000..26ac9ca89 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/scenes/DHTMon_mainMenu_scene.c @@ -0,0 +1,157 @@ +#include "../quenon_dht_mon.h" +//Π’Π΅ΠΊΡƒΡ‰ΠΈΠΉ Π²ΠΈΠ΄ +static View* view; +//Бписок +static VariableItemList* variable_item_list; + +/** + * @brief Ѐункция ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ "Назад" + * + * @param context Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ прилоТСния + * @return ID Π²ΠΈΠ΄Π° Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΡΡ + */ +static uint32_t actions_exitCallback(void* context) { + PluginData* app = context; + UNUSED(app); + //Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ ID Π²ΠΈΠ΄Π°, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒΡΡ + return VIEW_NONE; +} +/** + * @brief Ѐункция ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия срСднСй ΠΊΠ½ΠΎΠΏΠΊΠΈ + * + * @param context Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ прилоТСния + * @param index На ΠΊΠ°ΠΊΠΎΠΌ элСмСнтС списка Π±Ρ‹Π»Π° Π½Π°ΠΆΠ°Ρ‚Π° ΠΊΠ½ΠΎΠΏΠΊΠ° + */ +static void enterCallback(void* context, uint32_t index) { + PluginData* app = context; + if((uint8_t)index < (uint8_t)app->sensors_count) { + app->currentSensorEdit = &app->sensors[index]; + sensorActions_scene(app); + } + if((uint8_t)index == (uint8_t)app->sensors_count) { + app->currentSensorEdit = &app->sensors[app->sensors_count++]; + strcpy(app->currentSensorEdit->name, "NewSensor"); + app->currentSensorEdit->GPIO = DHTMon_GPIO_from_index(0); + app->currentSensorEdit->type = DHT11; + sensorEdit_scene(app); + } +} + +/** + * @brief Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ списка дСйствий с ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΌ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠΌ + * + * @param app Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + */ +void mainMenu_scene(PluginData* app) { + variable_item_list = variable_item_list_alloc(); + //Бброс всСх элСмСнтов мСню + variable_item_list_reset(variable_item_list); + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠΉ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠ² Π² качСствС элСмСнтов списка + for(uint8_t i = 0; i < app->sensors_count; i++) { + variable_item_list_add(variable_item_list, app->sensors[i].name, 1, NULL, NULL); + } + if(app->sensors_count < (uint8_t)MAX_SENSORS) { + variable_item_list_add(variable_item_list, " + Add new sensor +", 1, NULL, NULL); + } + + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ»Π±Π΅ΠΊΠ° Π½Π° Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ срСднСй ΠΊΠ½ΠΎΠΏΠΊΠΈ + variable_item_list_set_enter_callback(variable_item_list, enterCallback, app); + + //Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π²ΠΈΠ΄Π° ΠΈΠ· списка + view = variable_item_list_get_view(variable_item_list); + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ»Π±Π΅ΠΊΠ° Π½Π° Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ ΠΊΠ½ΠΎΠΏΠΊΠΈ "Назад" + view_set_previous_callback(view, actions_exitCallback); + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π²ΠΈΠ΄Π° Π² диспСтчСр + view_dispatcher_add_view(app->view_dispatcher, MAIN_MENU_VIEW, view); + + view_dispatcher_enable_queue(app->view_dispatcher); + + //ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° наш Π²ΠΈΠ΄ + view_dispatcher_switch_to_view(app->view_dispatcher, MAIN_MENU_VIEW); + + //Запуск диспСтчСра + view_dispatcher_run(app->view_dispatcher); + + //ΠžΡ‡ΠΈΡΡ‚ΠΊΠ° списка элСмСнтов + variable_item_list_free(variable_item_list); + //Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ Π²ΠΈΠ΄Π° послС ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ + view_dispatcher_remove_view(app->view_dispatcher, MAIN_MENU_VIEW); +} + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*/ + +// static VariableItemList* variable_item_list; +// /* ============== Π“Π»Π°Π²Π½ΠΎΠ΅ мСню ============== */ +// static uint32_t mainMenu_exitCallback(void* context) { +// UNUSED(context); +// variable_item_list_free(variable_item_list); +// DHT_sensors_reload(); +// return VIEW_NONE; +// } +// static void mainMenu_enterCallback(void* context, uint32_t index) { +// PluginData* app = context; +// if((uint8_t)index == (uint8_t)app->sensors_count) { +// addSensor_scene(app); +// view_dispatcher_run(app->view_dispatcher); +// } +// } +// void mainMenu_scene(PluginData* app) { +// variable_item_list = variable_item_list_alloc(); +// variable_item_list_reset(variable_item_list); +// for(uint8_t i = 0; i < app->sensors_count; i++) { +// variable_item_list_add(variable_item_list, app->sensors[i].name, 1, NULL, NULL); +// } +// variable_item_list_add(variable_item_list, "+ Add new sensor +", 1, NULL, NULL); + +// app->view = variable_item_list_get_view(variable_item_list); +// app->view_dispatcher = view_dispatcher_alloc(); + +// view_dispatcher_enable_queue(app->view_dispatcher); +// view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); +// view_dispatcher_add_view(app->view_dispatcher, MAIN_MENU_VIEW, app->view); +// view_dispatcher_switch_to_view(app->view_dispatcher, MAIN_MENU_VIEW); + +// variable_item_list_set_enter_callback(variable_item_list, mainMenu_enterCallback, app); +// view_set_previous_callback(app->view, mainMenu_exitCallback); +// } \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/scenes/DHTMon_main_scene.c b/applications/plugins/dht_temp_sensor/scenes/DHTMon_main_scene.c new file mode 100644 index 000000000..aab343752 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/scenes/DHTMon_main_scene.c @@ -0,0 +1,40 @@ +#include "../quenon_dht_mon.h" + +/* ============== Π“Π»Π°Π²Π½Ρ‹ΠΉ экран ============== */ +void scene_main(Canvas* const canvas, PluginData* app) { + //РисованиС Π±Π°Ρ€Π° + canvas_draw_box(canvas, 0, 0, 128, 14); + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 32, 11, "DHT Monitor"); + + canvas_set_color(canvas, ColorBlack); + if(app->sensors_count > 0) { + if(!furi_hal_power_is_otg_enabled()) { + furi_hal_power_enable_otg(); + } + for(uint8_t i = 0; i < app->sensors_count; i++) { + app->data = DHT_getData(&app->sensors[i]); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 24 + 10 * i, app->sensors[i].name); + + canvas_set_font(canvas, FontSecondary); + if(app->data.hum == -128.0f && app->data.temp == -128.0f) { + canvas_draw_str(canvas, 96, 24 + 10 * i, "timeout"); + } else { + snprintf( + app->txtbuff, + sizeof(app->txtbuff), + "%2.1f*C/%d%%", + (double)app->data.temp, + (int8_t)app->data.hum); + canvas_draw_str(canvas, 64, 24 + 10 * i, app->txtbuff); + } + } + } else { + canvas_set_font(canvas, FontSecondary); + if(app->sensors_count == 0) canvas_draw_str(canvas, 0, 24, "Sensors not found"); + if(app->sensors_count == -1) canvas_draw_str(canvas, 0, 24, "Loading..."); + } +} diff --git a/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorActions_scene.c b/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorActions_scene.c new file mode 100644 index 000000000..ae7674f70 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorActions_scene.c @@ -0,0 +1,194 @@ +#include "../quenon_dht_mon.h" + +//Π’Π΅ΠΊΡƒΡ‰ΠΈΠΉ Π²ΠΈΠ΄ +static View* view; +//Бписок +static VariableItemList* variable_item_list; + +/* ================== Π˜Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ ΠΎ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ΅ ================== */ +/** + * @brief Ѐункция ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ "Назад" + * + * @param context Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ прилоТСния + * @return ID Π²ΠΈΠ΄Π° Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΡΡ + */ +static uint32_t infoWidget_exitCallback(void* context) { + PluginData* app = context; + UNUSED(app); + //Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ ID Π²ΠΈΠ΄Π°, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒΡΡ + return SENSOR_ACTIONS_VIEW; +} +/** + * @brief ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Π½Π°ΠΆΠ°Ρ‚ΠΈΠΉ Π½Π° ΠΊΠ½ΠΎΠΏΠΊΡƒ Π² Π²ΠΈΠ΄ΠΆΠ΅Ρ‚Π΅ + * + * @param result Какая ΠΈΠ· ΠΊΠ½ΠΎΠΏΠΎΠΊ Π±Ρ‹Π»Π° Π½Π°ΠΆΠ°Ρ‚Π° + * @param type Π’ΠΈΠΏ наТатия + * @param context Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + */ +static void infoWidget_callback(GuiButtonType result, InputType type, void* context) { + PluginData* app = context; + //ΠšΠΎΡ€ΠΎΡ‚ΠΊΠΎ Π½Π°ΠΆΠ°Ρ‚Π° лСвая ΠΊΠ½ΠΎΠΏΠΊΠ° (Back) + if(result == GuiButtonTypeLeft && type == InputTypeShort) { + view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW); + } +} +/** + * @brief Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π²ΠΈΠ΄ΠΆΠ΅Ρ‚Π° ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ΅ + * + * @param app Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + */ +static void sensorInfo_widget(PluginData* app) { + //ΠžΡ‡ΠΈΡΡ‚ΠΊΠ° Π²ΠΈΠ΄ΠΆΠ΅Ρ‚Π° + widget_reset(app->widget); + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠ½ΠΎΠΏΠΎΠΊ + widget_add_button_element(app->widget, GuiButtonTypeLeft, "Back", infoWidget_callback, app); + + char str[32]; + snprintf(str, sizeof(str), "\e#%s\e#", app->currentSensorEdit->name); + widget_add_text_box_element(app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, str, false); + snprintf(str, sizeof(str), "\e#Type:\e# %s", app->currentSensorEdit->type ? "DHT22" : "DHT11"); + widget_add_text_box_element(app->widget, 0, 0, 128, 47, AlignLeft, AlignCenter, str, false); + snprintf( + str, sizeof(str), "\e#GPIO:\e# %s", DHTMon_GPIO_getName(app->currentSensorEdit->GPIO)); + widget_add_text_box_element(app->widget, 0, 0, 128, 72, AlignLeft, AlignCenter, str, false); + view_set_previous_callback(widget_get_view(app->widget), infoWidget_exitCallback); + view_dispatcher_switch_to_view(app->view_dispatcher, WIDGET_VIEW); +} + +/* ================== ΠŸΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ удалСния ================== */ +/** + * @brief Ѐункция ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ "Назад" + * + * @param context Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ прилоТСния + * @return ID Π²ΠΈΠ΄Π° Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΡΡ + */ +static uint32_t deleteWidget_exitCallback(void* context) { + PluginData* app = context; + UNUSED(app); + //Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ ID Π²ΠΈΠ΄Π°, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒΡΡ + return SENSOR_ACTIONS_VIEW; +} +/** + * @brief ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Π½Π°ΠΆΠ°Ρ‚ΠΈΠΉ Π½Π° ΠΊΠ½ΠΎΠΏΠΊΡƒ Π² Π²ΠΈΠ΄ΠΆΠ΅Ρ‚Π΅ + * + * @param result Какая ΠΈΠ· ΠΊΠ½ΠΎΠΏΠΎΠΊ Π±Ρ‹Π»Π° Π½Π°ΠΆΠ°Ρ‚Π° + * @param type Π’ΠΈΠΏ наТатия + * @param context Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + */ +static void deleteWidget_callback(GuiButtonType result, InputType type, void* context) { + PluginData* app = context; + //ΠšΠΎΡ€ΠΎΡ‚ΠΊΠΎ Π½Π°ΠΆΠ°Ρ‚Π° лСвая ΠΊΠ½ΠΎΠΏΠΊΠ° (Cancel) + if(result == GuiButtonTypeLeft && type == InputTypeShort) { + view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW); + } + //ΠšΠΎΡ€ΠΎΡ‚ΠΊΠΎ Π½Π°ΠΆΠ°Ρ‚Π° правая ΠΊΠ½ΠΎΠΏΠΊΠ° (Delete) + if(result == GuiButtonTypeRight && type == InputTypeShort) { + //Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + DHTMon_sensor_delete(app->currentSensorEdit); + //Π’Ρ‹Ρ…ΠΎΠ΄ ΠΈΠ· мСню + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); + } +} +/** + * @brief Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π²ΠΈΠ΄ΠΆΠ΅Ρ‚Π° удалСния Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + * + * @param app Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + */ +static void sensorDelete_widget(PluginData* app) { + //ΠžΡ‡ΠΈΡΡ‚ΠΊΠ° Π²ΠΈΠ΄ΠΆΠ΅Ρ‚Π° + widget_reset(app->widget); + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠ½ΠΎΠΏΠΎΠΊ + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Cancel", deleteWidget_callback, app); + widget_add_button_element( + app->widget, GuiButtonTypeRight, "Delete", deleteWidget_callback, app); + + char delete_str[32]; + snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", app->currentSensorEdit->name); + widget_add_text_box_element( + app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); + snprintf( + delete_str, + sizeof(delete_str), + "\e#Type:\e# %s", + app->currentSensorEdit->type ? "DHT22" : "DHT11"); + widget_add_text_box_element( + app->widget, 0, 0, 128, 47, AlignLeft, AlignCenter, delete_str, false); + snprintf( + delete_str, + sizeof(delete_str), + "\e#GPIO:\e# %s", + DHTMon_GPIO_getName(app->currentSensorEdit->GPIO)); + widget_add_text_box_element( + app->widget, 0, 0, 128, 72, AlignLeft, AlignCenter, delete_str, false); + view_set_previous_callback(widget_get_view(app->widget), deleteWidget_exitCallback); + view_dispatcher_switch_to_view(app->view_dispatcher, WIDGET_VIEW); +} + +/* ================== МСню дСйствий ================== */ +/** + * @brief Ѐункция ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия срСднСй ΠΊΠ½ΠΎΠΏΠΊΠΈ + * + * @param context Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ прилоТСния + * @param index На ΠΊΠ°ΠΊΠΎΠΌ элСмСнтС списка Π±Ρ‹Π»Π° Π½Π°ΠΆΠ°Ρ‚Π° ΠΊΠ½ΠΎΠΏΠΊΠ° + */ +static void enterCallback(void* context, uint32_t index) { + PluginData* app = context; + if(index == 0) { + sensorInfo_widget(app); + } + if(index == 1) { + sensorEdit_scene(app); + } + if(index == 2) { + sensorDelete_widget(app); + } +} + +/** + * @brief Ѐункция ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ наТатия ΠΊΠ½ΠΎΠΏΠΊΠΈ "Назад" + * + * @param context Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ прилоТСния + * @return ID Π²ΠΈΠ΄Π° Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΡΡ + */ +static uint32_t actions_exitCallback(void* context) { + PluginData* app = context; + UNUSED(app); + //Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ ID Π²ΠΈΠ΄Π°, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½ΡƒΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒΡΡ + return MAIN_MENU_VIEW; +} + +/** + * @brief Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ списка дСйствий с ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΌ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠΌ + * + * @param app Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠ»Π°Π³ΠΈΠ½Π° + */ +void sensorActions_sceneCreate(PluginData* app) { + variable_item_list = variable_item_list_alloc(); + //Бброс всСх элСмСнтов мСню + variable_item_list_reset(variable_item_list); + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ элСмСнтов Π² список + variable_item_list_add(variable_item_list, "Info", 0, NULL, NULL); + variable_item_list_add(variable_item_list, "Edit", 0, NULL, NULL); + variable_item_list_add(variable_item_list, "Delete", 0, NULL, NULL); + + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ»Π±Π΅ΠΊΠ° Π½Π° Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ срСднСй ΠΊΠ½ΠΎΠΏΠΊΠΈ + variable_item_list_set_enter_callback(variable_item_list, enterCallback, app); + + //Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π²ΠΈΠ΄Π° ΠΈΠ· списка + view = variable_item_list_get_view(variable_item_list); + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΠ»Π±Π΅ΠΊΠ° Π½Π° Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ ΠΊΠ½ΠΎΠΏΠΊΠΈ "Назад" + view_set_previous_callback(view, actions_exitCallback); + //Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π²ΠΈΠ΄Π° Π² диспСтчСр + view_dispatcher_add_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW, view); +} +void sensorActions_scene(PluginData* app) { + //Бброс Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΡƒΠ½ΠΊΡ‚Π° Π² ноль + variable_item_list_set_selected_item(variable_item_list, 0); + //ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° наш Π²ΠΈΠ΄ + view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW); +} + +void sensorActions_screneRemove(void) { + variable_item_list_free(variable_item_list); +} diff --git a/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorEdit_scene.c b/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorEdit_scene.c new file mode 100644 index 000000000..5decac3d1 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorEdit_scene.c @@ -0,0 +1,103 @@ +#include "../quenon_dht_mon.h" + +static VariableItem* nameItem; +static VariableItemList* variable_item_list; + +static const char* const sensorsTypes[2] = { + "DHT11", + "DHT22", +}; + +// /* ============== Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° ============== */ +static uint32_t addSensor_exitCallback(void* context) { + UNUSED(context); + DHTMon_sensors_reload(); + return VIEW_NONE; +} + +static void addSensor_sensorTypeChanged(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + PluginData* app = variable_item_get_context(item); + variable_item_set_current_value_text(item, sensorsTypes[index]); + app->currentSensorEdit->type = index; +} + +static void addSensor_GPIOChanged(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, DHTMon_GPIO_getName(DHTMon_GPIO_from_index(index))); + PluginData* app = variable_item_get_context(item); + app->currentSensorEdit->GPIO = DHTMon_GPIO_from_index(index); +} + +static void addSensor_sensorNameChanged(void* context) { + PluginData* app = context; + variable_item_set_current_value_text(nameItem, app->currentSensorEdit->name); + view_dispatcher_switch_to_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW); +} +static void addSensor_sensorNameChange(PluginData* app) { + text_input_set_header_text(app->text_input, "Sensor name"); + //По нСясной ΠΌΠ½Π΅ ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π΅ Π² Π΄Π»ΠΈΠ½Ρƒ строки Π²Ρ…ΠΎΠ΄ΠΈΡ‚ Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Ρ‚ΠΎΡ€. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ ΠΏΡ€ΠΈ Π΄Π»ΠΈΠ½Π΅ 10 приходится ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ 11 + text_input_set_result_callback( + app->text_input, addSensor_sensorNameChanged, app, app->currentSensorEdit->name, 11, true); + view_dispatcher_switch_to_view(app->view_dispatcher, TEXTINPUT_VIEW); +} + +static void addSensor_enterCallback(void* context, uint32_t index) { + PluginData* app = context; + if(index == 0) { + addSensor_sensorNameChange(app); + } + if(index == 3) { + //Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + DHTMon_sensors_save(); + DHTMon_sensors_reload(); + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); + } +} + +void sensorEdit_sceneCreate(PluginData* app) { + variable_item_list = variable_item_list_alloc(); + + variable_item_list_reset(variable_item_list); + + variable_item_list_set_enter_callback(variable_item_list, addSensor_enterCallback, app); + + app->view = variable_item_list_get_view(variable_item_list); + + view_set_previous_callback(app->view, addSensor_exitCallback); + + view_dispatcher_add_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW, app->view); +} +void sensorEdit_scene(PluginData* app) { + //ΠžΡ‡ΠΈΡΡ‚ΠΊΠ° списка + variable_item_list_reset(variable_item_list); + + //Имя Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΡƒΠ΅ΠΌΠΎΠ³ΠΎ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + nameItem = variable_item_list_add(variable_item_list, "Name: ", 1, NULL, NULL); + variable_item_set_current_value_index(nameItem, 0); + variable_item_set_current_value_text(nameItem, app->currentSensorEdit->name); + + //Π’ΠΈΠΏ Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° + app->item = + variable_item_list_add(variable_item_list, "Type:", 2, addSensor_sensorTypeChanged, app); + + variable_item_set_current_value_index(app->item, app->currentSensorEdit->type); + variable_item_set_current_value_text(app->item, sensorsTypes[app->currentSensorEdit->type]); + + //GPIO + app->item = + variable_item_list_add(variable_item_list, "GPIO:", 13, addSensor_GPIOChanged, app); + variable_item_set_current_value_index( + app->item, DHTMon_GPIO_to_index(app->currentSensorEdit->GPIO)); + variable_item_set_current_value_text( + app->item, DHTMon_GPIO_getName(app->currentSensorEdit->GPIO)); + variable_item_list_add(variable_item_list, "Save", 1, NULL, app); + + //Бброс Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΡƒΠ½ΠΊΡ‚Π° Π² ноль + variable_item_list_set_selected_item(variable_item_list, 0); + + view_dispatcher_switch_to_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW); +} +void sensorEdit_sceneRemove(void) { + variable_item_list_free(variable_item_list); +} \ No newline at end of file diff --git a/applications/plugins/dice/dice.c b/applications/plugins/dice/dice.c index 0b1e33488..84f510fa3 100644 --- a/applications/plugins/dice/dice.c +++ b/applications/plugins/dice/dice.c @@ -30,7 +30,7 @@ typedef struct { uint8_t playerOneScore; uint8_t playerTwoScore; char rollTime[1][15]; - char diceType[1][8]; + char diceType[1][11]; char strings[5][45]; char theScores[1][45]; bool letsRoll; @@ -52,7 +52,7 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) { } canvas_set_font(canvas, FontSecondary); - if(state->diceSelect < 229) { + if(state->diceSelect < 220) { if(state->diceQty == 1) { elements_button_left(canvas, "x1"); } else if(state->diceQty == 2) { @@ -68,11 +68,6 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) { } } if(state->letsRoll) { - static bool rand_generator_inited = false; - if(!rand_generator_inited) { - srand(furi_get_tick()); - rand_generator_inited = true; - } furi_hal_rtc_get_datetime(&state->datetime); uint8_t hour = state->datetime.hour; char strAMPM[3]; @@ -122,6 +117,39 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) { state->rollTime[0]); uint8_t d1_i = rand() % COUNT_OF(eightBall); snprintf(state->strings[1], sizeof(state->strings[1]), "%s", eightBall[d1_i]); + } else if(state->diceSelect == 228) { + const char* eightBall[] = { + "I'd do it.", + "Hell, yeah!", + "You bet your life!", + "What are you waiting for?", + "You could do worse things.", + "Sure, I won't tell.", + "Yeah, you got this. Would I lie to you?", + "Looks like fun to me. ", + "Yeah, sure, why not?", + "DO IT!!!", + "Who's it gonna hurt?", + "Can you blame someone else?", + "Ask me again later.", + "Maybe, maybe not, I can't tell right now. ", + "Are you the betting type? ", + "Don't blame me if you get caught.", + "What have you got to lose?", + "I wouldn't if I were you.", + "My money's on the snowball.", + "Oh Hell no!"}; + state->diceRoll = + ((rand() % state->diceSelect) + 1); // JUST TO GET IT GOING? AND FIX BUG + snprintf(state->diceType[0], sizeof(state->diceType[0]), "%s", "Devil Ball"); + snprintf( + state->strings[0], + sizeof(state->strings[0]), + "%s at %s", + state->diceType[0], + state->rollTime[0]); + uint8_t d1_i = rand() % COUNT_OF(eightBall); + snprintf(state->strings[1], sizeof(state->strings[1]), "%s", eightBall[d1_i]); } else if(state->diceSelect == 230) { const char* diceOne[] = { "Nibble", @@ -328,6 +356,11 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignCenter, state->strings[2]); canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignCenter, state->strings[3]); canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignCenter, state->strings[4]); + } else if(state->diceSelect == 228 || state->diceSelect == 229) { + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, state->strings[1]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, state->strings[0]); } else { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, state->strings[1]); @@ -346,7 +379,7 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) { canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignCenter, state->theScores[0]); } } - if(state->diceSelect == 229) { + if(state->diceSelect == 229 || state->diceSelect == 228) { elements_button_center(canvas, "Shake"); } else if(state->diceSelect == 231) { elements_button_center(canvas, "Draw"); @@ -377,6 +410,8 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) { elements_button_right(canvas, "d100"); } else if(state->diceSelect == 229) { elements_button_right(canvas, "8BALL"); + } else if(state->diceSelect == 228) { + elements_button_right(canvas, "DBALL"); } else if(state->diceSelect == 230) { elements_button_right(canvas, "SEX"); } else if(state->diceSelect == 231) { @@ -487,6 +522,8 @@ int32_t dice_app(void* p) { } else if(plugin_state->diceSelect == 231) { plugin_state->diceSelect = 229; } else if(plugin_state->diceSelect == 229) { + plugin_state->diceSelect = 228; + } else if(plugin_state->diceSelect == 228) { if(plugin_state->desktop_settings->is_dumbmode) { plugin_state->diceSelect = 59; } else { @@ -513,6 +550,8 @@ int32_t dice_app(void* p) { case InputKeyBack: processing = false; break; + default: + break; } } } else if(event.type == EventTypeTick) { diff --git a/applications/plugins/doom/doom.c b/applications/plugins/doom/doom.c index 7e64acf81..78a06055c 100644 --- a/applications/plugins/doom/doom.c +++ b/applications/plugins/doom/doom.c @@ -155,6 +155,9 @@ void spawnEntity(uint8_t type, uint8_t x, uint8_t y, PluginState* const plugin_s plugin_state->entity[plugin_state->num_entities] = create_medikit(x, y); plugin_state->num_entities++; break; + + default: + break; } } @@ -458,6 +461,9 @@ void updateEntities(const uint8_t level[], Canvas* const canvas, PluginState* co } break; } + + default: + break; } i++; diff --git a/applications/plugins/dtmf_dolphin/README.md b/applications/plugins/dtmf_dolphin/README.md index 68d123616..5c9561f4b 100644 --- a/applications/plugins/dtmf_dolphin/README.md +++ b/applications/plugins/dtmf_dolphin/README.md @@ -2,8 +2,15 @@ ## DTMF Dolphin -DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and future Redbox. +DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox. -Now in a release-ready state for both Dialer and Bluebox functionality. Redbox functionality awaits some changes for modulation. +Now in a release-ready state for both Dialer, Bluebox, and Redbox (US/UK) functionality! Please note that using the current tone output method, the 2600 tone is scaled about 33 Hz higher than it should be. This is a limitation of the current sample rate. + +### Educational Links: + +* http://www.phrack.org/issues/25/7.html#article +* https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling +* https://en.wikipedia.org/wiki/Blue_box +* https://en.wikipedia.org/wiki/Red_box_(phreaking) diff --git a/applications/plugins/dtmf_dolphin/application.fam b/applications/plugins/dtmf_dolphin/application.fam index 90f01ef87..0727f5f52 100644 --- a/applications/plugins/dtmf_dolphin/application.fam +++ b/applications/plugins/dtmf_dolphin/application.fam @@ -1,15 +1,16 @@ App( - appid="DTMF_Dolphin", + appid="dtmf_dolphin", name="DTMF Dolphin", apptype=FlipperAppType.EXTERNAL, entry_point="dtmf_dolphin_app", cdefines=["DTMF_DOLPHIN"], requires=[ + "storage", "gui", "dialogs", ], + fap_icon="phone.png", stack_size=8 * 1024, order=20, - fap_icon="dtmf_10px.png", - fap_category="Misc", + fap_category="Tools", ) diff --git a/applications/plugins/dtmf_dolphin/assets/dialer.jpg b/applications/plugins/dtmf_dolphin/assets/dialer.jpg index 4d86df33f..ff6fad7a8 100644 Binary files a/applications/plugins/dtmf_dolphin/assets/dialer.jpg and b/applications/plugins/dtmf_dolphin/assets/dialer.jpg differ diff --git a/applications/plugins/dtmf_dolphin/dtmf_10px.png b/applications/plugins/dtmf_dolphin/dtmf_10px.png deleted file mode 100644 index 2ca0be4ff..000000000 Binary files a/applications/plugins/dtmf_dolphin/dtmf_10px.png and /dev/null differ diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin.c b/applications/plugins/dtmf_dolphin/dtmf_dolphin.c index 2ff8ce0ba..c1b10defa 100644 --- a/applications/plugins/dtmf_dolphin/dtmf_dolphin.c +++ b/applications/plugins/dtmf_dolphin/dtmf_dolphin.c @@ -67,15 +67,10 @@ static void app_free(DTMFDolphinApp* app) { variable_item_list_free(app->main_menu_list); dtmf_dolphin_dialer_free(app->dtmf_dolphin_dialer); - // widget_free(app->dtmf_dolphin_play); view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); - // button_panel_free(app->dialer_button_panel); - // button_panel_free(app->bluebox_button_panel); - // button_panel_free(app->redbox_button_panel); - notification_message(app->notification, &sequence_display_backlight_enforce_auto); furi_record_close(RECORD_GUI); diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.c b/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.c index e7778b328..4b84ceb97 100644 --- a/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.c +++ b/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.c @@ -35,6 +35,15 @@ DTMFDolphinOsc* dtmf_dolphin_osc_alloc() { return osc; } +DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() { + DTMFDolphinPulseFilter* pf = malloc(sizeof(DTMFDolphinPulseFilter)); + pf->duration = 0; + pf->period = 0; + pf->offset = 0; + pf->lookup_table = NULL; + return pf; +} + DTMFDolphinAudio* dtmf_dolphin_audio_alloc() { DTMFDolphinAudio* player = malloc(sizeof(DTMFDolphinAudio)); player->buffer_length = SAMPLE_BUFFER_LENGTH; @@ -44,6 +53,8 @@ DTMFDolphinAudio* dtmf_dolphin_audio_alloc() { player->osc2 = dtmf_dolphin_osc_alloc(); player->volume = 1.0f; player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent)); + player->filter = dtmf_dolphin_pulse_filter_alloc(); + player->playing = false; dtmf_dolphin_audio_clear_samples(player); return player; @@ -80,6 +91,32 @@ void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) { } } +void filter_generate_lookup_table( + DTMFDolphinPulseFilter* pf, + uint16_t pulses, + uint16_t pulse_ms, + uint16_t gap_ms) { + if(pf->lookup_table != NULL) { + free(pf->lookup_table); + } + pf->offset = 0; + + uint16_t gap_period = calc_waveform_period(1000 / (float)gap_ms); + uint16_t pulse_period = calc_waveform_period(1000 / (float)pulse_ms); + pf->period = pulse_period + gap_period; + + if(!pf->period) { + pf->lookup_table = NULL; + return; + } + pf->duration = pf->period * pulses; + pf->lookup_table = malloc(sizeof(bool) * pf->duration); + + for(size_t i = 0; i < pf->duration; i++) { + pf->lookup_table[i] = i % pf->period < pulse_period; + } +} + float sample_frame(DTMFDolphinOsc* osc) { float frame = 0.0; @@ -91,13 +128,19 @@ float sample_frame(DTMFDolphinOsc* osc) { return frame; } -void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) { - furi_message_queue_free(player->queue); - dtmf_dolphin_osc_free(player->osc1); - dtmf_dolphin_osc_free(player->osc2); - free(player->sample_buffer); - free(player); - current_player = NULL; +bool sample_filter(DTMFDolphinPulseFilter* pf) { + bool frame = true; + + if(pf->duration) { + if(pf->offset < pf->duration) { + frame = pf->lookup_table[pf->offset]; + pf->offset = pf->offset + 1; + } else { + frame = false; + } + } + + return frame; } void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) { @@ -107,6 +150,23 @@ void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) { free(osc); } +void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) { + if(pf->lookup_table != NULL) { + free(pf->lookup_table); + } + free(pf); +} + +void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) { + furi_message_queue_free(player->queue); + dtmf_dolphin_osc_free(player->osc1); + dtmf_dolphin_osc_free(player->osc2); + dtmf_dolphin_filter_free(player->filter); + free(player->sample_buffer); + free(player); + current_player = NULL; +} + bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) { uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index]; @@ -117,7 +177,7 @@ bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) { } else { data = (sample_frame(player->osc1)); } - data *= player->volume; + data *= sample_filter(player->filter) ? player->volume : 0.0; data *= UINT8_MAX / 2; // scale -128..127 data += UINT8_MAX / 2; // to unsigned @@ -135,11 +195,21 @@ bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) { return true; } -bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) { +bool dtmf_dolphin_audio_play_tones( + float freq1, + float freq2, + uint16_t pulses, + uint16_t pulse_ms, + uint16_t gap_ms) { + if(current_player != NULL && current_player->playing) { + // Cannot start playing while still playing something else + return false; + } current_player = dtmf_dolphin_audio_alloc(); osc_generate_lookup_table(current_player->osc1, freq1); osc_generate_lookup_table(current_player->osc2, freq2); + filter_generate_lookup_table(current_player->filter, pulses, pulse_ms, gap_ms); generate_waveform(current_player, 0); generate_waveform(current_player, current_player->half_buffer_length); @@ -152,10 +222,20 @@ bool dtmf_dolphin_audio_play_tones(float freq1, float freq2) { dtmf_dolphin_dma_start(); dtmf_dolphin_speaker_start(); + current_player->playing = true; return true; } bool dtmf_dolphin_audio_stop_tones() { + if(current_player != NULL && !current_player->playing) { + // Can't stop a player that isn't playing. + return false; + } + while(current_player->filter->offset > 0 && + current_player->filter->offset < current_player->filter->duration) { + // run remaining ticks if needed to complete filter sequence + dtmf_dolphin_audio_handle_tick(); + } dtmf_dolphin_speaker_stop(); dtmf_dolphin_dma_stop(); diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.h b/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.h index 3c6cc4904..2dd1d6eb6 100644 --- a/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.h +++ b/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.h @@ -14,6 +14,13 @@ typedef struct { uint16_t offset; } DTMFDolphinOsc; +typedef struct { + float duration; + size_t period; + bool* lookup_table; + uint16_t offset; +} DTMFDolphinPulseFilter; + typedef struct { size_t buffer_length; size_t half_buffer_length; @@ -23,6 +30,8 @@ typedef struct { FuriMessageQueue* queue; DTMFDolphinOsc* osc1; DTMFDolphinOsc* osc2; + DTMFDolphinPulseFilter* filter; + bool playing; } DTMFDolphinAudio; DTMFDolphinOsc* dtmf_dolphin_osc_alloc(); @@ -33,7 +42,12 @@ void dtmf_dolphin_audio_free(DTMFDolphinAudio* player); void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc); -bool dtmf_dolphin_audio_play_tones(float freq1, float freq2); +bool dtmf_dolphin_audio_play_tones( + float freq1, + float freq2, + uint16_t pulses, + uint16_t pulse_ms, + uint16_t gap_ms); bool dtmf_dolphin_audio_stop_tones(); diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.c b/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.c index a99c13baa..72386b83d 100644 --- a/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.c +++ b/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.c @@ -77,13 +77,23 @@ DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = { {"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0}, }}; +DTMFDolphinSceneData DTMFDolphinSceneDataRedboxCA = { + .name = "Redbox (CA)", + .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA, + .tone_count = 3, + .tones = { + {"Nickel", 2200.0, 0.0, {0, 0, 5}, 1, 66, 0}, + {"Dime", 2200.0, 0.0, {1, 0, 5}, 2, 66, 66}, + {"Quarter", 2200.0, 0.0, {2, 0, 5}, 5, 33, 33}, + }}; + DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = { .name = "Redbox (UK)", .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK, .tone_count = 2, .tones = { - {"10p", 1000.0, 0.0, {0, 0, 3}, 1, 200, 0}, - {"50p", 1000.0, 0.0, {1, 0, 3}, 1, 350, 0}, + {"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0}, + {"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0}, }}; DTMFDolphinSceneData DTMFDolphinSceneDataMisc = { @@ -109,6 +119,9 @@ void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) { case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US: current_scene_data = &DTMFDolphinSceneDataRedboxUS; break; + case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA: + current_scene_data = &DTMFDolphinSceneDataRedboxCA; + break; case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK: current_scene_data = &DTMFDolphinSceneDataRedboxUK; break; @@ -141,6 +154,24 @@ bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t return false; } +bool dtmf_dolphin_data_get_filter_data( + uint16_t* pulses, + uint16_t* pulse_ms, + uint16_t* gap_ms, + uint8_t row, + uint8_t col) { + for(size_t i = 0; i < current_scene_data->tone_count; i++) { + DTMFDolphinTones tones = current_scene_data->tones[i]; + if(tones.pos.row == row && tones.pos.col == col) { + pulses[0] = tones.pulses; + pulse_ms[0] = tones.pulse_ms; + gap_ms[0] = tones.gap_duration; + return true; + } + } + return false; +} + const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) { for(size_t i = 0; i < current_scene_data->tone_count; i++) { DTMFDolphinTones tones = current_scene_data->tones[i]; @@ -186,4 +217,4 @@ uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col) { } } return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.h b/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.h index 056fb223e..56ceaf03d 100644 --- a/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.h +++ b/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.h @@ -10,6 +10,7 @@ typedef enum { DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX, DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US, DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK, + DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA, DTMF_DOLPHIN_TONE_BLOCK_MISC, } DTMFDolphinToneSection; @@ -19,6 +20,13 @@ DTMFDolphinToneSection dtmf_dolphin_data_get_current_section(); bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col); +bool dtmf_dolphin_data_get_filter_data( + uint16_t* pulses, + uint16_t* pulse_ms, + uint16_t* gap_ms, + uint8_t row, + uint8_t col); + const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col); const char* dtmf_dolphin_data_get_current_section_name(); diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_event.h b/applications/plugins/dtmf_dolphin/dtmf_dolphin_event.h index 75f5bb274..525d0eb04 100644 --- a/applications/plugins/dtmf_dolphin/dtmf_dolphin_event.h +++ b/applications/plugins/dtmf_dolphin/dtmf_dolphin_event.h @@ -8,6 +8,7 @@ typedef enum { DTMFDolphinEventStartBluebox, DTMFDolphinEventStartRedboxUS, DTMFDolphinEventStartRedboxUK, + DTMFDolphinEventStartRedboxCA, DTMFDolphinEventStartMisc, DTMFDolphinEventPlayTones, DTMFDolphinEventStopTones, diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_i.h b/applications/plugins/dtmf_dolphin/dtmf_dolphin_i.h index abdabd2b3..f8ae1530f 100644 --- a/applications/plugins/dtmf_dolphin/dtmf_dolphin_i.h +++ b/applications/plugins/dtmf_dolphin/dtmf_dolphin_i.h @@ -22,6 +22,7 @@ enum DTMFDolphinSceneState { DTMFDolphinSceneStateBluebox, DTMFDolphinSceneStateRedboxUS, DTMFDolphinSceneStateRedboxUK, + DTMFDolphinSceneStateRedboxCA, DTMFDolphinSceneStateMisc, }; diff --git a/applications/plugins/dtmf_dolphin/phone.png b/applications/plugins/dtmf_dolphin/phone.png new file mode 100644 index 000000000..443f847c3 Binary files /dev/null and b/applications/plugins/dtmf_dolphin/phone.png differ diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c b/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c index 9820f5c9a..06da595e0 100644 --- a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c +++ b/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c @@ -17,6 +17,9 @@ void dtmf_dolphin_scene_dialer_on_enter(void* context) { case DTMFDolphinSceneStateRedboxUK: dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK); break; + case DTMFDolphinSceneStateRedboxCA: + dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA); + break; case DTMFDolphinSceneStateMisc: dtmf_dolphin_data_set_current_section(DTMF_DOLPHIN_TONE_BLOCK_MISC); break; diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c b/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c index 1afd117fd..484e9e8eb 100644 --- a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c +++ b/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c @@ -2,11 +2,31 @@ static void dtmf_dolphin_scene_start_main_menu_enter_callback(void* context, uint32_t index) { DTMFDolphinApp* app = context; - if(index == DTMFDolphinSceneStateDialer) { - view_dispatcher_send_custom_event(app->view_dispatcher, DTMFDolphinEventStartDialer); - } else if(index == DTMFDolphinSceneStateBluebox) { - view_dispatcher_send_custom_event(app->view_dispatcher, DTMFDolphinEventStartBluebox); + uint8_t cust_event = 255; + switch(index) { + case 0: + cust_event = DTMFDolphinEventStartDialer; + break; + case 1: + cust_event = DTMFDolphinEventStartBluebox; + break; + case 2: + cust_event = DTMFDolphinEventStartRedboxUS; + break; + case 3: + cust_event = DTMFDolphinEventStartRedboxUK; + break; + case 4: + cust_event = DTMFDolphinEventStartRedboxCA; + break; + case 5: + cust_event = DTMFDolphinEventStartMisc; + break; + default: + return; } + + view_dispatcher_send_custom_event(app->view_dispatcher, cust_event); } void dtmf_dolphin_scene_start_on_enter(void* context) { @@ -17,9 +37,12 @@ void dtmf_dolphin_scene_start_on_enter(void* context) { variable_item_list_set_enter_callback( var_item_list, dtmf_dolphin_scene_start_main_menu_enter_callback, app); - variable_item_list_add(var_item_list, "Dialer", 0, NULL, NULL); - variable_item_list_add(var_item_list, "Bluebox", 0, NULL, NULL); - variable_item_list_add(var_item_list, "Misc", 0, NULL, NULL); + variable_item_list_add(var_item_list, "Dialer", 0, NULL, context); + variable_item_list_add(var_item_list, "Bluebox", 0, NULL, context); + variable_item_list_add(var_item_list, "Redbox (US)", 0, NULL, context); + variable_item_list_add(var_item_list, "Redbox (UK)", 0, NULL, context); + variable_item_list_add(var_item_list, "Redbox (CA)", 0, NULL, context); + variable_item_list_add(var_item_list, "Misc", 0, NULL, context); variable_item_list_set_selected_item( var_item_list, scene_manager_get_scene_state(app->scene_manager, DTMFDolphinSceneStart)); @@ -33,19 +56,33 @@ bool dtmf_dolphin_scene_start_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DTMFDolphinEventStartDialer) { - scene_manager_set_scene_state( - app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateDialer); - scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer); - } else if(event.event == DTMFDolphinEventStartBluebox) { - scene_manager_set_scene_state( - app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateBluebox); - scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer); - } else if(event.event == DTMFDolphinEventStartMisc) { - scene_manager_set_scene_state( - app->scene_manager, DTMFDolphinSceneDialer, DTMFDolphinSceneStateMisc); - scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer); + uint8_t sc_state; + + switch(event.event) { + case DTMFDolphinEventStartDialer: + sc_state = DTMFDolphinSceneStateDialer; + break; + case DTMFDolphinEventStartBluebox: + sc_state = DTMFDolphinSceneStateBluebox; + break; + case DTMFDolphinEventStartRedboxUS: + sc_state = DTMFDolphinSceneStateRedboxUS; + break; + case DTMFDolphinEventStartRedboxUK: + sc_state = DTMFDolphinSceneStateRedboxUK; + break; + case DTMFDolphinEventStartRedboxCA: + sc_state = DTMFDolphinSceneStateRedboxCA; + break; + case DTMFDolphinEventStartMisc: + sc_state = DTMFDolphinSceneStateMisc; + break; + default: + return consumed; } + scene_manager_set_scene_state(app->scene_manager, DTMFDolphinSceneDialer, sc_state); + scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneDialer); + consumed = true; } return consumed; diff --git a/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.c b/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.c index 15f0a8303..bdffa2313 100644 --- a/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.c +++ b/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.c @@ -15,6 +15,9 @@ typedef struct { float freq1; float freq2; bool playing; + uint16_t pulses; + uint16_t pulse_ms; + uint16_t gap_ms; } DTMFDolphinDialerModel; static bool dtmf_dolphin_dialer_process_up(DTMFDolphinDialer* dtmf_dolphin_dialer); @@ -93,6 +96,8 @@ void draw_dialer(Canvas* canvas, void* _model) { void update_frequencies(DTMFDolphinDialerModel* model) { dtmf_dolphin_data_get_tone_frequencies(&model->freq1, &model->freq2, model->row, model->col); + dtmf_dolphin_data_get_filter_data( + &model->pulses, &model->pulse_ms, &model->gap_ms, model->row, model->col); } static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) { @@ -133,8 +138,7 @@ static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) { draw_dialer(canvas, model); - FuriString* output; - output = furi_string_alloc(); + FuriString* output = furi_string_alloc(); if(model->freq1 && model->freq2) { furi_string_cat_printf( @@ -148,6 +152,12 @@ static void dtmf_dolphin_dialer_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); canvas_set_color(canvas, ColorBlack); + if(model->pulse_ms) { + furi_string_cat_printf(output, "P: %u * %u ms\n", model->pulses, model->pulse_ms); + } + if(model->gap_ms) { + furi_string_cat_printf(output, "Gaps: %u ms\n", model->gap_ms); + } elements_multiline_text( canvas, (max_span * DTMF_DOLPHIN_BUTTON_WIDTH) + 4, 21, furi_string_get_cstr(output)); @@ -272,7 +282,8 @@ static bool DTMFDolphinDialerModel * model, { if(event->type == InputTypePress) { - model->playing = dtmf_dolphin_audio_play_tones(model->freq1, model->freq2); + model->playing = dtmf_dolphin_audio_play_tones( + model->freq1, model->freq2, model->pulses, model->pulse_ms, model->gap_ms); } else if(event->type == InputTypeRelease) { model->playing = !dtmf_dolphin_audio_stop_tones(); } diff --git a/applications/plugins/flappy_bird/application.fam b/applications/plugins/flappy_bird/application.fam index dd8c67e85..0912178ca 100644 --- a/applications/plugins/flappy_bird/application.fam +++ b/applications/plugins/flappy_bird/application.fam @@ -9,4 +9,5 @@ App( order=100, fap_icon="flappy_10px.png", fap_category="Games", + fap_icon_assets="assets", ) diff --git a/applications/plugins/flappy_bird/assets/bird/frame_01.png b/applications/plugins/flappy_bird/assets/bird/frame_01.png new file mode 100644 index 000000000..0cd187053 Binary files /dev/null and b/applications/plugins/flappy_bird/assets/bird/frame_01.png differ diff --git a/applications/plugins/flappy_bird/assets/bird/frame_02.png b/applications/plugins/flappy_bird/assets/bird/frame_02.png new file mode 100644 index 000000000..3c37cdb8b Binary files /dev/null and b/applications/plugins/flappy_bird/assets/bird/frame_02.png differ diff --git a/applications/plugins/flappy_bird/assets/bird/frame_03.png b/applications/plugins/flappy_bird/assets/bird/frame_03.png new file mode 100644 index 000000000..a111ce163 Binary files /dev/null and b/applications/plugins/flappy_bird/assets/bird/frame_03.png differ diff --git a/applications/plugins/flappy_bird/assets/bird/frame_rate b/applications/plugins/flappy_bird/assets/bird/frame_rate new file mode 100644 index 000000000..e440e5c84 --- /dev/null +++ b/applications/plugins/flappy_bird/assets/bird/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/applications/plugins/flappy_bird/bird.h b/applications/plugins/flappy_bird/bird.h deleted file mode 100644 index 8162080be..000000000 --- a/applications/plugins/flappy_bird/bird.h +++ /dev/null @@ -1,54 +0,0 @@ -#include - -uint8_t bird_array[3][15][11] = { - { - {0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0}, - {0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0}, - {0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, - {0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1}, - {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1}, - {1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1}, - {1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1}, - {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, - {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, - {0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0}, - {0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}, - {0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0}, - {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, - }, - { - {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, - {0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0}, - {0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0}, - {0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1}, - {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1}, - {1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1}, - {1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1}, - {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, - {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, - {0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0}, - {0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}, - {0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0}, - {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, - }, - { - {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0}, - {0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0}, - {0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0}, - {0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1}, - {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, - {1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1}, - {1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1}, - {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, - {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, - {0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0}, - {0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}, - {0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0}, - {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, - }}; diff --git a/applications/plugins/flappy_bird/flappy_bird.c b/applications/plugins/flappy_bird/flappy_bird.c index 51914c43d..a86ddb2bb 100644 --- a/applications/plugins/flappy_bird/flappy_bird.c +++ b/applications/plugins/flappy_bird/flappy_bird.c @@ -1,10 +1,11 @@ -#include -#include -#include #include #include -#include "bird.h" +#include +#include +#include +#include +#include #define TAG "Flappy" #define DEBUG false @@ -13,10 +14,10 @@ #define FLAPPY_BIRD_WIDTH 10 #define FLAPPY_PILAR_MAX 6 -#define FLAPPY_PILAR_DIST 40 +#define FLAPPY_PILAR_DIST 35 #define FLAPPY_GAB_HEIGHT 25 -#define FLAPPY_GAB_WIDTH 5 +#define FLAPPY_GAB_WIDTH 10 #define FLAPPY_GRAVITY_JUMP -1.1 #define FLAPPY_GRAVITY_TICK 0.15 @@ -37,6 +38,7 @@ typedef struct { typedef struct { float gravity; POINT point; + IconAnimation* sprite; } BIRD; typedef struct { @@ -90,6 +92,7 @@ static void flappy_game_state_init(GameState* const game_state) { bird.gravity = 0.0f; bird.point.x = 15; bird.point.y = 32; + bird.sprite = icon_animation_alloc(&A_bird); game_state->debug = DEBUG; game_state->bird = bird; @@ -101,9 +104,10 @@ static void flappy_game_state_init(GameState* const game_state) { flappy_game_random_pilar(game_state); } -// static void flappy_game_reset(GameState* const game_state) { -// FURI_LOG_I(TAG, "Reset Game State\r\n"); // Resetting State -// } +static void flappy_game_state_free(GameState* const game_state) { + icon_animation_free(game_state->bird.sprite); + free(game_state); +} static void flappy_game_tick(GameState* const game_state) { if(game_state->state == GameStateLife) { @@ -137,11 +141,12 @@ static void flappy_game_tick(GameState* const game_state) { } if(pilar->point.x < -FLAPPY_GAB_WIDTH) pilar->visible = 0; - // Checking out of bounds - if(game_state->bird.point.y < 0 - FLAPPY_BIRD_WIDTH || - game_state->bird.point.y > FLIPPER_LCD_HEIGHT) { - game_state->state = GameStateGameOver; - break; + if(game_state->bird.point.y <= 0 - FLAPPY_BIRD_WIDTH) { + game_state->bird.point.y = 64; + } + + if(game_state->bird.point.y > 64 - FLAPPY_BIRD_WIDTH) { + game_state->bird.point.y = FLIPPER_LCD_HEIGHT - FLAPPY_BIRD_WIDTH; } // Bird inbetween pipes @@ -185,35 +190,48 @@ static void flappy_game_render_callback(Canvas* const canvas, void* ctx) { canvas_draw_frame( canvas, pilar->point.x, pilar->point.y, FLAPPY_GAB_WIDTH, pilar->height); + canvas_draw_frame( + canvas, pilar->point.x + 1, pilar->point.y, FLAPPY_GAB_WIDTH, pilar->height); + + canvas_draw_frame( + canvas, + pilar->point.x + 2, + pilar->point.y, + FLAPPY_GAB_WIDTH - 1, + pilar->height); + canvas_draw_frame( canvas, pilar->point.x, pilar->point.y + pilar->height + FLAPPY_GAB_HEIGHT, FLAPPY_GAB_WIDTH, FLIPPER_LCD_HEIGHT - pilar->height - FLAPPY_GAB_HEIGHT); - } - } - // Flappy - for(int h = 0; h < FLAPPY_BIRD_HEIGHT; h++) { - for(int w = 0; w < FLAPPY_BIRD_WIDTH; w++) { - // Switch animation - int bird = 0; - if(game_state->bird.gravity < -0.5) - bird = 1; - else - bird = 2; - // Draw bird pixels - if(bird_array[bird][h][w] == 1) { - int x = game_state->bird.point.x + h; - int y = game_state->bird.point.y + w; + canvas_draw_frame( + canvas, + pilar->point.x + 1, + pilar->point.y + pilar->height + FLAPPY_GAB_HEIGHT, + FLAPPY_GAB_WIDTH - 1, + FLIPPER_LCD_HEIGHT - pilar->height - FLAPPY_GAB_HEIGHT); - canvas_draw_dot(canvas, x, y); - } + canvas_draw_frame( + canvas, + pilar->point.x + 2, + pilar->point.y + pilar->height + FLAPPY_GAB_HEIGHT, + FLAPPY_GAB_WIDTH - 1, + FLIPPER_LCD_HEIGHT - pilar->height - FLAPPY_GAB_HEIGHT); } } - // Stats + // Switch animation + game_state->bird.sprite->frame = 1; + if(game_state->bird.gravity < -0.5) + game_state->bird.sprite->frame = 0; + else if(game_state->bird.gravity > 0.5) + game_state->bird.sprite->frame = 2; + + canvas_draw_icon_animation( + canvas, game_state->bird.point.x, game_state->bird.point.y, game_state->bird.sprite); canvas_set_font(canvas, FontSecondary); char buffer[12]; @@ -305,16 +323,16 @@ int32_t flappy_game_app(void* p) { if(event.input.type == InputTypePress) { switch(event.input.key) { case InputKeyUp: - game_state->bird.point.y--; + if(game_state->state == GameStateLife) { + flappy_game_flap(game_state); + } + break; case InputKeyDown: - game_state->bird.point.y++; break; case InputKeyRight: - game_state->bird.point.x++; break; case InputKeyLeft: - game_state->bird.point.x--; break; case InputKeyOk: if(game_state->state == GameStateGameOver) { @@ -329,14 +347,13 @@ int32_t flappy_game_app(void* p) { case InputKeyBack: processing = false; break; + default: + break; } } } else if(event.type == EventTypeTick) { flappy_game_tick(game_state); } - } else { - // FURI_LOG_D(TAG, "osMessageQueue: event timeout"); - // event timeout } view_port_update(view_port); @@ -351,7 +368,7 @@ int32_t flappy_game_app(void* p) { delete_mutex(&state_mutex); free_and_exit: - free(game_state); + flappy_game_state_free(game_state); furi_message_queue_free(event_queue); return return_code; diff --git a/applications/plugins/flashlight/LICENSE b/applications/plugins/flashlight/LICENSE new file mode 100644 index 000000000..28d693a7c --- /dev/null +++ b/applications/plugins/flashlight/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 MX + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/flashlight/README.md b/applications/plugins/flashlight/README.md new file mode 100644 index 000000000..a40cb2d5a --- /dev/null +++ b/applications/plugins/flashlight/README.md @@ -0,0 +1,7 @@ +# Flashlight Plugin for Flipper Zero + +Simple Flashlight special for @Svaarich by @xMasterX + +Enables 3.3v on pin 7/C3 and leaves it on when you exit app + +**Connect LED to (+ -> 7/C3) | (GND -> GND)** diff --git a/applications/plugins/flashlight/application.fam b/applications/plugins/flashlight/application.fam new file mode 100644 index 000000000..d6d5aa791 --- /dev/null +++ b/applications/plugins/flashlight/application.fam @@ -0,0 +1,14 @@ +App( + appid="Flashlight", + name="Flashlight", + apptype=FlipperAppType.EXTERNAL, + entry_point="flashlight_app", + cdefines=["APP_FLASHLIGHT"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=20, + fap_icon="flash10px.png", + fap_category="GPIO", +) \ No newline at end of file diff --git a/applications/plugins/flashlight/flash10px.png b/applications/plugins/flashlight/flash10px.png new file mode 100644 index 000000000..963a9ab5f Binary files /dev/null and b/applications/plugins/flashlight/flash10px.png differ diff --git a/applications/plugins/flashlight/flashlight.c b/applications/plugins/flashlight/flashlight.c new file mode 100644 index 000000000..9c5f600f7 --- /dev/null +++ b/applications/plugins/flashlight/flashlight.c @@ -0,0 +1,130 @@ +// by @xMasterX + +#include +#include +#include +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + bool is_on; +} PluginState; + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + if(plugin_state == NULL) { + return; + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Flashlight"); + + canvas_set_font(canvas, FontSecondary); + + if(!plugin_state->is_on) { + elements_multiline_text_aligned( + canvas, 64, 28, AlignCenter, AlignTop, "Press OK button turn on"); + } else { + elements_multiline_text_aligned(canvas, 64, 28, AlignCenter, AlignTop, "Light is on!"); + elements_multiline_text_aligned( + canvas, 64, 40, AlignCenter, AlignTop, "Press OK button to off"); + } + + release_mutex((ValueMutex*)ctx, plugin_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void flash_toggle(PluginState* const plugin_state) { + furi_hal_gpio_write(&gpio_ext_pc3, false); + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + if(plugin_state->is_on) { + furi_hal_gpio_write(&gpio_ext_pc3, false); + plugin_state->is_on = false; + } else { + furi_hal_gpio_write(&gpio_ext_pc3, true); + plugin_state->is_on = true; + } +} + +int32_t flashlight_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + PluginState* plugin_state = malloc(sizeof(PluginState)); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + FURI_LOG_E("flashlight", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(plugin_state); + return 255; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyRight: + case InputKeyLeft: + break; + case InputKeyOk: + flash_toggle(plugin_state); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } + } + + view_port_update(view_port); + release_mutex(&state_mutex, plugin_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/flipfrid/application.fam b/applications/plugins/flipfrid/application.fam index 2edb3172f..fc089de48 100644 --- a/applications/plugins/flipfrid/application.fam +++ b/applications/plugins/flipfrid/application.fam @@ -9,4 +9,5 @@ App( order=180, fap_icon="rfid_10px.png", fap_category="Tools", + fap_icon_assets="images", ) diff --git a/applications/plugins/flipfrid/flipfrid.c b/applications/plugins/flipfrid/flipfrid.c index dc6039c04..c2831f2f6 100644 --- a/applications/plugins/flipfrid/flipfrid.c +++ b/applications/plugins/flipfrid/flipfrid.c @@ -33,6 +33,8 @@ static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) { case SceneLoadCustomUids: flipfrid_scene_load_custom_uids_on_draw(canvas, flipfrid_state); break; + default: + break; } release_mutex((ValueMutex*)ctx, flipfrid_state); @@ -176,6 +178,8 @@ int32_t flipfrid_start(void* p) { case SceneLoadCustomUids: flipfrid_scene_load_custom_uids_on_event(event, flipfrid_state); break; + default: + break; } } else if(event.evt_type == EventTypeTick) { @@ -200,6 +204,8 @@ int32_t flipfrid_start(void* p) { break; case NoneScene: break; + default: + break; } // Trigger Entry Scene @@ -220,6 +226,8 @@ int32_t flipfrid_start(void* p) { case SceneLoadCustomUids: flipfrid_scene_load_custom_uids_on_enter(flipfrid_state); break; + default: + break; } flipfrid_state->previous_scene = flipfrid_state->current_scene; } @@ -242,6 +250,8 @@ int32_t flipfrid_start(void* p) { case SceneLoadCustomUids: flipfrid_scene_load_custom_uids_on_tick(flipfrid_state); break; + default: + break; } view_port_update(view_port); } diff --git a/applications/plugins/flipfrid/flipfrid.h b/applications/plugins/flipfrid/flipfrid.h index 248e2322e..8ce2cca79 100644 --- a/applications/plugins/flipfrid/flipfrid.h +++ b/applications/plugins/flipfrid/flipfrid.h @@ -15,6 +15,8 @@ #include #include +#include + #include #include @@ -79,6 +81,7 @@ typedef struct { LFRFIDWorker* worker; ProtocolDict* dict; ProtocolId protocol; + bool workr_rund; uint8_t time_between_cards; diff --git a/applications/plugins/flipfrid/images/125_10px.png b/applications/plugins/flipfrid/images/125_10px.png new file mode 100644 index 000000000..ce01284a2 Binary files /dev/null and b/applications/plugins/flipfrid/images/125_10px.png differ diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c index 3a1ce4951..1ac91625f 100644 --- a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c +++ b/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c @@ -134,6 +134,8 @@ void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* cont case InputKeyBack: context->is_running = false; break; + default: + break; } } } diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c index cc9e9871d..7bf7f5eaa 100644 --- a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c +++ b/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c @@ -1,6 +1,7 @@ #include "flipfrid_scene_load_custom_uids.h" #include "flipfrid_scene_run_attack.h" #include "flipfrid_scene_entrypoint.h" +#include "RFID_Fuzzer_icons.h" #define LFRFID_UIDS_EXTENSION ".txt" #define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer" @@ -71,6 +72,8 @@ void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState case InputKeyBack: context->current_scene = SceneEntryPoint; break; + default: + break; } } } diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c index 76864018b..d41549d73 100644 --- a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c +++ b/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c @@ -1,5 +1,6 @@ #include "flipfrid_scene_load_file.h" #include "flipfrid_scene_entrypoint.h" +#include "RFID_Fuzzer_icons.h" #define LFRFID_APP_EXTENSION ".rfid" #define LFRFID_APP_PATH_FOLDER "/ext/lfrfid" @@ -158,6 +159,8 @@ void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* conte case InputKeyBack: context->current_scene = SceneEntryPoint; break; + default: + break; } } } diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c index 3baa4ea49..f75e04604 100644 --- a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c +++ b/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c @@ -94,8 +94,11 @@ void flipfrid_scene_run_attack_on_enter(FlipFridState* context) { } void flipfrid_scene_run_attack_on_exit(FlipFridState* context) { - lfrfid_worker_stop(context->worker); - lfrfid_worker_stop_thread(context->worker); + if(context->workr_rund) { + lfrfid_worker_stop(context->worker); + lfrfid_worker_stop_thread(context->worker); + context->workr_rund = false; + } lfrfid_worker_free(context->worker); protocol_dict_free(context->dict); notification_message(context->notify, &sequence_blink_stop); @@ -109,9 +112,14 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { context->worker = lfrfid_worker_alloc(context->dict); lfrfid_worker_start_thread(context->worker); lfrfid_worker_emulate_start(context->worker, context->protocol); + context->workr_rund = true; } else if(0 == counter) { - lfrfid_worker_stop(context->worker); - lfrfid_worker_stop_thread(context->worker); + if(context->workr_rund) { + lfrfid_worker_stop(context->worker); + lfrfid_worker_stop_thread(context->worker); + context->workr_rund = false; + furi_delay_ms(200); + } switch(context->attack) { case FlipFridAttackDefaultValues: if(context->proto == EM4100) { @@ -486,6 +494,8 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); } break; + default: + break; } } } @@ -508,14 +518,14 @@ void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* cont break; case InputKeyLeft: if(!context->is_attacking) { - if(context->time_between_cards > 0) { + if(context->time_between_cards > 5) { context->time_between_cards--; } } break; case InputKeyRight: if(!context->is_attacking) { - if(context->time_between_cards < 60) { + if(context->time_between_cards < 70) { context->time_between_cards++; } } @@ -546,6 +556,30 @@ void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* cont notification_message(context->notify, &sequence_blink_stop); context->current_scene = SceneEntryPoint; break; + default: + break; + } + } + if(event.input_type == InputTypeLong) { + switch(event.key) { + case InputKeyLeft: + if(!context->is_attacking) { + if(context->time_between_cards > 0) { + if((context->time_between_cards - 10) > 5) { + context->time_between_cards -= 10; + } + } + } + break; + case InputKeyRight: + if(!context->is_attacking) { + if(context->time_between_cards < 70) { + context->time_between_cards += 10; + } + } + break; + default: + break; } } } diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_select_field.c b/applications/plugins/flipfrid/scene/flipfrid_scene_select_field.c index 79416d616..ccb49e910 100644 --- a/applications/plugins/flipfrid/scene/flipfrid_scene_select_field.c +++ b/applications/plugins/flipfrid/scene/flipfrid_scene_select_field.c @@ -129,6 +129,8 @@ void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* co furi_string_reset(context->notification_msg); context->current_scene = SceneSelectFile; break; + default: + break; } FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); } diff --git a/applications/plugins/flipper_i2ctools/.gitignore b/applications/plugins/flipper_i2ctools/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/applications/plugins/flipper_i2ctools/README.md b/applications/plugins/flipper_i2ctools/README.md new file mode 100644 index 000000000..07d57f64f --- /dev/null +++ b/applications/plugins/flipper_i2ctools/README.md @@ -0,0 +1,45 @@ +# flipperzero-i2ctools + +[Original link](https://github.com/NaejEL/flipperzero-i2ctools) + +Set of i2c tools for Flipper Zero + +![Preview](i2ctools.gif) + +## Wiring + +C0 -> SCL + +C1 -> SDA + +GND -> GND + +>/!\ Target must use 3v3 logic levels. If you not sure use an i2c isolator like ISO1541 + +## Tools + +### Scanner + +Look for i2c peripherals adresses + +### Sniffer + +Spy i2c traffic + +### Sender + +Send command to i2c peripherals and read result + +## TODO + +- [ ] Read more than 2 bytes in sender mode +- [ ] Add 10-bits adresses support +- [ ] Test with rate > 100khz +- [ ] Save records +- [ ] Play from files +- [ ] Kicad module +- [ ] Improve UI +- [ ] Refactor Event Management Code +- [ ] Add Documentation +- [ ] Remove max data size +- [ ] Remove max frames read size \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/plugins/flipper_i2ctools/application.fam new file mode 100644 index 000000000..b029e90fb --- /dev/null +++ b/applications/plugins/flipper_i2ctools/application.fam @@ -0,0 +1,13 @@ +App( + appid="i2cTools", + name="i2c Tools", + apptype=FlipperAppType.EXTERNAL, + entry_point="i2ctools_app", + cdefines=["APP_I2CTOOLS"], + requires=["gui"], + stack_size=2 * 1024, + order=175, + fap_icon="i2ctools.png", + fap_category="GPIO", + fap_icon_assets="images", +) \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/i2cscanner.c b/applications/plugins/flipper_i2ctools/i2cscanner.c new file mode 100644 index 000000000..dab618b78 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/i2cscanner.c @@ -0,0 +1,35 @@ +#include "i2cscanner.h" + +void scan_i2c_bus(i2cScanner* i2c_scanner) { + i2c_scanner->nb_found = 0; + i2c_scanner->scanned = true; + // Get the bus + furi_hal_i2c_acquire(I2C_BUS); + // scan + for(uint8_t addr = 0x01; addr <= MAX_I2C_ADDR << 1; addr++) { + // Check for peripherals + if(furi_hal_i2c_is_device_ready(I2C_BUS, addr, I2C_TIMEOUT)) { + // skip even 8-bit addr + if(addr % 2 != 0) { + continue; + } + // convert addr to 7-bits + i2c_scanner->addresses[i2c_scanner->nb_found] = addr >> 1; + i2c_scanner->nb_found++; + } + } + furi_hal_i2c_release(I2C_BUS); +} + +i2cScanner* i2c_scanner_alloc() { + i2cScanner* i2c_scanner = malloc(sizeof(i2cScanner)); + i2c_scanner->nb_found = 0; + i2c_scanner->menu_index = 0; + i2c_scanner->scanned = false; + return i2c_scanner; +} + +void i2c_scanner_free(i2cScanner* i2c_scanner) { + furi_assert(i2c_scanner); + free(i2c_scanner); +} \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/i2cscanner.h b/applications/plugins/flipper_i2ctools/i2cscanner.h new file mode 100644 index 000000000..5320ebb9e --- /dev/null +++ b/applications/plugins/flipper_i2ctools/i2cscanner.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +// I2C BUS +#define I2C_BUS &furi_hal_i2c_handle_external +#define I2C_TIMEOUT 3 + +// 7 bits addresses +#define MAX_I2C_ADDR 0x7F + +typedef struct { + uint8_t addresses[MAX_I2C_ADDR + 1]; + uint8_t nb_found; + uint8_t menu_index; + bool scanned; +} i2cScanner; + +void scan_i2c_bus(i2cScanner* i2c_scanner); + +i2cScanner* i2c_scanner_alloc(); +void i2c_scanner_free(i2cScanner* i2c_scanner); diff --git a/applications/plugins/flipper_i2ctools/i2csender.c b/applications/plugins/flipper_i2ctools/i2csender.c new file mode 100644 index 000000000..bdc98cd9e --- /dev/null +++ b/applications/plugins/flipper_i2ctools/i2csender.c @@ -0,0 +1,29 @@ +#include "i2csender.h" + +void i2c_send(i2cSender* i2c_sender) { + furi_hal_i2c_acquire(I2C_BUS); + uint8_t adress = i2c_sender->scanner->addresses[i2c_sender->address_idx] << 1; + i2c_sender->error = furi_hal_i2c_trx( + I2C_BUS, + adress, + &i2c_sender->value, + sizeof(i2c_sender->value), + i2c_sender->recv, + sizeof(i2c_sender->recv), + I2C_TIMEOUT); + furi_hal_i2c_release(I2C_BUS); + i2c_sender->must_send = false; + i2c_sender->sended = true; +} + +i2cSender* i2c_sender_alloc() { + i2cSender* i2c_sender = malloc(sizeof(i2cSender)); + i2c_sender->must_send = false; + i2c_sender->sended = false; + return i2c_sender; +} + +void i2c_sender_free(i2cSender* i2c_sender) { + furi_assert(i2c_sender); + free(i2c_sender); +} \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/i2csender.h b/applications/plugins/flipper_i2ctools/i2csender.h new file mode 100644 index 000000000..2aa74d6e2 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/i2csender.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include "i2cscanner.h" + +typedef struct { + uint8_t address_idx; + uint8_t value; + uint8_t recv[2]; + bool must_send; + bool sended; + bool error; + + i2cScanner* scanner; +} i2cSender; + +void i2c_send(); + +i2cSender* i2c_sender_alloc(); +void i2c_sender_free(i2cSender* i2c_sender); diff --git a/applications/plugins/flipper_i2ctools/i2csniffer.c b/applications/plugins/flipper_i2ctools/i2csniffer.c new file mode 100644 index 000000000..6a633cfaf --- /dev/null +++ b/applications/plugins/flipper_i2ctools/i2csniffer.c @@ -0,0 +1,101 @@ +#include "i2csniffer.h" + +void clear_sniffer_buffers(i2cSniffer* i2c_sniffer) { + furi_assert(i2c_sniffer); + for(uint8_t i = 0; i < MAX_RECORDS; i++) { + for(uint8_t j = 0; j < MAX_MESSAGE_SIZE; j++) { + i2c_sniffer->frames[i].ack[j] = false; + i2c_sniffer->frames[i].data[j] = 0; + } + i2c_sniffer->frames[i].bit_index = 0; + i2c_sniffer->frames[i].data_index = 0; + } + i2c_sniffer->frame_index = 0; + i2c_sniffer->state = I2C_BUS_FREE; + i2c_sniffer->first = true; +} + +void start_interrupts(i2cSniffer* i2c_sniffer) { + furi_assert(i2c_sniffer); + furi_hal_gpio_init(pinSCL, GpioModeInterruptRise, GpioPullNo, GpioSpeedHigh); + furi_hal_gpio_add_int_callback(pinSCL, SCLcallback, i2c_sniffer); + + // Add Rise and Fall Interrupt on SDA pin + furi_hal_gpio_init(pinSDA, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedHigh); + furi_hal_gpio_add_int_callback(pinSDA, SDAcallback, i2c_sniffer); +} + +void stop_interrupts() { + furi_hal_gpio_remove_int_callback(pinSCL); + furi_hal_gpio_remove_int_callback(pinSDA); + // Reset GPIO pins to default state + furi_hal_gpio_init(pinSCL, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(pinSDA, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +// Called on Fallin/Rising SDA +// Used to monitor i2c bus state +void SDAcallback(void* _i2c_sniffer) { + i2cSniffer* i2c_sniffer = _i2c_sniffer; + // SCL is low maybe cclock strecching + if(furi_hal_gpio_read(pinSCL) == false) { + return; + } + // Check for stop condition: SDA rising while SCL is High + if(i2c_sniffer->state == I2C_BUS_STARTED) { + if(furi_hal_gpio_read(pinSDA) == true) { + i2c_sniffer->state = I2C_BUS_FREE; + } + } + // Check for start condition: SDA falling while SCL is high + else if(furi_hal_gpio_read(pinSDA) == false) { + i2c_sniffer->state = I2C_BUS_STARTED; + if(i2c_sniffer->first) { + i2c_sniffer->first = false; + return; + } + i2c_sniffer->frame_index++; + if(i2c_sniffer->frame_index >= MAX_RECORDS) { + clear_sniffer_buffers(i2c_sniffer); + } + } + return; +} + +// Called on Rising SCL +// Used to read bus datas +void SCLcallback(void* _i2c_sniffer) { + i2cSniffer* i2c_sniffer = _i2c_sniffer; + if(i2c_sniffer->state == I2C_BUS_FREE) { + return; + } + uint8_t frame = i2c_sniffer->frame_index; + uint8_t bit = i2c_sniffer->frames[frame].bit_index; + uint8_t data_idx = i2c_sniffer->frames[frame].data_index; + if(bit < 8) { + i2c_sniffer->frames[frame].data[data_idx] <<= 1; + i2c_sniffer->frames[frame].data[data_idx] |= (int)furi_hal_gpio_read(pinSDA); + i2c_sniffer->frames[frame].bit_index++; + } else { + i2c_sniffer->frames[frame].ack[data_idx] = !furi_hal_gpio_read(pinSDA); + i2c_sniffer->frames[frame].data_index++; + i2c_sniffer->frames[frame].bit_index = 0; + } +} + +i2cSniffer* i2c_sniffer_alloc() { + i2cSniffer* i2c_sniffer = malloc(sizeof(i2cSniffer)); + i2c_sniffer->started = false; + i2c_sniffer->row_index = 0; + i2c_sniffer->menu_index = 0; + clear_sniffer_buffers(i2c_sniffer); + return i2c_sniffer; +} + +void i2c_sniffer_free(i2cSniffer* i2c_sniffer) { + furi_assert(i2c_sniffer); + if(i2c_sniffer->started) { + stop_interrupts(); + } + free(i2c_sniffer); +} diff --git a/applications/plugins/flipper_i2ctools/i2csniffer.h b/applications/plugins/flipper_i2ctools/i2csniffer.h new file mode 100644 index 000000000..eef26bea3 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/i2csniffer.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +// I2C Pins +#define pinSCL &gpio_ext_pc0 +#define pinSDA &gpio_ext_pc1 + +// Bus States +typedef enum { I2C_BUS_FREE, I2C_BUS_STARTED } i2cBusStates; + +// Max read size of i2c frame by message +// Arbitraly defined +// They're not real limit to maximum frames send +#define MAX_MESSAGE_SIZE 128 + +// Nb of records +#define MAX_RECORDS 128 + +/// @brief Struct used to store our reads +typedef struct { + uint8_t data[MAX_MESSAGE_SIZE]; + bool ack[MAX_MESSAGE_SIZE]; + uint8_t bit_index; + uint8_t data_index; +} i2cFrame; + +typedef struct { + bool started; + bool first; + i2cBusStates state; + i2cFrame frames[MAX_RECORDS]; + uint8_t frame_index; + uint8_t menu_index; + uint8_t row_index; +} i2cSniffer; + +void clear_sniffer_buffers(i2cSniffer* i2c_sniffer); +void start_interrupts(i2cSniffer* i2c_sniffer); +void stop_interrupts(); +void SDAcallback(void* _i2c_sniffer); +void SCLcallback(void* _i2c_sniffer); + +i2cSniffer* i2c_sniffer_alloc(); +void i2c_sniffer_free(i2cSniffer* i2c_sniffer); \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/i2ctools.c b/applications/plugins/flipper_i2ctools/i2ctools.c new file mode 100644 index 000000000..9d73a73b8 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/i2ctools.c @@ -0,0 +1,222 @@ +#include "i2ctools_i.h" + +void i2ctools_draw_callback(Canvas* canvas, void* ctx) { + i2cTools* i2ctools = acquire_mutex((ValueMutex*)ctx, 25); + + switch(i2ctools->main_view->current_view) { + case MAIN_VIEW: + draw_main_view(canvas, i2ctools->main_view); + break; + + case SCAN_VIEW: + draw_scanner_view(canvas, i2ctools->scanner); + break; + + case SNIFF_VIEW: + draw_sniffer_view(canvas, i2ctools->sniffer); + break; + + case SEND_VIEW: + draw_sender_view(canvas, i2ctools->sender); + break; + + default: + break; + } + release_mutex((ValueMutex*)ctx, i2ctools); +} + +void i2ctools_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t i2ctools_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + // Alloc i2ctools + i2cTools* i2ctools = malloc(sizeof(i2cTools)); + ValueMutex i2ctools_mutex; + if(!init_mutex(&i2ctools_mutex, i2ctools, sizeof(i2cTools))) { + FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); + free(i2ctools); + return -1; + } + + // Alloc viewport + i2ctools->view_port = view_port_alloc(); + view_port_draw_callback_set(i2ctools->view_port, i2ctools_draw_callback, &i2ctools_mutex); + view_port_input_callback_set(i2ctools->view_port, i2ctools_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, i2ctools->view_port, GuiLayerFullscreen); + + InputEvent event; + + i2ctools->main_view = i2c_main_view_alloc(); + + i2ctools->sniffer = i2c_sniffer_alloc(); + i2ctools->sniffer->menu_index = 0; + + i2ctools->scanner = i2c_scanner_alloc(); + + i2ctools->sender = i2c_sender_alloc(); + // Share scanner with sender + i2ctools->sender->scanner = i2ctools->scanner; + + while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + // Back + if(event.key == InputKeyBack && event.type == InputTypeRelease) { + if(i2ctools->main_view->current_view == MAIN_VIEW) { + break; + } else { + if(i2ctools->main_view->current_view == SNIFF_VIEW) { + stop_interrupts(); + i2ctools->sniffer->started = false; + i2ctools->sniffer->state = I2C_BUS_FREE; + } + i2ctools->main_view->current_view = MAIN_VIEW; + } + } + // Up + else if(event.key == InputKeyUp && event.type == InputTypeRelease) { + if(i2ctools->main_view->current_view == MAIN_VIEW) { + if((i2ctools->main_view->menu_index > SCAN_VIEW)) { + i2ctools->main_view->menu_index--; + } + } else if(i2ctools->main_view->current_view == SCAN_VIEW) { + if(i2ctools->scanner->menu_index > 0) { + i2ctools->scanner->menu_index--; + } + } else if(i2ctools->main_view->current_view == SNIFF_VIEW) { + if(i2ctools->sniffer->row_index > 0) { + i2ctools->sniffer->row_index--; + } + } else if(i2ctools->main_view->current_view == SEND_VIEW) { + if(i2ctools->sender->value < 0xFF) { + i2ctools->sender->value++; + i2ctools->sender->sended = false; + } + } + } + // Long Up + else if( + event.key == InputKeyUp && + (event.type == InputTypeLong || event.type == InputTypeRepeat)) { + if(i2ctools->main_view->current_view == SCAN_VIEW) { + if(i2ctools->scanner->menu_index > 5) { + i2ctools->scanner->menu_index -= 5; + } + } else if(i2ctools->main_view->current_view == SEND_VIEW) { + if(i2ctools->sender->value < 0xF9) { + i2ctools->sender->value += 5; + i2ctools->sender->sended = false; + } + } else if(i2ctools->main_view->current_view == SNIFF_VIEW) { + if(i2ctools->sniffer->row_index > 5) { + i2ctools->sniffer->row_index -= 5; + } else { + i2ctools->sniffer->row_index = 0; + } + } + } + // Down + else if(event.key == InputKeyDown && event.type == InputTypeRelease) { + if(i2ctools->main_view->current_view == MAIN_VIEW) { + if(i2ctools->main_view->menu_index < MENU_SIZE - 1) { + i2ctools->main_view->menu_index++; + } + } else if(i2ctools->main_view->current_view == SCAN_VIEW) { + if(i2ctools->scanner->menu_index < ((int)i2ctools->scanner->nb_found / 3)) { + i2ctools->scanner->menu_index++; + } + } else if(i2ctools->main_view->current_view == SNIFF_VIEW) { + if((i2ctools->sniffer->row_index + 3) < + (int)i2ctools->sniffer->frames[i2ctools->sniffer->menu_index].data_index) { + i2ctools->sniffer->row_index++; + } + } else if(i2ctools->main_view->current_view == SEND_VIEW) { + if(i2ctools->sender->value > 0x00) { + i2ctools->sender->value--; + i2ctools->sender->sended = false; + } + } + } + // Long Down + else if( + event.key == InputKeyDown && + (event.type == InputTypeLong || event.type == InputTypeRepeat)) { + if(i2ctools->main_view->current_view == SEND_VIEW) { + if(i2ctools->sender->value > 0x05) { + i2ctools->sender->value -= 5; + i2ctools->sender->sended = false; + } else { + i2ctools->sender->value = 0; + i2ctools->sender->sended = false; + } + } else if(i2ctools->main_view->current_view == SNIFF_VIEW) { + if((i2ctools->sniffer->row_index + 8) < + (int)i2ctools->sniffer->frames[i2ctools->sniffer->menu_index].data_index) { + i2ctools->sniffer->row_index += 5; + } + } + + } else if(event.key == InputKeyOk && event.type == InputTypeRelease) { + if(i2ctools->main_view->current_view == MAIN_VIEW) { + i2ctools->main_view->current_view = i2ctools->main_view->menu_index; + } else if(i2ctools->main_view->current_view == SCAN_VIEW) { + scan_i2c_bus(i2ctools->scanner); + } else if(i2ctools->main_view->current_view == SEND_VIEW) { + i2ctools->sender->must_send = true; + } else if(i2ctools->main_view->current_view == SNIFF_VIEW) { + if(i2ctools->sniffer->started) { + stop_interrupts(); + i2ctools->sniffer->started = false; + i2ctools->sniffer->state = I2C_BUS_FREE; + } else { + start_interrupts(i2ctools->sniffer); + i2ctools->sniffer->started = true; + i2ctools->sniffer->state = I2C_BUS_FREE; + } + } + } else if(event.key == InputKeyRight && event.type == InputTypeRelease) { + if(i2ctools->main_view->current_view == SEND_VIEW) { + if(i2ctools->sender->address_idx < (i2ctools->scanner->nb_found - 1)) { + i2ctools->sender->address_idx++; + i2ctools->sender->sended = false; + } + } else if(i2ctools->main_view->current_view == SNIFF_VIEW) { + if(i2ctools->sniffer->menu_index < i2ctools->sniffer->frame_index) { + i2ctools->sniffer->menu_index++; + i2ctools->sniffer->row_index = 0; + } + } + } else if(event.key == InputKeyLeft && event.type == InputTypeRelease) { + if(i2ctools->main_view->current_view == SEND_VIEW) { + if(i2ctools->sender->address_idx > 0) { + i2ctools->sender->address_idx--; + i2ctools->sender->sended = false; + } + } else if(i2ctools->main_view->current_view == SNIFF_VIEW) { + if(i2ctools->sniffer->menu_index > 0) { + i2ctools->sniffer->menu_index--; + i2ctools->sniffer->row_index = 0; + } + } + } + view_port_update(i2ctools->view_port); + } + gui_remove_view_port(gui, i2ctools->view_port); + view_port_free(i2ctools->view_port); + furi_message_queue_free(event_queue); + i2c_sniffer_free(i2ctools->sniffer); + i2c_scanner_free(i2ctools->scanner); + i2c_sender_free(i2ctools->sender); + i2c_main_view_free(i2ctools->main_view); + free(i2ctools); + furi_record_close(RECORD_GUI); + return 0; +} diff --git a/applications/plugins/flipper_i2ctools/i2ctools.gif b/applications/plugins/flipper_i2ctools/i2ctools.gif new file mode 100644 index 000000000..7ad9a582c Binary files /dev/null and b/applications/plugins/flipper_i2ctools/i2ctools.gif differ diff --git a/applications/plugins/flipper_i2ctools/i2ctools.png b/applications/plugins/flipper_i2ctools/i2ctools.png new file mode 100644 index 000000000..ba8485be8 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/i2ctools.png differ diff --git a/applications/plugins/flipper_i2ctools/i2ctools_i.h b/applications/plugins/flipper_i2ctools/i2ctools_i.h new file mode 100644 index 000000000..33917dc34 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/i2ctools_i.h @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +#include "i2csniffer.h" +#include "i2cscanner.h" +#include "i2csender.h" +#include "views/main_view.h" +#include "views/sniffer_view.h" +#include "views/scanner_view.h" +#include "views/sender_view.h" + +// App datas +typedef struct { + ViewPort* view_port; + i2cMainView* main_view; + + i2cScanner* scanner; + i2cSniffer* sniffer; + i2cSender* sender; +} i2cTools; diff --git a/applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png b/applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png differ diff --git a/applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png b/applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png differ diff --git a/applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png b/applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png differ diff --git a/applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png b/applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png differ diff --git a/applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png b/applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png differ diff --git a/applications/plugins/flipper_i2ctools/images/i2ctools_main_76x59.png b/applications/plugins/flipper_i2ctools/images/i2ctools_main_76x59.png new file mode 100644 index 000000000..a0b2a8983 Binary files /dev/null and b/applications/plugins/flipper_i2ctools/images/i2ctools_main_76x59.png differ diff --git a/applications/plugins/flipper_i2ctools/views/main_view.c b/applications/plugins/flipper_i2ctools/views/main_view.c new file mode 100644 index 000000000..abcb26224 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/views/main_view.c @@ -0,0 +1,62 @@ +#include "main_view.h" + +void draw_main_view(Canvas* canvas, i2cMainView* main_view) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 0, 0, 128, 64, 3); + canvas_draw_icon(canvas, 2, 2, &I_i2ctools_main_76x59); + canvas_set_font(canvas, FontPrimary); + + switch(main_view->menu_index) { + case SCAN_VIEW: + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, SNIFF_MENU_X, SNIFF_MENU_Y, AlignLeft, AlignTop, SNIFF_MENU_TEXT); + canvas_draw_str_aligned( + canvas, SEND_MENU_X, SEND_MENU_Y, AlignLeft, AlignTop, SEND_MENU_TEXT); + canvas_draw_rbox(canvas, 80, SCAN_MENU_Y - 2, 43, 13, 3); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str_aligned( + canvas, SCAN_MENU_X, SCAN_MENU_Y, AlignLeft, AlignTop, SCAN_MENU_TEXT); + break; + + case SNIFF_VIEW: + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, SCAN_MENU_X, SCAN_MENU_Y, AlignLeft, AlignTop, SCAN_MENU_TEXT); + canvas_draw_str_aligned( + canvas, SEND_MENU_X, SEND_MENU_Y, AlignLeft, AlignTop, SEND_MENU_TEXT); + canvas_draw_rbox(canvas, 80, SNIFF_MENU_Y - 2, 43, 13, 3); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str_aligned( + canvas, SNIFF_MENU_X, SNIFF_MENU_Y, AlignLeft, AlignTop, SNIFF_MENU_TEXT); + break; + + case SEND_VIEW: + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, SCAN_MENU_X, SCAN_MENU_Y, AlignLeft, AlignTop, SCAN_MENU_TEXT); + canvas_draw_str_aligned( + canvas, SNIFF_MENU_X, SNIFF_MENU_Y, AlignLeft, AlignTop, SNIFF_MENU_TEXT); + canvas_draw_rbox(canvas, 80, SEND_MENU_Y - 2, 43, 13, 3); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str_aligned( + canvas, SEND_MENU_X, SEND_MENU_Y, AlignLeft, AlignTop, SEND_MENU_TEXT); + break; + + default: + break; + } +} + +i2cMainView* i2c_main_view_alloc() { + i2cMainView* main_view = malloc(sizeof(i2cMainView)); + main_view->menu_index = SCAN_VIEW; + main_view->current_view = MAIN_VIEW; + return main_view; +} + +void i2c_main_view_free(i2cMainView* main_view) { + furi_assert(main_view); + free(main_view); +} \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/views/main_view.h b/applications/plugins/flipper_i2ctools/views/main_view.h new file mode 100644 index 000000000..050e41130 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/views/main_view.h @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#define APP_NAME "I2C Tools" + +#define SCAN_MENU_TEXT "Scan" +#define SCAN_MENU_X 90 +#define SCAN_MENU_Y 13 + +#define SNIFF_MENU_TEXT "Sniff" +#define SNIFF_MENU_X 90 +#define SNIFF_MENU_Y 27 + +#define SEND_MENU_TEXT "Send" +#define SEND_MENU_X 90 +#define SEND_MENU_Y 41 + +// Menu +typedef enum { + MAIN_VIEW, + SCAN_VIEW, + SNIFF_VIEW, + SEND_VIEW, + + /* Know menu Size*/ + MENU_SIZE +} i2cToolsViews; + +typedef struct { + i2cToolsViews current_view; + i2cToolsViews menu_index; +} i2cMainView; + +void draw_main_view(Canvas* canvas, i2cMainView* main_view); + +i2cMainView* i2c_main_view_alloc(); +void i2c_main_view_free(i2cMainView* main_view); \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.c b/applications/plugins/flipper_i2ctools/views/scanner_view.c new file mode 100644 index 000000000..f8bea6f40 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/views/scanner_view.c @@ -0,0 +1,47 @@ +#include "scanner_view.h" + +void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 0, 0, 128, 64, 3); + + char count_text[46]; + char count_text_fmt[] = "Peripherals Found: %d"; + canvas_set_font(canvas, FontSecondary); + snprintf(count_text, sizeof(count_text), count_text_fmt, (int)i2c_scanner->nb_found); + canvas_draw_str_aligned(canvas, 3, 3, AlignLeft, AlignTop, count_text); + uint8_t x_pos = 0; + uint8_t y_pos = 0; + uint8_t idx_to_print = 0; + for(uint8_t i = 0; i < (int)i2c_scanner->nb_found; i++) { + idx_to_print = i + i2c_scanner->menu_index * 3; + if(idx_to_print >= MAX_I2C_ADDR) { + break; + } + snprintf( + count_text, sizeof(count_text), "0x%02x ", (int)i2c_scanner->addresses[idx_to_print]); + const uint8_t x_start = 3; + if(i < 4) { + x_pos = x_start + (i * 26); + y_pos = 15; + } else if(i < 8) { + x_pos = x_start + ((i - 4) * 26); + y_pos = 25; + } else if(i < 12) { + x_pos = x_start + ((i - 8) * 26); + y_pos = 35; + } else { + break; + } + canvas_draw_str_aligned(canvas, x_pos, y_pos, AlignLeft, AlignTop, count_text); + } + // Right cursor + y_pos = 14 + i2c_scanner->menu_index; + canvas_draw_rbox(canvas, 125, y_pos, 3, 10, 1); + + // Button + canvas_draw_rbox(canvas, 45, 48, 45, 13, 3); + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, 50, 50, &I_Ok_btn_9x9); + canvas_draw_str_aligned(canvas, 62, 51, AlignLeft, AlignTop, "Scan"); +} \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.h b/applications/plugins/flipper_i2ctools/views/scanner_view.h new file mode 100644 index 000000000..02bc8fb1c --- /dev/null +++ b/applications/plugins/flipper_i2ctools/views/scanner_view.h @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include "../i2cscanner.h" + +#define SCAN_TEXT "SCAN" + +void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner); \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.c b/applications/plugins/flipper_i2ctools/views/sender_view.c new file mode 100644 index 000000000..216220209 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/views/sender_view.c @@ -0,0 +1,70 @@ +#include "sender_view.h" + +void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 0, 0, 128, 64, 3); + + if(!i2c_sender->scanner->scanned) { + scan_i2c_bus(i2c_sender->scanner); + } + + canvas_set_font(canvas, FontSecondary); + if(i2c_sender->scanner->nb_found <= 0) { + canvas_draw_str_aligned(canvas, 20, 5, AlignLeft, AlignTop, "No peripherals found"); + return; + } + // Send Button + canvas_draw_rbox(canvas, 45, 48, 45, 13, 3); + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, 50, 50, &I_Ok_btn_9x9); + canvas_draw_str_aligned(canvas, 62, 51, AlignLeft, AlignTop, "Send"); + // Addr + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 3, 5, AlignLeft, AlignTop, "Addr: "); + canvas_draw_icon(canvas, 33, 5, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 68, 5, &I_ButtonRight_4x7); + char addr_text[8]; + snprintf( + addr_text, + sizeof(addr_text), + "0x%02x", + (int)i2c_sender->scanner->addresses[i2c_sender->address_idx]); + canvas_draw_str_aligned(canvas, 43, 5, AlignLeft, AlignTop, addr_text); + // Value + canvas_draw_str_aligned(canvas, 3, 15, AlignLeft, AlignTop, "Value: "); + canvas_draw_icon(canvas, 33, 17, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 68, 17, &I_ButtonDown_7x4); + snprintf(addr_text, sizeof(addr_text), "0x%02x", (int)i2c_sender->value); + canvas_draw_str_aligned(canvas, 43, 15, AlignLeft, AlignTop, addr_text); + if(i2c_sender->must_send) { + i2c_send(i2c_sender); + } + // Result + canvas_draw_str_aligned(canvas, 3, 25, AlignLeft, AlignTop, "Result: "); + if(i2c_sender->sended) { + uint8_t row = 1; + uint8_t column = 1; + const uint8_t x_min = 3; + const uint8_t y_min = 25; + uint8_t x_pos = 0; + uint8_t y_pos = 0; + for(uint8_t i = 0; i < sizeof(i2c_sender->recv); i++) { + x_pos = x_min + (column - 1) * 35; + if(row == 1) { + x_pos += 40; + } + y_pos = y_min + (row - 1) * 10; + snprintf(addr_text, sizeof(addr_text), "0x%02x", (int)i2c_sender->recv[i]); + canvas_draw_str_aligned(canvas, x_pos, y_pos, AlignLeft, AlignTop, addr_text); + column++; + if((row > 1 && column > 3) || (row == 1 && column > 2)) { + column = 1; + row++; + } + if(row > 2) { + break; + } + } + } +} \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.h b/applications/plugins/flipper_i2ctools/views/sender_view.h new file mode 100644 index 000000000..5f48081dd --- /dev/null +++ b/applications/plugins/flipper_i2ctools/views/sender_view.h @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include "../i2csender.h" + +#define SEND_TEXT "SEND" + +void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender); \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.c b/applications/plugins/flipper_i2ctools/views/sniffer_view.c new file mode 100644 index 000000000..a05873930 --- /dev/null +++ b/applications/plugins/flipper_i2ctools/views/sniffer_view.c @@ -0,0 +1,92 @@ +#include "sniffer_view.h" + +void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 0, 0, 128, 64, 3); + canvas_set_font(canvas, FontSecondary); + + // Button + canvas_draw_rbox(canvas, 40, 48, 45, 13, 3); + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, 45, 50, &I_Ok_btn_9x9); + if(!i2c_sniffer->started) { + canvas_draw_str_aligned(canvas, 57, 51, AlignLeft, AlignTop, "Start"); + } else { + canvas_draw_str_aligned(canvas, 57, 51, AlignLeft, AlignTop, "Stop"); + } + canvas_set_color(canvas, ColorBlack); + if(i2c_sniffer->first) { + canvas_draw_str_aligned(canvas, 30, 3, AlignLeft, AlignTop, "Nothing Recorded"); + return; + } + char text_buffer[10]; + // nbFrame text + canvas_draw_str_aligned(canvas, 3, 3, AlignLeft, AlignTop, "Frame: "); + snprintf( + text_buffer, + sizeof(text_buffer), + "%d/%d", + (int)i2c_sniffer->menu_index + 1, + (int)i2c_sniffer->frame_index + 1); + canvas_draw_str_aligned(canvas, 38, 3, AlignLeft, AlignTop, text_buffer); + // Address text + snprintf( + text_buffer, + sizeof(text_buffer), + "0x%02x", + (int)(i2c_sniffer->frames[i2c_sniffer->menu_index].data[0] >> 1)); + canvas_draw_str_aligned(canvas, 3, 13, AlignLeft, AlignTop, "Addr: "); + canvas_draw_str_aligned(canvas, 30, 13, AlignLeft, AlignTop, text_buffer); + // R/W + if((int)(i2c_sniffer->frames[i2c_sniffer->menu_index].data[0]) % 2 == 0) { + canvas_draw_str_aligned(canvas, 58, 13, AlignLeft, AlignTop, "Write"); + } else { + canvas_draw_str_aligned(canvas, 58, 13, AlignLeft, AlignTop, "Read"); + } + // ACK + if(i2c_sniffer->frames[i2c_sniffer->menu_index].ack[0]) { + canvas_draw_str_aligned(canvas, 90, 13, AlignLeft, AlignTop, "ACK"); + } else { + canvas_draw_str_aligned(canvas, 90, 13, AlignLeft, AlignTop, "NACK"); + } + // Frames content + const uint8_t x_min = 3; + const uint8_t y_min = 23; + uint8_t x_pos = 0; + uint8_t y_pos = 0; + uint8_t row = 1; + uint8_t column = 1; + uint8_t frame_size = i2c_sniffer->frames[i2c_sniffer->menu_index].data_index; + uint8_t offset = i2c_sniffer->row_index; + if(i2c_sniffer->row_index > 0) { + offset += 1; + } + canvas_draw_str_aligned(canvas, x_min, y_min, AlignLeft, AlignTop, "Data:"); + for(uint8_t i = 1 + offset; i < frame_size; i++) { + snprintf( + text_buffer, + sizeof(text_buffer), + "0x%02x", + (int)i2c_sniffer->frames[i2c_sniffer->menu_index].data[i]); + x_pos = x_min + (column - 1) * 35; + if(row == 1) { + x_pos += 30; + } + y_pos = y_min + (row - 1) * 10; + canvas_draw_str_aligned(canvas, x_pos, y_pos, AlignLeft, AlignTop, text_buffer); + if(i2c_sniffer->frames[i2c_sniffer->menu_index].ack[i]) { + canvas_draw_str_aligned(canvas, x_pos + 24, y_pos, AlignLeft, AlignTop, "A"); + } else { + canvas_draw_str_aligned(canvas, x_pos + 24, y_pos, AlignLeft, AlignTop, "N"); + } + column++; + if((row > 1 && column > 3) || (row == 1 && column > 2)) { + column = 1; + row++; + } + if(row > 2) { + break; + } + } +} \ No newline at end of file diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.h b/applications/plugins/flipper_i2ctools/views/sniffer_view.h new file mode 100644 index 000000000..80c92f7fc --- /dev/null +++ b/applications/plugins/flipper_i2ctools/views/sniffer_view.h @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include "../i2csniffer.h" + +#define SNIFF_TEXT "SNIFF" + +void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer); \ No newline at end of file diff --git a/applications/plugins/game15/README.md b/applications/plugins/game15/README.md new file mode 100644 index 000000000..b1710c919 --- /dev/null +++ b/applications/plugins/game15/README.md @@ -0,0 +1,13 @@ + +# Game "15" for Flipper Zero + +[Original link](https://github.com/x27/flipperzero-game15) + +Logic game [Wikipedia](https://en.wikipedia.org/wiki/15_puzzle) + +![Game screen](images/Game15.png) + +![Restore game](images/Game15Restore.png) + +![Popoup](images/Game15Popup.png) + diff --git a/applications/plugins/game15/application.fam b/applications/plugins/game15/application.fam new file mode 100644 index 000000000..dc3a0da0b --- /dev/null +++ b/applications/plugins/game15/application.fam @@ -0,0 +1,12 @@ +App( + appid="Game15", + name="Game 15", + apptype=FlipperAppType.EXTERNAL, + entry_point="game15_app", + cdefines=["APP_GAME15"], + requires=["gui"], + stack_size=1 * 1024, + fap_icon="game15_10px.png", + order=30, + fap_category="Games", +) diff --git a/applications/plugins/game15/game15.c b/applications/plugins/game15/game15.c new file mode 100644 index 000000000..d9b059466 --- /dev/null +++ b/applications/plugins/game15/game15.c @@ -0,0 +1,468 @@ +#include +#include +#include +#include +#include + +#include "sandbox.h" + +#define FPS 20 +#define CELL_WIDTH 10 +#define CELL_HEIGHT 8 +#define MOVE_TICKS 5 +#define KEY_STACK_SIZE 16 +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/game15.save" +#define POPUP_MENU_ITEMS 2 + +typedef enum { + DirectionNone, + DirectionUp, + DirectionDown, + DirectionLeft, + DirectionRight +} direction_e; + +typedef enum { ScenePlay, SceneWin, ScenePopup } scene_e; + +typedef struct { + uint8_t cell_index; + uint8_t zero_index; + uint8_t move_direction; + uint8_t move_ticks; +} moving_cell_t; + +typedef struct { + uint16_t top_record; + scene_e scene; + uint16_t move_count; + uint32_t tick_count; + uint8_t board[16]; +} game_state_t; + +static game_state_t game_state; +static NotificationApp* notification; +static moving_cell_t moving_cell; +static uint8_t loaded_saving_ticks; +static uint8_t popup_menu_selected_item; + +static const char* popup_menu_strings[] = {"Continue", "Reset"}; + +static uint8_t keys[KEY_STACK_SIZE]; +static uint8_t key_stack_head = 0; + +static const uint8_t pic_cells[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x30, 0xfc, 0x38, 0xfc, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0xfc, 0x30, 0xfc, + 0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x60, 0xfc, 0x30, 0xfc, 0x18, 0xfc, 0x0c, 0xfc, 0xfc, 0xfc, + 0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x60, 0xfc, 0xc0, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc, + 0x70, 0xfc, 0x78, 0xfc, 0x68, 0xfc, 0x6c, 0xfc, 0x6c, 0xfc, 0xec, 0xfc, 0xfc, 0xfc, 0x60, 0xfc, + 0xfc, 0xfc, 0x0c, 0xfc, 0x0c, 0xfc, 0x7c, 0xfc, 0xc0, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc, + 0x78, 0xfc, 0x0c, 0xfc, 0x0c, 0xfc, 0x7c, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc, + 0xfc, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, + 0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0x78, 0xfc, + 0x78, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0xcc, 0xfc, 0xf8, 0xfc, 0xc0, 0xfc, 0xc0, 0xfc, 0x78, 0xfc, + 0xe6, 0xfd, 0x37, 0xff, 0x36, 0xff, 0x36, 0xff, 0x36, 0xff, 0x36, 0xff, 0x36, 0xff, 0xe6, 0xfd, + 0x8c, 0xfd, 0xce, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, 0x8c, 0xfd, + 0xe6, 0xfd, 0x37, 0xff, 0x36, 0xff, 0x86, 0xfd, 0xc6, 0xfc, 0x66, 0xfc, 0x36, 0xfc, 0xf6, 0xff, + 0xe6, 0xfd, 0x37, 0xff, 0x36, 0xff, 0x86, 0xfd, 0x06, 0xff, 0x36, 0xff, 0x36, 0xff, 0xe6, 0xfd, + 0xc6, 0xfd, 0xe7, 0xfd, 0xa6, 0xfd, 0xb6, 0xfd, 0xb6, 0xfd, 0xb6, 0xff, 0xf6, 0xff, 0x86, 0xfd, + 0xf6, 0xff, 0x37, 0xfc, 0x36, 0xfc, 0xf6, 0xfd, 0x06, 0xff, 0x36, 0xff, 0x36, 0xff, 0xe6, 0xfd, +}; + +static const uint8_t pic_digits[] = { + 0xf0, 0xf2, 0xf2, 0xf2, 0xf2, 0xf0, 0xf9, 0xf8, 0xf9, 0xf9, 0xf9, 0xf0, 0xf0, 0xf2, 0xf3, + 0xf1, 0xfc, 0xf0, 0xf0, 0xf3, 0xf1, 0xf3, 0xf2, 0xf0, 0xf3, 0xf1, 0xf2, 0xf2, 0xf0, 0xf3, + 0xf0, 0xfc, 0xf0, 0xf3, 0xf2, 0xf0, 0x00, 0x0c, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x03, 0x03, 0x00, +}; + +static const uint8_t pic_top[] = {11, 4, 0x88, 0xf8, 0xad, 0xfa, 0xad, 0xf8, 0x8d, 0xfe}; +static const uint8_t pic_move[] = + {17, 4, 0x2e, 0x2a, 0xfe, 0xa4, 0xaa, 0xff, 0xaa, 0x2a, 0xff, 0x2e, 0x36, 0xfe}; +static const uint8_t pic_time[] = {15, 4, 0xa8, 0x8b, 0x2d, 0xe9, 0xad, 0xca, 0xad, 0x8b}; + +static const uint8_t pic_puzzled[] = { + 0xff, 0xcf, 0x00, 0xf3, 0xff, 0xfc, 0x3f, 0x03, 0xc0, 0xff, 0xf3, 0x0f, 0xdc, 0xff, 0xcf, + 0x00, 0xf3, 0xff, 0xfc, 0x3f, 0x03, 0xc0, 0xff, 0xf3, 0x0f, 0xdc, 0x03, 0xcc, 0x00, 0x03, + 0x38, 0x00, 0x0e, 0x03, 0xc0, 0x00, 0x30, 0x30, 0xdc, 0x03, 0xcc, 0x00, 0x03, 0x1c, 0x00, + 0x07, 0x03, 0xc0, 0x00, 0x30, 0x30, 0xdc, 0xff, 0xcf, 0x00, 0x03, 0x0e, 0x80, 0x03, 0x03, + 0xc0, 0xff, 0x33, 0xc0, 0xdc, 0xff, 0xcf, 0x00, 0x03, 0x07, 0xc0, 0x01, 0x03, 0xc0, 0xff, + 0x33, 0xc0, 0xdc, 0x03, 0xc0, 0x00, 0x83, 0x03, 0xe0, 0x00, 0x03, 0xc0, 0x00, 0x30, 0xc0, + 0xd0, 0x03, 0xc0, 0x00, 0xc3, 0x01, 0x70, 0x00, 0x03, 0xc0, 0x00, 0x30, 0xc0, 0xd0, 0x03, + 0xc0, 0xff, 0xf3, 0xff, 0xfc, 0x3f, 0xff, 0xcf, 0xff, 0xf3, 0xff, 0xdc, 0x03, 0xc0, 0xff, + 0xf3, 0xff, 0xfc, 0x3f, 0xff, 0xcf, 0xff, 0xf3, 0xff, 0xdc}; + +static void key_stack_init() { + key_stack_head = 0; +} + +static uint8_t key_stack_pop() { + return keys[--key_stack_head]; +} + +static bool key_stack_is_empty() { + return key_stack_head == 0; +} + +static int key_stack_push(uint8_t value) { + if(key_stack_head != KEY_STACK_SIZE) { + keys[key_stack_head] = value; + key_stack_head++; + return key_stack_head; + } else + return -1; +} + +static bool storage_game_state_load() { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) + bytes_readed = storage_file_read(file, &game_state, sizeof(game_state_t)); + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return bytes_readed == sizeof(game_state_t); +} + +static void storage_game_state_save() { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, &game_state, sizeof(game_state_t)); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static void set_moving_cell_by_direction(direction_e direction) { + moving_cell.move_direction = DirectionNone; + moving_cell.zero_index = 0xff; + + for(int i = 0; i < 16; i++) { + if(!game_state.board[i]) { + moving_cell.zero_index = i; + break; + } + } + if(moving_cell.zero_index == 0xff) return; + + uint8_t x = moving_cell.zero_index % 4; + uint8_t y = moving_cell.zero_index / 4; + + moving_cell.cell_index = moving_cell.zero_index; + + if(direction == DirectionUp && y < 3) + moving_cell.cell_index += 4; + else if(direction == DirectionDown && y > 0) + moving_cell.cell_index -= 4; + else if(direction == DirectionLeft && x < 3) + moving_cell.cell_index++; + else if(direction == DirectionRight && x > 0) + moving_cell.cell_index--; + else + return; + + moving_cell.move_ticks = 0; + moving_cell.move_direction = direction; +} + +static bool is_board_has_solution() { + uint8_t i, j, inv = 0; + for(i = 0; i < 16; ++i) + if(game_state.board[i]) + for(j = 0; j < i; ++j) + if(game_state.board[j] > game_state.board[i]) ++inv; + for(i = 0; i < 16; ++i) + if(game_state.board[i] == 0) inv += 1 + i / 4; + + return inv % 2 == 0; +} + +static void board_init() { + for(int i = 0; i < 16; i++) { + game_state.board[i] = (i + 1) % 16; + } + + do { + for(int i = 15; i >= 1; i--) { + int j = rand() % (i + 1); + uint8_t tmp = game_state.board[j]; + game_state.board[j] = game_state.board[i]; + game_state.board[i] = tmp; + } + } while(!is_board_has_solution()); +} + +static void game_init() { + game_state.scene = ScenePlay; + game_state.move_count = 0; + game_state.tick_count = 0; + moving_cell.move_direction = DirectionNone; + board_init(); + key_stack_init(); + popup_menu_selected_item = 0; +} + +static bool is_board_solved() { + for(int i = 0; i < 16; i++) + if(((i + 1) % 16) != game_state.board[i]) return false; + return true; +} + +static void game_tick() { + switch(game_state.scene) { + case ScenePlay: + game_state.tick_count++; + if(loaded_saving_ticks) loaded_saving_ticks--; + if(moving_cell.move_direction == DirectionNone && !key_stack_is_empty()) { + set_moving_cell_by_direction(key_stack_pop()); + if(moving_cell.move_direction == DirectionNone) { + notification_message(notification, &sequence_single_vibro); + key_stack_init(); + } + } + + if(moving_cell.move_direction != DirectionNone) { + moving_cell.move_ticks++; + if(moving_cell.move_ticks == MOVE_TICKS) { + game_state.board[moving_cell.zero_index] = + game_state.board[moving_cell.cell_index]; + game_state.board[moving_cell.cell_index] = 0; + moving_cell.move_direction = DirectionNone; + game_state.move_count++; + } + if(is_board_solved()) { + notification_message(notification, &sequence_double_vibro); + if(game_state.move_count < game_state.top_record || game_state.top_record == 0) { + game_state.top_record = game_state.move_count; + storage_game_state_save(); + } + game_state.scene = SceneWin; + } + } + break; + + case SceneWin: + if(!key_stack_is_empty()) game_init(); + break; + + case ScenePopup: + if(!key_stack_is_empty()) { + switch(key_stack_pop()) { + case DirectionDown: + popup_menu_selected_item++; + popup_menu_selected_item = popup_menu_selected_item % POPUP_MENU_ITEMS; + break; + case DirectionUp: + popup_menu_selected_item--; + popup_menu_selected_item = popup_menu_selected_item % POPUP_MENU_ITEMS; + break; + case DirectionNone: + if(popup_menu_selected_item == 0) { + game_state.scene = ScenePlay; + notification_message(notification, &sequence_single_vibro); + } else if(popup_menu_selected_item == 1) { + notification_message(notification, &sequence_single_vibro); + game_init(); + } + break; + } + } + break; + } +} + +static void draw_cell(Canvas* canvas, uint8_t x, uint8_t y, uint8_t cell_number) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, x, y, 18, 14, 1); + canvas_set_color(canvas, ColorBlack); + canvas_draw_xbm(canvas, x + 4, y + 3, CELL_WIDTH, CELL_HEIGHT, pic_cells + cell_number * 16); +} + +static void board_draw(Canvas* canvas) { + for(int i = 0; i < 16; i++) { + if(game_state.board[i]) { + if(moving_cell.move_direction == DirectionNone || moving_cell.cell_index != i) + draw_cell(canvas, (i % 4) * 20 + 7, (i / 4) * 16 + 1, game_state.board[i]); + if(moving_cell.move_direction != DirectionNone && moving_cell.cell_index == i) { + uint8_t from_x = (moving_cell.cell_index % 4) * 20 + 7; + uint8_t from_y = (moving_cell.cell_index / 4) * 16 + 1; + uint8_t to_x = (moving_cell.zero_index % 4) * 20 + 7; + uint8_t to_y = (moving_cell.zero_index / 4) * 16 + 1; + int now_x = from_x + (to_x - from_x) * moving_cell.move_ticks / MOVE_TICKS; + int now_y = from_y + (to_y - from_y) * moving_cell.move_ticks / MOVE_TICKS; + draw_cell(canvas, now_x, now_y, game_state.board[i]); + } + } + } +} + +static void number_draw(Canvas* canvas, uint8_t y, uint32_t value) { + uint8_t x = 121; + while(true) { + uint8_t digit = value % 10; + canvas_draw_xbm(canvas, x, y, 4, 6, pic_digits + digit * 6); + x -= 5; + value = value / 10; + if(!value) break; + } +} + +static void plate_draw( + Canvas* canvas, + uint8_t y, + const uint8_t* header, + uint32_t value, + bool dont_draw_zero_value) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_rbox(canvas, 92, y, 35, 19, 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_xbm(canvas, 95, y + 3, header[0], header[1], &header[2]); + if((!value && !dont_draw_zero_value) || value) number_draw(canvas, y + 10, value); +} + +static void info_draw(Canvas* canvas) { + plate_draw(canvas, 1, pic_top, game_state.top_record, true); + plate_draw(canvas, 22, pic_move, game_state.move_count, false); + plate_draw(canvas, 43, pic_time, game_state.tick_count / FPS, false); +} + +static void gray_screen(Canvas* const canvas) { + canvas_set_color(canvas, ColorWhite); + for(int x = 0; x < 128; x += 2) { + for(int y = 0; y < 64; y++) { + canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y); + } + } +} + +static void render_callback(Canvas* const canvas) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 0, 128, 64); + + if(game_state.scene == ScenePlay || game_state.scene == SceneWin || + game_state.scene == ScenePopup) { + canvas_set_color(canvas, ColorBlack); + board_draw(canvas); + info_draw(canvas); + + if(loaded_saving_ticks && game_state.scene != ScenePopup) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, 20, 24, 88, 16, 4); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 20, 24, 88, 16, 4); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "Restore game ..."); + } + } + + if(game_state.scene == SceneWin) { + gray_screen(canvas); + canvas_draw_box(canvas, 7, 20, 114, 24); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 8, 21, 112, 22); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 10, 23, 108, 18); + canvas_set_color(canvas, ColorBlack); + canvas_draw_xbm(canvas, 14, 27, 100, 10, pic_puzzled); + } else if(game_state.scene == ScenePopup) { + gray_screen(canvas); + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, 28, 16, 72, 32, 4); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 28, 16, 72, 32, 4); + + for(int i = 0; i < POPUP_MENU_ITEMS; i++) { + if(i == popup_menu_selected_item) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 34, 20 + 12 * i, 60, 12); + } + + canvas_set_color(canvas, i == popup_menu_selected_item ? ColorWhite : ColorBlack); + canvas_draw_str_aligned( + canvas, 64, 26 + 12 * i, AlignCenter, AlignCenter, popup_menu_strings[i]); + } + } +} + +static void game_event_handler(GameEvent const event) { + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + key_stack_push(DirectionUp); + break; + case InputKeyDown: + key_stack_push(DirectionDown); + break; + case InputKeyRight: + key_stack_push(DirectionRight); + break; + case InputKeyLeft: + key_stack_push(DirectionLeft); + break; + case InputKeyOk: + if(game_state.scene == ScenePlay) { + game_state.scene = ScenePopup; + key_stack_init(); + } else + key_stack_push(DirectionNone); + break; + case InputKeyBack: + if(game_state.scene == ScenePopup) { + game_state.scene = ScenePlay; + } else { + storage_game_state_save(); + sandbox_loop_exit(); + } + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + game_tick(); + } +} + +static void game_alloc() { + key_stack_init(); + notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_display_backlight_enforce_on); +} + +static void game_free() { + notification_message_block(notification, &sequence_display_backlight_enforce_auto); + furi_record_close(RECORD_NOTIFICATION); +} + +int32_t game15_app() { + game_alloc(); + game_init(); + + loaded_saving_ticks = 0; + if(storage_game_state_load()) { + if(game_state.scene != ScenePlay) + game_init(); + else + loaded_saving_ticks = FPS; + } else + game_init(); + + sandbox_init( + FPS, (SandboxRenderCallback)render_callback, (SandboxEventHandler)game_event_handler); + sandbox_loop(); + sandbox_free(); + game_free(); + return 0; +} diff --git a/applications/plugins/game15/game15_10px.png b/applications/plugins/game15/game15_10px.png new file mode 100644 index 000000000..16c4f1038 Binary files /dev/null and b/applications/plugins/game15/game15_10px.png differ diff --git a/applications/plugins/game15/images/Game15.png b/applications/plugins/game15/images/Game15.png new file mode 100644 index 000000000..f13c2907b Binary files /dev/null and b/applications/plugins/game15/images/Game15.png differ diff --git a/applications/plugins/game15/images/Game15Popup.png b/applications/plugins/game15/images/Game15Popup.png new file mode 100644 index 000000000..1df14729f Binary files /dev/null and b/applications/plugins/game15/images/Game15Popup.png differ diff --git a/applications/plugins/game15/images/Game15Restore.png b/applications/plugins/game15/images/Game15Restore.png new file mode 100644 index 000000000..05aac27f6 Binary files /dev/null and b/applications/plugins/game15/images/Game15Restore.png differ diff --git a/applications/plugins/game15/sandbox.c b/applications/plugins/game15/sandbox.c new file mode 100644 index 000000000..e3b759fc8 --- /dev/null +++ b/applications/plugins/game15/sandbox.c @@ -0,0 +1,93 @@ +#include +#include +#include "sandbox.h" + +FuriMessageQueue* sandbox_event_queue; +FuriMutex** sandbox_mutex; +ViewPort* sandbox_view_port; +Gui* sandbox_gui; +FuriTimer* sandbox_timer; +bool sandbox_loop_processing; +SandboxRenderCallback sandbox_user_render_callback; +SandboxEventHandler sandbox_user_event_handler; + +static void sandbox_render_callback(Canvas* const canvas, void* context) { + UNUSED(context); + if(furi_mutex_acquire(sandbox_mutex, 25) != FuriStatusOk) return; + + if(sandbox_user_render_callback) sandbox_user_render_callback(canvas); + + furi_mutex_release(sandbox_mutex); +} + +static void sandbox_input_callback(InputEvent* input_event, void* context) { + UNUSED(context); + GameEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(sandbox_event_queue, &event, FuriWaitForever); +} + +static void sandbox_timer_callback(void* context) { + UNUSED(context); + GameEvent event = {.type = EventTypeTick}; + furi_message_queue_put(sandbox_event_queue, &event, 0); +} + +void sandbox_loop() { + sandbox_loop_processing = true; + while(sandbox_loop_processing) { + GameEvent event; + FuriStatus event_status = furi_message_queue_get(sandbox_event_queue, &event, 100); + if(event_status != FuriStatusOk) { + // timeout + continue; + } + + furi_mutex_acquire(sandbox_mutex, FuriWaitForever); + + if(sandbox_user_event_handler) sandbox_user_event_handler(event); + + view_port_update(sandbox_view_port); + furi_mutex_release(sandbox_mutex); + } +} + +void sandbox_loop_exit() { + sandbox_loop_processing = false; +} + +void sandbox_init( + uint8_t fps, + SandboxRenderCallback u_render_callback, + SandboxEventHandler u_event_handler) { + sandbox_user_render_callback = u_render_callback; + sandbox_user_event_handler = u_event_handler; + + sandbox_event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + sandbox_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + sandbox_view_port = view_port_alloc(); + view_port_draw_callback_set(sandbox_view_port, sandbox_render_callback, NULL); + view_port_input_callback_set(sandbox_view_port, sandbox_input_callback, NULL); + + sandbox_gui = furi_record_open(RECORD_GUI); + gui_add_view_port(sandbox_gui, sandbox_view_port, GuiLayerFullscreen); + + if(fps > 0) { + sandbox_timer = furi_timer_alloc(sandbox_timer_callback, FuriTimerTypePeriodic, NULL); + furi_timer_start(sandbox_timer, furi_kernel_get_tick_frequency() / fps); + } else + sandbox_timer = NULL; +} + +void sandbox_free() { + if(sandbox_timer) furi_timer_free(sandbox_timer); + + gui_remove_view_port(sandbox_gui, sandbox_view_port); + view_port_enabled_set(sandbox_view_port, false); + view_port_free(sandbox_view_port); + + if(furi_mutex_acquire(sandbox_mutex, FuriWaitForever) == FuriStatusOk) { + furi_mutex_free(sandbox_mutex); + } + furi_message_queue_free(sandbox_event_queue); +} diff --git a/applications/plugins/game15/sandbox.h b/applications/plugins/game15/sandbox.h new file mode 100644 index 000000000..ea7dff37b --- /dev/null +++ b/applications/plugins/game15/sandbox.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +typedef void (*SandboxRenderCallback)(Canvas* canvas); +typedef void (*SandboxEventHandler)(GameEvent event); + +void sandbox_init( + uint8_t fps, + SandboxRenderCallback render_callback, + SandboxEventHandler event_handler); +void sandbox_loop(); +void sandbox_loop_exit(); +void sandbox_free(); diff --git a/applications/plugins/game2048/game_2048.c b/applications/plugins/game2048/game_2048.c index 9e39267fd..706b95ce5 100644 --- a/applications/plugins/game2048/game_2048.c +++ b/applications/plugins/game2048/game_2048.c @@ -466,6 +466,8 @@ int32_t game_2048_app(void* p) { case InputKeyBack: loop = false; break; + default: + break; } } else if(event.type == InputTypeLong) { if(event.key == InputKeyOk) { diff --git a/applications/plugins/Tuning Fork/LICENSE b/applications/plugins/gps_nmea_uart/LICENSE similarity index 100% rename from applications/plugins/Tuning Fork/LICENSE rename to applications/plugins/gps_nmea_uart/LICENSE diff --git a/applications/plugins/gps_nmea_uart/README.md b/applications/plugins/gps_nmea_uart/README.md new file mode 100644 index 000000000..3f7a5ef3d --- /dev/null +++ b/applications/plugins/gps_nmea_uart/README.md @@ -0,0 +1,38 @@ +# GPS for Flipper Zero + +A simple Flipper Zero application for NMEA 0183 serial GPS modules, such as the + +[Original link](https://github.com/ezod/flipperzero-gps) +[Adafruit Ultimate GPS Breakout]. + +[Original link](https://github.com/ezod/flipperzero-gps) + +![ui](ui.png) + +Heavy lifting (NMEA parsing) provided by [minmea], which is included in this +repository. + +## Hardware Setup + +Connect the GPS module to power and the USART using GPIO pins 9 (3.3V), 11 +(GND), 13 (TX), and 14 (RX), as appropriate. + +![wiring](wiring.png) + + +## Contributing + +This project was a learning exercise and is more or less "complete" from my +perspective, but I will happily accept pull requests that improve and enhance +the functionality for others. + +Currently, the app only parses RMC and GGA sentences, and displays a subset of +the data that fits on the screen. The UART is also hard-coded to 9600 baud. +These limitations are largely driven by the GPS module I have to work with. A +more elaborate UI with scrolling or multiple screens, as well as a configurable +baud rate, may be useful for other GPS modules. + +[Adafruit Ultimate GPS Breakout]: https://www.adafruit.com/product/746 +[minmea]: https://github.com/kosma/minmea +[flipperzero-firmware]: https://github.com/flipperdevices/flipperzero-firmware +[qFlipper]: https://flipperzero.one/update diff --git a/applications/plugins/gps_nmea_uart/application.fam b/applications/plugins/gps_nmea_uart/application.fam new file mode 100644 index 000000000..138fb3f29 --- /dev/null +++ b/applications/plugins/gps_nmea_uart/application.fam @@ -0,0 +1,12 @@ +App( + appid="NMEA_GPS", + name="[NMEA] GPS", + apptype=FlipperAppType.EXTERNAL, + entry_point="gps_app", + cdefines=["APP_GPS"], + requires=["gui"], + stack_size=1 * 1024, + order=35, + fap_icon="gps_10px.png", + fap_category="GPIO", +) diff --git a/applications/plugins/gps_nmea_uart/gps.c b/applications/plugins/gps_nmea_uart/gps.c new file mode 100644 index 000000000..62053cede --- /dev/null +++ b/applications/plugins/gps_nmea_uart/gps.c @@ -0,0 +1,133 @@ +#include "gps_uart.h" + +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +static void render_callback(Canvas* const canvas, void* context) { + const GpsUart* gps_uart = acquire_mutex((ValueMutex*)context, 25); + if(gps_uart == NULL) { + return; + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 32, 8, AlignCenter, AlignBottom, "Latitude"); + canvas_draw_str_aligned(canvas, 96, 8, AlignCenter, AlignBottom, "Longitude"); + canvas_draw_str_aligned(canvas, 21, 30, AlignCenter, AlignBottom, "Course"); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignBottom, "Speed"); + canvas_draw_str_aligned(canvas, 107, 30, AlignCenter, AlignBottom, "Altitude"); + canvas_draw_str_aligned(canvas, 32, 52, AlignCenter, AlignBottom, "Satellites"); + canvas_draw_str_aligned(canvas, 96, 52, AlignCenter, AlignBottom, "Last Fix"); + + canvas_set_font(canvas, FontSecondary); + char buffer[64]; + snprintf(buffer, 64, "%f", (double)gps_uart->status.latitude); + canvas_draw_str_aligned(canvas, 32, 18, AlignCenter, AlignBottom, buffer); + snprintf(buffer, 64, "%f", (double)gps_uart->status.longitude); + canvas_draw_str_aligned(canvas, 96, 18, AlignCenter, AlignBottom, buffer); + snprintf(buffer, 64, "%.1f", (double)gps_uart->status.course); + canvas_draw_str_aligned(canvas, 21, 40, AlignCenter, AlignBottom, buffer); + snprintf(buffer, 64, "%.2f kn", (double)gps_uart->status.speed); + canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignBottom, buffer); + snprintf( + buffer, + 64, + "%.1f %c", + (double)gps_uart->status.altitude, + tolower(gps_uart->status.altitude_units)); + canvas_draw_str_aligned(canvas, 107, 40, AlignCenter, AlignBottom, buffer); + snprintf(buffer, 64, "%d", gps_uart->status.satellites_tracked); + canvas_draw_str_aligned(canvas, 32, 62, AlignCenter, AlignBottom, buffer); + snprintf( + buffer, + 64, + "%02d:%02d:%02d UTC", + gps_uart->status.time_hours, + gps_uart->status.time_minutes, + gps_uart->status.time_seconds); + canvas_draw_str_aligned(canvas, 96, 62, AlignCenter, AlignBottom, buffer); + + release_mutex((ValueMutex*)context, gps_uart); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +int32_t gps_app(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + GpsUart* gps_uart = gps_uart_enable(); + + ValueMutex gps_uart_mutex; + if(!init_mutex(&gps_uart_mutex, gps_uart, sizeof(GpsUart))) { + FURI_LOG_E("GPS", "cannot create mutex\r\n"); + free(gps_uart); + return 255; + } + + // set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &gps_uart_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + GpsUart* gps_uart = (GpsUart*)acquire_mutex_block(&gps_uart_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyRight: + case InputKeyLeft: + case InputKeyOk: + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } + } + + view_port_update(view_port); + release_mutex(&gps_uart_mutex, gps_uart); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&gps_uart_mutex); + gps_uart_disable(gps_uart); + + return 0; +} diff --git a/applications/plugins/gps_nmea_uart/gps_10px.png b/applications/plugins/gps_nmea_uart/gps_10px.png new file mode 100644 index 000000000..841787a2a Binary files /dev/null and b/applications/plugins/gps_nmea_uart/gps_10px.png differ diff --git a/applications/plugins/gps_nmea_uart/gps_uart.c b/applications/plugins/gps_nmea_uart/gps_uart.c new file mode 100644 index 000000000..52ba660bc --- /dev/null +++ b/applications/plugins/gps_nmea_uart/gps_uart.c @@ -0,0 +1,173 @@ +#include + +#include "minmea.h" +#include "gps_uart.h" + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), +} WorkerEvtFlags; + +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) + +static void gps_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { + GpsUart* gps_uart = (GpsUart*)context; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(gps_uart->rx_stream, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtRxDone); + } +} + +static void gps_uart_serial_init(GpsUart* gps_uart) { + furi_hal_console_disable(); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, gps_uart_on_irq_cb, gps_uart); + furi_hal_uart_set_br(FuriHalUartIdUSART1, GPS_BAUDRATE); +} + +static void gps_uart_serial_deinit(GpsUart* gps_uart) { + UNUSED(gps_uart); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); + furi_hal_console_enable(); +} + +static void gps_uart_parse_nmea(GpsUart* gps_uart, char* line) { + switch(minmea_sentence_id(line, false)) { + case MINMEA_SENTENCE_RMC: { + struct minmea_sentence_rmc frame; + if(minmea_parse_rmc(&frame, line)) { + gps_uart->status.valid = frame.valid; + gps_uart->status.latitude = minmea_tocoord(&frame.latitude); + gps_uart->status.longitude = minmea_tocoord(&frame.longitude); + gps_uart->status.speed = minmea_tofloat(&frame.speed); + gps_uart->status.course = minmea_tofloat(&frame.course); + gps_uart->status.time_hours = frame.time.hours; + gps_uart->status.time_minutes = frame.time.minutes; + gps_uart->status.time_seconds = frame.time.seconds; + + notification_message_block(gps_uart->notifications, &sequence_blink_green_10); + } + } break; + + case MINMEA_SENTENCE_GGA: { + struct minmea_sentence_gga frame; + if(minmea_parse_gga(&frame, line)) { + gps_uart->status.latitude = minmea_tocoord(&frame.latitude); + gps_uart->status.longitude = minmea_tocoord(&frame.longitude); + gps_uart->status.altitude = minmea_tofloat(&frame.altitude); + gps_uart->status.altitude_units = frame.altitude_units; + gps_uart->status.fix_quality = frame.fix_quality; + gps_uart->status.satellites_tracked = frame.satellites_tracked; + gps_uart->status.time_hours = frame.time.hours; + gps_uart->status.time_minutes = frame.time.minutes; + gps_uart->status.time_seconds = frame.time.seconds; + + notification_message_block(gps_uart->notifications, &sequence_blink_magenta_10); + } + } break; + + default: + break; + } +} + +static int32_t gps_uart_worker(void* context) { + GpsUart* gps_uart = (GpsUart*)context; + + gps_uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE * 5, 1); + size_t rx_offset = 0; + + gps_uart_serial_init(gps_uart); + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEvtStop) { + break; + } + + if(events & WorkerEvtRxDone) { + size_t len = 0; + do { + len = furi_stream_buffer_receive( + gps_uart->rx_stream, + gps_uart->rx_buf + rx_offset, + RX_BUF_SIZE - 1 - rx_offset, + 0); + if(len > 0) { + rx_offset += len; + gps_uart->rx_buf[rx_offset] = '\0'; + + char* line_current = (char*)gps_uart->rx_buf; + while(1) { + while(*line_current == '\0' && + line_current < (char*)gps_uart->rx_buf + rx_offset - 1) { + line_current++; + } + + char* newline = strchr(line_current, '\n'); + if(newline) { + *newline = '\0'; + gps_uart_parse_nmea(gps_uart, line_current); + line_current = newline + 1; + } else { + if(line_current > (char*)gps_uart->rx_buf) { + rx_offset = 0; + while(*line_current) { + gps_uart->rx_buf[rx_offset++] = *(line_current++); + } + } + break; + } + } + } + } while(len > 0); + } + } + + gps_uart_serial_deinit(gps_uart); + furi_stream_buffer_free(gps_uart->rx_stream); + + return 0; +} + +GpsUart* gps_uart_enable() { + GpsUart* gps_uart = malloc(sizeof(GpsUart)); + + gps_uart->status.valid = false; + gps_uart->status.latitude = 0.0; + gps_uart->status.longitude = 0.0; + gps_uart->status.speed = 0.0; + gps_uart->status.course = 0.0; + gps_uart->status.altitude = 0.0; + gps_uart->status.altitude_units = ' '; + gps_uart->status.fix_quality = 0; + gps_uart->status.satellites_tracked = 0; + gps_uart->status.time_hours = 0; + gps_uart->status.time_minutes = 0; + gps_uart->status.time_seconds = 0; + + gps_uart->notifications = furi_record_open(RECORD_NOTIFICATION); + + gps_uart->thread = furi_thread_alloc(); + furi_thread_set_name(gps_uart->thread, "GpsUartWorker"); + furi_thread_set_stack_size(gps_uart->thread, 1024); + furi_thread_set_context(gps_uart->thread, gps_uart); + furi_thread_set_callback(gps_uart->thread, gps_uart_worker); + + furi_thread_start(gps_uart->thread); + return gps_uart; +} + +void gps_uart_disable(GpsUart* gps_uart) { + furi_assert(gps_uart); + furi_thread_flags_set(furi_thread_get_id(gps_uart->thread), WorkerEvtStop); + furi_thread_join(gps_uart->thread); + furi_thread_free(gps_uart->thread); + + furi_record_close(RECORD_NOTIFICATION); + + free(gps_uart); +} diff --git a/applications/plugins/gps_nmea_uart/gps_uart.h b/applications/plugins/gps_nmea_uart/gps_uart.h new file mode 100644 index 000000000..d6aafae9f --- /dev/null +++ b/applications/plugins/gps_nmea_uart/gps_uart.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#define GPS_BAUDRATE 9600 +#define RX_BUF_SIZE 1024 + +typedef struct { + bool valid; + float latitude; + float longitude; + float speed; + float course; + float altitude; + char altitude_units; + int fix_quality; + int satellites_tracked; + int time_hours; + int time_minutes; + int time_seconds; +} GpsStatus; + +typedef struct { + FuriThread* thread; + FuriStreamBuffer* rx_stream; + uint8_t rx_buf[RX_BUF_SIZE]; + + NotificationApp* notifications; + + GpsStatus status; +} GpsUart; + +GpsUart* gps_uart_enable(); + +void gps_uart_disable(GpsUart* gps_uart); diff --git a/applications/plugins/gps_nmea_uart/minmea.c b/applications/plugins/gps_nmea_uart/minmea.c new file mode 100644 index 000000000..1b7a84b1c --- /dev/null +++ b/applications/plugins/gps_nmea_uart/minmea.c @@ -0,0 +1,640 @@ +/* + * Copyright Β© 2014 Kosma Moczek + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#include "minmea.h" + +#include +#include +#include + +#define boolstr(s) ((s) ? "true" : "false") + +static int hex2int(char c) { + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'A' && c <= 'F') return c - 'A' + 10; + if(c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; +} + +uint8_t minmea_checksum(const char* sentence) { + // Support senteces with or without the starting dollar sign. + if(*sentence == '$') sentence++; + + uint8_t checksum = 0x00; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while(*sentence && *sentence != '*') checksum ^= *sentence++; + + return checksum; +} + +bool minmea_check(const char* sentence, bool strict) { + uint8_t checksum = 0x00; + + // A valid sentence starts with "$". + if(*sentence++ != '$') return false; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while(*sentence && *sentence != '*' && isprint((unsigned char)*sentence)) + checksum ^= *sentence++; + + // If checksum is present... + if(*sentence == '*') { + // Extract checksum. + sentence++; + int upper = hex2int(*sentence++); + if(upper == -1) return false; + int lower = hex2int(*sentence++); + if(lower == -1) return false; + int expected = upper << 4 | lower; + + // Check for checksum mismatch. + if(checksum != expected) return false; + } else if(strict) { + // Discard non-checksummed frames in strict mode. + return false; + } + + // The only stuff allowed at this point is a newline. + while(*sentence == '\r' || *sentence == '\n') { + sentence++; + } + + if(*sentence) { + return false; + } + + return true; +} + +bool minmea_scan(const char* sentence, const char* format, ...) { + bool result = false; + bool optional = false; + + if(sentence == NULL) return false; + + va_list ap; + va_start(ap, format); + + const char* field = sentence; +#define next_field() \ + do { \ + /* Progress to the next field. */ \ + while(minmea_isfield(*sentence)) sentence++; \ + /* Make sure there is a field there. */ \ + if(*sentence == ',') { \ + sentence++; \ + field = sentence; \ + } else { \ + field = NULL; \ + } \ + } while(0) + + while(*format) { + char type = *format++; + + if(type == ';') { + // All further fields are optional. + optional = true; + continue; + } + + if(!field && !optional) { + // Field requested but we ran out if input. Bail out. + goto parse_error; + } + + switch(type) { + case 'c': { // Single character field (char). + char value = '\0'; + + if(field && minmea_isfield(*field)) value = *field; + + *va_arg(ap, char*) = value; + } break; + + case 'd': { // Single character direction field (int). + int value = 0; + + if(field && minmea_isfield(*field)) { + switch(*field) { + case 'N': + case 'E': + value = 1; + break; + case 'S': + case 'W': + value = -1; + break; + default: + goto parse_error; + } + } + + *va_arg(ap, int*) = value; + } break; + + case 'f': { // Fractional value with scale (struct minmea_float). + int sign = 0; + int_least32_t value = -1; + int_least32_t scale = 0; + + if(field) { + while(minmea_isfield(*field)) { + if(*field == '+' && !sign && value == -1) { + sign = 1; + } else if(*field == '-' && !sign && value == -1) { + sign = -1; + } else if(isdigit((unsigned char)*field)) { + int digit = *field - '0'; + if(value == -1) value = 0; + if(value > (INT_LEAST32_MAX - digit) / 10) { + /* we ran out of bits, what do we do? */ + if(scale) { + /* truncate extra precision */ + break; + } else { + /* integer overflow. bail out. */ + goto parse_error; + } + } + value = (10 * value) + digit; + if(scale) scale *= 10; + } else if(*field == '.' && scale == 0) { + scale = 1; + } else if(*field == ' ') { + /* Allow spaces at the start of the field. Not NMEA + * conformant, but some modules do this. */ + if(sign != 0 || value != -1 || scale != 0) goto parse_error; + } else { + goto parse_error; + } + field++; + } + } + + if((sign || scale) && value == -1) goto parse_error; + + if(value == -1) { + /* No digits were scanned. */ + value = 0; + scale = 0; + } else if(scale == 0) { + /* No decimal point. */ + scale = 1; + } + if(sign) value *= sign; + + *va_arg(ap, struct minmea_float*) = (struct minmea_float){value, scale}; + } break; + + case 'i': { // Integer value, default 0 (int). + int value = 0; + + if(field) { + char* endptr; + value = strtol(field, &endptr, 10); + if(minmea_isfield(*endptr)) goto parse_error; + } + + *va_arg(ap, int*) = value; + } break; + + case 's': { // String value (char *). + char* buf = va_arg(ap, char*); + + if(field) { + while(minmea_isfield(*field)) *buf++ = *field++; + } + + *buf = '\0'; + } break; + + case 't': { // NMEA talker+sentence identifier (char *). + // This field is always mandatory. + if(!field) goto parse_error; + + if(field[0] != '$') goto parse_error; + for(int f = 0; f < 5; f++) + if(!minmea_isfield(field[1 + f])) goto parse_error; + + char* buf = va_arg(ap, char*); + memcpy(buf, field + 1, 5); + buf[5] = '\0'; + } break; + + case 'D': { // Date (int, int, int), -1 if empty. + struct minmea_date* date = va_arg(ap, struct minmea_date*); + + int d = -1, m = -1, y = -1; + + if(field && minmea_isfield(*field)) { + // Always six digits. + for(int f = 0; f < 6; f++) + if(!isdigit((unsigned char)field[f])) goto parse_error; + + char dArr[] = {field[0], field[1], '\0'}; + char mArr[] = {field[2], field[3], '\0'}; + char yArr[] = {field[4], field[5], '\0'}; + d = strtol(dArr, NULL, 10); + m = strtol(mArr, NULL, 10); + y = strtol(yArr, NULL, 10); + } + + date->day = d; + date->month = m; + date->year = y; + } break; + + case 'T': { // Time (int, int, int, int), -1 if empty. + struct minmea_time* time_ = va_arg(ap, struct minmea_time*); + + int h = -1, i = -1, s = -1, u = -1; + + if(field && minmea_isfield(*field)) { + // Minimum required: integer time. + for(int f = 0; f < 6; f++) + if(!isdigit((unsigned char)field[f])) goto parse_error; + + char hArr[] = {field[0], field[1], '\0'}; + char iArr[] = {field[2], field[3], '\0'}; + char sArr[] = {field[4], field[5], '\0'}; + h = strtol(hArr, NULL, 10); + i = strtol(iArr, NULL, 10); + s = strtol(sArr, NULL, 10); + field += 6; + + // Extra: fractional time. Saved as microseconds. + if(*field++ == '.') { + uint32_t value = 0; + uint32_t scale = 1000000LU; + while(isdigit((unsigned char)*field) && scale > 1) { + value = (value * 10) + (*field++ - '0'); + scale /= 10; + } + u = value * scale; + } else { + u = 0; + } + } + + time_->hours = h; + time_->minutes = i; + time_->seconds = s; + time_->microseconds = u; + } break; + + case '_': { // Ignore the field. + } break; + + default: { // Unknown. + goto parse_error; + } + } + + next_field(); + } + + result = true; + +parse_error: + va_end(ap); + return result; +} + +bool minmea_talker_id(char talker[3], const char* sentence) { + char type[6]; + if(!minmea_scan(sentence, "t", type)) return false; + + talker[0] = type[0]; + talker[1] = type[1]; + talker[2] = '\0'; + + return true; +} + +enum minmea_sentence_id minmea_sentence_id(const char* sentence, bool strict) { + if(!minmea_check(sentence, strict)) return MINMEA_INVALID; + + char type[6]; + if(!minmea_scan(sentence, "t", type)) return MINMEA_INVALID; + + if(!strcmp(type + 2, "GBS")) return MINMEA_SENTENCE_GBS; + if(!strcmp(type + 2, "GGA")) return MINMEA_SENTENCE_GGA; + if(!strcmp(type + 2, "GLL")) return MINMEA_SENTENCE_GLL; + if(!strcmp(type + 2, "GSA")) return MINMEA_SENTENCE_GSA; + if(!strcmp(type + 2, "GST")) return MINMEA_SENTENCE_GST; + if(!strcmp(type + 2, "GSV")) return MINMEA_SENTENCE_GSV; + if(!strcmp(type + 2, "RMC")) return MINMEA_SENTENCE_RMC; + if(!strcmp(type + 2, "VTG")) return MINMEA_SENTENCE_VTG; + if(!strcmp(type + 2, "ZDA")) return MINMEA_SENTENCE_ZDA; + + return MINMEA_UNKNOWN; +} + +bool minmea_parse_gbs(struct minmea_sentence_gbs* frame, const char* sentence) { + // $GNGBS,170556.00,3.0,2.9,8.3,,,,*5C + char type[6]; + if(!minmea_scan( + sentence, + "tTfffifff", + type, + &frame->time, + &frame->err_latitude, + &frame->err_longitude, + &frame->err_altitude, + &frame->svid, + &frame->prob, + &frame->bias, + &frame->stddev)) + return false; + if(strcmp(type + 2, "GBS")) return false; + + return true; +} + +bool minmea_parse_rmc(struct minmea_sentence_rmc* frame, const char* sentence) { + // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 + char type[6]; + char validity; + int latitude_direction; + int longitude_direction; + int variation_direction; + if(!minmea_scan( + sentence, + "tTcfdfdffDfd", + type, + &frame->time, + &validity, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->speed, + &frame->course, + &frame->date, + &frame->variation, + &variation_direction)) + return false; + if(strcmp(type + 2, "RMC")) return false; + + frame->valid = (validity == 'A'); + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + frame->variation.value *= variation_direction; + + return true; +} + +bool minmea_parse_gga(struct minmea_sentence_gga* frame, const char* sentence) { + // $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 + char type[6]; + int latitude_direction; + int longitude_direction; + + if(!minmea_scan( + sentence, + "tTfdfdiiffcfcf_", + type, + &frame->time, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->fix_quality, + &frame->satellites_tracked, + &frame->hdop, + &frame->altitude, + &frame->altitude_units, + &frame->height, + &frame->height_units, + &frame->dgps_age)) + return false; + if(strcmp(type + 2, "GGA")) return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gsa(struct minmea_sentence_gsa* frame, const char* sentence) { + // $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39 + char type[6]; + + if(!minmea_scan( + sentence, + "tciiiiiiiiiiiiifff", + type, + &frame->mode, + &frame->fix_type, + &frame->sats[0], + &frame->sats[1], + &frame->sats[2], + &frame->sats[3], + &frame->sats[4], + &frame->sats[5], + &frame->sats[6], + &frame->sats[7], + &frame->sats[8], + &frame->sats[9], + &frame->sats[10], + &frame->sats[11], + &frame->pdop, + &frame->hdop, + &frame->vdop)) + return false; + if(strcmp(type + 2, "GSA")) return false; + + return true; +} + +bool minmea_parse_gll(struct minmea_sentence_gll* frame, const char* sentence) { + // $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41$; + char type[6]; + int latitude_direction; + int longitude_direction; + + if(!minmea_scan( + sentence, + "tfdfdTc;c", + type, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->time, + &frame->status, + &frame->mode)) + return false; + if(strcmp(type + 2, "GLL")) return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gst(struct minmea_sentence_gst* frame, const char* sentence) { + // $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58 + char type[6]; + + if(!minmea_scan( + sentence, + "tTfffffff", + type, + &frame->time, + &frame->rms_deviation, + &frame->semi_major_deviation, + &frame->semi_minor_deviation, + &frame->semi_major_orientation, + &frame->latitude_error_deviation, + &frame->longitude_error_deviation, + &frame->altitude_error_deviation)) + return false; + if(strcmp(type + 2, "GST")) return false; + + return true; +} + +bool minmea_parse_gsv(struct minmea_sentence_gsv* frame, const char* sentence) { + // $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 + // $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D + // $GPGSV,4,2,11,08,51,203,30,09,45,215,28*75 + // $GPGSV,4,4,13,39,31,170,27*40 + // $GPGSV,4,4,13*7B + char type[6]; + + if(!minmea_scan( + sentence, + "tiii;iiiiiiiiiiiiiiii", + type, + &frame->total_msgs, + &frame->msg_nr, + &frame->total_sats, + &frame->sats[0].nr, + &frame->sats[0].elevation, + &frame->sats[0].azimuth, + &frame->sats[0].snr, + &frame->sats[1].nr, + &frame->sats[1].elevation, + &frame->sats[1].azimuth, + &frame->sats[1].snr, + &frame->sats[2].nr, + &frame->sats[2].elevation, + &frame->sats[2].azimuth, + &frame->sats[2].snr, + &frame->sats[3].nr, + &frame->sats[3].elevation, + &frame->sats[3].azimuth, + &frame->sats[3].snr)) { + return false; + } + if(strcmp(type + 2, "GSV")) return false; + + return true; +} + +bool minmea_parse_vtg(struct minmea_sentence_vtg* frame, const char* sentence) { + // $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48 + // $GPVTG,156.1,T,140.9,M,0.0,N,0.0,K*41 + // $GPVTG,096.5,T,083.5,M,0.0,N,0.0,K,D*22 + // $GPVTG,188.36,T,,M,0.820,N,1.519,K,A*3F + char type[6]; + char c_true, c_magnetic, c_knots, c_kph, c_faa_mode; + + if(!minmea_scan( + sentence, + "t;fcfcfcfcc", + type, + &frame->true_track_degrees, + &c_true, + &frame->magnetic_track_degrees, + &c_magnetic, + &frame->speed_knots, + &c_knots, + &frame->speed_kph, + &c_kph, + &c_faa_mode)) + return false; + if(strcmp(type + 2, "VTG")) return false; + // values are only valid with the accompanying characters + if(c_true != 'T') frame->true_track_degrees.scale = 0; + if(c_magnetic != 'M') frame->magnetic_track_degrees.scale = 0; + if(c_knots != 'N') frame->speed_knots.scale = 0; + if(c_kph != 'K') frame->speed_kph.scale = 0; + frame->faa_mode = (enum minmea_faa_mode)c_faa_mode; + + return true; +} + +bool minmea_parse_zda(struct minmea_sentence_zda* frame, const char* sentence) { + // $GPZDA,201530.00,04,07,2002,00,00*60 + char type[6]; + + if(!minmea_scan( + sentence, + "tTiiiii", + type, + &frame->time, + &frame->date.day, + &frame->date.month, + &frame->date.year, + &frame->hour_offset, + &frame->minute_offset)) + return false; + if(strcmp(type + 2, "ZDA")) return false; + + // check offsets + if(abs(frame->hour_offset) > 13 || frame->minute_offset > 59 || frame->minute_offset < 0) + return false; + + return true; +} + +int minmea_getdatetime( + struct tm* tm, + const struct minmea_date* date, + const struct minmea_time* time_) { + if(date->year == -1 || time_->hours == -1) return -1; + + memset(tm, 0, sizeof(*tm)); + if(date->year < 80) { + tm->tm_year = 2000 + date->year - 1900; // 2000-2079 + } else if(date->year >= 1900) { + tm->tm_year = date->year - 1900; // 4 digit year, use directly + } else { + tm->tm_year = date->year; // 1980-1999 + } + tm->tm_mon = date->month - 1; + tm->tm_mday = date->day; + tm->tm_hour = time_->hours; + tm->tm_min = time_->minutes; + tm->tm_sec = time_->seconds; + + return 0; +} + +int minmea_gettime( + struct timespec* ts, + const struct minmea_date* date, + const struct minmea_time* time_) { + struct tm tm; + if(minmea_getdatetime(&tm, date, time_)) return -1; + + time_t timestamp = mktime(&tm); /* See README.md if your system lacks timegm(). */ + if(timestamp != (time_t)-1) { + ts->tv_sec = timestamp; + ts->tv_nsec = time_->microseconds * 1000; + return 0; + } else { + return -1; + } +} + +/* vim: set ts=4 sw=4 et: */ diff --git a/applications/plugins/gps_nmea_uart/minmea.h b/applications/plugins/gps_nmea_uart/minmea.h new file mode 100644 index 000000000..88eec4ae9 --- /dev/null +++ b/applications/plugins/gps_nmea_uart/minmea.h @@ -0,0 +1,295 @@ +/* + * Copyright Β© 2014 Kosma Moczek + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#ifndef MINMEA_H +#define MINMEA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#ifdef MINMEA_INCLUDE_COMPAT +#include +#endif + +#ifndef MINMEA_MAX_SENTENCE_LENGTH +#define MINMEA_MAX_SENTENCE_LENGTH 80 +#endif + +enum minmea_sentence_id { + MINMEA_INVALID = -1, + MINMEA_UNKNOWN = 0, + MINMEA_SENTENCE_GBS, + MINMEA_SENTENCE_GGA, + MINMEA_SENTENCE_GLL, + MINMEA_SENTENCE_GSA, + MINMEA_SENTENCE_GST, + MINMEA_SENTENCE_GSV, + MINMEA_SENTENCE_RMC, + MINMEA_SENTENCE_VTG, + MINMEA_SENTENCE_ZDA, +}; + +struct minmea_float { + int_least32_t value; + int_least32_t scale; +}; + +struct minmea_date { + int day; + int month; + int year; +}; + +struct minmea_time { + int hours; + int minutes; + int seconds; + int microseconds; +}; + +struct minmea_sentence_gbs { + struct minmea_time time; + struct minmea_float err_latitude; + struct minmea_float err_longitude; + struct minmea_float err_altitude; + int svid; + struct minmea_float prob; + struct minmea_float bias; + struct minmea_float stddev; +}; + +struct minmea_sentence_rmc { + struct minmea_time time; + bool valid; + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_float speed; + struct minmea_float course; + struct minmea_date date; + struct minmea_float variation; +}; + +struct minmea_sentence_gga { + struct minmea_time time; + struct minmea_float latitude; + struct minmea_float longitude; + int fix_quality; + int satellites_tracked; + struct minmea_float hdop; + struct minmea_float altitude; + char altitude_units; + struct minmea_float height; + char height_units; + struct minmea_float dgps_age; +}; + +enum minmea_gll_status { + MINMEA_GLL_STATUS_DATA_VALID = 'A', + MINMEA_GLL_STATUS_DATA_NOT_VALID = 'V', +}; + +// FAA mode added to some fields in NMEA 2.3. +enum minmea_faa_mode { + MINMEA_FAA_MODE_AUTONOMOUS = 'A', + MINMEA_FAA_MODE_DIFFERENTIAL = 'D', + MINMEA_FAA_MODE_ESTIMATED = 'E', + MINMEA_FAA_MODE_MANUAL = 'M', + MINMEA_FAA_MODE_SIMULATED = 'S', + MINMEA_FAA_MODE_NOT_VALID = 'N', + MINMEA_FAA_MODE_PRECISE = 'P', +}; + +struct minmea_sentence_gll { + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_time time; + char status; + char mode; +}; + +struct minmea_sentence_gst { + struct minmea_time time; + struct minmea_float rms_deviation; + struct minmea_float semi_major_deviation; + struct minmea_float semi_minor_deviation; + struct minmea_float semi_major_orientation; + struct minmea_float latitude_error_deviation; + struct minmea_float longitude_error_deviation; + struct minmea_float altitude_error_deviation; +}; + +enum minmea_gsa_mode { + MINMEA_GPGSA_MODE_AUTO = 'A', + MINMEA_GPGSA_MODE_FORCED = 'M', +}; + +enum minmea_gsa_fix_type { + MINMEA_GPGSA_FIX_NONE = 1, + MINMEA_GPGSA_FIX_2D = 2, + MINMEA_GPGSA_FIX_3D = 3, +}; + +struct minmea_sentence_gsa { + char mode; + int fix_type; + int sats[12]; + struct minmea_float pdop; + struct minmea_float hdop; + struct minmea_float vdop; +}; + +struct minmea_sat_info { + int nr; + int elevation; + int azimuth; + int snr; +}; + +struct minmea_sentence_gsv { + int total_msgs; + int msg_nr; + int total_sats; + struct minmea_sat_info sats[4]; +}; + +struct minmea_sentence_vtg { + struct minmea_float true_track_degrees; + struct minmea_float magnetic_track_degrees; + struct minmea_float speed_knots; + struct minmea_float speed_kph; + enum minmea_faa_mode faa_mode; +}; + +struct minmea_sentence_zda { + struct minmea_time time; + struct minmea_date date; + int hour_offset; + int minute_offset; +}; + +/** + * Calculate raw sentence checksum. Does not check sentence integrity. + */ +uint8_t minmea_checksum(const char* sentence); + +/** + * Check sentence validity and checksum. Returns true for valid sentences. + */ +bool minmea_check(const char* sentence, bool strict); + +/** + * Determine talker identifier. + */ +bool minmea_talker_id(char talker[3], const char* sentence); + +/** + * Determine sentence identifier. + */ +enum minmea_sentence_id minmea_sentence_id(const char* sentence, bool strict); + +/** + * Scanf-like processor for NMEA sentences. Supports the following formats: + * c - single character (char *) + * d - direction, returned as 1/-1, default 0 (int *) + * f - fractional, returned as value + scale (struct minmea_float *) + * i - decimal, default zero (int *) + * s - string (char *) + * t - talker identifier and type (char *) + * D - date (struct minmea_date *) + * T - time stamp (struct minmea_time *) + * _ - ignore this field + * ; - following fields are optional + * Returns true on success. See library source code for details. + */ +bool minmea_scan(const char* sentence, const char* format, ...); + +/* + * Parse a specific type of sentence. Return true on success. + */ +bool minmea_parse_gbs(struct minmea_sentence_gbs* frame, const char* sentence); +bool minmea_parse_rmc(struct minmea_sentence_rmc* frame, const char* sentence); +bool minmea_parse_gga(struct minmea_sentence_gga* frame, const char* sentence); +bool minmea_parse_gsa(struct minmea_sentence_gsa* frame, const char* sentence); +bool minmea_parse_gll(struct minmea_sentence_gll* frame, const char* sentence); +bool minmea_parse_gst(struct minmea_sentence_gst* frame, const char* sentence); +bool minmea_parse_gsv(struct minmea_sentence_gsv* frame, const char* sentence); +bool minmea_parse_vtg(struct minmea_sentence_vtg* frame, const char* sentence); +bool minmea_parse_zda(struct minmea_sentence_zda* frame, const char* sentence); + +/** + * Convert GPS UTC date/time representation to a UNIX calendar time. + */ +int minmea_getdatetime( + struct tm* tm, + const struct minmea_date* date, + const struct minmea_time* time_); + +/** + * Convert GPS UTC date/time representation to a UNIX timestamp. + */ +int minmea_gettime( + struct timespec* ts, + const struct minmea_date* date, + const struct minmea_time* time_); + +/** + * Rescale a fixed-point value to a different scale. Rounds towards zero. + */ +static inline int_least32_t minmea_rescale(const struct minmea_float* f, int_least32_t new_scale) { + if(f->scale == 0) return 0; + if(f->scale == new_scale) return f->value; + if(f->scale > new_scale) + return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale / new_scale / 2) / + (f->scale / new_scale); + else + return f->value * (new_scale / f->scale); +} + +/** + * Convert a fixed-point value to a floating-point value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tofloat(const struct minmea_float* f) { + if(f->scale == 0) return NAN; + return (float)f->value / (float)f->scale; +} + +/** + * Convert a raw coordinate to a floating point DD.DDD... value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tocoord(const struct minmea_float* f) { + if(f->scale == 0) return NAN; + if(f->scale > (INT_LEAST32_MAX / 100)) return NAN; + if(f->scale < (INT_LEAST32_MIN / 100)) return NAN; + int_least32_t degrees = f->value / (f->scale * 100); + int_least32_t minutes = f->value % (f->scale * 100); + return (float)degrees + (float)minutes / (60 * f->scale); +} + +/** + * Check whether a character belongs to the set of characters allowed in a + * sentence data field. + */ +static inline bool minmea_isfield(char c) { + return isprint((unsigned char)c) && c != ',' && c != '*'; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MINMEA_H */ + +/* vim: set ts=4 sw=4 et: */ diff --git a/applications/plugins/gps_nmea_uart/ui.png b/applications/plugins/gps_nmea_uart/ui.png new file mode 100644 index 000000000..8e5214574 Binary files /dev/null and b/applications/plugins/gps_nmea_uart/ui.png differ diff --git a/applications/plugins/gps_nmea_uart/wiring.png b/applications/plugins/gps_nmea_uart/wiring.png new file mode 100644 index 000000000..74b4a4401 Binary files /dev/null and b/applications/plugins/gps_nmea_uart/wiring.png differ diff --git a/applications/plugins/hc_sr04/README.md b/applications/plugins/hc_sr04/README.md new file mode 100644 index 000000000..2fb3b8ce4 --- /dev/null +++ b/applications/plugins/hc_sr04/README.md @@ -0,0 +1 @@ +## (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo) \ No newline at end of file diff --git a/applications/plugins/hc_sr04/application.fam b/applications/plugins/hc_sr04/application.fam new file mode 100644 index 000000000..ad451a102 --- /dev/null +++ b/applications/plugins/hc_sr04/application.fam @@ -0,0 +1,14 @@ +App( + appid="hc_sr04", + name="[HC-SR] Dist. Sensor", + apptype=FlipperAppType.EXTERNAL, + entry_point="hc_sr04_app", + cdefines=["APP_HC_SR04"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=20, + fap_icon="dist_sensor10px.png", + fap_category="GPIO", +) \ No newline at end of file diff --git a/applications/plugins/hc_sr04/dist_sensor10px.png b/applications/plugins/hc_sr04/dist_sensor10px.png new file mode 100644 index 000000000..af9aa7358 Binary files /dev/null and b/applications/plugins/hc_sr04/dist_sensor10px.png differ diff --git a/applications/plugins/hc_sr04/hc_sr04.c b/applications/plugins/hc_sr04/hc_sr04.c new file mode 100644 index 000000000..dbbf4f3ec --- /dev/null +++ b/applications/plugins/hc_sr04/hc_sr04.c @@ -0,0 +1,273 @@ +// insired by +// https://github.com/esphome/esphome/blob/ac0d921413c3884752193fe568fa82853f0f99e9/esphome/components/ultrasonic/ultrasonic_sensor.cpp +// Ported and modified by @xMasterX + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + NotificationApp* notification; + bool have_5v; + bool measurement_made; + uint32_t echo; // ms + float distance; // meters +} PluginState; + +const NotificationSequence sequence_done = { + &message_display_backlight_on, + &message_green_255, + &message_note_c5, + &message_delay_50, + &message_sound_off, + NULL, +}; + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + if(plugin_state == NULL) { + return; + } + // border around the edge of the screen + // canvas_draw_frame(canvas, 0, 0, 128, 64); + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, 64, 2, AlignCenter, AlignTop, "HC-SR04 Ultrasonic\nDistance Sensor"); + + canvas_set_font(canvas, FontSecondary); + + if(!plugin_state->have_5v) { + elements_multiline_text_aligned( + canvas, + 4, + 28, + AlignLeft, + AlignTop, + "5V on GPIO must be\nenabled, or USB must\nbe connected."); + } else { + if(!plugin_state->measurement_made) { + elements_multiline_text_aligned( + canvas, 64, 28, AlignCenter, AlignTop, "Press OK button to measure"); + elements_multiline_text_aligned( + canvas, 64, 40, AlignCenter, AlignTop, "13/TX -> Trig\n14/RX -> Echo"); + } else { + elements_multiline_text_aligned(canvas, 4, 28, AlignLeft, AlignTop, "Readout:"); + + FuriString* str_buf; + str_buf = furi_string_alloc(); + furi_string_printf(str_buf, "Echo: %ld ms", plugin_state->echo); + + canvas_draw_str_aligned( + canvas, 8, 38, AlignLeft, AlignTop, furi_string_get_cstr(str_buf)); + furi_string_printf(str_buf, "Distance: %02f m", (double)plugin_state->distance); + canvas_draw_str_aligned( + canvas, 8, 48, AlignLeft, AlignTop, furi_string_get_cstr(str_buf)); + + furi_string_free(str_buf); + } + } + + release_mutex((ValueMutex*)ctx, plugin_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void hc_sr04_state_init(PluginState* const plugin_state) { + plugin_state->echo = -1; + plugin_state->distance = -1; + plugin_state->measurement_made = false; + + furi_hal_power_suppress_charge_enter(); + + plugin_state->have_5v = false; + if(furi_hal_power_is_otg_enabled() || furi_hal_power_is_charging()) { + plugin_state->have_5v = true; + } else { + furi_hal_power_enable_otg(); + plugin_state->have_5v = true; + } +} + +float hc_sr04_ms_to_m(uint32_t ms) { + const float speed_sound_m_per_s = 343.0f; + const float time_s = ms / 1e3f; + const float total_dist = time_s * speed_sound_m_per_s; + return total_dist / 2.0f; +} + +static void hc_sr04_measure(PluginState* const plugin_state) { + //plugin_state->echo = 1; + //return; + + if(!plugin_state->have_5v) { + if(furi_hal_power_is_otg_enabled() || furi_hal_power_is_charging()) { + plugin_state->have_5v = true; + } else { + return; + } + } + + //furi_hal_light_set(LightRed, 0xFF); + notification_message(plugin_state->notification, &sequence_blink_start_yellow); + + const uint32_t timeout_ms = 2000; + // Pin 13 / TX -> Trig + furi_hal_gpio_write(&gpio_usart_tx, false); + furi_hal_gpio_init(&gpio_usart_tx, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + // Pin 14 / RX -> Echo + furi_hal_gpio_write(&gpio_usart_rx, false); + furi_hal_gpio_init(&gpio_usart_rx, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + + //FURI_CRITICAL_ENTER(); + // 10 ms pulse on TX + furi_hal_gpio_write(&gpio_usart_tx, true); + furi_delay_ms(10); + furi_hal_gpio_write(&gpio_usart_tx, false); + + // TODO change from furi_get_tick(), which returns ms, + // to DWT->CYCCNT, which is a more precise counter with + // us precision (see furi_hal_cortex_delay_us) + + const uint32_t start = furi_get_tick(); + + while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx)) + ; + while(furi_get_tick() - start < timeout_ms && !furi_hal_gpio_read(&gpio_usart_rx)) + ; + + const uint32_t pulse_start = furi_get_tick(); + + while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx)) + ; + + const uint32_t pulse_end = furi_get_tick(); + //FURI_CRITICAL_EXIT(); + + plugin_state->echo = pulse_end - pulse_start; + plugin_state->distance = hc_sr04_ms_to_m(pulse_end - pulse_start); + plugin_state->measurement_made = true; + + //furi_hal_light_set(LightRed, 0x00); + notification_message(plugin_state->notification, &sequence_blink_stop); + notification_message(plugin_state->notification, &sequence_done); +} + +int32_t hc_sr04_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + PluginState* plugin_state = malloc(sizeof(PluginState)); + + hc_sr04_state_init(plugin_state); + + furi_hal_console_disable(); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + FURI_LOG_E("hc_sr04", "cannot create mutex\r\n"); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + furi_hal_console_enable(); + furi_hal_power_suppress_charge_exit(); + furi_message_queue_free(event_queue); + free(plugin_state); + return 255; + } + + plugin_state->notification = furi_record_open(RECORD_NOTIFICATION); + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyRight: + case InputKeyLeft: + break; + case InputKeyOk: + hc_sr04_measure(plugin_state); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } + } + + view_port_update(view_port); + release_mutex(&state_mutex, plugin_state); + } + + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + furi_hal_power_suppress_charge_exit(); + + // Return TX / RX back to usart mode + furi_hal_gpio_init_ex( + &gpio_usart_tx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + furi_hal_gpio_init_ex( + &gpio_usart_rx, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn7USART1); + furi_hal_console_enable(); + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/heap_defence_game/application.fam b/applications/plugins/heap_defence_game/application.fam new file mode 100644 index 000000000..bedd27dbc --- /dev/null +++ b/applications/plugins/heap_defence_game/application.fam @@ -0,0 +1,11 @@ +App( + appid="Heap_Defence", + name="Heap Defence", + apptype=FlipperAppType.EXTERNAL, + entry_point="heap_defence_app", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Games", + fap_icon="box.png", + fap_icon_assets="assets_images", +) diff --git a/applications/plugins/heap_defence_game/assets_images/Background_128x64.png b/applications/plugins/heap_defence_game/assets_images/Background_128x64.png new file mode 100644 index 000000000..a7eb1326f Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Background_128x64.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Box1_10x10.png b/applications/plugins/heap_defence_game/assets_images/Box1_10x10.png new file mode 100644 index 000000000..d168f9511 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Box1_10x10.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Box2_10x10.png b/applications/plugins/heap_defence_game/assets_images/Box2_10x10.png new file mode 100644 index 000000000..e3bbb48e7 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Box2_10x10.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Box3_10x10.png b/applications/plugins/heap_defence_game/assets_images/Box3_10x10.png new file mode 100644 index 000000000..e20e75b00 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Box3_10x10.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Box4_10x10.png b/applications/plugins/heap_defence_game/assets_images/Box4_10x10.png new file mode 100644 index 000000000..98d134104 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Box4_10x10.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Box5_10x10.png b/applications/plugins/heap_defence_game/assets_images/Box5_10x10.png new file mode 100644 index 000000000..f8dbf339f Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Box5_10x10.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Box6p_10x10.png b/applications/plugins/heap_defence_game/assets_images/Box6p_10x10.png new file mode 100644 index 000000000..da50f8c86 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Box6p_10x10.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Box7p_10x10.png b/applications/plugins/heap_defence_game/assets_images/Box7p_10x10.png new file mode 100644 index 000000000..efcd2ac0c Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Box7p_10x10.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Box8p_10x10.png b/applications/plugins/heap_defence_game/assets_images/Box8p_10x10.png new file mode 100644 index 000000000..57ca46e9c Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Box8p_10x10.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Game_over_128x64.png b/applications/plugins/heap_defence_game/assets_images/Game_over_128x64.png new file mode 100644 index 000000000..d4837e635 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Game_over_128x64.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png new file mode 100644 index 000000000..d4837e635 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png new file mode 100644 index 000000000..a88122d90 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png new file mode 100644 index 000000000..02fa41df0 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png new file mode 100644 index 000000000..d0c63d484 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png new file mode 100644 index 000000000..1c2756fdb Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png new file mode 100644 index 000000000..313fd6961 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png new file mode 100644 index 000000000..cf854656c Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate new file mode 100644 index 000000000..b8626c4cf --- /dev/null +++ b/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate @@ -0,0 +1 @@ +4 diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png b/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png new file mode 100644 index 000000000..104a779c9 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png b/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png new file mode 100644 index 000000000..afc4a932f Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate b/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate @@ -0,0 +1 @@ +2 diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png b/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png new file mode 100644 index 000000000..58c2ef68d Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png b/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png new file mode 100644 index 000000000..afee3ec83 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate b/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate @@ -0,0 +1 @@ +2 diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png new file mode 100644 index 000000000..e8bb70be6 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png new file mode 100644 index 000000000..d7dd740f7 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png new file mode 100644 index 000000000..a8a9fe7e7 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png new file mode 100644 index 000000000..d7dd740f7 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate @@ -0,0 +1 @@ +2 diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png new file mode 100644 index 000000000..fc2150343 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png new file mode 100644 index 000000000..9a03083a0 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png new file mode 100644 index 000000000..5c2911fbc Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png new file mode 100644 index 000000000..9a03083a0 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate @@ -0,0 +1 @@ +2 diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_01.png b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_01.png new file mode 100644 index 000000000..7fd2f8627 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_01.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_02.png b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_02.png new file mode 100644 index 000000000..32fc98b74 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_02.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_03.png b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_03.png new file mode 100644 index 000000000..e7beb004d Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_03.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_04.png b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_04.png new file mode 100644 index 000000000..32fc98b74 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_04.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_rate b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_rate new file mode 100644 index 000000000..00750edc0 --- /dev/null +++ b/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_rate @@ -0,0 +1 @@ +3 diff --git a/applications/plugins/heap_defence_game/assets_images/Person4_1_10x20.png b/applications/plugins/heap_defence_game/assets_images/Person4_1_10x20.png new file mode 100644 index 000000000..104a779c9 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Person4_1_10x20.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Person4_2_10x20.png b/applications/plugins/heap_defence_game/assets_images/Person4_2_10x20.png new file mode 100644 index 000000000..afc4a932f Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Person4_2_10x20.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Person5_1_10x20.png b/applications/plugins/heap_defence_game/assets_images/Person5_1_10x20.png new file mode 100644 index 000000000..58c2ef68d Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Person5_1_10x20.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Person5_2_10x20.png b/applications/plugins/heap_defence_game/assets_images/Person5_2_10x20.png new file mode 100644 index 000000000..afee3ec83 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Person5_2_10x20.png differ diff --git a/applications/plugins/heap_defence_game/assets_images/Start_128x64.png b/applications/plugins/heap_defence_game/assets_images/Start_128x64.png new file mode 100644 index 000000000..32fc98b74 Binary files /dev/null and b/applications/plugins/heap_defence_game/assets_images/Start_128x64.png differ diff --git a/applications/plugins/heap_defence_game/box.png b/applications/plugins/heap_defence_game/box.png new file mode 100644 index 000000000..9ad9b3900 Binary files /dev/null and b/applications/plugins/heap_defence_game/box.png differ diff --git a/applications/plugins/heap_defence_game/heap_defence.c b/applications/plugins/heap_defence_game/heap_defence.c new file mode 100644 index 000000000..a22a8db22 --- /dev/null +++ b/applications/plugins/heap_defence_game/heap_defence.c @@ -0,0 +1,592 @@ +// +// Created by moh on 30.11.2021. +// +// Ported to latest firmware by @xMasterX - 18 Oct 2022 +// + +#include + +#include "hede_assets.h" +#include "Heap_Defence_icons.h" + +#include +#include +#include +#include +#include +#include + +#define Y_FIELD_SIZE 6 +#define Y_LAST (Y_FIELD_SIZE - 1) +#define X_FIELD_SIZE 12 +#define X_LAST (X_FIELD_SIZE - 1) + +#define DRAW_X_OFFSET 4 + +#define TAG "HeDe" + +#define BOX_HEIGHT 10 +#define BOX_WIDTH 10 +#define TIMER_UPDATE_FREQ 8 +#define BOX_GENERATION_RATE 15 + +static IconAnimation* BOX_DESTROYED; +static const Icon* boxes[] = { + (Icon*)&A_HD_BoxDestroyed_10x10, + &I_Box1_10x10, + &I_Box2_10x10, + &I_Box3_10x10, + &I_Box4_10x10, + &I_Box5_10x10}; + +static uint8_t BOX_TEXTURE_COUNT = sizeof(boxes) / sizeof(Icon*); + +typedef enum { + AnimationGameOver = 0, + AnimationPause, + AnimationLeft, + AnimationRight, +} Animations; + +static IconAnimation* animations[4]; + +typedef u_int8_t byte; + +typedef enum { + GameStatusVibro = 1 << 0, + GameStatusInProgress = 1 << 1, +} GameStatuses; + +typedef struct { + uint8_t x; + uint8_t y; +} Position; + +typedef enum { PlayerRising = 1, PlayerFalling = -1, PlayerNothing = 0 } PlayerStates; + +typedef struct { + Position p; + int8_t x_direction; + int8_t j_tick; + int8_t h_tick; + int8_t states; + bool right_frame; +} Person; + +typedef struct { + uint8_t offset : 4; + uint8_t box_id : 3; + uint8_t exists : 1; +} Box; + +static const uint8_t ROW_BYTE_SIZE = sizeof(Box) * X_FIELD_SIZE; + +typedef struct { + Box** field; + Person* person; + Animations animation; + GameStatuses game_status; +} GameState; + +typedef Box** Field; + +typedef enum { EventGameTick, EventKeyPress } EventType; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +/** + * #Construct / Destroy + */ + +static void game_reset_field_and_player(GameState* game) { + ///Reset field + bzero(game->field[0], X_FIELD_SIZE * Y_FIELD_SIZE * sizeof(Box)); + + ///Reset person + bzero(game->person, sizeof(Person)); + game->person->p.x = X_FIELD_SIZE / 2; + game->person->p.y = Y_LAST; +} + +static GameState* allocGameState() { + GameState* game = malloc(sizeof(GameState)); + + game->person = malloc(sizeof(Person)); + + game->field = malloc(Y_FIELD_SIZE * sizeof(Box*)); + game->field[0] = malloc(X_FIELD_SIZE * Y_FIELD_SIZE * sizeof(Box)); + for(int y = 1; y < Y_FIELD_SIZE; ++y) { + game->field[y] = game->field[0] + (y * X_FIELD_SIZE); + } + game_reset_field_and_player(game); + + game->game_status = GameStatusInProgress; + return game; +} + +static void game_destroy(GameState* game) { + furi_assert(game); + free(game->field[0]); + free(game->field); + free(game); +} + +static void assets_load() { + /// Init animations + animations[AnimationPause] = icon_animation_alloc(&A_HD_start_128x64); + animations[AnimationGameOver] = icon_animation_alloc(&A_HD_game_over_128x64); + animations[AnimationLeft] = icon_animation_alloc(&A_HD_person_left_10x20); + animations[AnimationRight] = icon_animation_alloc(&A_HD_person_right_10x20); + + BOX_DESTROYED = icon_animation_alloc(&A_HD_BoxDestroyed_10x10); + + icon_animation_start(animations[AnimationLeft]); + icon_animation_start(animations[AnimationRight]); +} + +static void assets_clear() { + for(int i = 0; i < 4; ++i) { + icon_animation_stop(animations[i]); + icon_animation_free(animations[i]); + } + icon_animation_free(BOX_DESTROYED); +} + +/** + * Box utils + */ + +static inline bool is_empty(Box* box) { + return !box->exists; +} + +static inline bool has_dropped(Box* box) { + return box->offset == 0; +} + +static Box* get_upper_box(Field field, Position current) { + return (&field[current.y - 1][current.x]); +} + +static Box* get_lower_box(Field field, Position current) { + return (&field[current.y + 1][current.x]); +} + +static Box* get_next_box(Field field, Position current, int x_direction) { + return (&field[current.y][current.x + x_direction]); +} + +static inline void decrement_y_offset_to_zero(Box* n) { + if(n->offset) --n->offset; +} + +static inline void heap_swap(Box* first, Box* second) { + Box temp = *first; + + *first = *second; + *second = temp; +} + +/** + * #Box logic + */ + +static void generate_box(GameState const* game) { + furi_assert(game); + + static byte tick_count = BOX_GENERATION_RATE; + if(tick_count++ != BOX_GENERATION_RATE) { + return; + } + tick_count = 0; + + int x_offset = rand() % X_FIELD_SIZE; + while(game->field[1][x_offset].exists) { + x_offset = rand() % X_FIELD_SIZE; + } + + game->field[1][x_offset].exists = true; + game->field[1][x_offset].offset = BOX_HEIGHT; + game->field[1][x_offset].box_id = (rand() % (BOX_TEXTURE_COUNT - 1)) + 1; +} + +static void drop_box(GameState* game) { + furi_assert(game); + + for(int y = Y_LAST; y > 0; y--) { + for(int x = 0; x < X_FIELD_SIZE; x++) { + Box* current_box = game->field[y] + x; + Box* upper_box = game->field[y - 1] + x; + + if(y == Y_LAST) { + decrement_y_offset_to_zero(current_box); + } + + decrement_y_offset_to_zero(upper_box); + + if(is_empty(current_box) && !is_empty(upper_box) && has_dropped(upper_box)) { + upper_box->offset = BOX_HEIGHT; + heap_swap(current_box, upper_box); + } + } + } +} + +static bool clear_rows(Box** field) { + for(int x = 0; x < X_FIELD_SIZE; ++x) { + if(is_empty(field[Y_LAST] + x) || !has_dropped(field[Y_LAST] + x)) { + return false; + } + } + + memset(field[Y_LAST], 128, ROW_BYTE_SIZE); + return true; +} + +/** + * Input Handling + */ + +static inline bool on_ground(Person* person, Field field) { + return person->p.y == Y_LAST || field[person->p.y + 1][person->p.x].exists; +} + +static void handle_key_presses(Person* person, InputEvent* input, GameState* game) { + switch(input->key) { + case InputKeyUp: + if(person->states == PlayerNothing && on_ground(person, game->field)) { + person->states = PlayerRising; + person->j_tick = 0; + } + break; + case InputKeyLeft: + person->right_frame = false; + if(person->h_tick == 0) { + person->h_tick = 1; + person->x_direction = -1; + } + break; + case InputKeyRight: + person->right_frame = true; + if(person->h_tick == 0) { + person->h_tick = 1; + person->x_direction = 1; + } + break; + case InputKeyOk: + game->game_status &= ~GameStatusInProgress; + game->animation = AnimationPause; + icon_animation_start(animations[AnimationPause]); + default: + break; + } +} + +/** + * #Person logic + */ + +static inline bool ground_box_check(Field field, Position new_position) { + Box* lower_box = get_lower_box(field, new_position); + + bool ground_box_dropped = + (new_position.y == Y_LAST || //Eсли ΠΌΡ‹ ΠΈ Ρ‚Π°ΠΊ Π² самом Π½ΠΈΠ·Ρƒ + is_empty(lower_box) || // EcΠ»ΠΈ снизу пустота + has_dropped(lower_box)); //Eсли бокс снизу Π΄ΠΎΠΏΠ°Π΄Π°Π» + return ground_box_dropped; +} + +static inline bool is_movable(Field field, Position box_pos, int x_direction) { + //TODO::MoΠΆΠ΅Ρ‚ ΠΈ Π½Π΅ Π΄Π²ΡƒΡ…, ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ + bool out_of_bounds = box_pos.x == 0 || box_pos.x == X_LAST; + if(out_of_bounds) return false; + bool box_on_top = box_pos.y < 1 || get_upper_box(field, box_pos)->exists; + if(box_on_top) return false; + bool has_next_box = get_next_box(field, box_pos, x_direction)->exists; + if(has_next_box) return false; + + return true; +} + +static bool horizontal_move(Person* person, Field field) { + Position new_position = person->p; + + if(!person->x_direction) return false; + + new_position.x += person->x_direction; + + bool on_edge_column = new_position.x > X_LAST; + if(on_edge_column) return false; + + if(is_empty(&field[new_position.y][new_position.x])) { + bool ground_box_dropped = ground_box_check(field, new_position); + if(ground_box_dropped) { + person->p = new_position; + return true; + } + } else if(is_movable(field, new_position, person->x_direction)) { + *get_next_box(field, new_position, person->x_direction) = + field[new_position.y][new_position.x]; + + field[new_position.y][new_position.x] = (Box){0}; + person->p = new_position; + return true; + } + return false; +} + +void hd_person_set_state(Person* person, PlayerStates state) { + person->states = state; + person->j_tick = 0; +} + +static void person_move(Person* person, Field field) { + /// Left-right logic + FURI_LOG_W(TAG, "[JUMP]func:[%s] line: %d", __FUNCTION__, __LINE__); + + if(person->states == PlayerNothing) { + if(!on_ground(person, field)) { + hd_person_set_state(person, PlayerFalling); + } + } else if(person->states == PlayerRising) { + if(person->j_tick++ == 0) { + person->p.y--; + } else if(person->j_tick == 6) { + hd_person_set_state(person, PlayerNothing); + } + + /// Destroy upper box + get_upper_box(field, person->p)->box_id = 0; + field[person->p.y][person->p.x].box_id = 0; + + } else if(person->states == PlayerFalling) { + if(person->j_tick++ == 0) { + if(on_ground(person, field)) { // TODO: Test the bugfix + hd_person_set_state(person, PlayerNothing); + } else { + person->p.y++; + } + } else if(person->j_tick == 5) { + if(on_ground(person, field)) { + hd_person_set_state(person, PlayerNothing); + } else { + hd_person_set_state(person, PlayerFalling); + } + } + } + + switch(person->h_tick) { + case 0: + break; + case 1: + person->h_tick++; + FURI_LOG_W(TAG, "[JUMP]func:[%s] line: %d", __FUNCTION__, __LINE__); + bool moved = horizontal_move(person, field); + if(!moved) { + person->h_tick = 0; + person->x_direction = 0; + } + break; + case 5: + FURI_LOG_W(TAG, "[JUMP]func:[%s] line: %d", __FUNCTION__, __LINE__); + person->h_tick = 0; + person->x_direction = 0; + break; + default: + FURI_LOG_W(TAG, "[JUMP]func:[%s] line: %d", __FUNCTION__, __LINE__); + person->h_tick++; + } +} + +static inline bool is_person_dead(Person* person, Box** field) { + return get_upper_box(field, person->p)->box_id != 0; +} + +/** + * #Callback + */ + +static void draw_box(Canvas* canvas, Box* box, int x, int y) { + if(is_empty(box)) { + return; + } + byte y_screen = y * BOX_HEIGHT - box->offset; + byte x_screen = x * BOX_WIDTH + DRAW_X_OFFSET; + + if(box->box_id == 0) { + canvas_set_bitmap_mode(canvas, true); + icon_animation_start(BOX_DESTROYED); + canvas_draw_icon_animation(canvas, x_screen, y_screen, BOX_DESTROYED); + if(icon_animation_is_last_frame(BOX_DESTROYED)) { + *box = (Box){0}; + icon_animation_stop(BOX_DESTROYED); + } + canvas_set_bitmap_mode(canvas, false); + } else { + canvas_draw_icon(canvas, x_screen, y_screen, boxes[box->box_id]); + } +} + +static void heap_defense_render_callback(Canvas* const canvas, void* mutex) { + furi_assert(mutex); + + const GameState* game = acquire_mutex((ValueMutex*)mutex, 25); + + ///Draw GameOver or Pause + if(!(game->game_status & GameStatusInProgress)) { + FURI_LOG_W(TAG, "[DAED_DRAW]func: [%s] line: %d ", __FUNCTION__, __LINE__); + + canvas_draw_icon_animation(canvas, 0, 0, animations[game->animation]); + release_mutex((ValueMutex*)mutex, game); + return; + } + + ///Draw field + canvas_draw_icon(canvas, 0, 0, &I_Background_128x64); + + ///Draw Person + const Person* person = game->person; + IconAnimation* player_animation = person->right_frame ? animations[AnimationRight] : + animations[AnimationLeft]; + + uint8_t x_screen = person->p.x * BOX_WIDTH + DRAW_X_OFFSET; + if(person->h_tick && person->h_tick != 1) { + if(person->right_frame) { + x_screen += (person->h_tick) * 2 - BOX_WIDTH; + } else { + x_screen -= (person->h_tick) * 2 - BOX_WIDTH; + } + } + + uint8_t y_screen = (person->p.y - 1) * BOX_HEIGHT; + if(person->j_tick) { + if(person->states == PlayerRising) { + y_screen += BOX_HEIGHT - (person->j_tick) * 2; + } else if(person->states == PlayerFalling) { + y_screen -= BOX_HEIGHT - (person->j_tick) * 2; + } + } + + canvas_draw_icon_animation(canvas, x_screen, y_screen, player_animation); + + ///Draw Boxes + canvas_set_color(canvas, ColorBlack); + for(int y = 1; y < Y_FIELD_SIZE; ++y) { + for(int x = 0; x < X_FIELD_SIZE; ++x) { + draw_box(canvas, &(game->field[y][x]), x, y); + } + } + + release_mutex((ValueMutex*)mutex, game); +} + +static void heap_defense_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + if(input_event->type != InputTypePress && input_event->type != InputTypeLong) return; + + furi_assert(event_queue); + GameEvent event = {.type = EventKeyPress, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void heap_defense_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventGameTick, .input = {0}}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t heap_defence_app(void* p) { + UNUSED(p); + + //FURI_LOG_W(TAG, "Heap defence start %d", __LINE__); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + GameState* game = allocGameState(); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, game, sizeof(GameState))) { + game_destroy(game); + return 1; + } + + assets_load(); + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, heap_defense_render_callback, &state_mutex); + view_port_input_callback_set(view_port, heap_defense_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(heap_defense_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / TIMER_UPDATE_FREQ); + + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open("notification"); + + memset(game->field[Y_LAST], 128, ROW_BYTE_SIZE); + game->person->p.y -= 2; + game->game_status = 0; + game->animation = AnimationPause; + + GameEvent event = {0}; + while(event.input.key != InputKeyBack) { + if(furi_message_queue_get(event_queue, &event, 100) != FuriStatusOk) { + continue; + } + + game = (GameState*)acquire_mutex_block(&state_mutex); + + //unset vibration + if(game->game_status & GameStatusVibro) { + notification_message(notification, &sequence_reset_vibro); + game->game_status &= ~GameStatusVibro; + icon_animation_stop(BOX_DESTROYED); + memset(game->field[Y_LAST], 0, ROW_BYTE_SIZE); + } + + if(!(game->game_status & GameStatusInProgress)) { + if(event.type == EventKeyPress && event.input.key == InputKeyOk) { + game->game_status |= GameStatusInProgress; + icon_animation_stop(animations[game->animation]); + } + + } else if(event.type == EventKeyPress) { + handle_key_presses(game->person, &(event.input), game); + } else { // EventGameTick + + drop_box(game); + generate_box(game); + if(clear_rows(game->field)) { + notification_message(notification, &sequence_set_vibro_on); + icon_animation_start(BOX_DESTROYED); + game->game_status |= GameStatusVibro; + } + person_move(game->person, game->field); + + if(is_person_dead(game->person, game->field)) { + game->game_status &= ~GameStatusInProgress; + game->animation = AnimationGameOver; + icon_animation_start(animations[AnimationGameOver]); + game_reset_field_and_player(game); + notification_message(notification, &sequence_error); + } + } + release_mutex(&state_mutex, game); + view_port_update(view_port); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_record_close("gui"); + furi_record_close("notification"); + furi_message_queue_free(event_queue); + assets_clear(); + delete_mutex(&state_mutex); + game_destroy(game); + + return 0; +} diff --git a/applications/plugins/heap_defence_game/hede_assets.c b/applications/plugins/heap_defence_game/hede_assets.c new file mode 100644 index 000000000..f45c0583d --- /dev/null +++ b/applications/plugins/heap_defence_game/hede_assets.c @@ -0,0 +1,28 @@ +// +// Created by user on 15.12.2021. +// +#include "hede_assets.h" +#include + +const uint8_t _A_HD_BoxDestroyed_10x10_0[] = { + 0x01, 0x00, 0x10, 0x00, 0x00, 0x1d, 0xa2, 0x01, 0xc8, 0x80, + 0x6d, 0x20, 0x15, 0x08, 0x06, 0x72, 0x01, 0x48, 0x07, 0xa0, +}; +const uint8_t _A_HD_BoxDestroyed_10x10_1[] = { + 0x00, 0x00, 0x00, 0x28, 0x01, 0x4A, 0x00, 0xA8, 0x01, 0x84, 0x00, + 0x22, 0x00, 0x88, 0x00, 0x58, 0x01, 0x22, 0x00, 0x00, 0x00, +}; +const uint8_t _A_HD_BoxDestroyed_10x10_2[] = { + 0x00, 0x00, 0x00, 0x08, 0x01, 0x42, 0x00, 0x09, 0x01, 0x00, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x21, 0x00, 0x42, 0x02, +}; +const uint8_t* _A_HD_BoxDestroyed_10x10[] = { + _A_HD_BoxDestroyed_10x10_0, + _A_HD_BoxDestroyed_10x10_1, + _A_HD_BoxDestroyed_10x10_2}; +const Icon A_HD_BoxDestroyed_10x10 = { + .width = 10, + .height = 10, + .frame_count = 3, + .frame_rate = 4, + .frames = _A_HD_BoxDestroyed_10x10}; \ No newline at end of file diff --git a/applications/plugins/heap_defence_game/hede_assets.h b/applications/plugins/heap_defence_game/hede_assets.h new file mode 100644 index 000000000..3bcabc59f --- /dev/null +++ b/applications/plugins/heap_defence_game/hede_assets.h @@ -0,0 +1,11 @@ +// +// Created by user on 15.12.2021. +// + +#ifndef HEDE_ASSETS_H +#define HEDE_ASSETS_H +#include + +extern const Icon A_HD_BoxDestroyed_10x10; + +#endif diff --git a/applications/plugins/hex_viewer/LICENSE b/applications/plugins/hex_viewer/LICENSE new file mode 100644 index 000000000..69004dc62 --- /dev/null +++ b/applications/plugins/hex_viewer/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Roman Shchekin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/hex_viewer/README.md b/applications/plugins/hex_viewer/README.md new file mode 100644 index 000000000..35f15a5d5 --- /dev/null +++ b/applications/plugins/hex_viewer/README.md @@ -0,0 +1,7 @@ +# flipper-zero-hex-viewer + +Hex Viewer application for Flipper Zero! + +![Hex Viewer app!](https://habrastorage.org/r/w1560/getpro/habr/upload_files/46e/28a/d97/46e28ad973d144b123a4ce513c895d18.png) + +[Link to FAP](https://nightly.link/QtRoS/flipper-zero-hex-viewer/actions/artifacts/448677581.zip) diff --git a/applications/plugins/hex_viewer/application.fam b/applications/plugins/hex_viewer/application.fam new file mode 100644 index 000000000..7204e07c8 --- /dev/null +++ b/applications/plugins/hex_viewer/application.fam @@ -0,0 +1,16 @@ +App( + appid="hex_viewer", + name="HEX Viewer", + apptype=FlipperAppType.EXTERNAL, + entry_point="hex_viewer_app", + cdefines=["APP_HEX_VIEWER"], + requires=[ + "gui", + "dialogs", + ], + stack_size=2 * 1024, + order=20, + fap_icon="icons/hex_10px.png", + fap_category="Misc", + fap_icon_assets="icons", +) diff --git a/applications/plugins/hex_viewer/hex_viewer.c b/applications/plugins/hex_viewer/hex_viewer.c new file mode 100644 index 000000000..5289c8654 --- /dev/null +++ b/applications/plugins/hex_viewer/hex_viewer.c @@ -0,0 +1,285 @@ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TAG "HexViewer" + +#define HEX_VIEWER_APP_PATH_FOLDER "/any" +#define HEX_VIEWER_APP_EXTENSION "*" + +#define HEX_VIEWER_BYTES_PER_ROW 4 +#define HEX_VIEWER_ROW_COUNT 4 +#define HEX_VIEWER_BUF_SIZE (HEX_VIEWER_BYTES_PER_ROW * HEX_VIEWER_ROW_COUNT) + +typedef struct { + uint8_t file_bytes[HEX_VIEWER_ROW_COUNT][HEX_VIEWER_ROW_COUNT]; + uint32_t line; + uint32_t read_bytes; + uint32_t file_size; + Stream* stream; + bool mode; // Print address or content +} HexViewerModel; + +typedef struct { + HexViewerModel* model; + FuriMutex** mutex; + + FuriMessageQueue* input_queue; + + ViewPort* view_port; + Gui* gui; + Storage* storage; +} HexViewer; + +static void render_callback(Canvas* canvas, void* ctx) { + HexViewer* hex_viewer = ctx; + furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + elements_button_left(canvas, hex_viewer->model->mode ? "Addr" : "Text"); + elements_button_right(canvas, "Info"); + + int ROW_HEIGHT = 12; + int TOP_OFFSET = 10; + int LEFT_OFFSET = 3; + + uint32_t line_count = hex_viewer->model->file_size / HEX_VIEWER_BYTES_PER_ROW; + if(hex_viewer->model->file_size % HEX_VIEWER_BYTES_PER_ROW != 0) line_count += 1; + if(line_count > HEX_VIEWER_ROW_COUNT) { + uint8_t width = canvas_width(canvas); + elements_scrollbar_pos( + canvas, + width, + 0, + ROW_HEIGHT * HEX_VIEWER_ROW_COUNT, + hex_viewer->model->line, + line_count - (HEX_VIEWER_ROW_COUNT - 1)); + } + + char temp_buf[32]; + uint32_t row_iters = hex_viewer->model->read_bytes / HEX_VIEWER_BYTES_PER_ROW; + if(hex_viewer->model->read_bytes % HEX_VIEWER_BYTES_PER_ROW != 0) row_iters += 1; + + for(uint32_t i = 0; i < row_iters; ++i) { + uint32_t bytes_left_per_row = hex_viewer->model->read_bytes - i * HEX_VIEWER_BYTES_PER_ROW; + if(bytes_left_per_row > HEX_VIEWER_BYTES_PER_ROW) + bytes_left_per_row = HEX_VIEWER_BYTES_PER_ROW; + + if(hex_viewer->model->mode) { + memcpy(temp_buf, hex_viewer->model->file_bytes[i], bytes_left_per_row); + temp_buf[bytes_left_per_row] = '\0'; + for(uint32_t j = 0; j < bytes_left_per_row; ++j) + if(!isprint((int)temp_buf[j])) temp_buf[j] = '.'; + + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); + } else { + int addr = (i + hex_viewer->model->line) * HEX_VIEWER_BYTES_PER_ROW; + snprintf(temp_buf, 32, "%04X", addr); + + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); + } + + char* p = temp_buf; + for(uint32_t j = 0; j < bytes_left_per_row; ++j) + p += snprintf(p, 32, "%02X ", hex_viewer->model->file_bytes[i][j]); + + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str(canvas, LEFT_OFFSET + 41, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); + } + + furi_mutex_release(hex_viewer->mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + HexViewer* hex_viewer = ctx; + if(input_event->type == InputTypeShort) { + furi_message_queue_put(hex_viewer->input_queue, input_event, 0); + } +} + +static HexViewer* hex_viewer_alloc() { + HexViewer* instance = malloc(sizeof(HexViewer)); + + instance->model = malloc(sizeof(HexViewerModel)); + memset(instance->model, 0x0, sizeof(HexViewerModel)); + + instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, render_callback, instance); + view_port_input_callback_set(instance->view_port, input_callback, instance); + + instance->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + instance->storage = furi_record_open(RECORD_STORAGE); + + return instance; +} + +static void hex_viewer_free(HexViewer* instance) { + furi_record_close(RECORD_STORAGE); + + gui_remove_view_port(instance->gui, instance->view_port); + furi_record_close(RECORD_GUI); + view_port_free(instance->view_port); + + furi_message_queue_free(instance->input_queue); + + furi_mutex_free(instance->mutex); + + if(instance->model->stream) buffered_file_stream_close(instance->model->stream); + + free(instance->model); + free(instance); +} + +static bool hex_viewer_open_file(HexViewer* hex_viewer, const char* file_path) { + furi_assert(hex_viewer); + furi_assert(file_path); + + hex_viewer->model->stream = buffered_file_stream_alloc(hex_viewer->storage); + bool isOk = true; + + do { + if(!buffered_file_stream_open( + hex_viewer->model->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); + isOk = false; + break; + }; + + hex_viewer->model->file_size = stream_size(hex_viewer->model->stream); + } while(false); + + return isOk; +} + +static bool hex_viewer_read_file(HexViewer* hex_viewer) { + furi_assert(hex_viewer); + furi_assert(hex_viewer->model->stream); + + memset(hex_viewer->model->file_bytes, 0x0, HEX_VIEWER_BUF_SIZE); + bool isOk = true; + + do { + uint32_t offset = hex_viewer->model->line * HEX_VIEWER_BYTES_PER_ROW; + if(!stream_seek(hex_viewer->model->stream, offset, true)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + isOk = false; + break; + } + + hex_viewer->model->read_bytes = stream_read( + hex_viewer->model->stream, + (uint8_t*)hex_viewer->model->file_bytes, + HEX_VIEWER_BUF_SIZE); + } while(false); + + return isOk; +} + +int32_t hex_viewer_app(void* p) { + HexViewer* hex_viewer = hex_viewer_alloc(); + + FuriString* file_path; + file_path = furi_string_alloc(); + + do { + if(p && strlen(p)) { + furi_string_set(file_path, (const char*)p); + } else { + furi_string_set(file_path, HEX_VIEWER_APP_PATH_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, HEX_VIEWER_APP_EXTENSION, &I_hex_10px); + browser_options.hide_ext = false; + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + if(!res) { + FURI_LOG_I(TAG, "No file selected"); + break; + } + } + + if(!hex_viewer_open_file(hex_viewer, furi_string_get_cstr(file_path))) break; + hex_viewer_read_file(hex_viewer); + + InputEvent input; + while(furi_message_queue_get(hex_viewer->input_queue, &input, FuriWaitForever) == + FuriStatusOk) { + if(input.key == InputKeyBack) { + break; + } else if(input.key == InputKeyUp) { + furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); + if(hex_viewer->model->line > 0) { + hex_viewer->model->line--; + + if(!hex_viewer_read_file(hex_viewer)) break; + } + furi_mutex_release(hex_viewer->mutex); + } else if(input.key == InputKeyDown) { + furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); + uint32_t cur_pos = hex_viewer->model->line * HEX_VIEWER_BYTES_PER_ROW + + hex_viewer->model->read_bytes; + + if(hex_viewer->model->file_size > cur_pos) { + hex_viewer->model->line++; + if(!hex_viewer_read_file(hex_viewer)) break; + } + furi_mutex_release(hex_viewer->mutex); + } else if(input.key == InputKeyLeft) { + furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); + hex_viewer->model->mode = !hex_viewer->model->mode; + furi_mutex_release(hex_viewer->mutex); + } else if(input.key == InputKeyRight) { + FuriString* buffer; + buffer = furi_string_alloc(); + furi_string_printf( + buffer, + "File path: %s\nFile size: %lu bytes", + furi_string_get_cstr(file_path), + hex_viewer->model->file_size); + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Hex Viewer", 16, 2, AlignLeft, AlignTop); + dialog_message_set_icon(message, &I_hex_10px, 3, 2); + dialog_message_set_text( + message, furi_string_get_cstr(buffer), 3, 16, AlignLeft, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, "Back"); + dialog_message_show(dialogs, message); + + furi_string_free(buffer); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } + + view_port_update(hex_viewer->view_port); + } + } while(false); + + furi_string_free(file_path); + hex_viewer_free(hex_viewer); + + return 0; +} diff --git a/applications/plugins/hex_viewer/icons/hex_10px.png b/applications/plugins/hex_viewer/icons/hex_10px.png new file mode 100644 index 000000000..582e288c6 Binary files /dev/null and b/applications/plugins/hex_viewer/icons/hex_10px.png differ diff --git a/applications/plugins/htu21d_temp_sensor/Readme.md b/applications/plugins/htu21d_temp_sensor/Readme.md new file mode 100644 index 000000000..45c332306 --- /dev/null +++ b/applications/plugins/htu21d_temp_sensor/Readme.md @@ -0,0 +1,65 @@ +[Original link](https://github.com/Mywk/FlipperTemperatureSensor) + +# Flipper Temperature Sensor + +## Supported sensors + +> HTU2xD, SHT2x, SI702x, SI700x, SI701x, AM2320 + +## What is this? + +A small app for the [Flipper Zero](https://flipperzero.one) that reads the [I2C](https://en.wikipedia.org/wiki/I%C2%B2C) signal from a few temperature sensors and displays the current temperature and humidity. + +I'm using a [Sparkfun HTU21D sensor](https://learn.sparkfun.com/tutorials/htu21d-humidity-sensor-hookup-guide), also tested with a clone and with the Si7021 variant. + +![Flipper Temperature Sensor](images/Flipper.png) + +![App](images/App.png) + +
+ +# How to Connect the HTU21D sensor +![Connection](images/Connection.png) + + +# How to install + +If you have the FAP loader, just copy the fap file from the Releases into your Flipper apps folder and you should be able to launch it from the menu. + +If you don't have the FAP loader you will have to bake this application together with your firmware (aka compile it all together). + +# FAQ + +## The app says the sensor is not found! + +1- Are the four connectors correctly soldered? + +2- Are the SCL and SDA connections correct? Re-check the "How to Connect the sensor" above. + +3- For the HTU21D, on the sensor board, there should be three contacts in the center, for it to work correctly they must be soldered together (basically drop a blob of solder to connect the three of them). Without the solder it looks like this: + +![Sensor](images/Sensor.png) + +## Which Flipper versions was this app tested on? + +Version 1.2 +- RM11221439-0.71.2 +- unlshd-015 +- 0.71.1 + +Version 1.1 +- RM10302252-0.70.1 +- unlshd-012 +- 0.68.2-1007-RM +- 0.68.1 +- 0.67.2 + +## I can't build the app together with the firmware? + +In the *application.fam*, don't forget to change the apptype, it should not be EXTERNAL but APP. + +# How to compile + +Place the temperature_sensor folder in the applications_user folder and compile using FBT. + +Please refer to the [Flipper Build Tool documentation](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md). \ No newline at end of file diff --git a/applications/plugins/htu21d_temp_sensor/application.fam b/applications/plugins/htu21d_temp_sensor/application.fam new file mode 100644 index 000000000..9ae3bdffd --- /dev/null +++ b/applications/plugins/htu21d_temp_sensor/application.fam @@ -0,0 +1,14 @@ +App( + appid="Temperature_Sensor", + name="Temperature Sensor", + apptype=FlipperAppType.EXTERNAL, + entry_point="temperature_sensor_app", + cdefines=["APP_TEMPERATURE_SENSOR"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=90, + fap_icon="temperature_sensor.png", + fap_category="GPIO", +) diff --git a/applications/plugins/htu21d_temp_sensor/docs/App.png b/applications/plugins/htu21d_temp_sensor/docs/App.png new file mode 100644 index 000000000..a0373bdbd Binary files /dev/null and b/applications/plugins/htu21d_temp_sensor/docs/App.png differ diff --git a/applications/plugins/htu21d_temp_sensor/docs/Connection.png b/applications/plugins/htu21d_temp_sensor/docs/Connection.png new file mode 100644 index 000000000..b38f5c250 Binary files /dev/null and b/applications/plugins/htu21d_temp_sensor/docs/Connection.png differ diff --git a/applications/plugins/htu21d_temp_sensor/docs/Flipper.png b/applications/plugins/htu21d_temp_sensor/docs/Flipper.png new file mode 100644 index 000000000..c85319593 Binary files /dev/null and b/applications/plugins/htu21d_temp_sensor/docs/Flipper.png differ diff --git a/applications/plugins/htu21d_temp_sensor/temperature_sensor.c b/applications/plugins/htu21d_temp_sensor/temperature_sensor.c new file mode 100644 index 000000000..b04e40a7d --- /dev/null +++ b/applications/plugins/htu21d_temp_sensor/temperature_sensor.c @@ -0,0 +1,387 @@ +/* Flipper App to read the values from a HTU2XD, SHT2X, SI702X, SI700X, SI701X or AM2320 Sensor */ +/* Created by Mywk - https://github.com/Mywk - https://mywk.net */ +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#define TS_DEFAULT_VALUE 0xFFFF + +#define TS_AVAILABLE_SENSORS 2 + +// HTU2XD, SHT2X, SI702X, SI700X address +#define HTU2XD_SHT2X_SI702X_SI700X_ADDRESS (0x40 << 1) +// SI701X ADDRESS +#define SI701X_ADDRESS (0x41 << 1) + +// HTU2XD, SHT2X, SI702X, SI700X commands +#define HTU21D_CMD_TEMPERATURE 0xE3 +#define HTU21D_CMD_HUMIDITY 0xE5 + +// AM2320 address +#define AM2320_ADDRESS (0x5C << 1) + +// Used for the temperature and humidity buffers +#define DATA_BUFFER_SIZE 8 + +// External I2C BUS +#define I2C_BUS &furi_hal_i2c_handle_external + +// Typedef enums to make everything easier to read + +typedef enum { TSSCmdNone, TSSCmdTemperature, TSSCmdHumidity } TSSCmdType; + +typedef enum { + TSSInitializing, + TSSNoSensor, + TSSPendingUpdate, +} TSStatus; + +typedef enum { + TSEventTypeTick, + TSEventTypeInput, +} TSEventType; + +typedef struct { + TSEventType type; + InputEvent input; +} TSEvent; + +// Possible return values for sensor_cmd +typedef enum { + TSCmdRet_Error, + TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X, + TSCmdRet_SI701X, + TSCmdRet_AM2320, +} TSCmdRet; + +// External NotificationSequence RGB +extern const NotificationSequence sequence_blink_red_100; +extern const NotificationSequence sequence_blink_green_100; +extern const NotificationSequence sequence_blink_blue_100; + +// Current status of the temperature sensor app +static TSStatus temperature_sensor_current_status = TSSInitializing; + +// We keep track of the last cmd return +static TSCmdRet temperature_sensor_last_cmd_ret = TSCmdRet_Error; + +// Temperature and Humidity data buffers, ready to print +char ts_data_buffer_temperature_c[DATA_BUFFER_SIZE]; +char ts_data_buffer_temperature_f[DATA_BUFFER_SIZE]; +char ts_data_buffer_relative_humidity[DATA_BUFFER_SIZE]; +char ts_data_buffer_absolute_humidity[DATA_BUFFER_SIZE]; + +// +// Executes an I2C cmd (trx) +// +// +// CRC +// +// +// true if fetch was successful, false otherwise +// +static TSCmdRet temperature_sensor_cmd(TSSCmdType cmd, uint8_t* buffer) { + uint32_t timeout = furi_ms_to_ticks(100); + TSCmdRet ret = TSCmdRet_Error; + + // Aquire I2C and check if device is ready, then release + furi_hal_i2c_acquire(I2C_BUS); + + // Check if HTU2XD, SHT2X, SI702X, SI700X sensor is available + uint8_t isAddress40 = + furi_hal_i2c_is_device_ready(I2C_BUS, HTU2XD_SHT2X_SI702X_SI700X_ADDRESS, timeout); + uint8_t isAddress41 = 0; + + // Check if SI701X sensor is available if necessary + if(!isAddress40) isAddress41 = furi_hal_i2c_is_device_ready(I2C_BUS, SI701X_ADDRESS, timeout); + + if(isAddress40 || isAddress41) { + uint8_t address = isAddress40 ? HTU2XD_SHT2X_SI702X_SI700X_ADDRESS : SI701X_ADDRESS; + + // Better safe than sorry delay + furi_delay_ms(15); + + // Extra delay for the SI70XX + if(isAddress41) furi_delay_ms(50); + + // Transmit either the temperature or the humidity command depending on TSSCmdType + uint8_t c = (cmd == TSSCmdTemperature) ? HTU21D_CMD_TEMPERATURE : HTU21D_CMD_HUMIDITY; + if(furi_hal_i2c_tx(I2C_BUS, address, &c, 1, timeout)) { + // Receive data (2 bytes) + if(furi_hal_i2c_rx(I2C_BUS, address, buffer, 2, timeout + 50)) + ret = isAddress40 ? TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X : TSCmdRet_SI701X; + } + } else { + // The AM2320 goes to sleep after a period of inactivity, wake it up (check AM2320 datasheet for more info) + furi_hal_i2c_is_device_ready(I2C_BUS, AM2320_ADDRESS, timeout); + furi_delay_ms(30); + + // Check if it's really available + if(furi_hal_i2c_is_device_ready(I2C_BUS, AM2320_ADDRESS, timeout)) { + // {Address, Register, Len} + const uint8_t request[3] = {0x03, 0x00, 0x04}; + + if(furi_hal_i2c_tx(I2C_BUS, AM2320_ADDRESS, request, 3, timeout)) { + // 6 bytes - usually 8 but we currently don't check the CRC + if(furi_hal_i2c_rx(I2C_BUS, (uint8_t)AM2320_ADDRESS, buffer, 6, timeout)) + ret = TSCmdRet_AM2320; + } + } + } + + furi_hal_i2c_release(I2C_BUS); + + temperature_sensor_last_cmd_ret = ret; + return ret; +} + +// +// Fetches temperature and humidity from sensor +// +// +// temperature in C +// humidity in relative humidity +// +// +// Temperature and humidity must be preallocated +// +// +// CRC +// +// +// true if fetch was successful, false otherwise +// +static bool temperature_sensor_fetch_data(double* temperature, double* humidity) { + bool ret = false; + + uint16_t adc_raw; + + uint8_t buffer[DATA_BUFFER_SIZE] = {0x00}; + + // Check if the sensor is the HTU21D by attempting to fetch the temperature + TSCmdRet cmdRet = temperature_sensor_cmd(TSSCmdTemperature, buffer); + if(cmdRet == TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X || cmdRet == TSCmdRet_SI701X) { + // Calculate temperature + adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1])); + *temperature = (float)(adc_raw * 175.72 / 65536.00) - 46.85; + + // Fetch humidity + if(temperature_sensor_cmd(TSSCmdHumidity, buffer)) { + // Calculate humidity + adc_raw = ((uint16_t)(buffer[0] << 8) | (buffer[1])); + *humidity = (float)(adc_raw * 125.0 / 65536.00) - 6.0; + + ret = true; + } + } else if(cmdRet == TSCmdRet_AM2320) { + // The AM2320 returns all the data immediately so we just process it all + // Note: CRC isn't currently present in the buffer + + // Temperature + float temp = (((buffer[4] & 0x7F) << 8) + buffer[5]) / 10; + *temperature = ((buffer[4] & 0x80) >> 7) == 1 ? temp * (-1) : temp; + + // Humidity + temp = ((buffer[2] << 8) + buffer[3]) / 10; + *humidity = temp; + + ret = true; + } + + return ret; +} + +// +// Draw callback +// +static void temperature_sensor_draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + + // Update title accordingly (this could be improved by checking the hardware id) + switch(temperature_sensor_last_cmd_ret) { + case TSCmdRet_Error: + canvas_draw_str(canvas, 2, 10, "Temperature Sensor"); + break; + + case TSCmdRet_HTU2XD_SHT2X_SI702X_SI700X: + canvas_draw_str(canvas, 2, 10, "HTU/SHT/SI70 Sensor"); + break; + + case TSCmdRet_SI701X: + canvas_draw_str(canvas, 2, 10, "SI701X Sensor"); + break; + + case TSCmdRet_AM2320: + canvas_draw_str(canvas, 2, 10, "AM2320 Sensor"); + break; + + default: + break; + } + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 62, "Press back to exit."); + + switch(temperature_sensor_current_status) { + case TSSInitializing: + canvas_draw_str(canvas, 2, 30, "Initializing.."); + break; + case TSSNoSensor: + canvas_draw_str(canvas, 2, 30, "No sensor found!"); + break; + case TSSPendingUpdate: { + canvas_draw_str(canvas, 3, 24, "Temperature"); + canvas_draw_str(canvas, 68, 24, "Humidity"); + + // Draw vertical lines + canvas_draw_line(canvas, 61, 16, 61, 50); + canvas_draw_line(canvas, 62, 16, 62, 50); + + // Draw horizontal line + canvas_draw_line(canvas, 2, 27, 122, 27); + + // Draw temperature and humidity values + canvas_draw_str(canvas, 8, 38, ts_data_buffer_temperature_c); + canvas_draw_str(canvas, 42, 38, "C"); + canvas_draw_str(canvas, 8, 48, ts_data_buffer_temperature_f); + canvas_draw_str(canvas, 42, 48, "F"); + canvas_draw_str(canvas, 68, 38, ts_data_buffer_relative_humidity); + canvas_draw_str(canvas, 100, 38, "%"); + canvas_draw_str(canvas, 68, 48, ts_data_buffer_absolute_humidity); + canvas_draw_str(canvas, 100, 48, "g/m3"); + } break; + default: + break; + } +} + +// +// Input callback +// +static void temperature_sensor_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + TSEvent event = {.type = TSEventTypeInput, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +// +// Timer callback +// +static void temperature_sensor_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + TSEvent event = {.type = TSEventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +// +// App entry point +// +int32_t temperature_sensor_app(void* p) { + UNUSED(p); + + // Declare our variables and assign variables a default value + TSEvent tsEvent; + bool sensorFound = false; + double celsius, fahrenheit, rel_humidity, abs_humidity = TS_DEFAULT_VALUE; + + // Used for absolute humidity calculation + double vapour_pressure = 0; + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TSEvent)); + + // Register callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, temperature_sensor_draw_callback, NULL); + view_port_input_callback_set(view_port, temperature_sensor_input_callback, event_queue); + + // Create timer and register its callback + FuriTimer* timer = + furi_timer_alloc(temperature_sensor_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency()); + + // Register viewport + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // Used to notify the user by blinking red (error) or blue (fetch successful) + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); + + while(1) { + furi_check(furi_message_queue_get(event_queue, &tsEvent, FuriWaitForever) == FuriStatusOk); + + // Handle events + if(tsEvent.type == TSEventTypeInput) { + // Exit on back key + if(tsEvent.input.key == + InputKeyBack) // We dont check for type here, we can check the type of keypress like: (event.input.type == InputTypeShort) + break; + } else if(tsEvent.type == TSEventTypeTick) { + // Update sensor data + // Fetch data and set the sensor current status accordingly + sensorFound = temperature_sensor_fetch_data(&celsius, &rel_humidity); + temperature_sensor_current_status = (sensorFound ? TSSPendingUpdate : TSSNoSensor); + + if(sensorFound) { + // Blink blue + notification_message(notifications, &sequence_blink_blue_100); + + if(celsius != TS_DEFAULT_VALUE && rel_humidity != TS_DEFAULT_VALUE) { + // Convert celsius to fahrenheit + fahrenheit = (celsius * 9 / 5) + 32; + + // Calculate absolute humidity - For more info refer to https://github.com/Mywk/FlipperTemperatureSensor/issues/1 + // Calculate saturation vapour pressure first + vapour_pressure = + (double)6.11 * + pow(10, (double)(((double)7.5 * celsius) / ((double)237.3 + celsius))); + // Then the vapour pressure in Pa + vapour_pressure = vapour_pressure * rel_humidity; + // Calculate absolute humidity + abs_humidity = + (double)2.16679 * (double)(vapour_pressure / ((double)273.15 + celsius)); + + // Fill our buffers here, not on the canvas draw callback + snprintf(ts_data_buffer_temperature_c, DATA_BUFFER_SIZE, "%.2f", celsius); + snprintf(ts_data_buffer_temperature_f, DATA_BUFFER_SIZE, "%.2f", fahrenheit); + snprintf( + ts_data_buffer_relative_humidity, DATA_BUFFER_SIZE, "%.2f", rel_humidity); + snprintf( + ts_data_buffer_absolute_humidity, DATA_BUFFER_SIZE, "%.2f", abs_humidity); + } + } else { + // Reset our variables to their default values + celsius = fahrenheit = rel_humidity = abs_humidity = TS_DEFAULT_VALUE; + + // Blink red + notification_message(notifications, &sequence_blink_red_100); + } + } + + furi_delay_ms(!sensorFound ? 100 : 500); + } + + // Dobby is freee (free our variables, Flipper will crash if we don't do this!) + furi_timer_free(timer); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/htu21d_temp_sensor/temperature_sensor.png b/applications/plugins/htu21d_temp_sensor/temperature_sensor.png new file mode 100644 index 000000000..b6fe6d7fe Binary files /dev/null and b/applications/plugins/htu21d_temp_sensor/temperature_sensor.png differ diff --git a/applications/plugins/subbrute/LICENSE.md b/applications/plugins/ibtn_fuzzer/LICENSE.md similarity index 76% rename from applications/plugins/subbrute/LICENSE.md rename to applications/plugins/ibtn_fuzzer/LICENSE.md index a856581c9..ba3b84456 100644 --- a/applications/plugins/subbrute/LICENSE.md +++ b/applications/plugins/ibtn_fuzzer/LICENSE.md @@ -1,7 +1,7 @@ /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): - * @G4N4P4T1 wrote this file. As long as you retain this notice you + * @xMasterX and @G4N4P4T1(made original version) wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. * ---------------------------------------------------------------------------- diff --git a/applications/plugins/ibtn_fuzzer/application.fam b/applications/plugins/ibtn_fuzzer/application.fam new file mode 100644 index 000000000..b27f47ba9 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/application.fam @@ -0,0 +1,13 @@ +App( + appid="iBtn_Fuzzer", + name="iButton Fuzzer", + apptype=FlipperAppType.EXTERNAL, + entry_point="ibtnfuzzer_start", + cdefines=["APP_IBTN_FUZZ"], + requires=["gui", "storage", "dialogs", "input", "notification"], + stack_size=1 * 1024, + order=15, + fap_icon="ibutt_10px.png", + fap_category="Tools", + fap_icon_assets="images", +) diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c new file mode 100644 index 000000000..f02da3b82 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c @@ -0,0 +1,268 @@ +#include "ibtnfuzzer.h" + +#include "scene/ibtnfuzzer_scene_entrypoint.h" +#include "scene/ibtnfuzzer_scene_load_file.h" +#include "scene/ibtnfuzzer_scene_select_field.h" +#include "scene/ibtnfuzzer_scene_run_attack.h" +#include "scene/ibtnfuzzer_scene_load_custom_uids.h" + +#define IBTNFUZZER_APP_FOLDER "/ext/ibtnfuzzer" + +static void ibtnfuzzer_draw_callback(Canvas* const canvas, void* ctx) { + iBtnFuzzerState* ibtnfuzzer_state = (iBtnFuzzerState*)acquire_mutex((ValueMutex*)ctx, 100); + + if(ibtnfuzzer_state == NULL) { + return; + } + + // Draw correct Canvas + switch(ibtnfuzzer_state->current_scene) { + case NoneScene: + case SceneEntryPoint: + ibtnfuzzer_scene_entrypoint_on_draw(canvas, ibtnfuzzer_state); + break; + case SceneSelectFile: + ibtnfuzzer_scene_load_file_on_draw(canvas, ibtnfuzzer_state); + break; + case SceneSelectField: + ibtnfuzzer_scene_select_field_on_draw(canvas, ibtnfuzzer_state); + break; + case SceneAttack: + ibtnfuzzer_scene_run_attack_on_draw(canvas, ibtnfuzzer_state); + break; + case SceneLoadCustomUids: + ibtnfuzzer_scene_load_custom_uids_on_draw(canvas, ibtnfuzzer_state); + break; + } + + release_mutex((ValueMutex*)ctx, ibtnfuzzer_state); +} + +void ibtnfuzzer_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + iBtnFuzzerEvent event = { + .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; + furi_message_queue_put(event_queue, &event, 25); +} + +static void ibtnfuzzer_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + iBtnFuzzerEvent event = { + .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; + furi_message_queue_put(event_queue, &event, 25); +} + +iBtnFuzzerState* ibtnfuzzer_alloc() { + iBtnFuzzerState* ibtnfuzzer = malloc(sizeof(iBtnFuzzerState)); + ibtnfuzzer->notification_msg = furi_string_alloc(); + ibtnfuzzer->attack_name = furi_string_alloc(); + ibtnfuzzer->proto_name = furi_string_alloc(); + ibtnfuzzer->data_str = furi_string_alloc(); + + ibtnfuzzer->previous_scene = NoneScene; + ibtnfuzzer->current_scene = SceneEntryPoint; + ibtnfuzzer->is_running = true; + ibtnfuzzer->is_attacking = false; + ibtnfuzzer->key_index = 0; + ibtnfuzzer->menu_index = 0; + ibtnfuzzer->menu_proto_index = 0; + + ibtnfuzzer->attack = iBtnFuzzerAttackDefaultValues; + ibtnfuzzer->notify = furi_record_open(RECORD_NOTIFICATION); + + ibtnfuzzer->data[0] = 0x00; + ibtnfuzzer->data[1] = 0x00; + ibtnfuzzer->data[2] = 0x00; + ibtnfuzzer->data[3] = 0x00; + ibtnfuzzer->data[4] = 0x00; + ibtnfuzzer->data[5] = 0x00; + ibtnfuzzer->data[6] = 0x00; + ibtnfuzzer->data[7] = 0x00; + + ibtnfuzzer->payload[0] = 0x00; + ibtnfuzzer->payload[1] = 0x00; + ibtnfuzzer->payload[2] = 0x00; + ibtnfuzzer->payload[3] = 0x00; + ibtnfuzzer->payload[4] = 0x00; + ibtnfuzzer->payload[5] = 0x00; + ibtnfuzzer->payload[6] = 0x00; + ibtnfuzzer->payload[7] = 0x00; + + //Dialog + ibtnfuzzer->dialogs = furi_record_open(RECORD_DIALOGS); + + return ibtnfuzzer; +} + +void ibtnfuzzer_free(iBtnFuzzerState* ibtnfuzzer) { + //Dialog + furi_record_close(RECORD_DIALOGS); + notification_message(ibtnfuzzer->notify, &sequence_blink_stop); + + // Strings + furi_string_free(ibtnfuzzer->notification_msg); + furi_string_free(ibtnfuzzer->attack_name); + furi_string_free(ibtnfuzzer->proto_name); + furi_string_free(ibtnfuzzer->data_str); + + free(ibtnfuzzer->data); + free(ibtnfuzzer->payload); + + // The rest + free(ibtnfuzzer); +} + +// ENTRYPOINT +int32_t ibtnfuzzer_start(void* p) { + UNUSED(p); + // Input + FURI_LOG_I(TAG, "Initializing input"); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(iBtnFuzzerEvent)); + iBtnFuzzerState* ibtnfuzzer_state = ibtnfuzzer_alloc(); + ValueMutex ibtnfuzzer_state_mutex; + + // Mutex + FURI_LOG_I(TAG, "Initializing ibtnfuzzer mutex"); + if(!init_mutex(&ibtnfuzzer_state_mutex, ibtnfuzzer_state, sizeof(iBtnFuzzerState))) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + furi_record_close(RECORD_NOTIFICATION); + ibtnfuzzer_free(ibtnfuzzer_state); + return 255; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage_simply_mkdir(storage, IBTNFUZZER_APP_FOLDER)) { + FURI_LOG_E(TAG, "Could not create folder %s", IBTNFUZZER_APP_FOLDER); + } + furi_record_close(RECORD_STORAGE); + + // Configure view port + FURI_LOG_I(TAG, "Initializing viewport"); + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, ibtnfuzzer_draw_callback, &ibtnfuzzer_state_mutex); + view_port_input_callback_set(view_port, ibtnfuzzer_input_callback, event_queue); + + // Configure timer + FURI_LOG_I(TAG, "Initializing timer"); + FuriTimer* timer = + furi_timer_alloc(ibtnfuzzer_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second + + // Register view port in GUI + FURI_LOG_I(TAG, "Initializing gui"); + Gui* gui = (Gui*)furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // Init values + iBtnFuzzerEvent event; + while(ibtnfuzzer_state->is_running) { + // Get next event + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); + if(event_status == FuriStatusOk) { + if(event.evt_type == EventTypeKey) { + //Handle event key + switch(ibtnfuzzer_state->current_scene) { + case NoneScene: + case SceneEntryPoint: + ibtnfuzzer_scene_entrypoint_on_event(event, ibtnfuzzer_state); + break; + case SceneSelectFile: + ibtnfuzzer_scene_load_file_on_event(event, ibtnfuzzer_state); + break; + case SceneSelectField: + ibtnfuzzer_scene_select_field_on_event(event, ibtnfuzzer_state); + break; + case SceneAttack: + ibtnfuzzer_scene_run_attack_on_event(event, ibtnfuzzer_state); + break; + case SceneLoadCustomUids: + ibtnfuzzer_scene_load_custom_uids_on_event(event, ibtnfuzzer_state); + break; + } + + } else if(event.evt_type == EventTypeTick) { + //Handle event tick + if(ibtnfuzzer_state->current_scene != ibtnfuzzer_state->previous_scene) { + // Trigger Exit Scene + switch(ibtnfuzzer_state->previous_scene) { + case SceneEntryPoint: + ibtnfuzzer_scene_entrypoint_on_exit(ibtnfuzzer_state); + break; + case SceneSelectFile: + ibtnfuzzer_scene_load_file_on_exit(ibtnfuzzer_state); + break; + case SceneSelectField: + ibtnfuzzer_scene_select_field_on_exit(ibtnfuzzer_state); + break; + case SceneAttack: + ibtnfuzzer_scene_run_attack_on_exit(ibtnfuzzer_state); + break; + case SceneLoadCustomUids: + ibtnfuzzer_scene_load_custom_uids_on_exit(ibtnfuzzer_state); + break; + case NoneScene: + break; + } + + // Trigger Entry Scene + switch(ibtnfuzzer_state->current_scene) { + case NoneScene: + case SceneEntryPoint: + ibtnfuzzer_scene_entrypoint_on_enter(ibtnfuzzer_state); + break; + case SceneSelectFile: + ibtnfuzzer_scene_load_file_on_enter(ibtnfuzzer_state); + break; + case SceneSelectField: + ibtnfuzzer_scene_select_field_on_enter(ibtnfuzzer_state); + break; + case SceneAttack: + ibtnfuzzer_scene_run_attack_on_enter(ibtnfuzzer_state); + break; + case SceneLoadCustomUids: + ibtnfuzzer_scene_load_custom_uids_on_enter(ibtnfuzzer_state); + break; + } + ibtnfuzzer_state->previous_scene = ibtnfuzzer_state->current_scene; + } + + // Trigger Tick Scene + switch(ibtnfuzzer_state->current_scene) { + case NoneScene: + case SceneEntryPoint: + ibtnfuzzer_scene_entrypoint_on_tick(ibtnfuzzer_state); + break; + case SceneSelectFile: + ibtnfuzzer_scene_load_file_on_tick(ibtnfuzzer_state); + break; + case SceneSelectField: + ibtnfuzzer_scene_select_field_on_tick(ibtnfuzzer_state); + break; + case SceneAttack: + ibtnfuzzer_scene_run_attack_on_tick(ibtnfuzzer_state); + break; + case SceneLoadCustomUids: + ibtnfuzzer_scene_load_custom_uids_on_tick(ibtnfuzzer_state); + break; + } + view_port_update(view_port); + } + } + } + + // Cleanup + furi_timer_stop(timer); + furi_timer_free(timer); + + FURI_LOG_I(TAG, "Cleaning up"); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + ibtnfuzzer_free(ibtnfuzzer_state); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h new file mode 100644 index 000000000..1af5e9ff1 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h @@ -0,0 +1,89 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#define TAG "iBtnFuzzer" + +typedef enum { + iBtnFuzzerAttackDefaultValues, + iBtnFuzzerAttackLoadFile, + iBtnFuzzerAttackLoadFileCustomUids, +} iBtnFuzzerAttacks; + +typedef enum { + DS1990, + Metakom, + Cyfral, +} iBtnFuzzerProtos; + +typedef enum { + NoneScene, + SceneEntryPoint, + SceneSelectFile, + SceneSelectField, + SceneAttack, + SceneLoadCustomUids, +} iBtnFuzzerScene; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType evt_type; + InputKey key; + InputType input_type; +} iBtnFuzzerEvent; + +// STRUCTS +typedef struct { + bool is_running; + bool is_attacking; + iBtnFuzzerScene current_scene; + iBtnFuzzerScene previous_scene; + NotificationApp* notify; + u_int8_t menu_index; + u_int8_t menu_proto_index; + + FuriString* data_str; + uint8_t data[8]; + uint8_t payload[8]; + uint8_t attack_step; + iBtnFuzzerAttacks attack; + iBtnFuzzerProtos proto; + FuriString* attack_name; + FuriString* proto_name; + + DialogsApp* dialogs; + FuriString* notification_msg; + uint8_t key_index; + iButtonWorker* worker; + iButtonKey* key; + iButtonKeyType keytype; + bool workr_rund; + bool enter_rerun; + + uint8_t time_between_cards; + + // Used for custom dictionnary + Stream* uids_stream; +} iBtnFuzzerState; \ No newline at end of file diff --git a/applications/plugins/ibtn_fuzzer/ibutt_10px.png b/applications/plugins/ibtn_fuzzer/ibutt_10px.png new file mode 100644 index 000000000..2fdaf123a Binary files /dev/null and b/applications/plugins/ibtn_fuzzer/ibutt_10px.png differ diff --git a/applications/plugins/ibtn_fuzzer/images/ibutt_10px.png b/applications/plugins/ibtn_fuzzer/images/ibutt_10px.png new file mode 100644 index 000000000..2fdaf123a Binary files /dev/null and b/applications/plugins/ibtn_fuzzer/images/ibutt_10px.png differ diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c new file mode 100644 index 000000000..a951e0c1f --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c @@ -0,0 +1,215 @@ +#include "ibtnfuzzer_scene_entrypoint.h" + +FuriString* main_menu_items[3]; +FuriString* main_menu_proto_items[3]; + +void ibtnfuzzer_scene_entrypoint_menu_callback( + iBtnFuzzerState* context, + uint32_t index, + uint32_t proto_index) { + switch(index) { + case iBtnFuzzerAttackDefaultValues: + context->attack = iBtnFuzzerAttackDefaultValues; + context->current_scene = SceneAttack; + furi_string_set(context->attack_name, "Default Values"); + break; + case iBtnFuzzerAttackLoadFile: + context->attack = iBtnFuzzerAttackLoadFile; + context->current_scene = SceneSelectFile; + furi_string_set(context->attack_name, "Load File"); + break; + case iBtnFuzzerAttackLoadFileCustomUids: + context->attack = iBtnFuzzerAttackLoadFileCustomUids; + context->current_scene = SceneLoadCustomUids; + furi_string_set(context->attack_name, "Load Custom UIDs"); + break; + default: + break; + } + + switch(proto_index) { + case DS1990: + context->proto = DS1990; + furi_string_set(context->proto_name, "DS1990"); + break; + case Metakom: + context->proto = Metakom; + furi_string_set(context->proto_name, "Metakom"); + break; + case Cyfral: + context->proto = Cyfral; + furi_string_set(context->proto_name, "Cyfral"); + break; + default: + break; + } +} + +void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context) { + // Clear the previous payload + context->payload[0] = 0x00; + context->payload[1] = 0x00; + context->payload[2] = 0x00; + context->payload[3] = 0x00; + context->payload[4] = 0x00; + context->payload[5] = 0x00; + context->payload[6] = 0x00; + context->payload[7] = 0x00; + + context->menu_index = 0; + /*for(uint32_t i = 0; i < 4; i++) { + menu_items[i] = furi_string_alloc(); + }*/ + + main_menu_items[0] = furi_string_alloc_set("Default Values"); + main_menu_items[1] = furi_string_alloc_set("Load File"); + main_menu_items[2] = furi_string_alloc_set("Load uids from file"); + + context->menu_proto_index = 0; + /*for(uint32_t i = 0; i < 4; i++) { + menu_proto_items[i] = furi_string_alloc(); + }*/ + + main_menu_proto_items[0] = furi_string_alloc_set("DS1990"); + main_menu_proto_items[1] = furi_string_alloc_set("Metakom"); + main_menu_proto_items[2] = furi_string_alloc_set("Cyfral"); +} + +void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context) { + context->enter_rerun = false; + + for(uint32_t i = 0; i < 3; i++) { + furi_string_free(main_menu_items[i]); + } + + for(uint32_t i = 0; i < 3; i++) { + furi_string_free(main_menu_proto_items[i]); + } +} + +void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context) { + UNUSED(context); +} + +void ibtnfuzzer_scene_entrypoint_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { + context->menu_index++; + } + break; + case InputKeyUp: + if(context->menu_index > iBtnFuzzerAttackDefaultValues) { + context->menu_index--; + } + break; + case InputKeyLeft: + if(context->menu_proto_index > DS1990) { + context->menu_proto_index--; + } else if(context->menu_proto_index == DS1990) { + context->menu_proto_index = Cyfral; + } + break; + case InputKeyRight: + if(context->menu_proto_index < Cyfral) { + context->menu_proto_index++; + } else if(context->menu_proto_index == Cyfral) { + context->menu_proto_index = DS1990; + } + break; + case InputKeyOk: + ibtnfuzzer_scene_entrypoint_menu_callback( + context, context->menu_index, context->menu_proto_index); + break; + case InputKeyBack: + context->is_running = false; + break; + default: + break; + } + } + } +} + +void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* context) { + if(!context->enter_rerun) { + ibtnfuzzer_scene_entrypoint_on_enter(context); + context->enter_rerun = true; + } + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(main_menu_items[context->menu_index] != NULL) { + if(context->menu_index > iBtnFuzzerAttackDefaultValues) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 24, + AlignCenter, + AlignTop, + furi_string_get_cstr(main_menu_items[context->menu_index - 1])); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, + 64, + 36, + AlignCenter, + AlignTop, + furi_string_get_cstr(main_menu_items[context->menu_index])); + + if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 48, + AlignCenter, + AlignTop, + furi_string_get_cstr(main_menu_items[context->menu_index + 1])); + } + + if(context->menu_proto_index > DS1990) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + -12, + AlignCenter, + AlignTop, + furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index - 1])); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); + + canvas_set_font(canvas, FontPrimary); + if(main_menu_proto_items[context->menu_proto_index] != NULL) { + canvas_draw_str_aligned( + canvas, + 64, + 4, + AlignCenter, + AlignTop, + furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index])); + } + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); + + if(context->menu_proto_index < Cyfral) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + -12, + AlignCenter, + AlignTop, + furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index + 1])); + } + } +} \ No newline at end of file diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h new file mode 100644 index 000000000..b77aec369 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h @@ -0,0 +1,8 @@ +#pragma once +#include "../ibtnfuzzer.h" + +void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context); +void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context); +void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context); +void ibtnfuzzer_scene_entrypoint_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); +void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c new file mode 100644 index 000000000..8f1a422b3 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c @@ -0,0 +1,85 @@ +#include "ibtnfuzzer_scene_load_custom_uids.h" +#include "ibtnfuzzer_scene_run_attack.h" +#include "ibtnfuzzer_scene_entrypoint.h" + +#define IBTNFUZZER_UIDS_EXTENSION ".txt" +#define IBTNFUZZER_APP_PATH_FOLDER "/ext/ibtnfuzzer" + +bool ibtnfuzzer_load_uids(iBtnFuzzerState* context, const char* file_path) { + bool result = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + context->uids_stream = buffered_file_stream_alloc(storage); + result = + buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); + // Close if loading fails + if(!result) { + buffered_file_stream_close(context->uids_stream); + return false; + } + return result; +} + +bool ibtnfuzzer_load_custom_uids_from_file(iBtnFuzzerState* context) { + // Input events and views are managed by file_select + FuriString* uid_path; + uid_path = furi_string_alloc(); + furi_string_set(uid_path, IBTNFUZZER_APP_PATH_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, IBTNFUZZER_UIDS_EXTENSION, &I_ibutt_10px); + browser_options.hide_ext = false; + + bool res = dialog_file_browser_show(context->dialogs, uid_path, uid_path, &browser_options); + + if(res) { + res = ibtnfuzzer_load_uids(context, furi_string_get_cstr(uid_path)); + } + + furi_string_free(uid_path); + + return res; +} + +void ibtnfuzzer_scene_load_custom_uids_on_enter(iBtnFuzzerState* context) { + if(ibtnfuzzer_load_custom_uids_from_file(context)) { + // Force context loading + ibtnfuzzer_scene_run_attack_on_enter(context); + context->current_scene = SceneAttack; + } else { + ibtnfuzzer_scene_entrypoint_on_enter(context); + context->current_scene = SceneEntryPoint; + } +} + +void ibtnfuzzer_scene_load_custom_uids_on_exit(iBtnFuzzerState* context) { + UNUSED(context); +} + +void ibtnfuzzer_scene_load_custom_uids_on_tick(iBtnFuzzerState* context) { + UNUSED(context); +} + +void ibtnfuzzer_scene_load_custom_uids_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + case InputKeyLeft: + case InputKeyRight: + case InputKeyOk: + case InputKeyBack: + context->current_scene = SceneEntryPoint; + break; + default: + break; + } + } + } +} + +void ibtnfuzzer_scene_load_custom_uids_on_draw(Canvas* canvas, iBtnFuzzerState* context) { + UNUSED(context); + UNUSED(canvas); +} diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h new file mode 100644 index 000000000..bb51c7079 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h @@ -0,0 +1,9 @@ +#pragma once +#include "../ibtnfuzzer.h" + +void ibtnfuzzer_scene_load_custom_uids_on_enter(iBtnFuzzerState* context); +void ibtnfuzzer_scene_load_custom_uids_on_exit(iBtnFuzzerState* context); +void ibtnfuzzer_scene_load_custom_uids_on_tick(iBtnFuzzerState* context); +void ibtnfuzzer_scene_load_custom_uids_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); +void ibtnfuzzer_scene_load_custom_uids_on_draw(Canvas* canvas, iBtnFuzzerState* context); +bool ibtnfuzzer_load_custom_uids_from_file(iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c new file mode 100644 index 000000000..99e6db733 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c @@ -0,0 +1,179 @@ +#include "ibtnfuzzer_scene_load_file.h" +#include "ibtnfuzzer_scene_entrypoint.h" + +#define IBUTTON_FUZZER_APP_EXTENSION ".ibtn" +#define IBUTTON_FUZZER_APP_PATH_FOLDER "/ext/ibutton" + +bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { + bool result = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + if(!flipper_format_file_open_existing(fff_data_file, file_path)) { + FURI_LOG_E(TAG, "Error open file %s", file_path); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Error open file"); + break; + } + + // FileType + if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Filetype"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Missing or incorrect Filetypes"); + break; + } else { + FURI_LOG_I(TAG, "Filetype: %s", furi_string_get_cstr(temp_str)); + } + + // Key type + if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Missing or incorrect Key type"); + break; + } else { + FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); + + if(context->proto == DS1990) { + if(strcmp(furi_string_get_cstr(temp_str), "Dallas") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } else if(context->proto == Cyfral) { + if(strcmp(furi_string_get_cstr(temp_str), "Cyfral") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } else { + if(strcmp(furi_string_get_cstr(temp_str), "Metakom") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } + } + + // Data + if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Data"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Missing or incorrect Key"); + break; + } else { + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); + + if(context->proto == DS1990) { + if(furi_string_size(context->data_str) != 23) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } else if(context->proto == Cyfral) { + if(furi_string_size(context->data_str) != 5) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } else { + if(furi_string_size(context->data_str) != 11) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } + + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } + } + + result = true; + } while(0); + furi_string_free(temp_str); + flipper_format_free(fff_data_file); + if(result) { + FURI_LOG_I(TAG, "Loaded successfully"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Source loaded."); + } + return result; +} + +void ibtnfuzzer_scene_load_file_on_enter(iBtnFuzzerState* context) { + if(ibtnfuzzer_load_protocol_from_file(context)) { + context->current_scene = SceneSelectField; + } else { + ibtnfuzzer_scene_entrypoint_on_enter(context); + context->current_scene = SceneEntryPoint; + } +} + +void ibtnfuzzer_scene_load_file_on_exit(iBtnFuzzerState* context) { + UNUSED(context); +} + +void ibtnfuzzer_scene_load_file_on_tick(iBtnFuzzerState* context) { + UNUSED(context); +} + +void ibtnfuzzer_scene_load_file_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + case InputKeyLeft: + case InputKeyRight: + case InputKeyOk: + case InputKeyBack: + context->current_scene = SceneEntryPoint; + break; + default: + break; + } + } + } +} + +void ibtnfuzzer_scene_load_file_on_draw(Canvas* canvas, iBtnFuzzerState* context) { + UNUSED(context); + UNUSED(canvas); +} + +bool ibtnfuzzer_load_protocol_from_file(iBtnFuzzerState* context) { + FuriString* user_file_path; + user_file_path = furi_string_alloc(); + furi_string_set(user_file_path, IBUTTON_FUZZER_APP_PATH_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, IBUTTON_FUZZER_APP_EXTENSION, &I_ibutt_10px); + + // Input events and views are managed by file_select + bool res = dialog_file_browser_show( + context->dialogs, user_file_path, user_file_path, &browser_options); + + if(res) { + res = ibtnfuzzer_load(context, furi_string_get_cstr(user_file_path)); + } + + furi_string_free(user_file_path); + + return res; +} \ No newline at end of file diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h new file mode 100644 index 000000000..34031d08c --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h @@ -0,0 +1,9 @@ +#pragma once +#include "../ibtnfuzzer.h" + +void ibtnfuzzer_scene_load_file_on_enter(iBtnFuzzerState* context); +void ibtnfuzzer_scene_load_file_on_exit(iBtnFuzzerState* context); +void ibtnfuzzer_scene_load_file_on_tick(iBtnFuzzerState* context); +void ibtnfuzzer_scene_load_file_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); +void ibtnfuzzer_scene_load_file_on_draw(Canvas* canvas, iBtnFuzzerState* context); +bool ibtnfuzzer_load_protocol_from_file(iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c new file mode 100644 index 000000000..5efd4f052 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c @@ -0,0 +1,493 @@ +#include "ibtnfuzzer_scene_run_attack.h" +#include + +uint8_t counter = 0; + +uint8_t id_list_ds1990[25][8] = { + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– ΠΊΠΎΠ΄ ΡƒΠ½ΠΈΠ²Π΅Ρ€ΡΠ°Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π°, для Vizit + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ + {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ + {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Π’ΠΈΠ·ΠΈΡ‚ ΠΈΠ½ΠΎΠ³Π΄Π° ΠšΠ•Π™ΠœΠΠΠ« + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½(ΠΌΠ΅Ρ‚Π°ΠΊΠΎΠΌ, Ρ†ΠΈΡ„Ρ€Π°Π», Π’Π˜Π—Π˜Π’). + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½ Π’ΠΈΠ·ΠΈΡ‚, ΠœΠ΅Ρ‚Π°ΠΊΠΎΠΌΡ‹, ΠšΠžΠΠ”ΠžΠ  + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-ΠžΡ‚ΠΊΡ€Ρ‹Π²Π°Π°Π΅Ρ‚ 98% ΠœΠ΅Ρ‚Π°ΠΊΠΎΠΌ ΠΈ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π¦ΠΈΡ„Ρ€Π°Π» + {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-ΠžΡ‚Π»ΠΈΡ‡Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Π½Π° старых Π΄ΠΎΠΌΠΎΡ„ΠΎΠ½Π°Ρ… + {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-ΠžΡ‚ΠΊΡ€Ρ‹Π²Π°Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π΄ΠΎΠ»ΠΆΠ΅Π½ + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-Π΄ΠΎΠΌΠΎΡ„ΠΎΠ½Ρ‹ Π’ΠΈΠ·ΠΈΡ‚ (Vizit) - Π΄ΠΎ 99% + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-Π΄ΠΎΠΌΠΎΡ„ΠΎΠ½Ρ‹ Cyfral CCD-20 - Π΄ΠΎ 70% + {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-Π΄ΠΎΠΌΠΎΡ„ΠΎΠ½Ρ‹ КСйман (KEYMAN) + {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-Π΄ΠΎΠΌΠΎΡ„ΠΎΠ½Ρ‹ Π€ΠΎΡ€Π²Π°Ρ€Π΄ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 +}; + +uint8_t id_list_metakom[17][4] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // ?? + {0x34, 0x00, 0x29, 0x3d}, // ?? + {0x04, 0xdf, 0x00, 0x00}, // ?? + {0xCA, 0xCA, 0xCA, 0xCA}, // ?? +}; + +uint8_t id_list_cyfral[14][2] = { + {0x00, 0x00}, // Null bytes + {0xFF, 0xFF}, // Only FF + {0x11, 0x11}, // Only 11 + {0x22, 0x22}, // Only 22 + {0x33, 0x33}, // Only 33 + {0x44, 0x44}, // Only 44 + {0x55, 0x55}, // Only 55 + {0x66, 0x66}, // Only 66 + {0x77, 0x77}, // Only 77 + {0x88, 0x88}, // Only 88 + {0x99, 0x99}, // Only 99 + {0x12, 0x34}, // Incremental UID + {0x56, 0x34}, // Decremental UID + {0xCA, 0xCA}, // ?? +}; + +void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) { + context->time_between_cards = 10; + context->attack_step = 0; + context->key = ibutton_key_alloc(); + context->worker = ibutton_worker_alloc(); + if(context->proto == Metakom) { + context->keytype = iButtonKeyMetakom; + } else if(context->proto == Cyfral) { + context->keytype = iButtonKeyCyfral; + } else { + context->keytype = iButtonKeyDS1990; + } + context->workr_rund = false; +} + +void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context) { + if(context->workr_rund) { + ibutton_worker_stop(context->worker); + ibutton_worker_stop_thread(context->worker); + context->workr_rund = false; + } + ibutton_worker_free(context->worker); + ibutton_key_free(context->key); + notification_message(context->notify, &sequence_blink_stop); +} + +void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { + if(context->is_attacking) { + if(1 == counter) { + ibutton_worker_start_thread(context->worker); + ibutton_key_set_type(context->key, context->keytype); + ibutton_key_set_data( + context->key, context->payload, ibutton_key_get_size_by_type(context->keytype)); + ibutton_worker_emulate_start(context->worker, context->key); + context->workr_rund = true; + } else if(0 == counter) { + if(context->workr_rund) { + ibutton_worker_stop(context->worker); + ibutton_worker_stop_thread(context->worker); + context->workr_rund = false; + furi_delay_ms(500); + } + switch(context->attack) { + case iBtnFuzzerAttackDefaultValues: + if(context->proto == DS1990) { + context->payload[0] = id_list_ds1990[context->attack_step][0]; + context->payload[1] = id_list_ds1990[context->attack_step][1]; + context->payload[2] = id_list_ds1990[context->attack_step][2]; + context->payload[3] = id_list_ds1990[context->attack_step][3]; + context->payload[4] = id_list_ds1990[context->attack_step][4]; + context->payload[5] = id_list_ds1990[context->attack_step][5]; + context->payload[6] = id_list_ds1990[context->attack_step][6]; + context->payload[7] = id_list_ds1990[context->attack_step][7]; + + if(context->attack_step == 24) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } else { + context->attack_step++; + } + break; + } else if(context->proto == Metakom) { + context->payload[0] = id_list_metakom[context->attack_step][0]; + context->payload[1] = id_list_metakom[context->attack_step][1]; + context->payload[2] = id_list_metakom[context->attack_step][2]; + context->payload[3] = id_list_metakom[context->attack_step][3]; + + if(context->attack_step == 16) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } else { + context->attack_step++; + } + break; + } else { + context->payload[0] = id_list_cyfral[context->attack_step][0]; + context->payload[1] = id_list_cyfral[context->attack_step][1]; + + if(context->attack_step == 13) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } else { + context->attack_step++; + } + break; + } + case iBtnFuzzerAttackLoadFile: + if(context->proto == DS1990) { + context->payload[0] = context->data[0]; + context->payload[1] = context->data[1]; + context->payload[2] = context->data[2]; + context->payload[3] = context->data[3]; + context->payload[4] = context->data[4]; + context->payload[5] = context->data[5]; + context->payload[6] = context->data[6]; + context->payload[7] = context->data[7]; + + context->payload[context->key_index] = context->attack_step; + + if(context->attack_step == 255) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + break; + } else { + context->attack_step++; + } + break; + } else if(context->proto == Cyfral) { + context->payload[0] = context->data[0]; + context->payload[1] = context->data[1]; + + context->payload[context->key_index] = context->attack_step; + + if(context->attack_step == 255) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + break; + } else { + context->attack_step++; + } + break; + } else { + context->payload[0] = context->data[0]; + context->payload[1] = context->data[1]; + context->payload[2] = context->data[2]; + context->payload[3] = context->data[3]; + + context->payload[context->key_index] = context->attack_step; + + if(context->attack_step == 255) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + break; + } else { + context->attack_step++; + } + break; + } + + case iBtnFuzzerAttackLoadFileCustomUids: + if(context->proto == DS1990) { + bool end_of_list = false; + while(true) { + furi_string_reset(context->data_str); + if(!stream_read_line(context->uids_stream, context->data_str)) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + stream_rewind(context->uids_stream); + end_of_list = true; + break; + }; + if(furi_string_get_char(context->data_str, 0) == '#') continue; + if(furi_string_size(context->data_str) != 17) break; + break; + } + if(end_of_list) break; + FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); + if(furi_string_size(context->data_str) != 17) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_error); + break; + }; + + // string is valid, parse it in context->payload + for(uint8_t i = 0; i < 8; i++) { + char temp_str[3]; + temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; + temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; + temp_str[2] = '\0'; + context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); + } + break; + } else if(context->proto == Cyfral) { + bool end_of_list = false; + while(true) { + furi_string_reset(context->data_str); + if(!stream_read_line(context->uids_stream, context->data_str)) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + stream_rewind(context->uids_stream); + end_of_list = true; + break; + }; + if(furi_string_get_char(context->data_str, 0) == '#') continue; + if(furi_string_size(context->data_str) != 5) break; + break; + } + if(end_of_list) break; + FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); + if(furi_string_size(context->data_str) != 5) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_error); + break; + }; + + // string is valid, parse it in context->payload + for(uint8_t i = 0; i < 2; i++) { + char temp_str[3]; + temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; + temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; + temp_str[2] = '\0'; + context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); + } + break; + } else { + bool end_of_list = false; + while(true) { + furi_string_reset(context->data_str); + if(!stream_read_line(context->uids_stream, context->data_str)) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + stream_rewind(context->uids_stream); + end_of_list = true; + break; + }; + if(furi_string_get_char(context->data_str, 0) == '#') continue; + if(furi_string_size(context->data_str) != 9) break; + break; + } + FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); + if(end_of_list) break; + if(furi_string_size(context->data_str) != 9) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_error); + break; + }; + + // string is valid, parse it in context->payload + for(uint8_t i = 0; i < 4; i++) { + char temp_str[3]; + temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; + temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; + temp_str[2] = '\0'; + context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); + } + break; + } + } + } + + if(counter > context->time_between_cards) { + counter = 0; + } else { + counter++; + } + } +} + +void ibtnfuzzer_scene_run_attack_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + break; + case InputKeyUp: + break; + case InputKeyLeft: + if(!context->is_attacking) { + if(context->time_between_cards > 8) { + context->time_between_cards--; + } + } + break; + case InputKeyRight: + if(!context->is_attacking) { + if(context->time_between_cards < 80) { + context->time_between_cards++; + } + } + break; + case InputKeyOk: + counter = 0; + if(!context->is_attacking) { + notification_message(context->notify, &sequence_blink_start_blue); + context->is_attacking = true; + } else { + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } + break; + case InputKeyBack: + context->is_attacking = false; + context->attack_step = 0; + counter = 0; + + if(context->attack == iBtnFuzzerAttackLoadFileCustomUids) { + furi_string_reset(context->data_str); + stream_rewind(context->uids_stream); + buffered_file_stream_close(context->uids_stream); + } + + furi_string_reset(context->notification_msg); + notification_message(context->notify, &sequence_blink_stop); + context->current_scene = SceneEntryPoint; + break; + default: + break; + } + } + if(event.input_type == InputTypeLong) { + switch(event.key) { + case InputKeyLeft: + if(!context->is_attacking) { + if(context->time_between_cards > 8) { + if((context->time_between_cards - 10) > 8) { + context->time_between_cards -= 10; + } + } + } + break; + case InputKeyRight: + if(!context->is_attacking) { + if(context->time_between_cards < 80) { + context->time_between_cards += 10; + } + } + break; + default: + break; + } + } + } +} + +void ibtnfuzzer_scene_run_attack_on_draw(Canvas* canvas, iBtnFuzzerState* context) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Frame + //canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(context->attack_name)); + + char uid[25]; + char speed[16]; + if(context->proto == Metakom) { + snprintf( + uid, + sizeof(uid), + "%02X:%02X:%02X:%02X", + context->payload[0], + context->payload[1], + context->payload[2], + context->payload[3]); + } else if(context->proto == Cyfral) { + snprintf(uid, sizeof(uid), "%02X:%02X", context->payload[0], context->payload[1]); + } else { + snprintf( + uid, + sizeof(uid), + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", + context->payload[0], + context->payload[1], + context->payload[2], + context->payload[3], + context->payload[4], + context->payload[5], + context->payload[6], + context->payload[7]); + } + + canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, uid); + + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned( + canvas, 64, 26, AlignCenter, AlignTop, furi_string_get_cstr(context->proto_name)); + + snprintf(speed, sizeof(speed), "Time delay: %d", context->time_between_cards); + + //canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Speed:"); + canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, speed); + //char start_stop_msg[20]; + if(context->is_attacking) { + elements_button_center(canvas, "Stop"); + //snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); + } else { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "TD -"); + elements_button_right(canvas, "+ TD"); + } + //canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); +} diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h new file mode 100644 index 000000000..9e44478f7 --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h @@ -0,0 +1,8 @@ +#pragma once +#include "../ibtnfuzzer.h" + +void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context); +void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context); +void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context); +void ibtnfuzzer_scene_run_attack_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); +void ibtnfuzzer_scene_run_attack_on_draw(Canvas* canvas, iBtnFuzzerState* context); diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c new file mode 100644 index 000000000..f3217f65e --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c @@ -0,0 +1,160 @@ +#include "ibtnfuzzer_scene_select_field.h" + +void ibtnfuzzer_center_displayed_key(iBtnFuzzerState* context, uint8_t index) { + char key_cstr[25]; + uint8_t key_len = 25; + uint8_t str_index = (index * 3); + int data_len = sizeof(context->data) / sizeof(context->data[0]); + int key_index = 0; + + if(context->proto == DS1990) { + key_len = 25; + } + if(context->proto == Metakom) { + key_len = 13; + } + if(context->proto == Cyfral) { + key_len = 7; + } + + for(uint8_t i = 0; i < data_len; i++) { + if(context->data[i] < 9) { + key_index += + snprintf(&key_cstr[key_index], key_len - key_index, "0%X ", context->data[i]); + } else { + key_index += + snprintf(&key_cstr[key_index], key_len - key_index, "%X ", context->data[i]); + } + } + + char display_menu[17] = { + 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; + + if(index > 1) { + display_menu[0] = key_cstr[str_index - 6]; + display_menu[1] = key_cstr[str_index - 5]; + } else { + display_menu[0] = ' '; + display_menu[1] = ' '; + } + + if(index > 0) { + display_menu[3] = key_cstr[str_index - 3]; + display_menu[4] = key_cstr[str_index - 2]; + } else { + display_menu[3] = ' '; + display_menu[4] = ' '; + } + + display_menu[7] = key_cstr[str_index]; + display_menu[8] = key_cstr[str_index + 1]; + + if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { + display_menu[11] = key_cstr[str_index + 3]; + display_menu[12] = key_cstr[str_index + 4]; + } else { + display_menu[11] = ' '; + display_menu[12] = ' '; + } + + if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { + display_menu[14] = key_cstr[str_index + 6]; + display_menu[15] = key_cstr[str_index + 7]; + } else { + display_menu[14] = ' '; + display_menu[15] = ' '; + } + + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, display_menu); +} + +void ibtnfuzzer_scene_select_field_on_enter(iBtnFuzzerState* context) { + furi_string_reset(context->notification_msg); +} + +void ibtnfuzzer_scene_select_field_on_exit(iBtnFuzzerState* context) { + UNUSED(context); +} + +void ibtnfuzzer_scene_select_field_on_tick(iBtnFuzzerState* context) { + UNUSED(context); +} + +void ibtnfuzzer_scene_select_field_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + const char* key_cstr = furi_string_get_cstr(context->data_str); + int data_len = sizeof(context->data) / sizeof(context->data[0]); + + // don't look, it's ugly but I'm a python dev so... + uint8_t nb_bytes = 0; + for(uint8_t i = 0; i < strlen(key_cstr); i++) { + if(' ' == key_cstr[i]) { + nb_bytes++; + } + } + + switch(event.key) { + case InputKeyDown: + for(uint8_t i = 0; i < data_len; i++) { + if(context->key_index == i) { + context->data[i] = (context->data[i] - 1); + } + } + break; + case InputKeyUp: + for(uint8_t i = 0; i < data_len; i++) { + if(context->key_index == i) { + context->data[i] = (context->data[i] + 1); + } + } + break; + case InputKeyLeft: + if(context->key_index > 0) { + context->key_index = context->key_index - 1; + } + break; + case InputKeyRight: + if(context->key_index < nb_bytes) { + context->key_index = context->key_index + 1; + } + break; + case InputKeyOk: + furi_string_reset(context->notification_msg); + context->current_scene = SceneAttack; + break; + case InputKeyBack: + context->key_index = 0; + furi_string_reset(context->notification_msg); + context->current_scene = SceneSelectFile; + break; + default: + break; + } + FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); + } + } +} + +void ibtnfuzzer_scene_select_field_on_draw(Canvas* canvas, iBtnFuzzerState* context) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Frame + //canvas_draw_frame(canvas, 0, 0, 128, 64); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 12, 5, AlignLeft, AlignTop, "Left and right: select byte"); + canvas_draw_str_aligned(canvas, 12, 15, AlignLeft, AlignTop, "Up and down: adjust byte"); + + char msg_index[18]; + canvas_set_font(canvas, FontPrimary); + snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); + + ibtnfuzzer_center_displayed_key(context, context->key_index); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 45, AlignCenter, AlignTop, furi_string_get_cstr(context->notification_msg)); +} diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h new file mode 100644 index 000000000..b6c684c3b --- /dev/null +++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h @@ -0,0 +1,9 @@ +#pragma once +#include "../ibtnfuzzer.h" + +void ibtnfuzzer_scene_select_field_on_enter(iBtnFuzzerState* context); +void ibtnfuzzer_scene_select_field_on_exit(iBtnFuzzerState* context); +void ibtnfuzzer_scene_select_field_on_tick(iBtnFuzzerState* context); +void ibtnfuzzer_scene_select_field_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); +void ibtnfuzzer_scene_select_field_on_draw(Canvas* canvas, iBtnFuzzerState* context); +void center_displayed_key(iBtnFuzzerState* context, uint8_t index); \ No newline at end of file diff --git a/applications/plugins/ifttt/application.fam b/applications/plugins/ifttt/application.fam new file mode 100644 index 000000000..495627429 --- /dev/null +++ b/applications/plugins/ifttt/application.fam @@ -0,0 +1,14 @@ +App( + appid="ESP8266_IFTTT_Virtual_Button", + name="[ESP8266] IFTTT Virtual Button", + apptype=FlipperAppType.EXTERNAL, + entry_point="ifttt_virtual_button_app", + cdefines=["APP_IFTTT_VIRTUAL_BUTTON"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=20, + fap_icon="icon.png", + fap_category="GPIO", +) \ No newline at end of file diff --git a/applications/plugins/ifttt/icon.png b/applications/plugins/ifttt/icon.png new file mode 100644 index 000000000..f6d586b38 Binary files /dev/null and b/applications/plugins/ifttt/icon.png differ diff --git a/applications/plugins/ifttt/ifttt_virtual_button.c b/applications/plugins/ifttt/ifttt_virtual_button.c new file mode 100644 index 000000000..e23b8715d --- /dev/null +++ b/applications/plugins/ifttt/ifttt_virtual_button.c @@ -0,0 +1,251 @@ +#include "ifttt_virtual_button.h" + +#define IFTTT_FOLDER "/ext/ifttt" +#define IFTTT_CONFIG_FOLDER "/ext/ifttt/config" +const char* CONFIG_FILE_PATH = "/ext/ifttt/config/config.settings"; + +#define FLIPPERZERO_SERIAL_BAUD 115200 +typedef enum ESerialCommand { ESerialCommand_Config } ESerialCommand; + +Settings save_settings(Settings settings) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { + flipper_format_update_string_cstr(file, CONF_SSID, settings.save_ssid); + flipper_format_update_string_cstr(file, CONF_PASSWORD, settings.save_password); + flipper_format_update_string_cstr(file, CONF_KEY, settings.save_key); + flipper_format_update_string_cstr(file, CONF_EVENT, settings.save_event); + } else { + } + flipper_format_file_close(file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + return settings; +} + +void save_settings_file(FlipperFormat* file, Settings* settings) { + flipper_format_write_header_cstr(file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION); + flipper_format_write_comment_cstr(file, "Enter here the SSID of the wifi network"); + flipper_format_write_string_cstr(file, CONF_SSID, settings->save_ssid); + flipper_format_write_comment_cstr(file, "Enter here the PASSWORD of the wifi network"); + flipper_format_write_string_cstr(file, CONF_PASSWORD, settings->save_password); + flipper_format_write_comment_cstr(file, "Enter here the WEBHOOKS of your IFTTT account"); + flipper_format_write_string_cstr(file, CONF_KEY, settings->save_key); + flipper_format_write_comment_cstr(file, "Enter here the EVENT name of your trigger"); + flipper_format_write_string_cstr(file, CONF_EVENT, settings->save_event); +} + +Settings* load_settings() { + Settings* settings = malloc(sizeof(Settings)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + FuriString* string_value; + string_value = furi_string_alloc(); + FuriString* text_ssid_value; + text_ssid_value = furi_string_alloc(); + FuriString* text_password_value; + text_password_value = furi_string_alloc(); + FuriString* text_key_value; + text_key_value = furi_string_alloc(); + FuriString* text_event_value; + text_event_value = furi_string_alloc(); + + if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) { + if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { + flipper_format_file_close(file); + } else { + settings->save_ssid = malloc(1); + settings->save_password = malloc(1); + settings->save_key = malloc(1); + settings->save_event = malloc(1); + + settings->save_ssid[0] = '\0'; + settings->save_password[0] = '\0'; + settings->save_key[0] = '\0'; + settings->save_event[0] = '\0'; + + save_settings_file(file, settings); + flipper_format_file_close(file); + } + } else { + if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { + flipper_format_file_close(file); + } else { + uint32_t value; + if(!flipper_format_read_header(file, string_value, &value)) { + } else { + if(flipper_format_read_string(file, CONF_SSID, text_ssid_value)) { + settings->save_ssid = malloc(furi_string_size(text_ssid_value) + 1); + strcpy(settings->save_ssid, furi_string_get_cstr(text_ssid_value)); + } + if(flipper_format_read_string(file, CONF_PASSWORD, text_password_value)) { + settings->save_password = malloc(furi_string_size(text_password_value) + 1); + strcpy(settings->save_password, furi_string_get_cstr(text_password_value)); + } + if(flipper_format_read_string(file, CONF_KEY, text_key_value)) { + settings->save_key = malloc(furi_string_size(text_key_value) + 1); + strcpy(settings->save_key, furi_string_get_cstr(text_key_value)); + } + if(flipper_format_read_string(file, CONF_EVENT, text_event_value)) { + settings->save_event = malloc(furi_string_size(text_event_value) + 1); + strcpy(settings->save_event, furi_string_get_cstr(text_event_value)); + } + } + flipper_format_file_close(file); + } + } + + furi_string_free(text_ssid_value); + furi_string_free(text_password_value); + furi_string_free(text_key_value); + furi_string_free(text_event_value); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + return settings; +} + +void send_serial_command_config(ESerialCommand command, Settings* settings) { + uint8_t data[1] = {0}; + + char config_tmp[100]; + strcpy(config_tmp, "config,"); + strcat(config_tmp, settings->save_key); + char config_tmp2[5]; + strcpy(config_tmp2, config_tmp); + strcat(config_tmp2, ","); + char config_tmp3[100]; + strcpy(config_tmp3, config_tmp2); + strcat(config_tmp3, settings->save_ssid); + char config_tmp4[5]; + strcpy(config_tmp4, config_tmp3); + strcat(config_tmp4, ","); + char config_tmp5[100]; + strcpy(config_tmp5, config_tmp4); + strcat(config_tmp5, settings->save_password); + char config_tmp6[5]; + strcpy(config_tmp6, config_tmp5); + strcat(config_tmp6, ","); + char config[350]; + strcpy(config, config_tmp6); + strcat(config, settings->save_event); + + int length = strlen(config); + for(int i = 0; i < length; i++) { + switch(command) { + case ESerialCommand_Config: + data[0] = config[i]; + break; + default: + return; + }; + + furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + } +} + +static bool ifttt_virtual_button_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + VirtualButtonApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool ifttt_virtual_button_back_event_callback(void* context) { + furi_assert(context); + VirtualButtonApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void ifttt_virtual_button_tick_event_callback(void* context) { + furi_assert(context); + VirtualButtonApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +VirtualButtonApp* ifttt_virtual_button_app_alloc(uint32_t first_scene) { + VirtualButtonApp* app = malloc(sizeof(VirtualButtonApp)); + + // Records + app->gui = furi_record_open(RECORD_GUI); + app->power = furi_record_open(RECORD_POWER); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&virtual_button_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, ifttt_virtual_button_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, ifttt_virtual_button_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, ifttt_virtual_button_tick_event_callback, 2000); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Views + app->sen_view = send_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, VirtualButtonAppViewSendView, send_view_get_view(app->sen_view)); + + app->abou_view = about_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, VirtualButtonAppViewAboutView, about_view_get_view(app->abou_view)); + + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, VirtualButtonAppViewSubmenu, submenu_get_view(app->submenu)); + app->dialog = dialog_ex_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, VirtualButtonAppViewDialog, dialog_ex_get_view(app->dialog)); + + // Set first scene + scene_manager_next_scene(app->scene_manager, first_scene); + return app; +} + +void ifttt_virtual_button_app_free(VirtualButtonApp* app) { + furi_assert(app); + + free(app->settings.save_ssid); + free(app->settings.save_password); + free(app->settings.save_key); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewSendView); + send_view_free(app->sen_view); + view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewAboutView); + about_view_free(app->abou_view); + view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewDialog); + dialog_ex_free(app->dialog); + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + // Records + furi_record_close(RECORD_POWER); + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t ifttt_virtual_button_app(void* p) { + UNUSED(p); + + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage_simply_mkdir(storage, IFTTT_FOLDER)) { + } + if(!storage_simply_mkdir(storage, IFTTT_CONFIG_FOLDER)) { + } + furi_record_close(RECORD_STORAGE); + + uint32_t first_scene = VirtualButtonAppSceneStart; + VirtualButtonApp* app = ifttt_virtual_button_app_alloc(first_scene); + memcpy(&app->settings, load_settings(), sizeof(Settings)); + send_serial_command_config(ESerialCommand_Config, &(app->settings)); + + view_dispatcher_run(app->view_dispatcher); + ifttt_virtual_button_app_free(app); + return 0; +} \ No newline at end of file diff --git a/applications/plugins/ifttt/ifttt_virtual_button.h b/applications/plugins/ifttt/ifttt_virtual_button.h new file mode 100644 index 000000000..563f5cd95 --- /dev/null +++ b/applications/plugins/ifttt/ifttt_virtual_button.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "views/send_view.h" +#include "views/about_view.h" +#include +#include +#include +#include +#include +#include +#include "scenes/virtual_button_scene.h" + +#define APP_NAME "[ESP8266] IFTTT Virtual Button" + +#define CONF_SSID "wifi_ssid" +#define CONF_PASSWORD "wifi_password" +#define CONF_KEY "webhooks_key" +#define CONF_EVENT "event" +#define CONFIG_FILE_HEADER "IFTTT Virtual Button Config File" +#define CONFIG_FILE_VERSION 1 + +typedef struct { + char* save_ssid; + char* save_password; + char* save_key; + char* save_event; +} Settings; + +typedef struct { + Power* power; + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + SendView* sen_view; + AboutView* abou_view; + Submenu* submenu; + DialogEx* dialog; + PowerInfo info; + Settings settings; +} VirtualButtonApp; + +typedef enum { + VirtualButtonAppViewSendView, + VirtualButtonAppViewAboutView, + VirtualButtonAppViewSubmenu, + VirtualButtonAppViewDialog, +} VirtualButtonAppView; + +Settings save_settings(Settings settings); +Settings* load_settings(); \ No newline at end of file diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene.c b/applications/plugins/ifttt/scenes/virtual_button_scene.c new file mode 100644 index 000000000..a75d822fc --- /dev/null +++ b/applications/plugins/ifttt/scenes/virtual_button_scene.c @@ -0,0 +1,30 @@ +#include "virtual_button_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const virtual_button_on_enter_handlers[])(void*) = { +#include "virtual_button_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const virtual_button_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "virtual_button_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const virtual_button_on_exit_handlers[])(void* context) = { +#include "virtual_button_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers virtual_button_scene_handlers = { + .on_enter_handlers = virtual_button_on_enter_handlers, + .on_event_handlers = virtual_button_on_event_handlers, + .on_exit_handlers = virtual_button_on_exit_handlers, + .scene_num = VirtualButtonAppSceneNum, +}; diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene.h b/applications/plugins/ifttt/scenes/virtual_button_scene.h new file mode 100644 index 000000000..870807dee --- /dev/null +++ b/applications/plugins/ifttt/scenes/virtual_button_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) VirtualButtonAppScene##id, +typedef enum { +#include "virtual_button_scene_config.h" + VirtualButtonAppSceneNum, +} VirtualButtonAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers virtual_button_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "virtual_button_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "virtual_button_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "virtual_button_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene_about.c b/applications/plugins/ifttt/scenes/virtual_button_scene_about.c new file mode 100644 index 000000000..86fe1a9d0 --- /dev/null +++ b/applications/plugins/ifttt/scenes/virtual_button_scene_about.c @@ -0,0 +1,26 @@ +#include "../ifttt_virtual_button.h" + +static void virtual_button_scene_about_view_update_model(VirtualButtonApp* app) { + power_get_info(app->power, &app->info); +} + +void virtual_button_scene_about_view_on_enter(void* context) { + VirtualButtonApp* app = context; + virtual_button_scene_about_view_update_model(app); + view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewAboutView); +} + +bool virtual_button_scene_about_view_on_event(void* context, SceneManagerEvent event) { + VirtualButtonApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + virtual_button_scene_about_view_update_model(app); + consumed = true; + } + return consumed; +} + +void virtual_button_scene_about_view_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene_config.h b/applications/plugins/ifttt/scenes/virtual_button_scene_config.h new file mode 100644 index 000000000..70af5ccf7 --- /dev/null +++ b/applications/plugins/ifttt/scenes/virtual_button_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(virtual_button, start, Start) +ADD_SCENE(virtual_button, send_view, SendView) +ADD_SCENE(virtual_button, about_view, AboutView) diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene_send.c b/applications/plugins/ifttt/scenes/virtual_button_scene_send.c new file mode 100644 index 000000000..caa23fadf --- /dev/null +++ b/applications/plugins/ifttt/scenes/virtual_button_scene_send.c @@ -0,0 +1,26 @@ +#include "../ifttt_virtual_button.h" + +static void virtual_button_scene_send_view_update_model(VirtualButtonApp* app) { + power_get_info(app->power, &app->info); +} + +void virtual_button_scene_send_view_on_enter(void* context) { + VirtualButtonApp* app = context; + virtual_button_scene_send_view_update_model(app); + view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewSendView); +} + +bool virtual_button_scene_send_view_on_event(void* context, SceneManagerEvent event) { + VirtualButtonApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + virtual_button_scene_send_view_update_model(app); + consumed = true; + } + return consumed; +} + +void virtual_button_scene_send_view_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene_start.c b/applications/plugins/ifttt/scenes/virtual_button_scene_start.c new file mode 100644 index 000000000..6b03a35f0 --- /dev/null +++ b/applications/plugins/ifttt/scenes/virtual_button_scene_start.c @@ -0,0 +1,55 @@ +#include "../ifttt_virtual_button.h" + +enum VirtualButtonSubmenuIndex { + VirtualButtonSubmenuIndexSendView, + VirtualButtonSubmenuIndexAboutView, +}; + +static void virtual_button_scene_start_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + VirtualButtonApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void virtual_button_scene_start_on_enter(void* context) { + VirtualButtonApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Send IFTTT command", + VirtualButtonSubmenuIndexSendView, + virtual_button_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + VirtualButtonSubmenuIndexAboutView, + virtual_button_scene_start_submenu_callback, + app); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, VirtualButtonAppSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewSubmenu); +} + +bool virtual_button_scene_start_on_event(void* context, SceneManagerEvent event) { + VirtualButtonApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == VirtualButtonSubmenuIndexSendView) { + scene_manager_next_scene(app->scene_manager, VirtualButtonAppSceneSendView); + } else if(event.event == VirtualButtonSubmenuIndexAboutView) { + scene_manager_next_scene(app->scene_manager, VirtualButtonAppSceneAboutView); + } + scene_manager_set_scene_state(app->scene_manager, VirtualButtonAppSceneStart, event.event); + consumed = true; + } + return consumed; +} + +void virtual_button_scene_start_on_exit(void* context) { + VirtualButtonApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/ifttt/views/about_view.c b/applications/plugins/ifttt/views/about_view.c new file mode 100644 index 000000000..80c00883a --- /dev/null +++ b/applications/plugins/ifttt/views/about_view.c @@ -0,0 +1,48 @@ +#include "about_view.h" +#include +#include +#include +#include + +struct AboutView { + View* view; +}; + +typedef struct { + bool connected; +} AboutViewModel; + +static void about_view_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "IFTTT Virtual button"); + canvas_draw_str_aligned(canvas, 0, 15, AlignLeft, AlignTop, "Version 0.2"); + canvas_draw_str_aligned(canvas, 0, 50, AlignLeft, AlignTop, "press back"); +} + +AboutView* about_view_alloc() { + AboutView* about_view = malloc(sizeof(AboutView)); + about_view->view = view_alloc(); + view_set_context(about_view->view, about_view); + view_allocate_model(about_view->view, ViewModelTypeLocking, sizeof(AboutViewModel)); + view_set_draw_callback(about_view->view, about_view_draw_callback); + return about_view; +} + +void about_view_free(AboutView* about_view) { + furi_assert(about_view); + view_free(about_view->view); + free(about_view); +} + +View* about_view_get_view(AboutView* about_view) { + furi_assert(about_view); + return about_view->view; +} + +void about_view_set_data(AboutView* about_view, bool connected) { + furi_assert(about_view); + with_view_model( + about_view->view, AboutViewModel * model, { model->connected = connected; }, true); +} \ No newline at end of file diff --git a/applications/plugins/ifttt/views/about_view.h b/applications/plugins/ifttt/views/about_view.h new file mode 100644 index 000000000..d1ac287e3 --- /dev/null +++ b/applications/plugins/ifttt/views/about_view.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef struct AboutView AboutView; + +AboutView* about_view_alloc(); + +void about_view_free(AboutView* about_view); + +View* about_view_get_view(AboutView* about_view); \ No newline at end of file diff --git a/applications/plugins/ifttt/views/send_view.c b/applications/plugins/ifttt/views/send_view.c new file mode 100644 index 000000000..6046c39e3 --- /dev/null +++ b/applications/plugins/ifttt/views/send_view.c @@ -0,0 +1,137 @@ +#include "send_view.h" +#include +#include +#include +#include +#include +#include +#include + +#define FLIPPERZERO_SERIAL_BAUD 115200 + +typedef enum ESerialCommand { ESerialCommand_Send } ESerialCommand; + +struct SendView { + View* view; +}; + +typedef struct { + bool right_pressed; + bool connected; +} SendViewModel; + +static void Shake(void) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_single_vibro); + furi_record_close(RECORD_NOTIFICATION); +} + +void send_serial_command_send(ESerialCommand command) { + uint8_t data[1] = {0}; + + char name[10] = "send"; + int length = strlen(name); + for(int i = 0; i < length; i++) { + switch(command) { + case ESerialCommand_Send: + data[0] = name[i]; + break; + default: + return; + }; + + furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + } +} + +static void send_view_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + SendViewModel* model = context; + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "SEND MODULE"); + canvas_draw_line(canvas, 0, 10, 128, 10); + canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "Press right to send IFTTT"); + canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignTop, "command or press and hold"); + canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignTop, "back to return to the menu"); + + // Right + if(model->right_pressed) { + } +} + +static void send_view_process(SendView* send_view, InputEvent* event) { + with_view_model( + send_view->view, + SendViewModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyUp) { + } else if(event->key == InputKeyDown) { + } else if(event->key == InputKeyLeft) { + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + Shake(); + send_serial_command_send(ESerialCommand_Send); + } else if(event->key == InputKeyOk) { + } else if(event->key == InputKeyBack) { + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyUp) { + } else if(event->key == InputKeyDown) { + } else if(event->key == InputKeyLeft) { + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + } else if(event->key == InputKeyOk) { + } else if(event->key == InputKeyBack) { + } + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + } + } + }, + true); +} + +static bool send_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); + SendView* send_view = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + } else { + send_view_process(send_view, event); + consumed = true; + } + + return consumed; +} + +SendView* send_view_alloc() { + SendView* send_view = malloc(sizeof(SendView)); + send_view->view = view_alloc(); + view_set_context(send_view->view, send_view); + view_allocate_model(send_view->view, ViewModelTypeLocking, sizeof(SendViewModel)); + view_set_draw_callback(send_view->view, send_view_draw_callback); + view_set_input_callback(send_view->view, send_view_input_callback); + furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); + + return send_view; +} + +void send_view_free(SendView* send_view) { + furi_assert(send_view); + view_free(send_view->view); + free(send_view); +} + +View* send_view_get_view(SendView* send_view) { + furi_assert(send_view); + return send_view->view; +} + +void send_view_set_data(SendView* send_view, bool connected) { + furi_assert(send_view); + with_view_model( + send_view->view, SendViewModel * model, { model->connected = connected; }, true); +} \ No newline at end of file diff --git a/applications/plugins/ifttt/views/send_view.h b/applications/plugins/ifttt/views/send_view.h new file mode 100644 index 000000000..4b1944dd4 --- /dev/null +++ b/applications/plugins/ifttt/views/send_view.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef struct SendView SendView; + +SendView* send_view_alloc(); + +void send_view_free(SendView* send_view); + +View* send_view_get_view(SendView* send_view); \ No newline at end of file diff --git a/applications/plugins/lightmeter/LICENSE b/applications/plugins/lightmeter/LICENSE new file mode 100644 index 000000000..cb2f65db5 --- /dev/null +++ b/applications/plugins/lightmeter/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Oleksii Kutuzov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/lightmeter/README.md b/applications/plugins/lightmeter/README.md new file mode 100644 index 000000000..d9c071e67 --- /dev/null +++ b/applications/plugins/lightmeter/README.md @@ -0,0 +1,21 @@ +# flipperzero-lightmeter + +[Original link](https://github.com/oleksiikutuzov/flipperzero-lightmeter) + + + + +## Wiring + +``` +VCC -> 3.3V +GND -> GND +SCL -> C0 +SDA -> C1 +``` + +## TODO +- [ ] Save settings to sd card + +## References +App inspired by [lightmeter](https://github.com/vpominchuk/lightmeter) project for Arduino by [vpominchuk](https://github.com/vpominchuk). diff --git a/applications/plugins/lightmeter/application.fam b/applications/plugins/lightmeter/application.fam new file mode 100644 index 000000000..8cd90ee26 --- /dev/null +++ b/applications/plugins/lightmeter/application.fam @@ -0,0 +1,24 @@ +App( + appid="lightmeter", + name="[BH1750] Lightmeter", + apptype=FlipperAppType.EXTERNAL, + entry_point="lightmeter_app", + cdefines=["APP_LIGHTMETER"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + order=90, + fap_icon="lightmeter.png", + fap_category="GPIO", + fap_private_libs=[ + Lib( + name="BH1750", + cincludes=["."], + sources=[ + "BH1750.c", + ], + ), + ], + fap_icon_assets="icons", +) diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.c b/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.c new file mode 100644 index 000000000..2487d5817 --- /dev/null +++ b/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.c @@ -0,0 +1,30 @@ +#include "lightmeter_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const lightmeter_on_enter_handlers[])(void*) = { +#include "lightmeter_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const lightmeter_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "lightmeter_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const lightmeter_on_exit_handlers[])(void* context) = { +#include "lightmeter_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers lightmeter_scene_handlers = { + .on_enter_handlers = lightmeter_on_enter_handlers, + .on_event_handlers = lightmeter_on_event_handlers, + .on_exit_handlers = lightmeter_on_exit_handlers, + .scene_num = LightMeterAppSceneNum, +}; diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.h b/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.h new file mode 100644 index 000000000..9d5931384 --- /dev/null +++ b/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) LightMeterAppScene##id, +typedef enum { +#include "lightmeter_scene_config.h" + LightMeterAppSceneNum, +} LightMeterAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers lightmeter_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "lightmeter_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "lightmeter_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "lightmeter_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene_config.h b/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene_config.h new file mode 100644 index 000000000..c72a7713e --- /dev/null +++ b/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene_config.h @@ -0,0 +1,4 @@ +ADD_SCENE(lightmeter, main, Main) +ADD_SCENE(lightmeter, config, Config) +ADD_SCENE(lightmeter, help, Help) +ADD_SCENE(lightmeter, about, About) diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_about.c b/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_about.c new file mode 100644 index 000000000..1508b4c00 --- /dev/null +++ b/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_about.c @@ -0,0 +1,71 @@ +#include "../../lightmeter.h" + +void lightmeter_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { + LightMeterApp* app = context; + + UNUSED(app); + UNUSED(result); + UNUSED(type); + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void lightmeter_scene_about_on_enter(void* context) { + LightMeterApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", LM_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", LM_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", LM_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, + "Showing suggested camera\nsettings based on ambient\nlight or flash.\n\nInspired by a lightmeter\nproject by vpominchuk\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! Lightmeter \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewAbout); +} + +bool lightmeter_scene_about_on_event(void* context, SceneManagerEvent event) { + LightMeterApp* app = context; + + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void lightmeter_scene_about_on_exit(void* context) { + LightMeterApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_config.c b/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_config.c new file mode 100644 index 000000000..42952562b --- /dev/null +++ b/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_config.c @@ -0,0 +1,156 @@ +#include "../../lightmeter.h" + +static const char* iso_numbers[] = { + [ISO_6] = "6", + [ISO_12] = "12", + [ISO_25] = "25", + [ISO_50] = "50", + [ISO_100] = "100", + [ISO_200] = "200", + [ISO_400] = "400", + [ISO_800] = "800", + [ISO_1600] = "1600", + [ISO_3200] = "3200", + [ISO_6400] = "6400", + [ISO_12800] = "12800", + [ISO_25600] = "25600", + [ISO_51200] = "51200", + [ISO_102400] = "102400", +}; + +static const char* nd_numbers[] = { + [ND_0] = "0", + [ND_2] = "2", + [ND_4] = "4", + [ND_8] = "8", + [ND_16] = "16", + [ND_32] = "32", + [ND_64] = "64", + [ND_128] = "128", + [ND_256] = "256", + [ND_512] = "512", + [ND_1024] = "1024", + [ND_2048] = "2048", + [ND_4096] = "4096", +}; + +static const char* diffusion_dome[] = { + [WITHOUT_DOME] = "No", + [WITH_DOME] = "Yes", +}; + +enum LightMeterSubmenuIndex { + LightMeterSubmenuIndexISO, + LightMeterSubmenuIndexND, + LightMeterSubmenuIndexDome, +}; + +static void iso_numbers_cb(VariableItem* item) { + LightMeterApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, iso_numbers[index]); + + LightMeterConfig* config = app->config; + config->iso = index; + lightmeter_app_set_config(app, config); +} + +static void nd_numbers_cb(VariableItem* item) { + LightMeterApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, nd_numbers[index]); + + LightMeterConfig* config = app->config; + config->nd = index; + lightmeter_app_set_config(app, config); +} + +static void dome_presence_cb(VariableItem* item) { + LightMeterApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, diffusion_dome[index]); + + LightMeterConfig* config = app->config; + config->dome = index; + lightmeter_app_set_config(app, config); +} + +static void ok_cb(void* context, uint32_t index) { + LightMeterApp* app = context; + UNUSED(app); + switch(index) { + case 3: + view_dispatcher_send_custom_event(app->view_dispatcher, LightMeterAppCustomEventHelp); + break; + case 4: + view_dispatcher_send_custom_event(app->view_dispatcher, LightMeterAppCustomEventAbout); + break; + default: + break; + } +} + +void lightmeter_scene_config_on_enter(void* context) { + LightMeterApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + LightMeterConfig* config = app->config; + + item = + variable_item_list_add(var_item_list, "ISO", COUNT_OF(iso_numbers), iso_numbers_cb, app); + variable_item_set_current_value_index(item, config->iso); + variable_item_set_current_value_text(item, iso_numbers[config->iso]); + + item = variable_item_list_add( + var_item_list, "ND factor", COUNT_OF(nd_numbers), nd_numbers_cb, app); + variable_item_set_current_value_index(item, config->nd); + variable_item_set_current_value_text(item, nd_numbers[config->nd]); + + item = variable_item_list_add( + var_item_list, "Diffusion dome", COUNT_OF(diffusion_dome), dome_presence_cb, app); + variable_item_set_current_value_index(item, config->dome); + variable_item_set_current_value_text(item, diffusion_dome[config->dome]); + + item = variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); + item = variable_item_list_add(var_item_list, "About", 0, NULL, NULL); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, LightMeterAppSceneConfig)); + + variable_item_list_set_enter_callback(var_item_list, ok_cb, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewVarItemList); +} + +bool lightmeter_scene_config_on_event(void* context, SceneManagerEvent event) { + LightMeterApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case LightMeterAppCustomEventHelp: + scene_manager_next_scene(app->scene_manager, LightMeterAppSceneHelp); + consumed = true; + break; + case LightMeterAppCustomEventAbout: + scene_manager_next_scene(app->scene_manager, LightMeterAppSceneAbout); + consumed = true; + break; + } + } + return consumed; +} + +void lightmeter_scene_config_on_exit(void* context) { + LightMeterApp* app = context; + variable_item_list_reset(app->var_item_list); + main_view_set_iso(app->main_view, app->config->iso); + main_view_set_nd(app->main_view, app->config->nd); + main_view_set_dome(app->main_view, app->config->dome); +} diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_help.c b/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_help.c new file mode 100644 index 000000000..7b6d45864 --- /dev/null +++ b/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_help.c @@ -0,0 +1,32 @@ +#include "../../lightmeter.h" + +void lightmeter_scene_help_on_enter(void* context) { + LightMeterApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf( + temp_str, "App works with BH1750\nambient light sensor\nconnected via I2C interface\n\n"); + furi_string_cat(temp_str, "\e#Pinout:\r\n"); + furi_string_cat( + temp_str, + " SDA: 15 [C1]\r\n" + " SCL: 16 [C0]\r\n"); + + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewHelp); +} + +bool lightmeter_scene_help_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void lightmeter_scene_help_on_exit(void* context) { + LightMeterApp* app = context; + + widget_reset(app->widget); +} diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_main.c b/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_main.c new file mode 100644 index 000000000..8ba474461 --- /dev/null +++ b/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_main.c @@ -0,0 +1,43 @@ +#include "../../lightmeter.h" + +static void lightmeter_scene_main_on_left(void* context) { + LightMeterApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, LightMeterAppCustomEventConfig); +} + +void lightmeter_scene_main_on_enter(void* context) { + LightMeterApp* app = context; + + lightmeter_main_view_set_left_callback(app->main_view, lightmeter_scene_main_on_left, app); + view_dispatcher_switch_to_view(app->view_dispatcher, LightMeterAppViewMainView); +} + +bool lightmeter_scene_main_on_event(void* context, SceneManagerEvent event) { + LightMeterApp* app = context; + + bool response = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + if(event.event == LightMeterAppCustomEventConfig) { + scene_manager_next_scene(app->scene_manager, LightMeterAppSceneConfig); + response = true; + } + break; + + case SceneManagerEventTypeTick: + lightmeter_app_i2c_callback(app); + response = true; + break; + + default: + break; + } + + return response; +} + +void lightmeter_scene_main_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/lightmeter/gui/views/main_view.c b/applications/plugins/lightmeter/gui/views/main_view.c new file mode 100644 index 000000000..756346fa4 --- /dev/null +++ b/applications/plugins/lightmeter/gui/views/main_view.c @@ -0,0 +1,434 @@ +#include "main_view.h" +#include +#include +#include +#include "../../lightmeter.h" +#include "../../lightmeter_helper.h" + +#define WORKER_TAG "Main View" + +static const int iso_numbers[] = { + [ISO_6] = 6, + [ISO_12] = 12, + [ISO_25] = 25, + [ISO_50] = 50, + [ISO_100] = 100, + [ISO_200] = 200, + [ISO_400] = 400, + [ISO_800] = 800, + [ISO_1600] = 1600, + [ISO_3200] = 3200, + [ISO_6400] = 6400, + [ISO_12800] = 12800, + [ISO_25600] = 25600, + [ISO_51200] = 51200, + [ISO_102400] = 102400, +}; + +static const int nd_numbers[] = { + [ND_0] = 0, + [ND_2] = 2, + [ND_4] = 4, + [ND_8] = 8, + [ND_16] = 16, + [ND_32] = 32, + [ND_64] = 64, + [ND_128] = 128, + [ND_256] = 256, + [ND_512] = 512, + [ND_1024] = 1024, + [ND_2048] = 2048, + [ND_4096] = 4096, +}; + +static const float aperture_numbers[] = { + [AP_1] = 1.0, + [AP_1_4] = 1.4, + [AP_2] = 2.0, + [AP_2_8] = 2.8, + [AP_4] = 4.0, + [AP_5_6] = 5.6, + [AP_8] = 8, + [AP_11] = 11, + [AP_16] = 16, + [AP_22] = 22, + [AP_32] = 32, + [AP_45] = 45, + [AP_64] = 64, + [AP_90] = 90, + [AP_128] = 128, +}; + +static const float speed_numbers[] = { + [SPEED_8000] = 1.0 / 8000, [SPEED_4000] = 1.0 / 4000, [SPEED_2000] = 1.0 / 2000, + [SPEED_1000] = 1.0 / 1000, [SPEED_500] = 1.0 / 500, [SPEED_250] = 1.0 / 250, + [SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_30] = 1.0 / 30, + [SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8, [SPEED_4] = 1.0 / 4, + [SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0, [SPEED_2S] = 2.0, + [SPEED_4S] = 4.0, [SPEED_8S] = 8.0, [SPEED_15S] = 15.0, + [SPEED_30S] = 30.0, +}; + +struct MainView { + View* view; + LightMeterMainViewButtonCallback cb_left; + void* cb_context; +}; + +void lightmeter_main_view_set_left_callback( + MainView* lightmeter_main_view, + LightMeterMainViewButtonCallback callback, + void* context) { + with_view_model( + lightmeter_main_view->view, + MainViewModel * model, + { + UNUSED(model); + lightmeter_main_view->cb_left = callback; + lightmeter_main_view->cb_context = context; + }, + true); +} + +static void main_view_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + MainViewModel* model = context; + + // FURI_LOG_D("MAIN VIEW", "Drawing"); + + canvas_clear(canvas); + + // top row + draw_top_row(canvas, model); + + // add f, T values + canvas_set_font(canvas, FontBigNumbers); + + // draw f icon and number + canvas_draw_icon(canvas, 15, 17, &I_f_10x14); + draw_aperture(canvas, model); + + // draw T icon and number + canvas_draw_icon(canvas, 15, 34, &I_T_10x14); + draw_speed(canvas, model); + + // draw button + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "Config"); + + // draw ND number + draw_nd_number(canvas, model); + + // draw EV number + canvas_set_font(canvas, FontSecondary); + draw_EV_number(canvas, model); + + // draw mode indicator + draw_mode_indicator(canvas, model); +} + +static void main_view_process(MainView* main_view, InputEvent* event) { + with_view_model( + main_view->view, + MainViewModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyUp) { + switch(model->current_mode) { + case FIXED_APERTURE: + if(model->aperture < AP_NUM - 1) model->aperture++; + break; + + case FIXED_SPEED: + if(model->speed < SPEED_NUM - 1) model->speed++; + break; + + default: + break; + } + } else if(event->key == InputKeyDown) { + switch(model->current_mode) { + case FIXED_APERTURE: + if(model->aperture > 0) model->aperture--; + break; + + case FIXED_SPEED: + if(model->speed > 0) model->speed--; + break; + + default: + break; + } + } else if(event->key == InputKeyOk) { + switch(model->current_mode) { + case FIXED_SPEED: + model->current_mode = FIXED_APERTURE; + break; + + case FIXED_APERTURE: + model->current_mode = FIXED_SPEED; + break; + + default: + break; + } + } + } + }, + true); +} + +static bool main_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); + MainView* main_view = context; + bool consumed = false; + + if(event->type == InputTypeShort && event->key == InputKeyLeft) { + if(main_view->cb_left) { + main_view->cb_left(main_view->cb_context); + } + consumed = true; + } else if(event->type == InputTypeShort && event->key == InputKeyBack) { + } else { + main_view_process(main_view, event); + consumed = true; + } + + return consumed; +} + +MainView* main_view_alloc() { + MainView* main_view = malloc(sizeof(MainView)); + main_view->view = view_alloc(); + view_set_context(main_view->view, main_view); + view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(MainViewModel)); + view_set_draw_callback(main_view->view, main_view_draw_callback); + view_set_input_callback(main_view->view, main_view_input_callback); + + return main_view; +} + +void main_view_free(MainView* main_view) { + furi_assert(main_view); + view_free(main_view->view); + free(main_view); +} + +View* main_view_get_view(MainView* main_view) { + furi_assert(main_view); + return main_view->view; +} + +void main_view_set_lux(MainView* main_view, float val) { + furi_assert(main_view); + with_view_model( + main_view->view, MainViewModel * model, { model->lux = val; }, true); +} + +void main_view_set_EV(MainView* main_view, float val) { + furi_assert(main_view); + with_view_model( + main_view->view, MainViewModel * model, { model->EV = val; }, true); +} + +void main_view_set_response(MainView* main_view, bool val) { + furi_assert(main_view); + with_view_model( + main_view->view, MainViewModel * model, { model->response = val; }, true); +} + +void main_view_set_iso(MainView* main_view, int iso) { + furi_assert(main_view); + with_view_model( + main_view->view, MainViewModel * model, { model->iso = iso; }, true); +} + +void main_view_set_nd(MainView* main_view, int nd) { + furi_assert(main_view); + with_view_model( + main_view->view, MainViewModel * model, { model->nd = nd; }, true); +} + +void main_view_set_aperture(MainView* main_view, int aperture) { + furi_assert(main_view); + with_view_model( + main_view->view, MainViewModel * model, { model->aperture = aperture; }, true); +} + +void main_view_set_speed(MainView* main_view, int speed) { + furi_assert(main_view); + with_view_model( + main_view->view, MainViewModel * model, { model->speed = speed; }, true); +} + +void main_view_set_dome(MainView* main_view, bool dome) { + furi_assert(main_view); + with_view_model( + main_view->view, MainViewModel * model, { model->dome = dome; }, true); +} + +bool main_view_get_dome(MainView* main_view) { + furi_assert(main_view); + bool val = false; + with_view_model( + main_view->view, MainViewModel * model, { val = model->dome; }, true); + return val; +} + +void draw_top_row(Canvas* canvas, MainViewModel* context) { + MainViewModel* model = context; + + char str[12]; + + if(!model->response) { + canvas_draw_box(canvas, 0, 0, 128, 12); + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 24, 10, "No sensor found"); + canvas_set_color(canvas, ColorBlack); + } else { + model->iso_val = iso_numbers[model->iso]; + if(model->nd > 0) model->iso_val /= nd_numbers[model->nd]; + + if(model->lux > 0) { + if(model->current_mode == FIXED_APERTURE) { + model->speed_val = 100 * pow(aperture_numbers[model->aperture], 2) / + (double)model->iso_val / pow(2, model->EV); + } else { + model->aperture_val = sqrt( + pow(2, model->EV) * (double)model->iso_val * + (double)speed_numbers[model->speed] / 100); + } + } + + // TODO when T:30, f/0 instead of f/128 + + canvas_draw_line(canvas, 0, 10, 128, 10); + + canvas_set_font(canvas, FontPrimary); + // metering mode A – ambient, F – flash + canvas_draw_str_aligned(canvas, 1, 1, AlignLeft, AlignTop, "A"); + + snprintf(str, sizeof(str), "ISO: %d", iso_numbers[model->iso]); + canvas_draw_str_aligned(canvas, 19, 1, AlignLeft, AlignTop, str); + + canvas_set_font(canvas, FontSecondary); + snprintf(str, sizeof(str), "lx: %.0f", (double)model->lux); + canvas_draw_str_aligned(canvas, 87, 2, AlignLeft, AlignTop, str); + } +} + +void draw_aperture(Canvas* canvas, MainViewModel* context) { + MainViewModel* model = context; + + char str[12]; + + switch(model->current_mode) { + case FIXED_APERTURE: + if(model->response) { + if(model->aperture < AP_8) { + snprintf(str, sizeof(str), "/%.1f", (double)aperture_numbers[model->aperture]); + } else { + snprintf(str, sizeof(str), "/%.0f", (double)aperture_numbers[model->aperture]); + } + } else { + snprintf(str, sizeof(str), " ---"); + } + canvas_draw_str_aligned(canvas, 27, 15, AlignLeft, AlignTop, str); + break; + case FIXED_SPEED: + if(model->aperture_val < aperture_numbers[0] || !model->response) { + snprintf(str, sizeof(str), " ---"); + } else if(model->aperture_val < aperture_numbers[AP_8]) { + snprintf(str, sizeof(str), "/%.1f", (double)normalizeAperture(model->aperture_val)); + } else { + snprintf(str, sizeof(str), "/%.0f", (double)normalizeAperture(model->aperture_val)); + } + canvas_draw_str_aligned(canvas, 27, 15, AlignLeft, AlignTop, str); + break; + default: + break; + } +} + +void draw_speed(Canvas* canvas, MainViewModel* context) { + MainViewModel* model = context; + + char str[12]; + + switch(model->current_mode) { + case FIXED_APERTURE: + if(model->lux > 0 && model->response) { + if(model->speed_val < 1 && model->speed_val > 0) { + snprintf(str, sizeof(str), ":1/%.0f", 1 / (double)normalizeTime(model->speed_val)); + } else { + snprintf(str, sizeof(str), ":%.0f", (double)normalizeTime(model->speed_val)); + } + } else { + snprintf(str, sizeof(str), " ---"); + } + canvas_draw_str_aligned(canvas, 27, 34, AlignLeft, AlignTop, str); + break; + + case FIXED_SPEED: + if(model->response) { + if(model->speed < SPEED_1S) { + snprintf(str, sizeof(str), ":1/%.0f", 1 / (double)speed_numbers[model->speed]); + } else { + snprintf(str, sizeof(str), ":%.0f", (double)speed_numbers[model->speed]); + } + } else { + snprintf(str, sizeof(str), " ---"); + } + canvas_draw_str_aligned(canvas, 27, 34, AlignLeft, AlignTop, str); + break; + + default: + break; + } +} + +void draw_mode_indicator(Canvas* canvas, MainViewModel* context) { + MainViewModel* model = context; + + switch(model->current_mode) { + case FIXED_SPEED: + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 3, 36, AlignLeft, AlignTop, "*"); + break; + + case FIXED_APERTURE: + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignTop, "*"); + break; + + default: + break; + } +} + +void draw_nd_number(Canvas* canvas, MainViewModel* context) { + MainViewModel* model = context; + + char str[9]; + + if(model->response) { + snprintf(str, sizeof(str), "ND: %d", nd_numbers[model->nd]); + } else { + snprintf(str, sizeof(str), "ND: ---"); + } + canvas_draw_str_aligned(canvas, 87, 20, AlignLeft, AlignBottom, str); +} + +void draw_EV_number(Canvas* canvas, MainViewModel* context) { + MainViewModel* model = context; + + char str[7]; + + if(model->lux > 0 && model->response) { + snprintf(str, sizeof(str), "EV: %1.0f", (double)model->EV); + canvas_draw_str_aligned(canvas, 87, 29, AlignLeft, AlignBottom, str); + } else { + canvas_draw_str_aligned(canvas, 87, 29, AlignLeft, AlignBottom, "EV: --"); + } +} diff --git a/applications/plugins/lightmeter/gui/views/main_view.h b/applications/plugins/lightmeter/gui/views/main_view.h new file mode 100644 index 000000000..4586e6a54 --- /dev/null +++ b/applications/plugins/lightmeter/gui/views/main_view.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include "lightmeter_icons.h" +#include "../../lightmeter_config.h" + +typedef struct MainView MainView; + +typedef enum { + FIXED_APERTURE, + FIXED_SPEED, + + MODES_SIZE +} MainViewMode; + +typedef struct { + uint8_t recv[2]; + MainViewMode current_mode; + float lux; + float EV; + float aperture_val; + float speed_val; + int iso_val; + bool response; + int iso; + int nd; + int aperture; + int speed; + bool dome; +} MainViewModel; + +typedef void (*LightMeterMainViewButtonCallback)(void* context); + +void lightmeter_main_view_set_left_callback( + MainView* lightmeter_main_view, + LightMeterMainViewButtonCallback callback, + void* context); + +MainView* main_view_alloc(); + +void main_view_free(MainView* main_view); + +View* main_view_get_view(MainView* main_view); + +void main_view_set_lux(MainView* main_view, float val); + +void main_view_set_EV(MainView* main_view_, float val); + +void main_view_set_response(MainView* main_view_, bool val); + +void main_view_set_iso(MainView* main_view, int val); + +void main_view_set_nd(MainView* main_view, int val); + +void main_view_set_aperture(MainView* main_view, int val); + +void main_view_set_speed(MainView* main_view, int val); + +void main_view_set_dome(MainView* main_view, bool val); + +bool main_view_get_dome(MainView* main_view); + +void draw_top_row(Canvas* canvas, MainViewModel* context); + +void draw_aperture(Canvas* canvas, MainViewModel* context); + +void draw_speed(Canvas* canvas, MainViewModel* context); + +void draw_mode_indicator(Canvas* canvas, MainViewModel* context); + +void draw_nd_number(Canvas* canvas, MainViewModel* context); + +void draw_EV_number(Canvas* canvas, MainViewModel* context); diff --git a/applications/plugins/lightmeter/icons/T_10x14.png b/applications/plugins/lightmeter/icons/T_10x14.png new file mode 100644 index 000000000..d81c2c424 Binary files /dev/null and b/applications/plugins/lightmeter/icons/T_10x14.png differ diff --git a/applications/plugins/lightmeter/icons/f_10x14.png b/applications/plugins/lightmeter/icons/f_10x14.png new file mode 100644 index 000000000..c3e85c0ec Binary files /dev/null and b/applications/plugins/lightmeter/icons/f_10x14.png differ diff --git a/applications/plugins/lightmeter/images/framed_gui.gif b/applications/plugins/lightmeter/images/framed_gui.gif new file mode 100644 index 000000000..86c4d79a5 Binary files /dev/null and b/applications/plugins/lightmeter/images/framed_gui.gif differ diff --git a/applications/plugins/lightmeter/images/framed_gui_config.png b/applications/plugins/lightmeter/images/framed_gui_config.png new file mode 100644 index 000000000..3d0f0c88a Binary files /dev/null and b/applications/plugins/lightmeter/images/framed_gui_config.png differ diff --git a/applications/plugins/lightmeter/images/framed_gui_main.png b/applications/plugins/lightmeter/images/framed_gui_main.png new file mode 100644 index 000000000..89aa1a11f Binary files /dev/null and b/applications/plugins/lightmeter/images/framed_gui_main.png differ diff --git a/applications/plugins/lightmeter/images/gui_config.png b/applications/plugins/lightmeter/images/gui_config.png new file mode 100644 index 000000000..ac7de4517 Binary files /dev/null and b/applications/plugins/lightmeter/images/gui_config.png differ diff --git a/applications/plugins/lightmeter/images/gui_main.png b/applications/plugins/lightmeter/images/gui_main.png new file mode 100644 index 000000000..ae523aa2f Binary files /dev/null and b/applications/plugins/lightmeter/images/gui_main.png differ diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.c b/applications/plugins/lightmeter/lib/BH1750/BH1750.c new file mode 100644 index 000000000..28616e040 --- /dev/null +++ b/applications/plugins/lightmeter/lib/BH1750/BH1750.c @@ -0,0 +1,144 @@ +/** + * @file BH1750.h + * @author Oleksii Kutuzov (oleksii.kutuzov@icloud.com) + * @brief + * @version 0.1 + * @date 2022-11-06 + * + * @copyright Copyright (c) 2022 + * + * Ported from: + * https://github.com/lamik/Light_Sensors_STM32 + */ + +#include "BH1750.h" + +BH1750_mode bh1750_mode = BH1750_DEFAULT_MODE; // Current sensor mode +uint8_t bh1750_mt_reg = BH1750_DEFAULT_MTREG; // Current MT register value + +BH1750_STATUS bh1750_init() { + if(BH1750_OK == bh1750_reset()) { + if(BH1750_OK == bh1750_set_mt_reg(BH1750_DEFAULT_MTREG)) { + return BH1750_OK; + } + } + return BH1750_ERROR; +} + +BH1750_STATUS bh1750_reset() { + uint8_t command = 0x07; + bool status; + + furi_hal_i2c_acquire(I2C_BUS); + status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &command, 1, I2C_TIMEOUT); + furi_hal_i2c_release(I2C_BUS); + + if(status) { + return BH1750_OK; + } + + return BH1750_ERROR; +} + +BH1750_STATUS bh1750_set_power_state(uint8_t PowerOn) { + PowerOn = (PowerOn ? 1 : 0); + bool status; + + furi_hal_i2c_acquire(I2C_BUS); + status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &PowerOn, 1, I2C_TIMEOUT); + furi_hal_i2c_release(I2C_BUS); + + if(status) { + return BH1750_OK; + } + + return BH1750_ERROR; +} + +BH1750_STATUS bh1750_set_mode(BH1750_mode mode) { + if(!((mode >> 4) || (mode >> 5))) { + return BH1750_ERROR; + } + + if((mode & 0x0F) > 3) { + return BH1750_ERROR; + } + + bool status; + + bh1750_mode = mode; + + furi_hal_i2c_acquire(I2C_BUS); + status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &mode, 1, I2C_TIMEOUT); + furi_hal_i2c_release(I2C_BUS); + + if(status) { + return BH1750_OK; + } + + return BH1750_ERROR; +} + +BH1750_STATUS bh1750_set_mt_reg(uint8_t mt_reg) { + if(mt_reg < 31 || mt_reg > 254) { + return BH1750_ERROR; + } + + bh1750_mt_reg = mt_reg; + + uint8_t tmp[2]; + bool status; + + tmp[0] = (0x40 | (mt_reg >> 5)); + tmp[1] = (0x60 | (mt_reg & 0x1F)); + + furi_hal_i2c_acquire(I2C_BUS); + status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &tmp[0], 1, I2C_TIMEOUT); + furi_hal_i2c_release(I2C_BUS); + if(!status) { + return BH1750_ERROR; + } + + furi_hal_i2c_acquire(I2C_BUS); + status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &tmp[1], 1, I2C_TIMEOUT); + furi_hal_i2c_release(I2C_BUS); + if(status) { + return BH1750_OK; + } + + return BH1750_ERROR; +} + +BH1750_STATUS bh1750_trigger_manual_conversion() { + if(BH1750_OK == bh1750_set_mode(bh1750_mode)) { + return BH1750_OK; + } + return BH1750_ERROR; +} + +BH1750_STATUS bh1750_read_light(float* result) { + float result_tmp; + uint8_t rcv[2]; + bool status; + + furi_hal_i2c_acquire(I2C_BUS); + status = furi_hal_i2c_rx(I2C_BUS, BH1750_ADDRESS, rcv, 2, I2C_TIMEOUT); + furi_hal_i2c_release(I2C_BUS); + + if(status) { + result_tmp = (rcv[0] << 8) | (rcv[1]); + + if(bh1750_mt_reg != BH1750_DEFAULT_MTREG) { + result_tmp *= (float)((uint8_t)BH1750_DEFAULT_MTREG / (float)bh1750_mt_reg); + } + + if(bh1750_mode == ONETIME_HIGH_RES_MODE_2 || bh1750_mode == CONTINUOUS_HIGH_RES_MODE_2) { + result_tmp /= 2.0; + } + + *result = result_tmp / BH1750_CONVERSION_FACTOR; + + return BH1750_OK; + } + return BH1750_ERROR; +} diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.h b/applications/plugins/lightmeter/lib/BH1750/BH1750.h new file mode 100644 index 000000000..991350f7f --- /dev/null +++ b/applications/plugins/lightmeter/lib/BH1750/BH1750.h @@ -0,0 +1,103 @@ +/** + * @file BH1750.h + * @author Oleksii Kutuzov (oleksii.kutuzov@icloud.com) + * @brief + * @version 0.1 + * @date 2022-11-06 + * + * @copyright Copyright (c) 2022 + * + * Ported from: + * https://github.com/lamik/Light_Sensors_STM32 + */ + +#include +#include + +#ifndef BH1750_H_ +#define BH1750_H_ + +// I2C BUS +#define I2C_BUS &furi_hal_i2c_handle_external +#define I2C_TIMEOUT 10 + +#define BH1750_ADDRESS (0x23 << 1) + +#define BH1750_POWER_DOWN 0x00 +#define BH1750_POWER_ON 0x01 +#define BH1750_RESET 0x07 +#define BH1750_DEFAULT_MTREG 69 +#define BH1750_DEFAULT_MODE ONETIME_HIGH_RES_MODE + +#define BH1750_CONVERSION_FACTOR 1.2 + +typedef enum { BH1750_OK = 0, BH1750_ERROR = 1 } BH1750_STATUS; + +typedef enum { + CONTINUOUS_HIGH_RES_MODE = 0x10, + CONTINUOUS_HIGH_RES_MODE_2 = 0x11, + CONTINUOUS_LOW_RES_MODE = 0x13, + ONETIME_HIGH_RES_MODE = 0x20, + ONETIME_HIGH_RES_MODE_2 = 0x21, + ONETIME_LOW_RES_MODE = 0x23 +} BH1750_mode; + +/** + * @brief Initialize the sensor. Sends the reset command and sets the measurement register to the default value. + * + * @return BH1750_STATUS + */ +BH1750_STATUS bh1750_init(); + +/** + * @brief Reset all registers to the default value. + * + * @return BH1750_STATUS + */ +BH1750_STATUS bh1750_reset(); + +/** + * @brief Sets the power state. 1 - running; 0 - sleep, low power. + * + * @param PowerOn sensor state. + * @return BH1750_STATUS + */ +BH1750_STATUS bh1750_set_power_state(uint8_t PowerOn); + +/** + * @brief Set the Measurement Time register. It allows to increase or decrease the sensitivity. + * + * @param MTreg value from 31 to 254, defaults to 69. + * + * @return BH1750_STATUS + */ +BH1750_STATUS bh1750_set_mt_reg(uint8_t MTreg); + +/** + * @brief Set the mode of converting. Look into the bh1750_mode enum. + * + * @param Mode mode enumerator + * @return BH1750_STATUS + */ +BH1750_STATUS bh1750_set_mode(BH1750_mode Mode); + +/** + * @brief Trigger the conversion in manual modes. + * + * @details a low-resolution mode, the conversion time is typically 16 ms, and for a high-resolution + * mode is 120 ms. You need to wait until reading the measurement value. There is no need + * to exit low-power mode for manual conversion. It makes automatically. + * + * @return BH1750_STATUS + */ +BH1750_STATUS bh1750_trigger_manual_conversion(); + +/** + * @brief Read the converted value and calculate the result. + * + * @param Result stores received value to this variable. + * @return BH1750_STATUS + */ +BH1750_STATUS bh1750_read_light(float* Result); + +#endif /* BH1750_H_ */ diff --git a/applications/plugins/lightmeter/lib/BH1750/LICENSE b/applications/plugins/lightmeter/lib/BH1750/LICENSE new file mode 100644 index 000000000..cb2f65db5 --- /dev/null +++ b/applications/plugins/lightmeter/lib/BH1750/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Oleksii Kutuzov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/lightmeter/lib/BH1750/README.md b/applications/plugins/lightmeter/lib/BH1750/README.md new file mode 100644 index 000000000..b1338d4ab --- /dev/null +++ b/applications/plugins/lightmeter/lib/BH1750/README.md @@ -0,0 +1,2 @@ +# flipperzero-BH1750 +BH1750 light sensor library for Flipper Zero diff --git a/applications/plugins/lightmeter/lib/BH1750/docs/BH1750.pdf b/applications/plugins/lightmeter/lib/BH1750/docs/BH1750.pdf new file mode 100644 index 000000000..267efddc6 Binary files /dev/null and b/applications/plugins/lightmeter/lib/BH1750/docs/BH1750.pdf differ diff --git a/applications/plugins/lightmeter/lightmeter.c b/applications/plugins/lightmeter/lightmeter.c new file mode 100644 index 000000000..6034a9ee9 --- /dev/null +++ b/applications/plugins/lightmeter/lightmeter.c @@ -0,0 +1,161 @@ +#include "lightmeter.h" +#include "lightmeter_helper.h" + +#define WORKER_TAG "MAIN APP" + +static bool lightmeter_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + LightMeterApp* app = context; + + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool lightmeter_back_event_callback(void* context) { + furi_assert(context); + LightMeterApp* app = context; + + return scene_manager_handle_back_event(app->scene_manager); +} + +static void lightmeter_tick_event_callback(void* context) { + furi_assert(context); + LightMeterApp* app = context; + + scene_manager_handle_tick_event(app->scene_manager); +} + +LightMeterApp* lightmeter_app_alloc(uint32_t first_scene) { + LightMeterApp* app = malloc(sizeof(LightMeterApp)); + + // Sensor + bh1750_set_power_state(1); + bh1750_init(); + bh1750_set_mode(ONETIME_HIGH_RES_MODE); + bh1750_set_mt_reg(100); + + // Set default values to config + app->config = malloc(sizeof(LightMeterConfig)); + app->config->iso = DEFAULT_ISO; + app->config->nd = DEFAULT_ND; + app->config->aperture = DEFAULT_APERTURE; + app->config->dome = DEFAULT_DOME; + + // Records + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + notification_message( + app->notifications, &sequence_display_backlight_enforce_on); // force on backlight + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&lightmeter_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, lightmeter_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, lightmeter_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, lightmeter_tick_event_callback, furi_ms_to_ticks(200)); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Views + app->main_view = main_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, LightMeterAppViewMainView, main_view_get_view(app->main_view)); + + // Set default values to main view from config + main_view_set_iso(app->main_view, app->config->iso); + main_view_set_nd(app->main_view, app->config->nd); + main_view_set_aperture(app->main_view, app->config->aperture); + main_view_set_speed(app->main_view, DEFAULT_SPEED); + main_view_set_dome(app->main_view, app->config->dome); + + // Variable item list + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + LightMeterAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, LightMeterAppViewAbout, widget_get_view(app->widget)); + view_dispatcher_add_view( + app->view_dispatcher, LightMeterAppViewHelp, widget_get_view(app->widget)); + + // Set first scene + scene_manager_next_scene(app->scene_manager, first_scene); + return app; +} + +void lightmeter_app_free(LightMeterApp* app) { + furi_assert(app); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, LightMeterAppViewMainView); + main_view_free(app->main_view); + + // Variable item list + view_dispatcher_remove_view(app->view_dispatcher, LightMeterAppViewVarItemList); + variable_item_list_free(app->var_item_list); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, LightMeterAppViewAbout); + view_dispatcher_remove_view(app->view_dispatcher, LightMeterAppViewHelp); + widget_free(app->widget); + + // View dispatcher + scene_manager_free(app->scene_manager); + view_dispatcher_free(app->view_dispatcher); + + // Records + furi_record_close(RECORD_GUI); + notification_message( + app->notifications, + &sequence_display_backlight_enforce_auto); // set backlight back to auto + furi_record_close(RECORD_NOTIFICATION); + + bh1750_set_power_state(0); + + free(app->config); + free(app); +} + +int32_t lightmeter_app(void* p) { + UNUSED(p); + uint32_t first_scene = LightMeterAppSceneMain; + LightMeterApp* app = lightmeter_app_alloc(first_scene); + view_dispatcher_run(app->view_dispatcher); + lightmeter_app_free(app); + return 0; +} + +void lightmeter_app_set_config(LightMeterApp* context, LightMeterConfig* config) { + LightMeterApp* app = context; + + app->config = config; +} + +void lightmeter_app_i2c_callback(LightMeterApp* context) { + LightMeterApp* app = context; + + float EV = 0; + float lux = 0; + bool response = 0; + + if(bh1750_trigger_manual_conversion() == BH1750_OK) response = 1; + + if(response) { + bh1750_read_light(&lux); + + if(main_view_get_dome(app->main_view)) lux *= DOME_COEFFICIENT; + + EV = lux2ev(lux); + } + + main_view_set_lux(app->main_view, lux); + main_view_set_EV(app->main_view, EV); + main_view_set_response(app->main_view, response); +} diff --git a/applications/plugins/lightmeter/lightmeter.h b/applications/plugins/lightmeter/lightmeter.h new file mode 100644 index 000000000..679b32d15 --- /dev/null +++ b/applications/plugins/lightmeter/lightmeter.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include "gui/views/main_view.h" + +#include +#include + +#include "gui/scenes/config/lightmeter_scene.h" +#include + +#include "lightmeter_config.h" +#include + +typedef struct { + int iso; + int nd; + int aperture; + int dome; +} LightMeterConfig; + +typedef struct { + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + MainView* main_view; + VariableItemList* var_item_list; + LightMeterConfig* config; + NotificationApp* notifications; + Widget* widget; +} LightMeterApp; + +typedef enum { + LightMeterAppViewMainView, + LightMeterAppViewConfigView, + LightMeterAppViewVarItemList, + LightMeterAppViewAbout, + LightMeterAppViewHelp, +} LightMeterAppView; + +typedef enum { + LightMeterAppCustomEventConfig, + LightMeterAppCustomEventHelp, + LightMeterAppCustomEventAbout, +} LightMeterAppCustomEvent; + +void lightmeter_app_set_config(LightMeterApp* context, LightMeterConfig* config); + +void lightmeter_app_i2c_callback(LightMeterApp* context); diff --git a/applications/plugins/lightmeter/lightmeter.png b/applications/plugins/lightmeter/lightmeter.png new file mode 100644 index 000000000..cacd2276f Binary files /dev/null and b/applications/plugins/lightmeter/lightmeter.png differ diff --git a/applications/plugins/lightmeter/lightmeter_config.h b/applications/plugins/lightmeter/lightmeter_config.h new file mode 100644 index 000000000..023235cff --- /dev/null +++ b/applications/plugins/lightmeter/lightmeter_config.h @@ -0,0 +1,99 @@ +#pragma once + +#define LM_VERSION_APP "0.5" +#define LM_DEVELOPED "Oleksii Kutuzov" +#define LM_GITHUB "https://github.com/oleksiikutuzov/flipperzero-lightmeter" + +#define DOME_COEFFICIENT 2.3 +#define DEFAULT_ISO ISO_100 +#define DEFAULT_ND ND_0 +#define DEFAULT_APERTURE AP_2_8 +#define DEFAULT_SPEED SPEED_125 +#define DEFAULT_DOME WITHOUT_DOME + +typedef enum { + ISO_6, + ISO_12, + ISO_25, + ISO_50, + ISO_100, + ISO_200, + ISO_400, + ISO_800, + ISO_1600, + ISO_3200, + ISO_6400, + ISO_12800, + ISO_25600, + ISO_51200, + ISO_102400, + + ISO_NUM, +} LightMeterISONumbers; + +typedef enum { + ND_0, + ND_2, + ND_4, + ND_8, + ND_16, + ND_32, + ND_64, + ND_128, + ND_256, + ND_512, + ND_1024, + ND_2048, + ND_4096, + + ND_NUM, +} LightMeterNDNumbers; + +typedef enum { + AP_1, + AP_1_4, + AP_2, + AP_2_8, + AP_4, + AP_5_6, + AP_8, + AP_11, + AP_16, + AP_22, + AP_32, + AP_45, + AP_64, + AP_90, + AP_128, + + AP_NUM, +} LightMeterApertureNumbers; + +typedef enum { + SPEED_8000, + SPEED_4000, + SPEED_2000, + SPEED_1000, + SPEED_500, + SPEED_250, + SPEED_125, + SPEED_60, + SPEED_30, + SPEED_15, + SPEED_8, + SPEED_4, + SPEED_2, + SPEED_1S, + SPEED_2S, + SPEED_4S, + SPEED_8S, + SPEED_15S, + SPEED_30S, + + SPEED_NUM, +} LightMeterSpeedNumbers; + +typedef enum { + WITHOUT_DOME, + WITH_DOME, +} LightMeterDomePresence; diff --git a/applications/plugins/lightmeter/lightmeter_helper.c b/applications/plugins/lightmeter/lightmeter_helper.c new file mode 100644 index 000000000..1cdddfca9 --- /dev/null +++ b/applications/plugins/lightmeter/lightmeter_helper.c @@ -0,0 +1,69 @@ +#include "lightmeter_helper.h" +#include "lightmeter_config.h" + +static const float aperture_numbers[] = { + [AP_1] = 1.0, + [AP_1_4] = 1.4, + [AP_2] = 2.0, + [AP_2_8] = 2.8, + [AP_4] = 4.0, + [AP_5_6] = 5.6, + [AP_8] = 8, + [AP_11] = 11, + [AP_16] = 16, + [AP_22] = 22, + [AP_32] = 32, + [AP_45] = 45, + [AP_64] = 64, + [AP_90] = 90, + [AP_128] = 128, +}; + +static const float time_numbers[] = { + [SPEED_8000] = 1.0 / 8000, [SPEED_4000] = 1.0 / 4000, [SPEED_2000] = 1.0 / 2000, + [SPEED_1000] = 1.0 / 1000, [SPEED_500] = 1.0 / 500, [SPEED_250] = 1.0 / 250, + [SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_30] = 1.0 / 30, + [SPEED_15] = 1.0 / 15, [SPEED_8] = 1.0 / 8, [SPEED_4] = 1.0 / 4, + [SPEED_2] = 1.0 / 2, [SPEED_1S] = 1.0, [SPEED_2S] = 2.0, + [SPEED_4S] = 4.0, [SPEED_8S] = 8.0, [SPEED_15S] = 15.0, + [SPEED_30S] = 30.0, +}; + +float lux2ev(float lux) { + return log2(lux / 2.5); +} + +float getMinDistance(float x, float v1, float v2) { + if(x - v1 > v2 - x) { + return v2; + } + + return v1; +} + +// Convert calculated aperture value to photography style aperture value. +float normalizeAperture(float a) { + for(int i = 0; i < AP_NUM; i++) { + float a1 = aperture_numbers[i]; + float a2 = aperture_numbers[i + 1]; + + if(a1 < a && a2 >= a) { + return getMinDistance(a, a1, a2); + } + } + + return 0; +} + +float normalizeTime(float a) { + for(int i = 0; i < SPEED_NUM; i++) { + float a1 = time_numbers[i]; + float a2 = time_numbers[i + 1]; + + if(a1 < a && a2 >= a) { + return getMinDistance(a, a1, a2); + } + } + + return 0; +} diff --git a/applications/plugins/lightmeter/lightmeter_helper.h b/applications/plugins/lightmeter/lightmeter_helper.h new file mode 100644 index 000000000..78ea6a8d8 --- /dev/null +++ b/applications/plugins/lightmeter/lightmeter_helper.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +float lux2ev(float lux); + +float getMinDistance(float x, float v1, float v2); + +float normalizeAperture(float a); + +float normalizeTime(float a); diff --git a/applications/plugins/mandelbrot/mandelbrot.c b/applications/plugins/mandelbrot/mandelbrot.c index bf3858165..bfddc6a97 100644 --- a/applications/plugins/mandelbrot/mandelbrot.c +++ b/applications/plugins/mandelbrot/mandelbrot.c @@ -149,6 +149,8 @@ int32_t mandelbrot_app(void* p) { case InputKeyBack: processing = false; break; + default: + break; } } } diff --git a/applications/plugins/metronome/application.fam b/applications/plugins/metronome/application.fam index c58cf881c..32588d06e 100644 --- a/applications/plugins/metronome/application.fam +++ b/applications/plugins/metronome/application.fam @@ -9,6 +9,7 @@ App( ], fap_icon="metronome_icon.png", fap_category="Music", + fap_icon_assets="images", stack_size=2 * 1024, order=20, ) diff --git a/applications/plugins/metronome/gui_extensions.c b/applications/plugins/metronome/gui_extensions.c index ccad67930..458eb137b 100644 --- a/applications/plugins/metronome/gui_extensions.c +++ b/applications/plugins/metronome/gui_extensions.c @@ -1,5 +1,6 @@ #include #include +#include //lib can only do bottom left/right void elements_button_top_left(Canvas* canvas, const char* str) { diff --git a/applications/plugins/metronome/icons/ButtonUp_7x4.png b/applications/plugins/metronome/icons/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/plugins/metronome/icons/ButtonUp_7x4.png differ diff --git a/applications/plugins/metronome/images/ButtonUp_7x4.png b/applications/plugins/metronome/images/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/plugins/metronome/images/ButtonUp_7x4.png differ diff --git a/applications/plugins/metronome/metronome.c b/applications/plugins/metronome/metronome.c index 76cb62a8a..18f68ea42 100644 --- a/applications/plugins/metronome/metronome.c +++ b/applications/plugins/metronome/metronome.c @@ -158,6 +158,8 @@ static void timer_callback(void* ctx) { break; case Silent: break; + default: + break; } } else { // unpronounced beat @@ -171,6 +173,8 @@ static void timer_callback(void* ctx) { break; case Silent: break; + default: + break; } }; @@ -192,6 +196,8 @@ static void timer_callback(void* ctx) { break; case Silent: break; + default: + break; } notification_message(metronome_state->notifications, &sequence_reset_rgb); @@ -320,6 +326,8 @@ int32_t metronome_app() { case InputKeyBack: processing = false; break; + default: + break; } } else if(event.input.type == InputTypeLong) { // hold events @@ -340,6 +348,8 @@ int32_t metronome_app() { case InputKeyBack: processing = false; break; + default: + break; } } else if(event.input.type == InputTypeRepeat) { // repeat events @@ -359,6 +369,8 @@ int32_t metronome_app() { case InputKeyBack: processing = false; break; + default: + break; } } } diff --git a/applications/plugins/minesweeper/minesweeper.c b/applications/plugins/minesweeper/minesweeper.c index 6d29bab2d..fa9fdcc78 100644 --- a/applications/plugins/minesweeper/minesweeper.c +++ b/applications/plugins/minesweeper/minesweeper.c @@ -42,10 +42,7 @@ typedef enum { TileTypeMine } TileType; -typedef enum { - FieldEmpty, // <-- same goes for this - FieldMine -} Field; +typedef enum { FieldEmpty, FieldMine } Field; typedef struct { Field minefield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT]; @@ -99,6 +96,8 @@ static void render_callback(Canvas* const canvas, void* ctx) { furi_string_printf(timeStr, "%01d:%02d", minutes, seconds); canvas_draw_str_aligned(canvas, 128, 0, AlignRight, AlignTop, furi_string_get_cstr(timeStr)); + uint8_t* tile_to_draw; + for(int y = 0; y < PLAYFIELD_HEIGHT; y++) { for(int x = 0; x < PLAYFIELD_WIDTH; x++) { if(x == minesweeper_state->cursor_x && y == minesweeper_state->cursor_y) { @@ -106,114 +105,53 @@ static void render_callback(Canvas* const canvas, void* ctx) { } switch(minesweeper_state->playfield[x][y]) { case TileType0: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_0_bits); + tile_to_draw = tile_0_bits; break; case TileType1: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_1_bits); + tile_to_draw = tile_1_bits; break; case TileType2: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_2_bits); + tile_to_draw = tile_2_bits; break; case TileType3: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_3_bits); + tile_to_draw = tile_3_bits; break; case TileType4: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_4_bits); + tile_to_draw = tile_4_bits; break; case TileType5: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_5_bits); + tile_to_draw = tile_5_bits; break; case TileType6: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_6_bits); + tile_to_draw = tile_6_bits; break; case TileType7: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_7_bits); + tile_to_draw = tile_7_bits; break; case TileType8: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_8_bits); + tile_to_draw = tile_8_bits; break; case TileTypeFlag: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_flag_bits); + tile_to_draw = tile_flag_bits; break; case TileTypeUncleared: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_uncleared_bits); + tile_to_draw = tile_uncleared_bits; break; case TileTypeMine: - canvas_draw_xbm( - canvas, - x * TILE_HEIGHT, // x - 8 + (y * TILE_WIDTH), // y - TILE_WIDTH, - TILE_HEIGHT, - tile_mine_bits); + tile_to_draw = tile_mine_bits; + break; + default: + // this should never happen + tile_to_draw = tile_mine_bits; break; } + canvas_draw_xbm( + canvas, + x * TILE_HEIGHT, // x + 8 + (y * TILE_WIDTH), // y + TILE_WIDTH, + TILE_HEIGHT, + tile_to_draw); if(x == minesweeper_state->cursor_x && y == minesweeper_state->cursor_y) { canvas_invert_color(canvas); } @@ -279,6 +217,10 @@ static bool game_lost(Minesweeper* minesweeper_state) { dialog_message_set_icon(message, NULL, 0, 10); + // Set cursor to initial position + minesweeper_state->cursor_x = 0; + minesweeper_state->cursor_y = 0; + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); notification_message(notifications, &sequence_set_vibro_on); furi_record_close(RECORD_NOTIFICATION); @@ -479,25 +421,25 @@ int32_t minesweeper_app(void* p) { case InputKeyUp: minesweeper_state->cursor_y--; if(minesweeper_state->cursor_y < 0) { - minesweeper_state->cursor_y = 0; + minesweeper_state->cursor_y = PLAYFIELD_HEIGHT - 1; } break; case InputKeyDown: minesweeper_state->cursor_y++; if(minesweeper_state->cursor_y >= PLAYFIELD_HEIGHT) { - minesweeper_state->cursor_y = PLAYFIELD_HEIGHT - 1; + minesweeper_state->cursor_y = 0; } break; case InputKeyRight: minesweeper_state->cursor_x++; if(minesweeper_state->cursor_x >= PLAYFIELD_WIDTH) { - minesweeper_state->cursor_x = PLAYFIELD_WIDTH - 1; + minesweeper_state->cursor_x = 0; } break; case InputKeyLeft: minesweeper_state->cursor_x--; if(minesweeper_state->cursor_x < 0) { - minesweeper_state->cursor_x = 0; + minesweeper_state->cursor_x = PLAYFIELD_WIDTH - 1; } break; case InputKeyOk: @@ -534,6 +476,8 @@ int32_t minesweeper_app(void* p) { // Exit the plugin processing = false; break; + default: + break; } } else if(event.input.type == InputTypeLong) { // hold events @@ -551,6 +495,8 @@ int32_t minesweeper_app(void* p) { case InputKeyBack: processing = false; break; + default: + break; } } } diff --git a/applications/plugins/montyhall/monteyhall.c b/applications/plugins/montyhall/monteyhall.c index 4d91895c5..024d539ec 100644 --- a/applications/plugins/montyhall/monteyhall.c +++ b/applications/plugins/montyhall/monteyhall.c @@ -422,6 +422,8 @@ int32_t montyhall_game_app(void* p) { case InputKeyBack: loop = false; break; + default: + break; } } } else if(event.type == InputTypeLong) { diff --git a/applications/plugins/morse_code/application.fam b/applications/plugins/morse_code/application.fam new file mode 100644 index 000000000..1cc7bdaa1 --- /dev/null +++ b/applications/plugins/morse_code/application.fam @@ -0,0 +1,15 @@ +App( + appid="Morse_Code", + name="Morse Code", + apptype=FlipperAppType.EXTERNAL, + entry_point="morse_code_app", + cdefines=["APP_MORSE_CODE"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + order=20, + fap_icon="morse_code_10px.png", + fap_category="Music" + +) \ No newline at end of file diff --git a/applications/plugins/morse_code/morse_code.c b/applications/plugins/morse_code/morse_code.c new file mode 100644 index 000000000..beb661222 --- /dev/null +++ b/applications/plugins/morse_code/morse_code.c @@ -0,0 +1,166 @@ +#include "morse_code_worker.h" +#include +#include +#include +#include +#include +#include +#include + +static const float MORSE_CODE_VOLUMES[] = {0, .25, .5, .75, 1}; + +typedef struct { + FuriString* words; + uint8_t volume; + uint32_t dit_delta; +} MorseCodeModel; + +typedef struct { + MorseCodeModel* model; + FuriMutex** model_mutex; + + FuriMessageQueue* input_queue; + + ViewPort* view_port; + Gui* gui; + + MorseCodeWorker* worker; +} MorseCode; + +static void render_callback(Canvas* const canvas, void* ctx) { + MorseCode* morse_code = ctx; + furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk); + // border around the edge of the screen + canvas_set_font(canvas, FontPrimary); + + //write words + elements_multiline_text_aligned( + canvas, 64, 30, AlignCenter, AlignCenter, furi_string_get_cstr(morse_code->model->words)); + + // volume view_port + uint8_t vol_bar_x_pos = 124; + uint8_t vol_bar_y_pos = 0; + const uint8_t volume_h = (64 / (COUNT_OF(MORSE_CODE_VOLUMES) - 1)) * morse_code->model->volume; + canvas_draw_frame(canvas, vol_bar_x_pos, vol_bar_y_pos, 4, 64); + canvas_draw_box(canvas, vol_bar_x_pos, vol_bar_y_pos + (64 - volume_h), 4, volume_h); + + //dit bpm + canvas_draw_str_aligned( + canvas, + 0, + 10, + AlignLeft, + AlignCenter, + furi_string_get_cstr( + furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta))); + + //button info + elements_button_center(canvas, "Press/Hold"); + furi_mutex_release(morse_code->model_mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + MorseCode* morse_code = ctx; + furi_message_queue_put(morse_code->input_queue, input_event, FuriWaitForever); +} + +static void morse_code_worker_callback(FuriString* words, void* context) { + MorseCode* morse_code = context; + furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk); + morse_code->model->words = words; + furi_mutex_release(morse_code->model_mutex); + view_port_update(morse_code->view_port); +} + +MorseCode* morse_code_alloc() { + MorseCode* instance = malloc(sizeof(MorseCode)); + + instance->model = malloc(sizeof(MorseCodeModel)); + instance->model->words = furi_string_alloc_set_str(""); + instance->model->volume = 3; + instance->model->dit_delta = 150; + instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + instance->worker = morse_code_worker_alloc(); + + morse_code_worker_set_callback(instance->worker, morse_code_worker_callback, instance); + + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, render_callback, instance); + view_port_input_callback_set(instance->view_port, input_callback, instance); + + // Open GUI and register view_port + instance->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + return instance; +} + +void morse_code_free(MorseCode* instance) { + gui_remove_view_port(instance->gui, instance->view_port); + furi_record_close(RECORD_GUI); + view_port_free(instance->view_port); + + morse_code_worker_free(instance->worker); + + furi_message_queue_free(instance->input_queue); + + furi_mutex_free(instance->model_mutex); + + free(instance->model); + free(instance); +} + +int32_t morse_code_app() { + MorseCode* morse_code = morse_code_alloc(); + InputEvent input; + morse_code_worker_start(morse_code->worker); + morse_code_worker_set_volume( + morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]); + morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta); + while(furi_message_queue_get(morse_code->input_queue, &input, FuriWaitForever) == + FuriStatusOk) { + furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk); + if(input.key == InputKeyBack && input.type == InputTypeLong) { + furi_mutex_release(morse_code->model_mutex); + break; + } else if(input.key == InputKeyBack && input.type == InputTypeShort) { + morse_code_worker_reset_text(morse_code->worker); + } else if(input.key == InputKeyOk) { + if(input.type == InputTypePress) + morse_code_worker_play(morse_code->worker, true); + else if(input.type == InputTypeRelease) + morse_code_worker_play(morse_code->worker, false); + } else if(input.key == InputKeyUp && input.type == InputTypePress) { + if(morse_code->model->volume < COUNT_OF(MORSE_CODE_VOLUMES) - 1) + morse_code->model->volume++; + morse_code_worker_set_volume( + morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]); + } else if(input.key == InputKeyDown && input.type == InputTypePress) { + if(morse_code->model->volume > 0) morse_code->model->volume--; + morse_code_worker_set_volume( + morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]); + } else if(input.key == InputKeyLeft && input.type == InputTypePress) { + if(morse_code->model->dit_delta > 10) morse_code->model->dit_delta -= 10; + morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta); + } else if(input.key == InputKeyRight && input.type == InputTypePress) { + if(morse_code->model->dit_delta >= 10) morse_code->model->dit_delta += 10; + morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta); + } + + FURI_LOG_D( + "Input", + "%s %s %ld", + input_get_key_name(input.key), + input_get_type_name(input.type), + input.sequence); + + furi_mutex_release(morse_code->model_mutex); + view_port_update(morse_code->view_port); + } + morse_code_worker_stop(morse_code->worker); + morse_code_free(morse_code); + return 0; +} \ No newline at end of file diff --git a/applications/plugins/morse_code/morse_code_10px.png b/applications/plugins/morse_code/morse_code_10px.png new file mode 100644 index 000000000..087c5b239 Binary files /dev/null and b/applications/plugins/morse_code/morse_code_10px.png differ diff --git a/applications/plugins/morse_code/morse_code_worker.c b/applications/plugins/morse_code/morse_code_worker.c new file mode 100644 index 000000000..3b7bd4417 --- /dev/null +++ b/applications/plugins/morse_code/morse_code_worker.c @@ -0,0 +1,170 @@ +#include "morse_code_worker.h" +#include +#include + +#define TAG "MorseCodeWorker" + +#define MORSE_CODE_VERSION 0 + +//A-Z0-1 +const char morse_array[36][6] = {".-", "-...", "-.-.", "-..", ".", "..-.", + "--.", "....", "..", ".---", "-.-", ".-..", + "--", "-.", "---", ".--.", "--.-", ".-.", + "...", "-", "..-", "...-", ".--", "-..-", + "-.--", "--..", ".----", "..---", "...--", "....-", + ".....", "-....", "--...", "---..", "----.", "-----"}; +const char symbol_array[36] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; + +struct MorseCodeWorker { + FuriThread* thread; + MorseCodeWorkerCallback callback; + void* callback_context; + bool is_running; + bool play; + float volume; + uint32_t dit_delta; + FuriString* buffer; + FuriString* words; +}; + +void morse_code_worker_fill_buffer(MorseCodeWorker* instance, uint32_t duration) { + FURI_LOG_D("MorseCode: Duration", "%ld", duration); + if(duration <= instance->dit_delta) + furi_string_push_back(instance->buffer, *DOT); + else if(duration <= (instance->dit_delta * 3)) + furi_string_push_back(instance->buffer, *LINE); + if(furi_string_size(instance->buffer) > 5) furi_string_reset(instance->buffer); + FURI_LOG_D("MorseCode: Buffer", "%s", furi_string_get_cstr(instance->buffer)); +} + +void morse_code_worker_fill_letter(MorseCodeWorker* instance) { + if(furi_string_size(instance->words) > 63) furi_string_reset(instance->words); + for(size_t i = 0; i < sizeof(morse_array); i++) { + if(furi_string_cmp_str(instance->buffer, morse_array[i]) == 0) { + furi_string_push_back(instance->words, symbol_array[i]); + break; + } + } + furi_string_reset(instance->buffer); + FURI_LOG_D("MorseCode: Words", "%s", furi_string_get_cstr(instance->words)); +} + +static int32_t morse_code_worker_thread_callback(void* context) { + furi_assert(context); + MorseCodeWorker* instance = context; + bool was_playing = false; + uint32_t start_tick = 0; + uint32_t end_tick = 0; + bool pushed = true; + bool spaced = true; + while(instance->is_running) { + furi_delay_ms(SLEEP); + if(instance->play) { + if(!was_playing) { + start_tick = furi_get_tick(); + furi_hal_speaker_start(FREQUENCY, instance->volume); + was_playing = true; + } + } else { + if(was_playing) { + pushed = false; + spaced = false; + furi_hal_speaker_stop(); + end_tick = furi_get_tick(); + was_playing = false; + morse_code_worker_fill_buffer(instance, end_tick - start_tick); + start_tick = 0; + } + } + if(!pushed) { + if(end_tick + (instance->dit_delta * 3) < furi_get_tick()) { + //NEW LETTER + morse_code_worker_fill_letter(instance); + if(instance->callback) + instance->callback(instance->words, instance->callback_context); + pushed = true; + } + } + if(!spaced) { + if(end_tick + (instance->dit_delta * 7) < furi_get_tick()) { + //NEW WORD + furi_string_push_back(instance->words, *SPACE); + if(instance->callback) + instance->callback(instance->words, instance->callback_context); + spaced = true; + } + } + } + return 0; +} + +MorseCodeWorker* morse_code_worker_alloc() { + MorseCodeWorker* instance = malloc(sizeof(MorseCodeWorker)); + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "MorseCodeWorker"); + furi_thread_set_stack_size(instance->thread, 1024); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, morse_code_worker_thread_callback); + instance->play = false; + instance->volume = 1.0f; + instance->dit_delta = 150; + instance->buffer = furi_string_alloc_set_str(""); + instance->words = furi_string_alloc_set_str(""); + return instance; +} + +void morse_code_worker_free(MorseCodeWorker* instance) { + furi_assert(instance); + furi_string_free(instance->buffer); + furi_string_free(instance->words); + furi_thread_free(instance->thread); + free(instance); +} + +void morse_code_worker_set_callback( + MorseCodeWorker* instance, + MorseCodeWorkerCallback callback, + void* context) { + furi_assert(instance); + instance->callback = callback; + instance->callback_context = context; +} + +void morse_code_worker_play(MorseCodeWorker* instance, bool play) { + furi_assert(instance); + instance->play = play; +} + +void morse_code_worker_set_volume(MorseCodeWorker* instance, float level) { + furi_assert(instance); + instance->volume = level; +} + +void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta) { + furi_assert(instance); + instance->dit_delta = delta; +} + +void morse_code_worker_reset_text(MorseCodeWorker* instance) { + furi_assert(instance); + furi_string_reset(instance->buffer); + furi_string_reset(instance->words); +} + +void morse_code_worker_start(MorseCodeWorker* instance) { + furi_assert(instance); + furi_assert(instance->is_running == false); + instance->is_running = true; + furi_thread_start(instance->thread); + FURI_LOG_D("MorseCode: Start", "is Running"); +} + +void morse_code_worker_stop(MorseCodeWorker* instance) { + furi_assert(instance); + furi_assert(instance->is_running == true); + instance->is_running = false; + furi_thread_join(instance->thread); + FURI_LOG_D("MorseCode: Stop", "Stop"); +} diff --git a/applications/plugins/morse_code/morse_code_worker.h b/applications/plugins/morse_code/morse_code_worker.h new file mode 100644 index 000000000..e880c9579 --- /dev/null +++ b/applications/plugins/morse_code/morse_code_worker.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#define FREQUENCY 261.63f +#define SLEEP 10 +#define DOT "." +#define LINE "-" +#define SPACE " " + +typedef void (*MorseCodeWorkerCallback)(FuriString* buffer, void* context); + +typedef struct MorseCodeWorker MorseCodeWorker; + +MorseCodeWorker* morse_code_worker_alloc(); + +void morse_code_worker_free(MorseCodeWorker* instance); + +void morse_code_worker_set_callback( + MorseCodeWorker* instance, + MorseCodeWorkerCallback callback, + void* context); + +void morse_code_worker_start(MorseCodeWorker* instance); + +void morse_code_worker_stop(MorseCodeWorker* instance); + +void morse_code_worker_play(MorseCodeWorker* instance, bool play); + +void morse_code_worker_reset_text(MorseCodeWorker* instance); + +void morse_code_worker_set_volume(MorseCodeWorker* instance, float level); + +void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta); diff --git a/applications/plugins/mouse_jiggler/application.fam b/applications/plugins/mouse_jiggler/application.fam index cee138495..6c529a883 100644 --- a/applications/plugins/mouse_jiggler/application.fam +++ b/applications/plugins/mouse_jiggler/application.fam @@ -7,6 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, order=150, - fap_icon="../../plugins/mousejacker/mouse_10px.png", + fap_icon="mouse_10px.png", fap_category="Misc", ) \ No newline at end of file diff --git a/applications/plugins/mouse_jiggler/mouse_10px.png b/applications/plugins/mouse_jiggler/mouse_10px.png new file mode 100644 index 000000000..94c3a7a14 Binary files /dev/null and b/applications/plugins/mouse_jiggler/mouse_10px.png differ diff --git a/applications/plugins/mousejacker/application.fam b/applications/plugins/mousejacker/application.fam index 6283d7c73..05835700b 100644 --- a/applications/plugins/mousejacker/application.fam +++ b/applications/plugins/mousejacker/application.fam @@ -12,4 +12,13 @@ App( order=50, fap_icon="mouse_10px.png", fap_category="GPIO", + fap_icon_assets="images", + fap_private_libs=[ + Lib( + name="nrf24", + sources=[ + "nrf24.c", + ], + ), + ], ) diff --git a/applications/plugins/mousejacker/icons/badusb_10px.png b/applications/plugins/mousejacker/icons/badusb_10px.png new file mode 100644 index 000000000..037474aa3 Binary files /dev/null and b/applications/plugins/mousejacker/icons/badusb_10px.png differ diff --git a/applications/plugins/mousejacker/icons/sub1_10px.png b/applications/plugins/mousejacker/icons/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/applications/plugins/mousejacker/icons/sub1_10px.png differ diff --git a/applications/plugins/mousejacker/images/badusb_10px.png b/applications/plugins/mousejacker/images/badusb_10px.png new file mode 100644 index 000000000..037474aa3 Binary files /dev/null and b/applications/plugins/mousejacker/images/badusb_10px.png differ diff --git a/applications/plugins/mousejacker/images/sub1_10px.png b/applications/plugins/mousejacker/images/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/applications/plugins/mousejacker/images/sub1_10px.png differ diff --git a/applications/plugins/mousejacker/lib/nrf24/nrf24.c b/applications/plugins/mousejacker/lib/nrf24/nrf24.c new file mode 100644 index 000000000..8b3776445 --- /dev/null +++ b/applications/plugins/mousejacker/lib/nrf24/nrf24.c @@ -0,0 +1,520 @@ +#include "nrf24.h" +#include +#include +#include +#include +#include + +void nrf24_init() { + furi_hal_spi_bus_handle_init(nrf24_HANDLE); + furi_hal_spi_acquire(nrf24_HANDLE); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_write(nrf24_CE_PIN, false); +} + +void nrf24_deinit() { + furi_hal_spi_release(nrf24_HANDLE); + furi_hal_spi_bus_handle_deinit(nrf24_HANDLE); + furi_hal_gpio_write(nrf24_CE_PIN, false); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void nrf24_spi_trx( + FuriHalSpiBusHandle* handle, + uint8_t* tx, + uint8_t* rx, + uint8_t size, + uint32_t timeout) { + UNUSED(timeout); + furi_hal_gpio_write(handle->cs, false); + furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT); + furi_hal_gpio_write(handle->cs, true); +} + +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { + uint8_t tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data}; + uint8_t rx[2] = {0}; + nrf24_spi_trx(handle, tx, rx, 2, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t + nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = W_REGISTER | (REGISTER_MASK & reg); + memcpy(&tx[1], data, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = R_REGISTER | (REGISTER_MASK & reg); + memset(&tx[1], 0, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + memcpy(data, &rx[1], size); + return rx[0]; +} + +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_RX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_TX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle) { + uint8_t maclen; + nrf24_read_reg(handle, REG_SETUP_AW, &maclen, 1); + maclen &= 3; + return maclen + 2; +} + +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) { + assert(maclen > 1 && maclen < 6); + uint8_t status = 0; + status = nrf24_write_reg(handle, REG_SETUP_AW, maclen - 2); + return status; +} + +uint8_t nrf24_status(FuriHalSpiBusHandle* handle) { + uint8_t status; + uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)}; + nrf24_spi_trx(handle, tx, &status, 1, nrf24_TIMEOUT); + return status; +} + +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) { + uint8_t setup = 0; + uint32_t rate = 0; + nrf24_read_reg(handle, REG_RF_SETUP, &setup, 1); + setup &= 0x28; + if(setup == 0x20) + rate = 250000; // 250kbps + else if(setup == 0x08) + rate = 2000000; // 2Mbps + else if(setup == 0x00) + rate = 1000000; // 1Mbps + + return rate; +} + +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate) { + uint8_t r6 = 0; + uint8_t status = 0; + if(!rate) rate = 2000000; + + nrf24_read_reg(handle, REG_RF_SETUP, &r6, 1); // RF_SETUP register + r6 = r6 & (~0x28); // Clear rate fields. + if(rate == 2000000) + r6 = r6 | 0x08; + else if(rate == 1000000) + r6 = r6; + else if(rate == 250000) + r6 = r6 | 0x20; + + status = nrf24_write_reg(handle, REG_RF_SETUP, r6); // Write new rate. + return status; +} + +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle) { + uint8_t channel = 0; + nrf24_read_reg(handle, REG_RF_CH, &channel, 1); + return channel; +} + +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan) { + uint8_t status; + status = nrf24_write_reg(handle, REG_RF_CH, chan); + return status; +} + +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_TX_ADDR, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle) { + uint8_t len = 0; + nrf24_read_reg(handle, RX_PW_P0, &len, 1); + return len; +} + +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { + uint8_t status = 0; + status = nrf24_write_reg(handle, RX_PW_P0, len); + return status; +} + +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full) { + uint8_t status = 0; + uint8_t size = 0; + uint8_t tx_pl_wid[] = {R_RX_PL_WID, 0}; + uint8_t rx_pl_wid[] = {0, 0}; + uint8_t tx_cmd[33] = {0}; // 32 max payload size + 1 for command + uint8_t tmp_packet[33] = {0}; + + status = nrf24_status(handle); + + if(status & 0x40) { + if(full) + size = nrf24_get_packetlen(handle); + else { + nrf24_spi_trx(handle, tx_pl_wid, rx_pl_wid, 2, nrf24_TIMEOUT); + size = rx_pl_wid[1]; + } + + tx_cmd[0] = R_RX_PAYLOAD; + nrf24_spi_trx(handle, tx_cmd, tmp_packet, size + 1, nrf24_TIMEOUT); + nrf24_write_reg(handle, REG_STATUS, 0x40); // clear bit. + memcpy(packet, &tmp_packet[1], size); + } else if(status == 0) { + nrf24_flush_rx(handle); + nrf24_write_reg(handle, REG_STATUS, 0x40); // clear bit. + } + + *packetsize = size; + return status; +} + +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) { + uint8_t status = 0; + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(tx, 0, size + 1); + memset(rx, 0, size + 1); + + if(!ack) + tx[0] = W_TX_PAYLOAD_NOACK; + else + tx[0] = W_TX_PAYLOAD; + + memcpy(&tx[1], payload, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + nrf24_set_tx_mode(handle); + + while(!(status & (TX_DS | MAX_RT))) status = nrf24_status(handle); + + if(status & MAX_RT) nrf24_flush_tx(handle); + + nrf24_set_idle(handle); + nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); + return status & TX_DS; +} + +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg = cfg | 2; + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_delay_ms(5000); + return status; +} + +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfc; // clear bottom two bits to power down the radio + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //nr204_write_reg(handle, REG_EN_RXADDR, 0x0); + furi_hal_gpio_write(nrf24_CE_PIN, false); + return status; +} + +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + //status = nrf24_write_reg(handle, REG_CONFIG, 0x0F); // enable 2-byte CRC, PWR_UP, and PRIM_RX + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg |= 0x03; // PWR_UP, and PRIM_RX + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //nr204_write_reg(REG_EN_RXADDR, 0x03) // Set RX Pipe 0 and 1 + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(2000); + return status; +} + +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + furi_hal_gpio_write(nrf24_CE_PIN, false); + nrf24_write_reg(handle, REG_STATUS, 0x30); + //status = nrf24_write_reg(handle, REG_CONFIG, 0x0E); // enable 2-byte CRC, PWR_UP + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfe; // disable PRIM_RX + cfg |= 0x02; // PWR_UP + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(2); + return status; +} + +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa) { + assert(channel <= 125); + assert(rate == 1 || rate == 2); + if(rate == 2) + rate = 8; // 2Mbps + else + rate = 0; // 1Mbps + + nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF + nrf24_set_idle(handle); + nrf24_write_reg(handle, REG_STATUS, 0x1c); // clear interrupts + if(disable_aa) + nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst + else + nrf24_write_reg(handle, REG_EN_AA, 0x1F); // Enable Shockburst + + nrf24_write_reg(handle, REG_DYNPD, 0x3F); // enable dynamic payload length on all pipes + if(noack) + nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack + else { + nrf24_write_reg(handle, REG_CONFIG, 0x0C); // 2 byte CRC + nrf24_write_reg(handle, REG_FEATURE, 0x07); // enable dyn payload and ack + nrf24_write_reg( + handle, REG_SETUP_RETR, 0x1f); // 15 retries for AA, 500us auto retransmit delay + } + + nrf24_set_idle(handle); + nrf24_flush_rx(handle); + nrf24_flush_tx(handle); + + if(maclen) nrf24_set_maclen(handle, maclen); + if(srcmac) nrf24_set_src_mac(handle, srcmac, maclen); + if(dstmac) nrf24_set_dst_mac(handle, dstmac, maclen); + + nrf24_write_reg(handle, REG_RF_CH, channel); + nrf24_write_reg(handle, REG_RF_SETUP, rate); + furi_delay_ms(200); +} + +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate) { + //uint8_t preamble[] = {0x55, 0x00}; // little endian + uint8_t preamble[] = {0xAA, 0x00}; // little endian + //uint8_t preamble[] = {0x00, 0x55}; // little endian + //uint8_t preamble[] = {0x00, 0xAA}; // little endian + nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF + nrf24_write_reg(handle, REG_STATUS, 0x1c); // clear interrupts + nrf24_write_reg(handle, REG_DYNPD, 0x0); // disable shockburst + nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst + nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack + nrf24_set_maclen(handle, 2); // shortest address + nrf24_set_src_mac(handle, preamble, 2); // set src mac to preamble bits to catch everything + nrf24_set_packetlen(handle, 32); // set max packet length + nrf24_set_idle(handle); + nrf24_flush_rx(handle); + nrf24_flush_tx(handle); + nrf24_write_reg(handle, REG_RF_CH, channel); + nrf24_write_reg(handle, REG_RF_SETUP, rate); + + // prime for RX, no checksum + nrf24_write_reg(handle, REG_CONFIG, 0x03); // PWR_UP and PRIM_RX, disable AA and CRC + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(100); +} + +void hexlify(uint8_t* in, uint8_t size, char* out) { + memset(out, 0, size * 2); + for(int i = 0; i < size; i++) + snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]); +} + +uint64_t bytes_to_int64(uint8_t* bytes, uint8_t size, bool bigendian) { + uint64_t ret = 0; + for(int i = 0; i < size; i++) + if(bigendian) + ret |= bytes[i] << ((size - 1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 8; i++) { + if(bigendian) + out[i] = (val >> ((7 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian) { + uint32_t ret = 0; + for(int i = 0; i < 4; i++) + if(bigendian) + ret |= bytes[i] << ((3 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 4; i++) { + if(bigendian) + out[i] = (val >> ((3 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint64_t bytes_to_int16(uint8_t* bytes, bool bigendian) { + uint16_t ret = 0; + for(int i = 0; i < 2; i++) + if(bigendian) + ret |= bytes[i] << ((1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 2; i++) { + if(bigendian) + out[i] = (val >> ((1 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +// handle iffyness with preamble processing sometimes being a bit (literally) off +void alt_address_old(uint8_t* packet, uint8_t* altaddr) { + uint8_t macmess_hi_b[4]; + uint8_t macmess_lo_b[2]; + uint32_t macmess_hi; + uint16_t macmess_lo; + uint8_t preserved; + + // get first 6 bytes into 32-bit and 16-bit variables + memcpy(macmess_hi_b, packet, 4); + memcpy(macmess_lo_b, packet + 4, 2); + + macmess_hi = bytes_to_int32(macmess_hi_b, true); + + //preserve least 7 bits from hi that will be shifted down to lo + preserved = macmess_hi & 0x7f; + macmess_hi >>= 7; + + macmess_lo = bytes_to_int16(macmess_lo_b, true); + macmess_lo >>= 7; + macmess_lo = (preserved << 9) | macmess_lo; + int32_to_bytes(macmess_hi, macmess_hi_b, true); + int16_to_bytes(macmess_lo, macmess_lo_b, true); + memcpy(altaddr, &macmess_hi_b[1], 3); + memcpy(altaddr + 3, macmess_lo_b, 2); +} + +bool validate_address(uint8_t* addr) { + uint8_t bad[][3] = {{0x55, 0x55}, {0xAA, 0xAA}, {0x00, 0x00}, {0xFF, 0xFF}}; + for(int i = 0; i < 4; i++) + for(int j = 0; j < 2; j++) + if(!memcmp(addr + j * 2, bad[i], 2)) return false; + + return true; +} + +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address) { + bool found = false; + uint8_t packet[32] = {0}; + uint8_t packetsize; + //char printit[65]; + uint8_t status = 0; + status = nrf24_rxpacket(handle, packet, &packetsize, true); + if(status & 0x40) { + if(validate_address(packet)) { + for(int i = 0; i < maclen; i++) address[i] = packet[maclen - 1 - i]; + + /* + alt_address(packet, packet); + + for(i = 0; i < maclen; i++) + address[i + 5] = packet[maclen - 1 - i]; + */ + + //memcpy(address, packet, maclen); + //hexlify(packet, packetsize, printit); + found = true; + } + } + + return found; +} + +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit) { + uint8_t ping_packet[] = {0x0f, 0x0f, 0x0f, 0x0f}; // this can be anything, we just need an ack + uint8_t ch = max_channel + 1; // means fail + nrf24_configure(handle, rate, srcmac, dstmac, maclen, 2, false, false); + for(ch = min_channel; ch <= max_channel + 1; ch++) { + nrf24_write_reg(handle, REG_RF_CH, ch); + if(nrf24_txpacket(handle, ping_packet, 4, true)) break; + } + + if(autoinit) { + FURI_LOG_D("nrf24", "initializing radio for channel %d", ch); + nrf24_configure(handle, rate, srcmac, dstmac, maclen, ch, false, false); + return ch; + } + + return ch; +} \ No newline at end of file diff --git a/applications/plugins/mousejacker/lib/nrf24/nrf24.h b/applications/plugins/mousejacker/lib/nrf24/nrf24.h new file mode 100644 index 000000000..3cfcfe089 --- /dev/null +++ b/applications/plugins/mousejacker/lib/nrf24/nrf24.h @@ -0,0 +1,366 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_TX_PAYLOAD_NOACK 0xB0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define RF24_NOP 0xFF + +#define REG_CONFIG 0x00 +#define REG_EN_AA 0x01 +#define REG_EN_RXADDR 0x02 +#define REG_SETUP_AW 0x03 +#define REG_SETUP_RETR 0x04 +#define REG_DYNPD 0x1C +#define REG_FEATURE 0x1D +#define REG_RF_SETUP 0x06 +#define REG_STATUS 0x07 +#define REG_RX_ADDR_P0 0x0A +#define REG_RF_CH 0x05 +#define REG_TX_ADDR 0x10 + +#define RX_PW_P0 0x11 +#define TX_DS 0x20 +#define MAX_RT 0x10 + +#define nrf24_TIMEOUT 500 +#define nrf24_CE_PIN &gpio_ext_pb2 +#define nrf24_HANDLE &furi_hal_spi_bus_handle_external + +/* Low level API */ + +/** Write device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * + * @return device status + */ +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); + +/** Write buffer to device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * @param size - size of data to write + * + * @return device status + */ +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Read device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param[out] data - pointer to data + * + * @return device status + */ +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Power up the radio for operation + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle); + +/** Power down the radio + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle); + +/** Sets the radio to RX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle); + +/** Sets the radio to TX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle); + +/*=============================================================================================================*/ + +/* High level API */ + +/** Must call this before using any other nrf24 API + * + */ +void nrf24_init(); + +/** Must call this when we end using nrf24 device + * + */ +void nrf24_deinit(); + +/** Send flush rx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle); + +/** Send flush tx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle); + +/** Gets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return packet length in data pipe 0 + */ +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle); + +/** Sets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * @param len - length to set + * + * @return device status + */ +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len); + +/** Gets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return MAC address length + */ +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle); + +/** Sets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length to set MAC address to, must be greater than 1 and less than 6 + * + * @return MAC address length + */ +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen); + +/** Gets the current status flags from the STATUS register + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return status flags + */ +uint8_t nrf24_status(FuriHalSpiBusHandle* handle); + +/** Gets the current transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return transfer rate in bps + */ +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle); + +/** Sets the transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * @param rate - the transfer rate in bps + * + * @return device status + */ +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate); + +/** Gets the current channel + * In nrf24, the channel number is multiplied times 1MHz and added to 2400MHz to get the frequency + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return channel + */ +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle); + +/** Sets the channel + * + * @param handle - pointer to FuriHalSpiHandle + * @param frequency - the frequency in hertz + * + * @return device status + */ +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan); + +/** Gets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Gets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Reads RX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] packet - the packet contents + * @param[out] packetsize - size of the received packet + * @param full - boolean set to true, packet length is determined by RX_PW_P0 register, false it is determined by dynamic payload length command + * + * @return device status + */ +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full); + +/** Sends TX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param packet - the packet contents + * @param size - packet size + * @param ack - boolean to determine whether an ACK is required for the packet or not + * + * @return device status + */ +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack); + +/** Configure the radio + * This is not comprehensive, but covers a lot of the common configuration options that may be changed + * @param handle - pointer to FuriHalSpiHandle + * @param rate - transfer rate in Mbps (1 or 2) + * @param srcmac - source mac address + * @param dstmac - destination mac address + * @param maclen - length of mac address + * @param channel - channel to tune to + * @param noack - if true, disable auto-acknowledge + * @param disable_aa - if true, disable ShockBurst + * + */ +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa); + +/** Configures the radio for "promiscuous mode" and primes it for rx + * This is not an actual mode of the nrf24, but this function exploits a few bugs in the chip that allows it to act as if it were. + * See http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html for details. + * @param handle - pointer to FuriHalSpiHandle + * @param channel - channel to tune to + * @param rate - transfer rate in Mbps (1 or 2) + */ +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate); + +/** Listens for a packet and returns first possible address sniffed + * Call this only after calling nrf24_init_promisc_mode + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length of target mac address + * @param[out] addresses - sniffed address + * + * @return success + */ +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address); + +/** Sends ping packet on each channel for designated tx mac looking for ack + * + * @param handle - pointer to FuriHalSpiHandle + * @param srcmac - source address + * @param dstmac - destination address + * @param maclen - length of address + * @param rate - transfer rate in Mbps (1 or 2) + * @param min_channel - channel to start with + * @param max_channel - channel to end at + * @param autoinit - if true, automatically configure radio for this channel + * + * @return channel that the address is listening on, if this value is above the max_channel param, it failed + */ +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit); + +/** Converts 64 bit value into uint8_t array + * @param val - 64-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian); + +/** Converts 32 bit value into uint8_t array + * @param val - 32-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian); + +/** Converts uint8_t array into 32 bit value + * @param bytes - uint8_t array + * @param bigendian - if true, convert as big endian, otherwise little endian + * + * @return 32-bit value + */ +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/plugins/mousejacker/mousejacker.c b/applications/plugins/mousejacker/mousejacker.c index 2c6e9d8d0..c45b0e502 100644 --- a/applications/plugins/mousejacker/mousejacker.c +++ b/applications/plugins/mousejacker/mousejacker.c @@ -11,6 +11,7 @@ #include #include "mousejacker_ducky.h" #include +#include "NRF24_Mouse_Jacker_icons.h" #define TAG "mousejacker" #define LOGITECH_MAX_CHANNEL 85 @@ -373,6 +374,8 @@ int32_t mousejacker_app(void* p) { plugin_state->close_thread_please = false; processing = false; break; + default: + break; } } } @@ -383,7 +386,7 @@ int32_t mousejacker_app(void* p) { } furi_thread_free(plugin_state->mjthread); - furi_hal_spi_release(nrf24_HANDLE); + nrf24_deinit(); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); diff --git a/applications/plugins/music_beeper/application.fam b/applications/plugins/music_beeper/application.fam index 4c74df5fd..3bde6aa52 100644 --- a/applications/plugins/music_beeper/application.fam +++ b/applications/plugins/music_beeper/application.fam @@ -11,14 +11,7 @@ App( provides=["music_beeper_start"], stack_size=2 * 1024, order=45, - fap_icon="../../../assets/icons/Archive/music_10px.png", + fap_icon="icons/music_10px.png", fap_category="Music", -) - -App( - appid="music_beeper_start", - apptype=FlipperAppType.STARTUP, - entry_point="music_beeper_on_system_start", - requires=["music_beeper"], - order=30, + fap_icon_assets="icons", ) diff --git a/applications/plugins/music_beeper/icons/music_10px.png b/applications/plugins/music_beeper/icons/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/music_beeper/icons/music_10px.png differ diff --git a/applications/plugins/music_beeper/music_beeper.c b/applications/plugins/music_beeper/music_beeper.c index a09cad226..b610b3aae 100644 --- a/applications/plugins/music_beeper/music_beeper.c +++ b/applications/plugins/music_beeper/music_beeper.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/applications/plugins/music_player/application.fam b/applications/plugins/music_player/application.fam index 5a13a31ac..1072f1524 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/plugins/music_player/application.fam @@ -11,7 +11,8 @@ App( provides=["music_player_start"], stack_size=2 * 1024, order=45, - fap_icon="../../../assets/icons/Archive/music_10px.png", + fap_icon="icons/music_10px.png", + fap_icon_assets="icons", fap_category="Music", ) 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 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/music_player/icons/music_10px.png differ diff --git a/applications/plugins/music_player/music_player.c b/applications/plugins/music_player/music_player.c index bcd7f4af5..24e2d0fbc 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/music_player/music_player_worker.c b/applications/plugins/music_player/music_player_worker.c index 3f1ac62f7..0d683f4a6 100644 --- a/applications/plugins/music_player/music_player_worker.c +++ b/applications/plugins/music_player/music_player_worker.c @@ -97,11 +97,8 @@ MusicPlayerWorker* music_player_worker_alloc() { NoteBlockArray_init(instance->notes); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "MusicPlayerWorker"); - furi_thread_set_stack_size(instance->thread, 1024); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, music_player_worker_thread_callback); + instance->thread = furi_thread_alloc_ex( + "MusicPlayerWorker", 1024, music_player_worker_thread_callback, instance); instance->volume = 1.0f; diff --git a/applications/plugins/musictracker/.github/workflows/build_dev.yml b/applications/plugins/musictracker/.github/workflows/build_dev.yml new file mode 100644 index 000000000..4d3da2331 --- /dev/null +++ b/applications/plugins/musictracker/.github/workflows/build_dev.yml @@ -0,0 +1,19 @@ +name: Build dev + +on: + push: + branches: + - master + +jobs: + build_dev: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Build + uses: oleksiikutuzov/flipperzero-ufbt-action@v1 + with: + channel: dev \ No newline at end of file diff --git a/applications/plugins/musictracker/README.md b/applications/plugins/musictracker/README.md new file mode 100644 index 000000000..584c2ff86 --- /dev/null +++ b/applications/plugins/musictracker/README.md @@ -0,0 +1,4 @@ +# Flipper Zero music tracker +-=-=- MVP Stage: minimum viable player -=-=- + +[>Get latest build<](https://nightly.link/DrZlo13/flipper-zero-music-tracker/workflows/build_dev/master/zero_tracker.fap.zip) diff --git a/applications/plugins/musictracker/application.fam b/applications/plugins/musictracker/application.fam new file mode 100644 index 000000000..c0f7edca6 --- /dev/null +++ b/applications/plugins/musictracker/application.fam @@ -0,0 +1,14 @@ +App( + appid="zero_tracker", + name="Zero Tracker", + apptype=FlipperAppType.PLUGIN, + entry_point="zero_tracker_app", + requires=[ + "gui", + ], + stack_size=4 * 1024, + order=20, + fap_icon="zero_tracker.png", + fap_category="Music", + fap_icon_assets="icons", +) diff --git a/applications/plugins/musictracker/icons/.gitignore b/applications/plugins/musictracker/icons/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/applications/plugins/musictracker/tracker_engine/speaker_hal.c b/applications/plugins/musictracker/tracker_engine/speaker_hal.c new file mode 100644 index 000000000..208fee04c --- /dev/null +++ b/applications/plugins/musictracker/tracker_engine/speaker_hal.c @@ -0,0 +1,102 @@ +#include "speaker_hal.h" + +#define FURI_HAL_SPEAKER_TIMER TIM16 +#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 +#define FURI_HAL_SPEAKER_PRESCALER 500 + +void tracker_speaker_play(float frequency, float pwm) { + uint32_t autoreload = (SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER / frequency) - 1; + if(autoreload < 2) { + autoreload = 2; + } else if(autoreload > UINT16_MAX) { + autoreload = UINT16_MAX; + } + + if(pwm < 0) pwm = 0; + if(pwm > 1) pwm = 1; + + uint32_t compare_value = pwm * autoreload; + + if(compare_value == 0) { + compare_value = 1; + } + + if(LL_TIM_OC_GetCompareCH1(FURI_HAL_SPEAKER_TIMER) != compare_value) { + LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, compare_value); + } + + if(LL_TIM_GetAutoReload(FURI_HAL_SPEAKER_TIMER) != autoreload) { + LL_TIM_SetAutoReload(FURI_HAL_SPEAKER_TIMER, autoreload); + if(LL_TIM_GetCounter(FURI_HAL_SPEAKER_TIMER) > autoreload) { + LL_TIM_SetCounter(FURI_HAL_SPEAKER_TIMER, 0); + } + } + + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); +} + +void tracker_speaker_stop() { + LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); +} + +void tracker_speaker_init() { + furi_hal_speaker_start(200.0f, 0.01f); + tracker_speaker_stop(); +} + +void tracker_speaker_deinit() { + furi_hal_speaker_stop(); +} + +static FuriHalInterruptISR tracker_isr; +static void* tracker_isr_context; +static void tracker_interrupt_cb(void* context) { + UNUSED(context); + + if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) { + LL_TIM_ClearFlag_UPDATE(TIM2); + + if(tracker_isr) { + tracker_isr(tracker_isr_context); + } + } +} + +void tracker_interrupt_init(float freq, FuriHalInterruptISR isr, void* context) { + tracker_isr = isr; + tracker_isr_context = context; + + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, tracker_interrupt_cb, NULL); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + // Prescaler to get 1kHz clock + TIM_InitStruct.Prescaler = SystemCoreClock / 1000000 - 1; + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + // Auto reload to get freq Hz interrupt + TIM_InitStruct.Autoreload = (1000000 / freq) - 1; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(TIM2, &TIM_InitStruct); + LL_TIM_EnableIT_UPDATE(TIM2); + LL_TIM_EnableAllOutputs(TIM2); + LL_TIM_EnableCounter(TIM2); +} + +void tracker_interrupt_deinit() { + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(TIM2); + FURI_CRITICAL_EXIT(); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); +} + +void tracker_debug_init() { + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); +} + +void tracker_debug_set(bool value) { + furi_hal_gpio_write(&gpio_ext_pc3, value); +} + +void tracker_debug_deinit() { + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} \ No newline at end of file diff --git a/applications/plugins/musictracker/tracker_engine/speaker_hal.h b/applications/plugins/musictracker/tracker_engine/speaker_hal.h new file mode 100644 index 000000000..7867fe93f --- /dev/null +++ b/applications/plugins/musictracker/tracker_engine/speaker_hal.h @@ -0,0 +1,19 @@ +#include + +void tracker_speaker_init(); + +void tracker_speaker_deinit(); + +void tracker_speaker_play(float frequency, float pwm); + +void tracker_speaker_stop(); + +void tracker_interrupt_init(float freq, FuriHalInterruptISR isr, void* context); + +void tracker_interrupt_deinit(); + +void tracker_debug_init(); + +void tracker_debug_set(bool value); + +void tracker_debug_deinit(); \ No newline at end of file diff --git a/applications/plugins/musictracker/tracker_engine/tracker.c b/applications/plugins/musictracker/tracker_engine/tracker.c new file mode 100644 index 000000000..e5efcea17 --- /dev/null +++ b/applications/plugins/musictracker/tracker_engine/tracker.c @@ -0,0 +1,441 @@ +#include "tracker.h" +#include +#include "speaker_hal.h" + +// SongState song_state = { +// .tick = 0, +// .tick_limit = 2, +// .row = 0, +// }; + +typedef struct { + uint8_t speed; + uint8_t depth; + int8_t direction; + int8_t value; +} IntegerOscillator; + +typedef struct { + float frequency; + float frequency_target; + float pwm; + bool play; + IntegerOscillator vibrato; +} ChannelState; + +typedef struct { + ChannelState* channels; + uint8_t tick; + uint8_t tick_limit; + + uint8_t pattern_index; + uint8_t row_index; + uint8_t order_list_index; +} SongState; + +typedef struct { + uint8_t note; + uint8_t effect; + uint8_t data; +} UnpackedRow; + +struct Tracker { + const Song* song; + bool playing; + TrackerMessageCallback callback; + void* context; + SongState song_state; +}; + +static void channels_state_init(ChannelState* channel) { + channel->frequency = 0; + channel->frequency_target = FREQUENCY_UNSET; + channel->pwm = PWM_DEFAULT; + channel->play = false; + channel->vibrato.speed = 0; + channel->vibrato.depth = 0; + channel->vibrato.direction = 0; + channel->vibrato.value = 0; +} + +static void tracker_song_state_init(Tracker* tracker) { + tracker->song_state.tick = 0; + tracker->song_state.tick_limit = 2; + tracker->song_state.row_index = 0; + tracker->song_state.order_list_index = 0; + tracker->song_state.pattern_index = tracker->song->order_list[0]; + + if(tracker->song_state.channels != NULL) { + free(tracker->song_state.channels); + } + + tracker->song_state.channels = malloc(sizeof(ChannelState) * tracker->song->channels_count); + for(uint8_t i = 0; i < tracker->song->channels_count; i++) { + channels_state_init(&tracker->song_state.channels[i]); + } +} + +static void tracker_song_state_clear(Tracker* tracker) { + if(tracker->song_state.channels != NULL) { + free(tracker->song_state.channels); + tracker->song_state.channels = NULL; + } +} + +static uint8_t record_get_note(Row note) { + return note & ROW_NOTE_MASK; +} + +static uint8_t record_get_effect(Row note) { + return (note >> 6) & ROW_EFFECT_MASK; +} + +static uint8_t record_get_effect_data(Row note) { + return (note >> 10) & ROW_EFFECT_DATA_MASK; +} + +#define NOTES_PER_OCT 12 +const float notes_oct[NOTES_PER_OCT] = { + 130.813f, + 138.591f, + 146.832f, + 155.563f, + 164.814f, + 174.614f, + 184.997f, + 195.998f, + 207.652f, + 220.00f, + 233.082f, + 246.942f, +}; + +static float note_to_freq(uint8_t note) { + if(note == NOTE_NONE) return 0.0f; + note = note - NOTE_C2; + uint8_t octave = note / NOTES_PER_OCT; + uint8_t note_in_oct = note % NOTES_PER_OCT; + return notes_oct[note_in_oct] * (1 << octave); +} + +static float frequency_offset_semitones(float frequency, uint8_t semitones) { + return frequency * (1.0f + ((1.0f / 12.0f) * semitones)); +} + +static float frequency_get_seventh_of_a_semitone(float frequency) { + return frequency * ((1.0f / 12.0f) / 7.0f); +} + +static UnpackedRow get_current_row(const Song* song, SongState* song_state, uint8_t channel) { + const Pattern* pattern = &song->patterns[song_state->pattern_index]; + const Row row = pattern->channels[channel].rows[song_state->row_index]; + return (UnpackedRow){ + .note = record_get_note(row), + .effect = record_get_effect(row), + .data = record_get_effect_data(row), + }; +} + +static int16_t advance_order_and_get_next_pattern_index(const Song* song, SongState* song_state) { + song_state->order_list_index++; + if(song_state->order_list_index >= song->order_list_size) { + return -1; + } else { + return song->order_list[song_state->order_list_index]; + } +} + +typedef struct { + int16_t pattern; + int16_t row; + bool change_pattern; + bool change_row; +} Location; + +static void tracker_send_position_message(Tracker* tracker) { + if(tracker->callback != NULL) { + tracker->callback( + (TrackerMessage){ + .type = TrackerPositionChanged, + .data = + { + .position = + { + .order_list_index = tracker->song_state.order_list_index, + .row = tracker->song_state.row_index, + }, + }, + }, + tracker->context); + } +} + +static void tracker_send_end_message(Tracker* tracker) { + if(tracker->callback != NULL) { + tracker->callback((TrackerMessage){.type = TrackerEndOfSong}, tracker->context); + } +} + +static void advance_to_pattern(Tracker* tracker, Location advance) { + if(advance.change_pattern) { + if(advance.pattern < 0 || advance.pattern >= tracker->song->patterns_count) { + tracker->playing = false; + tracker_send_end_message(tracker); + } else { + tracker->song_state.pattern_index = advance.pattern; + tracker->song_state.row_index = 0; + } + } + + if(advance.change_row) { + if(advance.row < 0) advance.row = 0; + if(advance.row >= PATTERN_SIZE) advance.row = PATTERN_SIZE - 1; + tracker->song_state.row_index = advance.row; + } + + tracker_send_position_message(tracker); +} + +static void tracker_interrupt_body(Tracker* tracker) { + if(!tracker->playing) { + tracker_speaker_stop(); + return; + } + + const uint8_t channel_index = 0; + SongState* song_state = &tracker->song_state; + ChannelState* channel_state = &song_state->channels[channel_index]; + const Song* song = tracker->song; + UnpackedRow row = get_current_row(song, song_state, channel_index); + + // load frequency from note at tick 0 + if(song_state->tick == 0) { + bool invalidate_row = false; + // handle "on first tick" effects + if(row.effect == EffectBreakPattern) { + int16_t next_row_index = row.data; + int16_t next_pattern_index = + advance_order_and_get_next_pattern_index(song, song_state); + advance_to_pattern( + tracker, + (Location){ + .pattern = next_pattern_index, + .row = next_row_index, + .change_pattern = true, + .change_row = true, + }); + + invalidate_row = true; + } + + if(row.effect == EffectJumpToOrder) { + song_state->order_list_index = row.data; + int16_t next_pattern_index = song->order_list[song_state->order_list_index]; + + advance_to_pattern( + tracker, + (Location){ + .pattern = next_pattern_index, + .change_pattern = true, + }); + + invalidate_row = true; + } + + // tracker state can be affected by effects + if(!tracker->playing) { + tracker_speaker_stop(); + return; + } + + if(invalidate_row) { + row = get_current_row(song, song_state, channel_index); + + if(row.effect == EffectSetSpeed) { + song_state->tick_limit = row.data; + } + } + + // handle note effects + if(row.note == NOTE_OFF) { + channel_state->play = false; + } else if((row.note > NOTE_NONE) && (row.note < NOTE_OFF)) { + channel_state->play = true; + + // reset vibrato + channel_state->vibrato.speed = 0; + channel_state->vibrato.depth = 0; + channel_state->vibrato.value = 0; + channel_state->vibrato.direction = 0; + + // reset pwm + channel_state->pwm = PWM_DEFAULT; + + if(row.effect == EffectSlideToNote) { + channel_state->frequency_target = note_to_freq(row.note); + } else { + channel_state->frequency = note_to_freq(row.note); + channel_state->frequency_target = FREQUENCY_UNSET; + } + } + } + + if(channel_state->play) { + float frequency, pwm; + + if((row.effect == EffectSlideUp || row.effect == EffectSlideDown) && + row.data != EFFECT_DATA_NONE) { + // apply slide effect + channel_state->frequency += (row.effect == EffectSlideUp ? 1 : -1) * row.data; + } else if(row.effect == EffectSlideToNote) { + // apply slide to note effect, if target frequency is set + if(channel_state->frequency_target > 0) { + if(channel_state->frequency_target > channel_state->frequency) { + channel_state->frequency += row.data; + if(channel_state->frequency > channel_state->frequency_target) { + channel_state->frequency = channel_state->frequency_target; + channel_state->frequency_target = FREQUENCY_UNSET; + } + } else if(channel_state->frequency_target < channel_state->frequency) { + channel_state->frequency -= row.data; + if(channel_state->frequency < channel_state->frequency_target) { + channel_state->frequency = channel_state->frequency_target; + channel_state->frequency_target = FREQUENCY_UNSET; + } + } + } + } + + frequency = channel_state->frequency; + pwm = channel_state->pwm; + + // apply arpeggio effect + if(row.effect == EffectArpeggio) { + if(row.data != EFFECT_DATA_NONE) { + if((song_state->tick % 3) == 1) { + uint8_t note_offset = EFFECT_DATA_GET_X(row.data); + frequency = frequency_offset_semitones(frequency, note_offset); + } else if((song_state->tick % 3) == 2) { + uint8_t note_offset = EFFECT_DATA_GET_Y(row.data); + frequency = frequency_offset_semitones(frequency, note_offset); + } + } + } else if(row.effect == EffectVibrato) { + // apply vibrato effect, data = speed, depth + uint8_t vibrato_speed = EFFECT_DATA_GET_X(row.data); + uint8_t vibrato_depth = EFFECT_DATA_GET_Y(row.data); + + // update vibrato parameters if speed or depth is non-zero + if(vibrato_speed != 0) channel_state->vibrato.speed = vibrato_speed; + if(vibrato_depth != 0) channel_state->vibrato.depth = vibrato_depth; + + // update vibrato value + channel_state->vibrato.value += + channel_state->vibrato.direction * channel_state->vibrato.speed; + + // change direction if value is at the limit + if(channel_state->vibrato.value > channel_state->vibrato.depth) { + channel_state->vibrato.direction = -1; + } else if(channel_state->vibrato.value < -channel_state->vibrato.depth) { + channel_state->vibrato.direction = 1; + } else if(channel_state->vibrato.direction == 0) { + // set initial direction, if it is not set + channel_state->vibrato.direction = 1; + } + + frequency += + (frequency_get_seventh_of_a_semitone(frequency) * channel_state->vibrato.value); + } else if(row.effect == EffectPWM) { + pwm = (pwm - PWM_MIN) / EFFECT_DATA_1_MAX * row.data + PWM_MIN; + } + + tracker_speaker_play(frequency, pwm); + } else { + tracker_speaker_stop(); + } + + song_state->tick++; + if(song_state->tick >= song_state->tick_limit) { + song_state->tick = 0; + + // next note + song_state->row_index = (song_state->row_index + 1); + + if(song_state->row_index >= PATTERN_SIZE) { + int16_t next_pattern_index = + advance_order_and_get_next_pattern_index(song, song_state); + advance_to_pattern( + tracker, + (Location){ + .pattern = next_pattern_index, + .change_pattern = true, + }); + } else { + tracker_send_position_message(tracker); + } + } +} + +static void tracker_interrupt_cb(void* context) { + Tracker* tracker = (Tracker*)context; + tracker_debug_set(true); + tracker_interrupt_body(tracker); + tracker_debug_set(false); +} + +/********************************************************************* + * Tracker Interface + *********************************************************************/ + +Tracker* tracker_alloc() { + Tracker* tracker = malloc(sizeof(Tracker)); + return tracker; +} + +void tracker_free(Tracker* tracker) { + tracker_song_state_clear(tracker); + free(tracker); +} + +void tracker_set_message_callback(Tracker* tracker, TrackerMessageCallback callback, void* context) { + furi_check(tracker->playing == false); + tracker->callback = callback; + tracker->context = context; +} + +void tracker_set_song(Tracker* tracker, const Song* song) { + furi_check(tracker->playing == false); + tracker->song = song; + tracker_song_state_init(tracker); +} + +void tracker_set_order_index(Tracker* tracker, uint8_t order_index) { + furi_check(tracker->playing == false); + furi_check(order_index < tracker->song->order_list_size); + tracker->song_state.order_list_index = order_index; + tracker->song_state.pattern_index = tracker->song->order_list[order_index]; +} + +void tracker_set_row(Tracker* tracker, uint8_t row) { + furi_check(tracker->playing == false); + furi_check(row < PATTERN_SIZE); + tracker->song_state.row_index = row; +} + +void tracker_start(Tracker* tracker) { + furi_check(tracker->song != NULL); + + tracker->playing = true; + tracker_send_position_message(tracker); + tracker_debug_init(); + tracker_speaker_init(); + tracker_interrupt_init(tracker->song->ticks_per_second, tracker_interrupt_cb, tracker); +} + +void tracker_stop(Tracker* tracker) { + tracker_interrupt_deinit(); + tracker_speaker_deinit(); + tracker_debug_deinit(); + + tracker->playing = false; +} \ No newline at end of file diff --git a/applications/plugins/musictracker/tracker_engine/tracker.h b/applications/plugins/musictracker/tracker_engine/tracker.h new file mode 100644 index 000000000..70bf4bd6b --- /dev/null +++ b/applications/plugins/musictracker/tracker_engine/tracker.h @@ -0,0 +1,38 @@ +#pragma once +#include "tracker_notes.h" +#include "tracker_song.h" + +typedef enum { + TrackerPositionChanged, + TrackerEndOfSong, +} TrackerMessageType; + +typedef struct { + TrackerMessageType type; + union tracker_message_data { + struct { + uint8_t order_list_index; + uint8_t row; + } position; + } data; +} TrackerMessage; + +typedef void (*TrackerMessageCallback)(TrackerMessage message, void* context); + +typedef struct Tracker Tracker; + +Tracker* tracker_alloc(); + +void tracker_free(Tracker* tracker); + +void tracker_set_message_callback(Tracker* tracker, TrackerMessageCallback callback, void* context); + +void tracker_set_song(Tracker* tracker, const Song* song); + +void tracker_set_order_index(Tracker* tracker, uint8_t order_index); + +void tracker_set_row(Tracker* tracker, uint8_t row); + +void tracker_start(Tracker* tracker); + +void tracker_stop(Tracker* tracker); \ No newline at end of file diff --git a/applications/plugins/musictracker/tracker_engine/tracker_notes.h b/applications/plugins/musictracker/tracker_engine/tracker_notes.h new file mode 100644 index 000000000..22ab3590f --- /dev/null +++ b/applications/plugins/musictracker/tracker_engine/tracker_notes.h @@ -0,0 +1,64 @@ +#pragma once + +#define NOTE_NONE 0 +#define NOTE_C2 1 +#define NOTE_Cs2 2 +#define NOTE_D2 3 +#define NOTE_Ds2 4 +#define NOTE_E2 5 +#define NOTE_F2 6 +#define NOTE_Fs2 7 +#define NOTE_G2 8 +#define NOTE_Gs2 9 +#define NOTE_A2 10 +#define NOTE_As2 11 +#define NOTE_B2 12 +#define NOTE_C3 13 +#define NOTE_Cs3 14 +#define NOTE_D3 15 +#define NOTE_Ds3 16 +#define NOTE_E3 17 +#define NOTE_F3 18 +#define NOTE_Fs3 19 +#define NOTE_G3 20 +#define NOTE_Gs3 21 +#define NOTE_A3 22 +#define NOTE_As3 23 +#define NOTE_B3 24 +#define NOTE_C4 25 +#define NOTE_Cs4 26 +#define NOTE_D4 27 +#define NOTE_Ds4 28 +#define NOTE_E4 29 +#define NOTE_F4 30 +#define NOTE_Fs4 31 +#define NOTE_G4 32 +#define NOTE_Gs4 33 +#define NOTE_A4 34 +#define NOTE_As4 35 +#define NOTE_B4 36 +#define NOTE_C5 37 +#define NOTE_Cs5 38 +#define NOTE_D5 39 +#define NOTE_Ds5 40 +#define NOTE_E5 41 +#define NOTE_F5 42 +#define NOTE_Fs5 43 +#define NOTE_G5 44 +#define NOTE_Gs5 45 +#define NOTE_A5 46 +#define NOTE_As5 47 +#define NOTE_B5 48 +#define NOTE_C6 49 +#define NOTE_Cs6 50 +#define NOTE_D6 51 +#define NOTE_Ds6 52 +#define NOTE_E6 53 +#define NOTE_F6 54 +#define NOTE_Fs6 55 +#define NOTE_G6 56 +#define NOTE_Gs6 57 +#define NOTE_A6 58 +#define NOTE_As6 59 +#define NOTE_B6 60 +#define NOTE_OFF 63 \ No newline at end of file diff --git a/applications/plugins/musictracker/tracker_engine/tracker_song.h b/applications/plugins/musictracker/tracker_engine/tracker_song.h new file mode 100644 index 000000000..7a054f7b1 --- /dev/null +++ b/applications/plugins/musictracker/tracker_engine/tracker_song.h @@ -0,0 +1,109 @@ +#pragma once +#include + +/** + * @brief Row + * + * AH AL + * FEDCBA98 76543210 + * nnnnnnee eedddddd + * -------- -------- + * nnnnnn = [0] do nothing, [1..60] note number, [61] note off, [62..63] not used + * ee ee = [0..F] effect + * 111222 = [0..63] or [0..7, 0..7] effect data + */ +typedef uint16_t Row; + +#define ROW_NOTE_MASK 0x3F +#define ROW_EFFECT_MASK 0x0F +#define ROW_EFFECT_DATA_MASK 0x3F + +typedef enum { + // 0xy, x - first semitones offset, y - second semitones offset. 0 - no offset .. 7 - +7 semitones... + // Play the arpeggio chord with three notes. The first note is the base note, the second and third are offset by x and y. + // Each note plays one tick. + EffectArpeggio = 0x00, + + // 1xx, xx - effect speed, 0 - no effect, 1 - slowest, 0x3F - fastest. + // Slide the note pitch up by xx Hz every tick. + EffectSlideUp = 0x01, + + // 2xx, xx - effect speed, 0 - no effect, 1 - slowest, 0x3F - fastest. + // Slide the note pitch down by xx Hz every tick. + EffectSlideDown = 0x02, + + // 3xx, xx - effect speed, 0 - no effect, 1 - slowest, 0x3F - fastest. + // Slide the already playing note pitch towards another one by xx Hz every tick. + // The note value is saved until the note is playing, so you don't have to repeat the note value to continue sliding. + EffectSlideToNote = 0x03, + + // 4xy, x - vibrato speed (0..7), y - vibrato depth (0..7). + // Vibrato effect. The pitch of the note increases by x Hz each tick to a positive vibrato depth, then decreases to a negative depth. + // Value 1 of depth means 1/7 of a semitone (about 14.28 ct), so value 7 means full semitone. + // Note will play without vibrato on the first tick at the beginning of the effect. + // Vibrato speed and depth are saved until the note is playing, and will be updated only if they are not zero, so you doesn't have to repeat them every tick. + EffectVibrato = 0x04, + + // Effect05 = 0x05, + // Effect06 = 0x06, + // Effect07 = 0x07, + // Effect08 = 0x08, + // Effect09 = 0x09, + // Effect0A = 0x0A, + + // Bxx, xx - pattern number + // Jump to the order xx in the pattern order table at first tick of current row. + // So if you want to jump to the pattern after note 4, you should put this effect on the 5th note. + EffectJumpToOrder = 0x0B, + + // Cxx, xx - pwm value + // Set the PWM value to xx for current row. + EffectPWM = 0x0C, + + // Bxx, xx - row number + // Jump to the row xx in next pattern at first tick of current row. + // So if you want to jump to the pattern after note 4, you should put this effect on the 5th note. + EffectBreakPattern = 0x0D, + + // Effect0E = 0x0E, + + // Fxx, xx - song speed, 0 - 1 tick per note, 1 - 2 ticks per note, 0x3F - 64 ticks per note. + // Set the speed of the song in terms of ticks per note. + // Will be applied at the first tick of current row. + EffectSetSpeed = 0x0F, +} Effect; + +#define EFFECT_DATA_2(x, y) ((x) | ((y) << 3)) +#define EFFECT_DATA_GET_X(data) ((data)&0x07) +#define EFFECT_DATA_GET_Y(data) (((data) >> 3) & 0x07) +#define EFFECT_DATA_NONE 0 +#define EFFECT_DATA_1_MAX 0x3F +#define EFFECT_DATA_2_MAX 0x07 + +#define FREQUENCY_UNSET -1.0f + +#define PWM_MIN 0.01f +#define PWM_MAX 0.5f +#define PWM_DEFAULT PWM_MAX + +#define PATTERN_SIZE 64 + +#define ROW_MAKE(note, effect, data) \ + ((Row)(((note)&0x3F) | (((effect)&0xF) << 6) | (((data)&0x3F) << 10))) + +typedef struct { + Row rows[PATTERN_SIZE]; +} Channel; + +typedef struct { + Channel* channels; +} Pattern; + +typedef struct { + uint8_t channels_count; + uint8_t patterns_count; + Pattern* patterns; + uint8_t order_list_size; + uint8_t* order_list; + uint16_t ticks_per_second; +} Song; \ No newline at end of file diff --git a/applications/plugins/musictracker/view/tracker_view.c b/applications/plugins/musictracker/view/tracker_view.c new file mode 100644 index 000000000..87e6b0fcf --- /dev/null +++ b/applications/plugins/musictracker/view/tracker_view.c @@ -0,0 +1,182 @@ +#include "tracker_view.h" +#include +#include + +typedef struct { + const Song* song; + uint8_t order_list_index; + uint8_t row; +} TrackerViewModel; + +struct TrackerView { + View* view; + void* back_context; + TrackerViewCallback back_callback; +}; + +static Channel* get_current_channel(TrackerViewModel* model) { + uint8_t channel_id = 0; + uint8_t pattern_id = model->song->order_list[model->order_list_index]; + Pattern* pattern = &model->song->patterns[pattern_id]; + return &pattern->channels[channel_id]; +} + +static const char* get_note_from_id(uint8_t note) { +#define NOTE_COUNT 12 + const char* notes[NOTE_COUNT] = { + "C ", + "C#", + "D ", + "D#", + "E ", + "F ", + "F#", + "G ", + "G#", + "A ", + "A#", + "B ", + }; + return notes[(note) % NOTE_COUNT]; +#undef NOTE_COUNT +} + +static uint8_t get_octave_from_id(uint8_t note) { + return ((note) / 12) + 2; +} + +static uint8_t get_first_row_id(uint8_t row) { + return (row / 10) * 10; +} + +static void + draw_row(Canvas* canvas, uint8_t i, Channel* channel, uint8_t row, FuriString* buffer) { + uint8_t x = 12 * (i + 1); + uint8_t first_row_id = get_first_row_id(row); + uint8_t current_row_id = first_row_id + i; + + if((current_row_id) >= 64) { + return; + } + + Row current_row = channel->rows[current_row_id]; + uint8_t note = current_row & ROW_NOTE_MASK; + uint8_t effect = (current_row >> 6) & ROW_EFFECT_MASK; + uint8_t data = (current_row >> 10) & ROW_EFFECT_DATA_MASK; + + if(current_row_id == row) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_line(canvas, x - 9, 1, x - 9, 62); + canvas_draw_box(canvas, x - 8, 0, 9, 64); + canvas_draw_line(canvas, x + 1, 1, x + 1, 62); + canvas_set_color(canvas, ColorWhite); + } + + furi_string_printf(buffer, "%02X", current_row_id); + canvas_draw_str(canvas, x, 61, furi_string_get_cstr(buffer)); + + if(note > 0 && note < NOTE_OFF) { + furi_string_printf( + buffer, "%s%d", get_note_from_id(note - 1), get_octave_from_id(note - 1)); + canvas_draw_str(canvas, x, 44, furi_string_get_cstr(buffer)); + } else if(note == NOTE_OFF) { + canvas_draw_str(canvas, x, 44, "OFF"); + } else { + canvas_draw_str(canvas, x, 44, "---"); + } + + if(effect == 0 && data == 0) { + canvas_draw_str(canvas, x, 21, "-"); + canvas_draw_str(canvas, x, 12, "--"); + } else { + furi_string_printf(buffer, "%X", effect); + canvas_draw_str(canvas, x, 21, furi_string_get_cstr(buffer)); + + if(effect == EffectArpeggio || effect == EffectVibrato) { + uint8_t data_x = EFFECT_DATA_GET_X(data); + uint8_t data_y = EFFECT_DATA_GET_Y(data); + furi_string_printf(buffer, "%d%d", data_x, data_y); + canvas_draw_str(canvas, x, 12, furi_string_get_cstr(buffer)); + } else { + furi_string_printf(buffer, "%02X", data); + canvas_draw_str(canvas, x, 12, furi_string_get_cstr(buffer)); + } + } + + if(current_row_id == row) { + canvas_set_color(canvas, ColorBlack); + } +} + +static void tracker_view_draw_callback(Canvas* canvas, void* _model) { + TrackerViewModel* model = _model; + if(model->song == NULL) { + return; + } + + canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); + canvas_set_font(canvas, FontKeyboard); + + Channel* channel = get_current_channel(model); + FuriString* buffer = furi_string_alloc(); + + for(uint8_t i = 0; i < 10; i++) { + draw_row(canvas, i, channel, model->row, buffer); + } + furi_string_free(buffer); +} + +static bool tracker_view_input_callback(InputEvent* event, void* context) { + TrackerView* tracker_view = context; + + if(tracker_view->back_callback) { + if(event->type == InputTypeShort && event->key == InputKeyBack) { + tracker_view->back_callback(tracker_view->back_context); + return true; + } + } + return false; +} + +TrackerView* tracker_view_alloc() { + TrackerView* tracker_view = malloc(sizeof(TrackerView)); + tracker_view->view = view_alloc(); + view_allocate_model(tracker_view->view, ViewModelTypeLocking, sizeof(TrackerViewModel)); + view_set_context(tracker_view->view, tracker_view); + view_set_draw_callback(tracker_view->view, (ViewDrawCallback)tracker_view_draw_callback); + view_set_input_callback(tracker_view->view, (ViewInputCallback)tracker_view_input_callback); + return tracker_view; +} + +void tracker_view_free(TrackerView* tracker_view) { + view_free(tracker_view->view); + free(tracker_view); +} + +View* tracker_view_get_view(TrackerView* tracker_view) { + return tracker_view->view; +} + +void tracker_view_set_back_callback( + TrackerView* tracker_view, + TrackerViewCallback callback, + void* context) { + tracker_view->back_callback = callback; + tracker_view->back_context = context; +} + +void tracker_view_set_song(TrackerView* tracker_view, const Song* song) { + with_view_model( + tracker_view->view, TrackerViewModel * model, { model->song = song; }, true); +} + +void tracker_view_set_position(TrackerView* tracker_view, uint8_t order_list_index, uint8_t row) { + with_view_model( + tracker_view->view, + TrackerViewModel * model, + { + model->order_list_index = order_list_index; + model->row = row; + }, + true); +} \ No newline at end of file diff --git a/applications/plugins/musictracker/view/tracker_view.h b/applications/plugins/musictracker/view/tracker_view.h new file mode 100644 index 000000000..6c2e69ba4 --- /dev/null +++ b/applications/plugins/musictracker/view/tracker_view.h @@ -0,0 +1,29 @@ +#include +#include "../tracker_engine/tracker.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct TrackerView TrackerView; + +TrackerView* tracker_view_alloc(); + +void tracker_view_free(TrackerView* tracker_view); + +View* tracker_view_get_view(TrackerView* tracker_view); + +typedef void (*TrackerViewCallback)(void* context); + +void tracker_view_set_back_callback( + TrackerView* tracker_view, + TrackerViewCallback callback, + void* context); + +void tracker_view_set_song(TrackerView* tracker_view, const Song* song); + +void tracker_view_set_position(TrackerView* tracker_view, uint8_t order_list_index, uint8_t row); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/plugins/musictracker/zero_tracker.c b/applications/plugins/musictracker/zero_tracker.c new file mode 100644 index 000000000..2ce219268 --- /dev/null +++ b/applications/plugins/musictracker/zero_tracker.c @@ -0,0 +1,534 @@ +#include +#include +#include +#include +#include "zero_tracker.h" +#include "tracker_engine/tracker.h" +#include "view/tracker_view.h" + +// Channel p_0_channels[] = { +// { +// .rows = +// { +// // 1/4 +// ROW_MAKE(NOTE_C3, EffectArpeggio, EFFECT_DATA_2(4, 7)), +// ROW_MAKE(0, EffectArpeggio, EFFECT_DATA_2(4, 7)), +// ROW_MAKE(NOTE_C4, EffectSlideToNote, 0x20), +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// // +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), +// // 2/4 +// ROW_MAKE(NOTE_C3, EffectSlideDown, 0x20), +// ROW_MAKE(0, EffectSlideDown, 0x20), +// ROW_MAKE(NOTE_C4, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(NOTE_OFF, EffectVibrato, EFFECT_DATA_2(3, 3)), +// // 3/4 +// ROW_MAKE(NOTE_C3, EffectArpeggio, EFFECT_DATA_2(4, 7)), +// ROW_MAKE(0, EffectArpeggio, EFFECT_DATA_2(4, 7)), +// ROW_MAKE(NOTE_OFF, 0, 0), +// ROW_MAKE(0, 0, 0), +// // +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// // +// ROW_MAKE(NOTE_C2, EffectPWM, 60), +// ROW_MAKE(0, EffectPWM, 32), +// ROW_MAKE(0, EffectPWM, 12), +// ROW_MAKE(NOTE_OFF, 0, 0), +// // +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// // 4/4 +// ROW_MAKE(NOTE_C3, EffectSlideDown, 0x20), +// ROW_MAKE(0, EffectSlideDown, 0x20), +// ROW_MAKE(0, EffectSlideDown, 0x20), +// ROW_MAKE(NOTE_OFF, 0, 0), +// // +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// // +// ROW_MAKE(NOTE_C2, EffectPWM, 60), +// ROW_MAKE(0, EffectPWM, 32), +// ROW_MAKE(0, EffectPWM, 12), +// ROW_MAKE(NOTE_OFF, 0, 0), +// // +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// }, +// }, +// }; + +Channel p_0_channels[] = { + { + .rows = + { + // + ROW_MAKE(NOTE_A4, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_C3, 0, 0), + ROW_MAKE(NOTE_F2, 0, 0), + ROW_MAKE(NOTE_C3, 0, 0), + // + ROW_MAKE(NOTE_E4, 0, 0), + ROW_MAKE(NOTE_C3, 0, 0), + ROW_MAKE(NOTE_E4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_C3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_F2, 0, 0), + ROW_MAKE(NOTE_C3, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_C3, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + // + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_B4, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_D3, 0, 0), + ROW_MAKE(NOTE_G2, 0, 0), + ROW_MAKE(NOTE_D3, 0, 0), + // + ROW_MAKE(NOTE_E4, 0, 0), + ROW_MAKE(NOTE_D3, 0, 0), + ROW_MAKE(NOTE_E4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_D3, EffectSlideDown, 0x3F), + ROW_MAKE(NOTE_G2, 0, 0), + ROW_MAKE(NOTE_D3, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_D3, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + // + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(NOTE_OFF, 0, 0), + }, + }, +}; + +Channel p_1_channels[] = { + { + .rows = + { + // + ROW_MAKE(NOTE_C5, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_G4, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C6, 0, 0), + ROW_MAKE(NOTE_E3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_G4, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + // + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_G4, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C6, 0, 0), + ROW_MAKE(NOTE_E3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_G4, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + // + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(NOTE_OFF, 0, 0), + }, + }, +}; + +Channel p_2_channels[] = { + { + .rows = + { + // + ROW_MAKE(NOTE_C5, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + // + ROW_MAKE(NOTE_C5, EffectPWM, 55), + ROW_MAKE(NOTE_A4, EffectPWM, 45), + ROW_MAKE(NOTE_C5, EffectPWM, 35), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(NOTE_C5, EffectPWM, 55), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_E3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_OFF, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_D5, EffectPWM, 55), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + // + ROW_MAKE(NOTE_D5, EffectPWM, 45), + ROW_MAKE(NOTE_B4, EffectPWM, 45), + ROW_MAKE(NOTE_D5, EffectPWM, 35), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + // + ROW_MAKE(NOTE_E5, EffectPWM, 55), + ROW_MAKE(NOTE_C5, EffectPWM, 45), + ROW_MAKE(NOTE_E5, EffectPWM, 35), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_E5, EffectPWM, 55), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_E3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_OFF, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_D5, EffectPWM, 55), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + // + ROW_MAKE(NOTE_D5, EffectPWM, 45), + ROW_MAKE(NOTE_B4, EffectPWM, 45), + ROW_MAKE(NOTE_D5, EffectPWM, 35), + ROW_MAKE(NOTE_OFF, 0, 0), + }, + }, +}; + +Channel p_3_channels[] = { + { + .rows = + { + // + ROW_MAKE(NOTE_Ds5, EffectArpeggio, EFFECT_DATA_2(4, 6)), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_Ds5, 0, 0), + ROW_MAKE(NOTE_C5, EffectPWM, 55), + // + ROW_MAKE(NOTE_Ds5, EffectPWM, 45), + ROW_MAKE(NOTE_C5, EffectPWM, 35), + ROW_MAKE(NOTE_Ds5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + // + ROW_MAKE(NOTE_D5, EffectPWM, 45), + ROW_MAKE(NOTE_B4, EffectPWM, 35), + ROW_MAKE(NOTE_D5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_Cs5, EffectArpeggio, EFFECT_DATA_2(4, 6)), + ROW_MAKE(NOTE_As4, 0, 0), + ROW_MAKE(NOTE_Cs5, 0, 0), + ROW_MAKE(NOTE_As4, EffectPWM, 55), + // + ROW_MAKE(NOTE_Cs5, EffectPWM, 45), + ROW_MAKE(NOTE_As4, EffectPWM, 35), + ROW_MAKE(NOTE_Cs5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, EffectPWM, 55), + // + ROW_MAKE(NOTE_C5, EffectPWM, 45), + ROW_MAKE(NOTE_A4, EffectPWM, 35), + ROW_MAKE(NOTE_C5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_B4, EffectArpeggio, EFFECT_DATA_2(4, 6)), + ROW_MAKE(NOTE_Gs4, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_Gs4, EffectPWM, 55), + // + ROW_MAKE(NOTE_B4, EffectPWM, 45), + ROW_MAKE(NOTE_Gs4, EffectPWM, 35), + ROW_MAKE(NOTE_B4, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, EffectPWM, 55), + // + ROW_MAKE(NOTE_C5, EffectPWM, 45), + ROW_MAKE(NOTE_A4, EffectPWM, 35), + ROW_MAKE(NOTE_C5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_Cs5, EffectArpeggio, EFFECT_DATA_2(4, 6)), + ROW_MAKE(NOTE_As4, 0, 0), + ROW_MAKE(NOTE_Cs5, 0, 0), + ROW_MAKE(NOTE_As4, EffectPWM, 55), + // + ROW_MAKE(NOTE_Cs5, EffectPWM, 45), + ROW_MAKE(NOTE_As4, EffectPWM, 35), + ROW_MAKE(NOTE_Cs5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + // + ROW_MAKE(NOTE_D5, EffectPWM, 45), + ROW_MAKE(NOTE_B4, EffectPWM, 35), + ROW_MAKE(NOTE_D5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + }, + }, +}; +Pattern patterns[] = { + {.channels = p_0_channels}, + {.channels = p_1_channels}, + {.channels = p_2_channels}, + {.channels = p_3_channels}, +}; + +uint8_t order_list[] = { + 0, + 1, + 0, + 2, + 0, + 1, + 0, + 3, +}; + +Song song = { + .channels_count = 1, + .patterns_count = sizeof(patterns) / sizeof(patterns[0]), + .patterns = patterns, + + .order_list_size = sizeof(order_list) / sizeof(order_list[0]), + .order_list = order_list, + + .ticks_per_second = 60, +}; + +void tracker_message(TrackerMessage message, void* context) { + FuriMessageQueue* queue = context; + furi_assert(queue); + furi_message_queue_put(queue, &message, 0); +} + +int32_t zero_tracker_app(void* p) { + UNUSED(p); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_display_backlight_enforce_on); + + Gui* gui = furi_record_open(RECORD_GUI); + ViewDispatcher* view_dispatcher = view_dispatcher_alloc(); + TrackerView* tracker_view = tracker_view_alloc(); + tracker_view_set_song(tracker_view, &song); + view_dispatcher_add_view(view_dispatcher, 0, tracker_view_get_view(tracker_view)); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + view_dispatcher_switch_to_view(view_dispatcher, 0); + + FuriMessageQueue* queue = furi_message_queue_alloc(8, sizeof(TrackerMessage)); + Tracker* tracker = tracker_alloc(); + tracker_set_message_callback(tracker, tracker_message, queue); + tracker_set_song(tracker, &song); + tracker_start(tracker); + + while(1) { + TrackerMessage message; + FuriStatus status = furi_message_queue_get(queue, &message, portMAX_DELAY); + if(status == FuriStatusOk) { + if(message.type == TrackerPositionChanged) { + uint8_t order_list_index = message.data.position.order_list_index; + uint8_t row = message.data.position.row; + uint8_t pattern = song.order_list[order_list_index]; + tracker_view_set_position(tracker_view, order_list_index, row); + FURI_LOG_I("Tracker", "O:%d P:%d R:%d", order_list_index, pattern, row); + } else if(message.type == TrackerEndOfSong) { + FURI_LOG_I("Tracker", "End of song"); + break; + } + } + } + + tracker_stop(tracker); + tracker_free(tracker); + furi_message_queue_free(queue); + + view_dispatcher_remove_view(view_dispatcher, 0); + tracker_view_free(tracker_view); + view_dispatcher_free(view_dispatcher); + + notification_message(notification, &sequence_display_backlight_enforce_auto); + + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/musictracker/zero_tracker.h b/applications/plugins/musictracker/zero_tracker.h new file mode 100644 index 000000000..e69de29bb diff --git a/applications/plugins/musictracker/zero_tracker.png b/applications/plugins/musictracker/zero_tracker.png new file mode 100644 index 000000000..61488d153 Binary files /dev/null and b/applications/plugins/musictracker/zero_tracker.png differ diff --git a/applications/plugins/namechanger/application.fam b/applications/plugins/namechanger/application.fam new file mode 100644 index 000000000..545f0b905 --- /dev/null +++ b/applications/plugins/namechanger/application.fam @@ -0,0 +1,13 @@ +App( + appid="NameChanger", + name="Name Changer", + apptype=FlipperAppType.EXTERNAL, + entry_point="namechanger_app", + cdefines=["APP_NAMECHANGER"], + requires=["gui","storage"], + stack_size=2 * 1024, + order=90, + fap_icon="namechanger_10px.png", + fap_category="Tools", + fap_icon_assets="icons", +) \ No newline at end of file diff --git a/applications/plugins/namechanger/icons/Cry_dolph_55x52.png b/applications/plugins/namechanger/icons/Cry_dolph_55x52.png new file mode 100644 index 000000000..86d9db1b4 Binary files /dev/null and b/applications/plugins/namechanger/icons/Cry_dolph_55x52.png differ diff --git a/applications/plugins/namechanger/icons/DolphinMafia_115x62.png b/applications/plugins/namechanger/icons/DolphinMafia_115x62.png new file mode 100644 index 000000000..66fdb40ff Binary files /dev/null and b/applications/plugins/namechanger/icons/DolphinMafia_115x62.png differ diff --git a/applications/plugins/namechanger/icons/DolphinNice_96x59.png b/applications/plugins/namechanger/icons/DolphinNice_96x59.png new file mode 100644 index 000000000..a299d3630 Binary files /dev/null and b/applications/plugins/namechanger/icons/DolphinNice_96x59.png differ diff --git a/applications/plugins/namechanger/icons/MarioBlock.png b/applications/plugins/namechanger/icons/MarioBlock.png new file mode 100644 index 000000000..86f159966 Binary files /dev/null and b/applications/plugins/namechanger/icons/MarioBlock.png differ diff --git a/applications/plugins/namechanger/icons/namechanger_10px.png b/applications/plugins/namechanger/icons/namechanger_10px.png new file mode 100644 index 000000000..60facf25e Binary files /dev/null and b/applications/plugins/namechanger/icons/namechanger_10px.png differ diff --git a/applications/plugins/namechanger/namechanger.c b/applications/plugins/namechanger/namechanger.c new file mode 100644 index 000000000..284fd2eb5 --- /dev/null +++ b/applications/plugins/namechanger/namechanger.c @@ -0,0 +1,275 @@ +#include "namechanger.h" +#include "scenes/namechanger_scene.h" + +#include +#include + +bool namechanger_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NameChanger* namechanger = context; + return scene_manager_handle_custom_event(namechanger->scene_manager, event); +} + +bool namechanger_back_event_callback(void* context) { + furi_assert(context); + NameChanger* namechanger = context; + return scene_manager_handle_back_event(namechanger->scene_manager); +} + +bool namechanger_make_app_folder(NameChanger* namechanger) { + bool created = false; + FURI_LOG_I(TAG, "folder1"); + + FuriString* folderpath = furi_string_alloc(); + furi_string_set(folderpath, "/ext/dolphin"); + FURI_LOG_I(TAG, "folder2"); + + //Make dir if doesn't exist + if(!storage_simply_mkdir(namechanger->storage, furi_string_get_cstr(folderpath))) { + FURI_LOG_I(TAG, "folder3"); + furi_string_set_str(namechanger->error, "Cannot create\napp folder."); + } else { + FURI_LOG_I(TAG, "folder4"); + created = true; + } + FURI_LOG_I(TAG, "folder5"); + furi_string_free(folderpath); + FURI_LOG_I(TAG, "folder6"); + return created; +} + +bool namechanger_name_read_write(NameChanger* namechanger, char* name, uint8_t mode) { + FuriString* file_path = furi_string_alloc(); + furi_string_set(file_path, "/ext/dolphin/name.txt"); + FURI_LOG_I(TAG, "name1"); + + bool result = false; + + if(mode == 2) { + FURI_LOG_I(TAG, "name2"); + UNUSED(name); + FlipperFormat* file = flipper_format_file_alloc(namechanger->storage); + //read + FuriString* data = furi_string_alloc(); + FURI_LOG_I(TAG, "name3"); + + do { + FURI_LOG_I(TAG, "name4"); + if(!flipper_format_file_open_existing(file, furi_string_get_cstr(file_path))) { + break; + } + FURI_LOG_I(TAG, "name4a"); + + // header + uint32_t version; + + if(!flipper_format_read_header(file, data, &version)) { + break; + } + FURI_LOG_I(TAG, "name4b"); + + if(furi_string_cmp_str(data, NAMECHANGER_HEADER) != 0) { + break; + } + FURI_LOG_I(TAG, "name4c"); + + if(version != 1) { + break; + } + FURI_LOG_I(TAG, "name4d"); + + // get Name + if(!flipper_format_read_string(file, "Name", data)) { + break; + } + FURI_LOG_I(TAG, "name4e"); + + result = true; + FURI_LOG_I(TAG, "name5"); + } while(false); + + flipper_format_free(file); + FURI_LOG_I(TAG, "name6"); + + if(!result) { + FURI_LOG_I(TAG, "name7"); + FURI_LOG_E(TAG, "Cannot load data from file."); + namechanger_text_store_set(namechanger, "%s", furi_hal_version_get_name_ptr()); + } else { + FURI_LOG_I(TAG, "name8"); + furi_string_trim(data); + + if(!furi_string_size(data)) { + FURI_LOG_I(TAG, "name9"); + namechanger_text_store_set(namechanger, "%s", furi_hal_version_get_name_ptr()); + } else { + FURI_LOG_I(TAG, "name10"); + char newname[8]; + snprintf(newname, 8, "%s", furi_string_get_cstr(data)); + namechanger_text_store_set(namechanger, "%s", newname); + } + } + FURI_LOG_I(TAG, "name11"); + + furi_string_free(data); + } else if(mode == 3) { + FURI_LOG_I(TAG, "name12"); + //save + FlipperFormat* file = flipper_format_file_alloc(namechanger->storage); + + do { + FURI_LOG_I(TAG, "name13"); + // Open file for write + if(!flipper_format_file_open_always(file, furi_string_get_cstr(file_path))) { + break; + } + + // Write header + if(!flipper_format_write_header_cstr(file, NAMECHANGER_HEADER, 1)) { + break; + } + + // Write comments + if(!flipper_format_write_comment_cstr( + file, "Changing the value below will change your FlipperZero device name.")) { + break; + } + + if(!flipper_format_write_comment_cstr( + file, + "Note: This is limited to 8 characters using the following: a-z, A-Z, 0-9, and _")) { + break; + } + + if(!flipper_format_write_comment_cstr( + file, "It cannot contain any other characters.")) { + break; + } + + //If name is eraseerase (set by Revert) - then don't write any name + //otherwise, write name as set in the variable + if(strcmp(name, "eraseerase") == 0) { + if(!flipper_format_write_string_cstr(file, "Name", "")) { + break; + } + } else { + if(!flipper_format_write_string_cstr(file, "Name", name)) { + break; + } + } + + FURI_LOG_I(TAG, "name14"); + result = true; + } while(false); + + flipper_format_free(file); + FURI_LOG_I(TAG, "name15"); + + if(!result) { + FURI_LOG_I(TAG, "name16"); + FURI_LOG_E(TAG, "Cannot save name file."); + furi_string_set_str(namechanger->error, "Cannot save\nname file."); + } + } else { + FURI_LOG_I(TAG, "name17"); + FURI_LOG_E(TAG, "Something broke."); + furi_string_set_str(namechanger->error, "Something broke."); + } + FURI_LOG_I(TAG, "name18"); + + return result; +} + +NameChanger* namechanger_alloc() { + NameChanger* namechanger = malloc(sizeof(namechanger)); + + namechanger->scene_manager = scene_manager_alloc(&namechanger_scene_handlers, namechanger); + + namechanger->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(namechanger->view_dispatcher); + view_dispatcher_set_event_callback_context(namechanger->view_dispatcher, namechanger); + view_dispatcher_set_custom_event_callback( + namechanger->view_dispatcher, namechanger_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + namechanger->view_dispatcher, namechanger_back_event_callback); + + namechanger->gui = furi_record_open(RECORD_GUI); + namechanger->storage = furi_record_open(RECORD_STORAGE); + + namechanger->submenu = submenu_alloc(); + view_dispatcher_add_view( + namechanger->view_dispatcher, + NameChangerViewSubmenu, + submenu_get_view(namechanger->submenu)); + + namechanger->text_input = text_input_alloc(); + view_dispatcher_add_view( + namechanger->view_dispatcher, + NameChangerViewTextInput, + text_input_get_view(namechanger->text_input)); + + namechanger->popup = popup_alloc(); + view_dispatcher_add_view( + namechanger->view_dispatcher, NameChangerViewPopup, popup_get_view(namechanger->popup)); + + namechanger->widget = widget_alloc(); + view_dispatcher_add_view( + namechanger->view_dispatcher, NameChangerViewWidget, widget_get_view(namechanger->widget)); + + return namechanger; +} + +void namechanger_free(NameChanger* namechanger) { + furi_assert(namechanger); + + view_dispatcher_remove_view(namechanger->view_dispatcher, NameChangerViewWidget); + widget_free(namechanger->widget); + view_dispatcher_remove_view(namechanger->view_dispatcher, NameChangerViewPopup); + popup_free(namechanger->popup); + + view_dispatcher_remove_view(namechanger->view_dispatcher, NameChangerViewTextInput); + text_input_free(namechanger->text_input); + + view_dispatcher_remove_view(namechanger->view_dispatcher, NameChangerViewSubmenu); + submenu_free(namechanger->submenu); + + view_dispatcher_free(namechanger->view_dispatcher); + scene_manager_free(namechanger->scene_manager); + + furi_string_free(namechanger->error); + + furi_record_close(RECORD_STORAGE); + + furi_record_close(RECORD_GUI); + + free(namechanger); +} + +void namechanger_text_store_set(NameChanger* namechanger, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(namechanger->text_store, NAMECHANGER_TEXT_STORE_SIZE, text, args); + + va_end(args); +} + +void namechanger_text_store_clear(NameChanger* namechanger) { + memset(namechanger->text_store, 0, NAMECHANGER_TEXT_STORE_SIZE); +} + +int32_t namechanger_app() { + NameChanger* namechanger = namechanger_alloc(); + + namechanger->error = furi_string_alloc(); + furi_string_set(namechanger->error, "Default"); + + view_dispatcher_attach_to_gui( + namechanger->view_dispatcher, namechanger->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(namechanger->scene_manager, NameChangerSceneStart); + + view_dispatcher_run(namechanger->view_dispatcher); + + namechanger_free(namechanger); + return 0; +} \ No newline at end of file diff --git a/applications/plugins/namechanger/namechanger.h b/applications/plugins/namechanger/namechanger.h new file mode 100644 index 000000000..9500c1c17 --- /dev/null +++ b/applications/plugins/namechanger/namechanger.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "namechanger_custom_event.h" +#include "scenes/namechanger_scene.h" + +#define NAMECHANGER_TEXT_STORE_SIZE 9 +#define NAMECHANGER_HEADER "Flipper Name File" + +#define TAG "NameChanger" + +typedef struct { + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + + Gui* gui; + Storage* storage; + + char text_store[NAMECHANGER_TEXT_STORE_SIZE + 1]; + FuriString* error; + + Submenu* submenu; + TextInput* text_input; + Popup* popup; + Widget* widget; +} NameChanger; + +typedef enum { + NameChangerViewSubmenu, + NameChangerViewTextInput, + NameChangerViewPopup, + NameChangerViewWidget, +} NameChangerView; + +bool namechanger_make_app_folder(NameChanger* namechanger); +bool namechanger_name_read_write(NameChanger* namechanger, char* name, uint8_t mode); +void namechanger_text_store_set(NameChanger* namechanger, const char* text, ...); +void namechanger_text_store_clear(NameChanger* namechanger); diff --git a/applications/plugins/namechanger/namechanger_10px.png b/applications/plugins/namechanger/namechanger_10px.png new file mode 100644 index 000000000..60facf25e Binary files /dev/null and b/applications/plugins/namechanger/namechanger_10px.png differ diff --git a/applications/plugins/namechanger/namechanger_custom_event.h b/applications/plugins/namechanger/namechanger_custom_event.h new file mode 100644 index 000000000..61418642f --- /dev/null +++ b/applications/plugins/namechanger/namechanger_custom_event.h @@ -0,0 +1,7 @@ +#pragma once + +enum NameChangerCustomEvent { + NameChangerCustomEventBack, + NameChangerCustomEventTextEditResult, + NameChangerCustomEventError, +}; diff --git a/applications/plugins/namechanger/scenes/namechanger_scene.c b/applications/plugins/namechanger/scenes/namechanger_scene.c new file mode 100644 index 000000000..82f96e466 --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene.c @@ -0,0 +1,30 @@ +#include "namechanger_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const namechanger_on_enter_handlers[])(void*) = { +#include "namechanger_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const namechanger_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "namechanger_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const namechanger_on_exit_handlers[])(void* context) = { +#include "namechanger_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers namechanger_scene_handlers = { + .on_enter_handlers = namechanger_on_enter_handlers, + .on_event_handlers = namechanger_on_event_handlers, + .on_exit_handlers = namechanger_on_exit_handlers, + .scene_num = NameChangerSceneNum, +}; diff --git a/applications/plugins/namechanger/scenes/namechanger_scene.h b/applications/plugins/namechanger/scenes/namechanger_scene.h new file mode 100644 index 000000000..42071ab98 --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NameChangerScene##id, +typedef enum { +#include "namechanger_scene_config.h" + NameChangerSceneNum, +} NameChangerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers namechanger_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "namechanger_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "namechanger_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "namechanger_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_change.c b/applications/plugins/namechanger/scenes/namechanger_scene_change.c new file mode 100644 index 000000000..08150a1bc --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene_change.c @@ -0,0 +1,64 @@ +#include "../namechanger.h" + +static void namechanger_scene_change_text_input_callback(void* context) { + NameChanger* namechanger = context; + + view_dispatcher_send_custom_event( + namechanger->view_dispatcher, NameChangerCustomEventTextEditResult); +} + +void namechanger_scene_change_on_enter(void* context) { + NameChanger* namechanger = context; + TextInput* text_input = namechanger->text_input; + + if(namechanger_name_read_write(namechanger, NULL, 2)) { + text_input_set_header_text(text_input, "Set Flipper Name"); + + text_input_set_result_callback( + text_input, + namechanger_scene_change_text_input_callback, + namechanger, + namechanger->text_store, + NAMECHANGER_TEXT_STORE_SIZE, + true); + + view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewTextInput); + } else { + view_dispatcher_send_custom_event( + namechanger->view_dispatcher, NameChangerCustomEventError); + } +} + +bool namechanger_scene_change_on_event(void* context, SceneManagerEvent event) { + NameChanger* namechanger = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == NameChangerCustomEventTextEditResult) { + if(namechanger_make_app_folder(namechanger)) { + if(namechanger_name_read_write(namechanger, namechanger->text_store, 3)) { + scene_manager_next_scene( + namechanger->scene_manager, NameChangerSceneChangeSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + namechanger->scene_manager, NameChangerSceneError); + } + } else { + scene_manager_search_and_switch_to_previous_scene( + namechanger->scene_manager, NameChangerSceneError); + } + } else if(event.event == NameChangerCustomEventError) { + scene_manager_search_and_switch_to_previous_scene( + namechanger->scene_manager, NameChangerSceneError); + } + } + return consumed; +} + +void namechanger_scene_change_on_exit(void* context) { + NameChanger* namechanger = context; + TextInput* text_input = namechanger->text_input; + + text_input_reset(text_input); +} diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_change_success.c b/applications/plugins/namechanger/scenes/namechanger_scene_change_success.c new file mode 100644 index 000000000..1e0c3c2bb --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene_change_success.c @@ -0,0 +1,51 @@ +#include "../namechanger.h" + +static void namechanger_scene_change_success_popup_callback(void* context) { + NameChanger* namechanger = context; + view_dispatcher_send_custom_event(namechanger->view_dispatcher, NameChangerCustomEventBack); +} + +void namechanger_scene_change_success_on_enter(void* context) { + NameChanger* namechanger = context; + Popup* popup = namechanger->popup; + + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 5, 5, AlignLeft, AlignTop); + popup_set_text(popup, "Rebooting...", 5, 17, AlignLeft, AlignTop); + + popup_set_callback(popup, namechanger_scene_change_success_popup_callback); + popup_set_context(popup, namechanger); + popup_set_timeout(popup, 5000); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewPopup); +} + +bool namechanger_scene_change_success_on_event(void* context, SceneManagerEvent event) { + NameChanger* namechanger = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == NameChangerCustomEventBack) { + scene_manager_search_and_switch_to_previous_scene( + namechanger->scene_manager, NameChangerSceneChange); + } + } + + return consumed; +} + +void namechanger_scene_change_success_on_exit(void* context) { + NameChanger* namechanger = context; + Popup* popup = namechanger->popup; + + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); + + popup_disable_timeout(popup); + popup_set_context(popup, NULL); + popup_set_callback(popup, NULL); + + furi_hal_power_reset(); +} diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_config.h b/applications/plugins/namechanger/scenes/namechanger_scene_config.h new file mode 100644 index 000000000..26236a057 --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(namechanger, start, Start) +ADD_SCENE(namechanger, change, Change) +ADD_SCENE(namechanger, change_success, ChangeSuccess) +ADD_SCENE(namechanger, revert, Revert) +ADD_SCENE(namechanger, revert_success, RevertSuccess) +ADD_SCENE(namechanger, error, Error) \ No newline at end of file diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_error.c b/applications/plugins/namechanger/scenes/namechanger_scene_error.c new file mode 100644 index 000000000..3fe2e7276 --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene_error.c @@ -0,0 +1,49 @@ +#include "../namechanger.h" + +static void namechanger_scene_error_popup_callback(void* context) { + NameChanger* namechanger = context; + view_dispatcher_send_custom_event(namechanger->view_dispatcher, NameChangerCustomEventBack); +} + +void namechanger_scene_error_on_enter(void* context) { + NameChanger* namechanger = context; + Popup* popup = namechanger->popup; + + popup_set_icon(popup, 60, 12, &I_Cry_dolph_55x52); + popup_set_header(popup, "Error", 5, 7, AlignLeft, AlignTop); + + popup_set_text(popup, furi_string_get_cstr(namechanger->error), 5, 20, AlignLeft, AlignTop); + + popup_set_callback(popup, namechanger_scene_error_popup_callback); + popup_set_context(popup, namechanger); + popup_set_timeout(popup, 10000); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewPopup); +} + +bool namechanger_scene_error_on_event(void* context, SceneManagerEvent event) { + NameChanger* namechanger = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == NameChangerCustomEventBack) { + view_dispatcher_stop(namechanger->view_dispatcher); + } + } + + return consumed; +} + +void namechanger_scene_error_on_exit(void* context) { + NameChanger* namechanger = context; + Popup* popup = namechanger->popup; + + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); + + popup_disable_timeout(popup); + popup_set_context(popup, NULL); + popup_set_callback(popup, NULL); +} diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_revert.c b/applications/plugins/namechanger/scenes/namechanger_scene_revert.c new file mode 100644 index 000000000..b1ab3d75a --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene_revert.c @@ -0,0 +1,68 @@ +#include "../namechanger.h" + +static void + namechanger_scene_revert_widget_callback(GuiButtonType result, InputType type, void* context) { + NameChanger* namechanger = context; + FURI_LOG_I(TAG, "revert1"); + if(type == InputTypeShort) { + FURI_LOG_I(TAG, "revert2"); + view_dispatcher_send_custom_event(namechanger->view_dispatcher, result); + } +} + +void namechanger_scene_revert_on_enter(void* context) { + NameChanger* namechanger = context; + Widget* widget = namechanger->widget; + FURI_LOG_I(TAG, "revert3"); + widget_add_text_box_element( + widget, 0, 0, 128, 25, AlignCenter, AlignCenter, "\e#Revert Name?\e#", false); + widget_add_icon_element(widget, 48, 20, &I_MarioBlock); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Cancel", namechanger_scene_revert_widget_callback, namechanger); + FURI_LOG_I(TAG, "revert4"); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Revert", + namechanger_scene_revert_widget_callback, + namechanger); + FURI_LOG_I(TAG, "revert5"); + view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewWidget); +} + +bool namechanger_scene_revert_on_event(void* context, SceneManagerEvent event) { + NameChanger* namechanger = context; + bool consumed = false; + FURI_LOG_I(TAG, "revert6"); + if(event.type == SceneManagerEventTypeBack) { + consumed = true; + FURI_LOG_I(TAG, "revert7"); + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + FURI_LOG_I(TAG, "revert8"); + if(event.event == GuiButtonTypeRight) { + FURI_LOG_I(TAG, "revert9"); + if(namechanger_name_read_write(namechanger, "eraseerase", 3)) { + FURI_LOG_I(TAG, "revert10"); + scene_manager_next_scene( + namechanger->scene_manager, NameChangerSceneRevertSuccess); + } else { + FURI_LOG_I(TAG, "revert11"); + scene_manager_search_and_switch_to_previous_scene( + namechanger->scene_manager, NameChangerSceneError); + } + } else if(event.event == GuiButtonTypeLeft) { + FURI_LOG_I(TAG, "revert12"); + scene_manager_search_and_switch_to_previous_scene( + namechanger->scene_manager, NameChangerSceneStart); + } + } + FURI_LOG_I(TAG, "revert13"); + return consumed; +} + +void namechanger_scene_revert_on_exit(void* context) { + NameChanger* namechanger = context; + FURI_LOG_I(TAG, "revert14"); + widget_reset(namechanger->widget); +} diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_revert_success.c b/applications/plugins/namechanger/scenes/namechanger_scene_revert_success.c new file mode 100644 index 000000000..0be0a40ef --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene_revert_success.c @@ -0,0 +1,55 @@ +#include "../namechanger.h" + +static void namechanger_scene_revert_success_popup_callback(void* context) { + NameChanger* namechanger = context; + view_dispatcher_send_custom_event(namechanger->view_dispatcher, NameChangerCustomEventBack); +} + +void namechanger_scene_revert_success_on_enter(void* context) { + NameChanger* namechanger = context; + Popup* popup = namechanger->popup; + + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Reverted!", 70, 5, AlignLeft, AlignTop); + popup_set_text(popup, "Rebooting...", 70, 16, AlignLeft, AlignTop); + + popup_set_callback(popup, namechanger_scene_revert_success_popup_callback); + popup_set_context(popup, namechanger); + popup_set_timeout(popup, 5000); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewPopup); +} + +bool namechanger_scene_revert_success_on_event(void* context, SceneManagerEvent event) { + NameChanger* namechanger = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; + scene_manager_search_and_switch_to_previous_scene( + namechanger->scene_manager, NameChangerSceneStart); + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == NameChangerCustomEventBack) { + scene_manager_search_and_switch_to_previous_scene( + namechanger->scene_manager, NameChangerSceneStart); + } + } + + return consumed; +} + +void namechanger_scene_revert_success_on_exit(void* context) { + NameChanger* namechanger = context; + Popup* popup = namechanger->popup; + + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); + + popup_disable_timeout(popup); + popup_set_context(popup, NULL); + popup_set_callback(popup, NULL); + + furi_hal_power_reset(); +} \ No newline at end of file diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_start.c b/applications/plugins/namechanger/scenes/namechanger_scene_start.c new file mode 100644 index 000000000..3fe93f5a2 --- /dev/null +++ b/applications/plugins/namechanger/scenes/namechanger_scene_start.c @@ -0,0 +1,58 @@ +#include "../namechanger.h" + +enum SubmenuIndex { + SubmenuIndexChange, + SubmenuIndexRevert, +}; + +void namechanger_scene_start_submenu_callback(void* context, uint32_t index) { + NameChanger* namechanger = context; + view_dispatcher_send_custom_event(namechanger->view_dispatcher, index); +} + +void namechanger_scene_start_on_enter(void* context) { + NameChanger* namechanger = context; + Submenu* submenu = namechanger->submenu; + + submenu_add_item( + submenu, + "Change", + SubmenuIndexChange, + namechanger_scene_start_submenu_callback, + namechanger); + + submenu_add_item( + submenu, + "Revert", + SubmenuIndexRevert, + namechanger_scene_start_submenu_callback, + namechanger); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(namechanger->scene_manager, NameChangerSceneStart)); + + view_dispatcher_switch_to_view(namechanger->view_dispatcher, NameChangerViewSubmenu); +} + +bool namechanger_scene_start_on_event(void* context, SceneManagerEvent event) { + NameChanger* namechanger = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + namechanger->scene_manager, NameChangerSceneStart, event.event); + consumed = true; + if(event.event == SubmenuIndexChange) { + scene_manager_next_scene(namechanger->scene_manager, NameChangerSceneChange); + } + if(event.event == SubmenuIndexRevert) { + scene_manager_next_scene(namechanger->scene_manager, NameChangerSceneRevert); + } + } + return consumed; +} + +void namechanger_scene_start_on_exit(void* context) { + NameChanger* namechanger = context; + submenu_reset(namechanger->submenu); +} diff --git a/applications/plugins/nfc_magic/application.fam b/applications/plugins/nfc_magic/application.fam new file mode 100644 index 000000000..1f324e85f --- /dev/null +++ b/applications/plugins/nfc_magic/application.fam @@ -0,0 +1,20 @@ +App( + appid="nfc_magic", + name="NFC Magic", + apptype=FlipperAppType.EXTERNAL, + entry_point="nfc_magic_app", + requires=[ + "storage", + "gui", + ], + stack_size=4 * 1024, + order=30, + fap_icon="../../../assets/icons/Archive/Nfc_10px.png", + fap_category="Tools", + fap_private_libs=[ + Lib( + name="magic", + ), + ], + fap_icon_assets="assets", +) diff --git a/applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png b/applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png differ diff --git a/applications/plugins/nfc_magic/assets/DolphinNice_96x59.png b/applications/plugins/nfc_magic/assets/DolphinNice_96x59.png new file mode 100644 index 000000000..a299d3630 Binary files /dev/null and b/applications/plugins/nfc_magic/assets/DolphinNice_96x59.png differ diff --git a/applications/plugins/nfc_magic/assets/Loading_24.png b/applications/plugins/nfc_magic/assets/Loading_24.png new file mode 100644 index 000000000..93a59fe68 Binary files /dev/null and b/applications/plugins/nfc_magic/assets/Loading_24.png differ diff --git a/applications/plugins/nfc_magic/assets/NFC_manual_60x50.png b/applications/plugins/nfc_magic/assets/NFC_manual_60x50.png new file mode 100644 index 000000000..787c0bcfe Binary files /dev/null and b/applications/plugins/nfc_magic/assets/NFC_manual_60x50.png differ diff --git a/applications/plugins/nfc_magic/lib/magic/magic.c b/applications/plugins/nfc_magic/lib/magic/magic.c new file mode 100644 index 000000000..a922bc7a8 --- /dev/null +++ b/applications/plugins/nfc_magic/lib/magic/magic.c @@ -0,0 +1,214 @@ +#include "magic.h" + +#include + +#define TAG "Magic" + +#define MAGIC_CMD_WUPA (0x40) +#define MAGIC_CMD_WIPE (0x41) +#define MAGIC_CMD_READ (0x43) +#define MAGIC_CMD_WRITE (0x43) + +#define MAGIC_MIFARE_READ_CMD (0x30) +#define MAGIC_MIFARE_WRITE_CMD (0xA0) + +#define MAGIC_ACK (0x0A) + +#define MAGIC_BUFFER_SIZE (32) + +bool magic_wupa() { + bool magic_activated = false; + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + // Setup nfc poller + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); + furi_hal_nfc_ll_poll(); + ret = furi_hal_nfc_ll_set_mode( + FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106); + if(ret != FuriHalNfcReturnOk) break; + + furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER); + furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER); + furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); + furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA); + + // Start communication + tx_data[0] = MAGIC_CMD_WUPA; + ret = furi_hal_nfc_ll_txrx_bits( + tx_data, + 7, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | + FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, + furi_hal_nfc_ll_ms2fc(20)); + if(ret != FuriHalNfcReturnIncompleteByte) break; + if(rx_len != 4) break; + if(rx_data[0] != MAGIC_ACK) break; + magic_activated = true; + } while(false); + + if(!magic_activated) { + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_start_sleep(); + } + + return magic_activated; +} + +bool magic_data_access_cmd() { + bool write_cmd_success = false; + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + tx_data[0] = MAGIC_CMD_WRITE; + ret = furi_hal_nfc_ll_txrx_bits( + tx_data, + 8, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | + FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, + furi_hal_nfc_ll_ms2fc(20)); + if(ret != FuriHalNfcReturnIncompleteByte) break; + if(rx_len != 4) break; + if(rx_data[0] != MAGIC_ACK) break; + + write_cmd_success = true; + } while(false); + + if(!write_cmd_success) { + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_start_sleep(); + } + + return write_cmd_success; +} + +bool magic_read_block(uint8_t block_num, MfClassicBlock* data) { + furi_assert(data); + + bool read_success = false; + + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + tx_data[0] = MAGIC_MIFARE_READ_CMD; + tx_data[1] = block_num; + ret = furi_hal_nfc_ll_txrx_bits( + tx_data, + 2 * 8, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON, + furi_hal_nfc_ll_ms2fc(20)); + + if(ret != FuriHalNfcReturnOk) break; + if(rx_len != 16 * 8) break; + memcpy(data->value, rx_data, sizeof(data->value)); + read_success = true; + } while(false); + + if(!read_success) { + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_start_sleep(); + } + + return read_success; +} + +bool magic_write_blk(uint8_t block_num, MfClassicBlock* data) { + furi_assert(data); + + bool write_success = false; + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + tx_data[0] = MAGIC_MIFARE_WRITE_CMD; + tx_data[1] = block_num; + ret = furi_hal_nfc_ll_txrx_bits( + tx_data, + 2 * 8, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, + furi_hal_nfc_ll_ms2fc(20)); + if(ret != FuriHalNfcReturnIncompleteByte) break; + if(rx_len != 4) break; + if(rx_data[0] != MAGIC_ACK) break; + + memcpy(tx_data, data->value, sizeof(data->value)); + ret = furi_hal_nfc_ll_txrx_bits( + tx_data, + 16 * 8, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, + furi_hal_nfc_ll_ms2fc(20)); + if(ret != FuriHalNfcReturnIncompleteByte) break; + if(rx_len != 4) break; + if(rx_data[0] != MAGIC_ACK) break; + + write_success = true; + } while(false); + + if(!write_success) { + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_start_sleep(); + } + + return write_success; +} + +bool magic_wipe() { + bool wipe_success = false; + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + tx_data[0] = MAGIC_CMD_WIPE; + ret = furi_hal_nfc_ll_txrx_bits( + tx_data, + 8, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | + FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, + furi_hal_nfc_ll_ms2fc(2000)); + + if(ret != FuriHalNfcReturnIncompleteByte) break; + if(rx_len != 4) break; + if(rx_data[0] != MAGIC_ACK) break; + + wipe_success = true; + } while(false); + + return wipe_success; +} + +void magic_deactivate() { + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_sleep(); +} diff --git a/applications/plugins/nfc_magic/lib/magic/magic.h b/applications/plugins/nfc_magic/lib/magic/magic.h new file mode 100644 index 000000000..64c60a0a7 --- /dev/null +++ b/applications/plugins/nfc_magic/lib/magic/magic.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +bool magic_wupa(); + +bool magic_read_block(uint8_t block_num, MfClassicBlock* data); + +bool magic_data_access_cmd(); + +bool magic_write_blk(uint8_t block_num, MfClassicBlock* data); + +bool magic_wipe(); + +void magic_deactivate(); diff --git a/applications/plugins/nfc_magic/nfc_magic.c b/applications/plugins/nfc_magic/nfc_magic.c new file mode 100644 index 000000000..38eecba6a --- /dev/null +++ b/applications/plugins/nfc_magic/nfc_magic.c @@ -0,0 +1,169 @@ +#include "nfc_magic_i.h" + +bool nfc_magic_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcMagic* nfc_magic = context; + return scene_manager_handle_custom_event(nfc_magic->scene_manager, event); +} + +bool nfc_magic_back_event_callback(void* context) { + furi_assert(context); + NfcMagic* nfc_magic = context; + return scene_manager_handle_back_event(nfc_magic->scene_manager); +} + +void nfc_magic_tick_event_callback(void* context) { + furi_assert(context); + NfcMagic* nfc_magic = context; + scene_manager_handle_tick_event(nfc_magic->scene_manager); +} + +void nfc_magic_show_loading_popup(void* context, bool show) { + NfcMagic* nfc_magic = context; + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +NfcMagic* nfc_magic_alloc() { + NfcMagic* nfc_magic = malloc(sizeof(NfcMagic)); + + nfc_magic->worker = nfc_magic_worker_alloc(); + nfc_magic->view_dispatcher = view_dispatcher_alloc(); + nfc_magic->scene_manager = scene_manager_alloc(&nfc_magic_scene_handlers, nfc_magic); + view_dispatcher_enable_queue(nfc_magic->view_dispatcher); + view_dispatcher_set_event_callback_context(nfc_magic->view_dispatcher, nfc_magic); + view_dispatcher_set_custom_event_callback( + nfc_magic->view_dispatcher, nfc_magic_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + nfc_magic->view_dispatcher, nfc_magic_back_event_callback); + view_dispatcher_set_tick_event_callback( + nfc_magic->view_dispatcher, nfc_magic_tick_event_callback, 100); + + // Nfc device + nfc_magic->nfc_dev = nfc_device_alloc(); + + // Open GUI record + nfc_magic->gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui( + nfc_magic->view_dispatcher, nfc_magic->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + nfc_magic->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Submenu + nfc_magic->submenu = submenu_alloc(); + view_dispatcher_add_view( + nfc_magic->view_dispatcher, NfcMagicViewMenu, submenu_get_view(nfc_magic->submenu)); + + // Popup + nfc_magic->popup = popup_alloc(); + view_dispatcher_add_view( + nfc_magic->view_dispatcher, NfcMagicViewPopup, popup_get_view(nfc_magic->popup)); + + // Loading + nfc_magic->loading = loading_alloc(); + view_dispatcher_add_view( + nfc_magic->view_dispatcher, NfcMagicViewLoading, loading_get_view(nfc_magic->loading)); + + // Text Input + nfc_magic->text_input = text_input_alloc(); + view_dispatcher_add_view( + nfc_magic->view_dispatcher, + NfcMagicViewTextInput, + text_input_get_view(nfc_magic->text_input)); + + // Custom Widget + nfc_magic->widget = widget_alloc(); + view_dispatcher_add_view( + nfc_magic->view_dispatcher, NfcMagicViewWidget, widget_get_view(nfc_magic->widget)); + + return nfc_magic; +} + +void nfc_magic_free(NfcMagic* nfc_magic) { + furi_assert(nfc_magic); + + // Nfc device + nfc_device_free(nfc_magic->nfc_dev); + + // Submenu + view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); + submenu_free(nfc_magic->submenu); + + // Popup + view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); + popup_free(nfc_magic->popup); + + // Loading + view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewLoading); + loading_free(nfc_magic->loading); + + // TextInput + view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewTextInput); + text_input_free(nfc_magic->text_input); + + // Custom Widget + view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); + widget_free(nfc_magic->widget); + + // Worker + nfc_magic_worker_stop(nfc_magic->worker); + nfc_magic_worker_free(nfc_magic->worker); + + // View Dispatcher + view_dispatcher_free(nfc_magic->view_dispatcher); + + // Scene Manager + scene_manager_free(nfc_magic->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + nfc_magic->gui = NULL; + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + nfc_magic->notifications = NULL; + + free(nfc_magic); +} + +static const NotificationSequence nfc_magic_sequence_blink_start_blue = { + &message_blink_start_10, + &message_blink_set_color_blue, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence nfc_magic_sequence_blink_stop = { + &message_blink_stop, + NULL, +}; + +void nfc_magic_blink_start(NfcMagic* nfc_magic) { + notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_blue); +} + +void nfc_magic_blink_stop(NfcMagic* nfc_magic) { + notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_stop); +} + +int32_t nfc_magic_app(void* p) { + UNUSED(p); + NfcMagic* nfc_magic = nfc_magic_alloc(); + + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneStart); + + view_dispatcher_run(nfc_magic->view_dispatcher); + + nfc_magic_free(nfc_magic); + + return 0; +} diff --git a/applications/plugins/nfc_magic/nfc_magic.h b/applications/plugins/nfc_magic/nfc_magic.h new file mode 100644 index 000000000..1abf1371e --- /dev/null +++ b/applications/plugins/nfc_magic/nfc_magic.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct NfcMagic NfcMagic; diff --git a/applications/plugins/nfc_magic/nfc_magic_i.h b/applications/plugins/nfc_magic/nfc_magic_i.h new file mode 100644 index 000000000..01b300826 --- /dev/null +++ b/applications/plugins/nfc_magic/nfc_magic_i.h @@ -0,0 +1,77 @@ +#pragma once + +#include "nfc_magic.h" +#include "nfc_magic_worker.h" + +#include "lib/magic/magic.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "scenes/nfc_magic_scene.h" + +#include +#include + +#include +#include "nfc_magic_icons.h" + +enum NfcMagicCustomEvent { + // Reserve first 100 events for button types and indexes, starting from 0 + NfcMagicCustomEventReserved = 100, + + NfcMagicCustomEventViewExit, + NfcMagicCustomEventWorkerExit, + NfcMagicCustomEventByteInputDone, + NfcMagicCustomEventTextInputDone, +}; + +struct NfcMagic { + NfcMagicWorker* worker; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + // NfcMagicDevice* dev; + NfcDevice* nfc_dev; + + FuriString* text_box_store; + + // Common Views + Submenu* submenu; + Popup* popup; + Loading* loading; + TextInput* text_input; + Widget* widget; +}; + +typedef enum { + NfcMagicViewMenu, + NfcMagicViewPopup, + NfcMagicViewLoading, + NfcMagicViewTextInput, + NfcMagicViewWidget, +} NfcMagicView; + +NfcMagic* nfc_magic_alloc(); + +void nfc_magic_text_store_set(NfcMagic* nfc_magic, const char* text, ...); + +void nfc_magic_text_store_clear(NfcMagic* nfc_magic); + +void nfc_magic_blink_start(NfcMagic* nfc_magic); + +void nfc_magic_blink_stop(NfcMagic* nfc_magic); + +void nfc_magic_show_loading_popup(void* context, bool show); diff --git a/applications/plugins/nfc_magic/nfc_magic_worker.c b/applications/plugins/nfc_magic/nfc_magic_worker.c new file mode 100644 index 000000000..523c794f7 --- /dev/null +++ b/applications/plugins/nfc_magic/nfc_magic_worker.c @@ -0,0 +1,170 @@ +#include "nfc_magic_worker_i.h" + +#include "lib/magic/magic.h" + +#define TAG "NfcMagicWorker" + +static void + nfc_magic_worker_change_state(NfcMagicWorker* nfc_magic_worker, NfcMagicWorkerState state) { + furi_assert(nfc_magic_worker); + + nfc_magic_worker->state = state; +} + +NfcMagicWorker* nfc_magic_worker_alloc() { + NfcMagicWorker* nfc_magic_worker = malloc(sizeof(NfcMagicWorker)); + + // Worker thread attributes + nfc_magic_worker->thread = + furi_thread_alloc_ex("NfcMagicWorker", 8192, nfc_magic_worker_task, nfc_magic_worker); + + nfc_magic_worker->callback = NULL; + nfc_magic_worker->context = NULL; + + nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady); + + return nfc_magic_worker; +} + +void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker) { + furi_assert(nfc_magic_worker); + + furi_thread_free(nfc_magic_worker->thread); + free(nfc_magic_worker); +} + +void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker) { + furi_assert(nfc_magic_worker); + + nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateStop); + furi_thread_join(nfc_magic_worker->thread); +} + +void nfc_magic_worker_start( + NfcMagicWorker* nfc_magic_worker, + NfcMagicWorkerState state, + NfcDeviceData* dev_data, + NfcMagicWorkerCallback callback, + void* context) { + furi_assert(nfc_magic_worker); + furi_assert(dev_data); + + nfc_magic_worker->callback = callback; + nfc_magic_worker->context = context; + nfc_magic_worker->dev_data = dev_data; + nfc_magic_worker_change_state(nfc_magic_worker, state); + furi_thread_start(nfc_magic_worker->thread); +} + +int32_t nfc_magic_worker_task(void* context) { + NfcMagicWorker* nfc_magic_worker = context; + + if(nfc_magic_worker->state == NfcMagicWorkerStateCheck) { + nfc_magic_worker_check(nfc_magic_worker); + } else if(nfc_magic_worker->state == NfcMagicWorkerStateWrite) { + nfc_magic_worker_write(nfc_magic_worker); + } else if(nfc_magic_worker->state == NfcMagicWorkerStateWipe) { + nfc_magic_worker_wipe(nfc_magic_worker); + } + + nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady); + + return 0; +} + +void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { + bool card_found_notified = false; + FuriHalNfcDevData nfc_data = {}; + MfClassicData* src_data = &nfc_magic_worker->dev_data->mf_classic_data; + + while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + if(!card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + } + furi_hal_nfc_sleep(); + + if(!magic_wupa()) { + FURI_LOG_E(TAG, "Not Magic card"); + nfc_magic_worker->callback( + NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); + break; + } + if(!magic_data_access_cmd()) { + FURI_LOG_E(TAG, "Not Magic card"); + nfc_magic_worker->callback( + NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); + break; + } + for(size_t i = 0; i < 64; i++) { + FURI_LOG_D(TAG, "Writing block %d", i); + if(!magic_write_blk(i, &src_data->block[i])) { + FURI_LOG_E(TAG, "Failed to write %d block", i); + nfc_magic_worker->callback(NfcMagicWorkerEventFail, nfc_magic_worker->context); + break; + } + } + nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + break; + } else { + if(card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } + magic_deactivate(); +} + +void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { + bool card_found_notified = false; + + while(nfc_magic_worker->state == NfcMagicWorkerStateCheck) { + if(magic_wupa()) { + if(!card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + } + + nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + break; + } else { + if(card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; + } + } + furi_delay_ms(300); + } + magic_deactivate(); +} + +void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) { + MfClassicBlock block; + memset(&block, 0, sizeof(MfClassicBlock)); + block.value[0] = 0x01; + block.value[1] = 0x02; + block.value[2] = 0x03; + block.value[3] = 0x04; + block.value[4] = 0x04; + block.value[5] = 0x08; + block.value[6] = 0x04; + + while(nfc_magic_worker->state == NfcMagicWorkerStateWipe) { + magic_deactivate(); + furi_delay_ms(300); + if(!magic_wupa()) continue; + if(!magic_wipe()) continue; + if(!magic_data_access_cmd()) continue; + if(!magic_write_blk(0, &block)) continue; + nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + break; + } + magic_deactivate(); +} diff --git a/applications/plugins/nfc_magic/nfc_magic_worker.h b/applications/plugins/nfc_magic/nfc_magic_worker.h new file mode 100644 index 000000000..9d29bb3a8 --- /dev/null +++ b/applications/plugins/nfc_magic/nfc_magic_worker.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +typedef struct NfcMagicWorker NfcMagicWorker; + +typedef enum { + NfcMagicWorkerStateReady, + + NfcMagicWorkerStateCheck, + NfcMagicWorkerStateWrite, + NfcMagicWorkerStateWipe, + + NfcMagicWorkerStateStop, +} NfcMagicWorkerState; + +typedef enum { + NfcMagicWorkerEventSuccess, + NfcMagicWorkerEventFail, + NfcMagicWorkerEventCardDetected, + NfcMagicWorkerEventNoCardDetected, + NfcMagicWorkerEventWrongCard, +} NfcMagicWorkerEvent; + +typedef bool (*NfcMagicWorkerCallback)(NfcMagicWorkerEvent event, void* context); + +NfcMagicWorker* nfc_magic_worker_alloc(); + +void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker); + +void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker); + +void nfc_magic_worker_start( + NfcMagicWorker* nfc_magic_worker, + NfcMagicWorkerState state, + NfcDeviceData* dev_data, + NfcMagicWorkerCallback callback, + void* context); diff --git a/applications/plugins/nfc_magic/nfc_magic_worker_i.h b/applications/plugins/nfc_magic/nfc_magic_worker_i.h new file mode 100644 index 000000000..0cde2e712 --- /dev/null +++ b/applications/plugins/nfc_magic/nfc_magic_worker_i.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "nfc_magic_worker.h" + +struct NfcMagicWorker { + FuriThread* thread; + + NfcDeviceData* dev_data; + + NfcMagicWorkerCallback callback; + void* context; + + NfcMagicWorkerState state; +}; + +int32_t nfc_magic_worker_task(void* context); + +void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker); + +void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker); + +void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker); diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene.c new file mode 100644 index 000000000..520ef2a9d --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene.c @@ -0,0 +1,30 @@ +#include "nfc_magic_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const nfc_magic_on_enter_handlers[])(void*) = { +#include "nfc_magic_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const nfc_magic_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "nfc_magic_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const nfc_magic_on_exit_handlers[])(void* context) = { +#include "nfc_magic_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers nfc_magic_scene_handlers = { + .on_enter_handlers = nfc_magic_on_enter_handlers, + .on_event_handlers = nfc_magic_on_event_handlers, + .on_exit_handlers = nfc_magic_on_exit_handlers, + .scene_num = NfcMagicSceneNum, +}; diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene.h b/applications/plugins/nfc_magic/scenes/nfc_magic_scene.h new file mode 100644 index 000000000..f1e9f715d --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcMagicScene##id, +typedef enum { +#include "nfc_magic_scene_config.h" + NfcMagicSceneNum, +} NfcMagicScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_magic_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_magic_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_magic_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_magic_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_check.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_check.c new file mode 100644 index 000000000..d51797242 --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_check.c @@ -0,0 +1,87 @@ +#include "../nfc_magic_i.h" + +enum { + NfcMagicSceneCheckStateCardSearch, + NfcMagicSceneCheckStateCardFound, +}; + +bool nfc_magic_check_worker_callback(NfcMagicWorkerEvent event, void* context) { + furi_assert(context); + + NfcMagic* nfc_magic = context; + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event); + + return true; +} + +static void nfc_magic_scene_check_setup_view(NfcMagic* nfc_magic) { + Popup* popup = nfc_magic->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneCheck); + + if(state == NfcMagicSceneCheckStateCardSearch) { + popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); + popup_set_text( + nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter); + } else { + popup_set_icon(popup, 12, 23, &I_Loading_24); + popup_set_header(popup, "Checking\nDon't move...", 52, 32, AlignLeft, AlignCenter); + } + + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); +} + +void nfc_magic_scene_check_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); + nfc_magic_scene_check_setup_view(nfc_magic); + + // Setup and start worker + nfc_magic_worker_start( + nfc_magic->worker, + NfcMagicWorkerStateCheck, + &nfc_magic->nfc_dev->dev_data, + nfc_magic_check_worker_callback, + nfc_magic); + nfc_magic_blink_start(nfc_magic); +} + +bool nfc_magic_scene_check_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcMagicWorkerEventSuccess) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneMagicInfo); + consumed = true; + } else if(event.event == NfcMagicWorkerEventWrongCard) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); + consumed = true; + } else if(event.event == NfcMagicWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardFound); + nfc_magic_scene_check_setup_view(nfc_magic); + consumed = true; + } else if(event.event == NfcMagicWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); + nfc_magic_scene_check_setup_view(nfc_magic); + consumed = true; + } + } + return consumed; +} + +void nfc_magic_scene_check_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + nfc_magic_worker_stop(nfc_magic->worker); + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); + // Clear view + popup_reset(nfc_magic->popup); + + nfc_magic_blink_stop(nfc_magic); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_config.h b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_config.h new file mode 100644 index 000000000..557e26914 --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_config.h @@ -0,0 +1,12 @@ +ADD_SCENE(nfc_magic, start, Start) +ADD_SCENE(nfc_magic, file_select, FileSelect) +ADD_SCENE(nfc_magic, write_confirm, WriteConfirm) +ADD_SCENE(nfc_magic, wrong_card, WrongCard) +ADD_SCENE(nfc_magic, write, Write) +ADD_SCENE(nfc_magic, write_fail, WriteFail) +ADD_SCENE(nfc_magic, success, Success) +ADD_SCENE(nfc_magic, check, Check) +ADD_SCENE(nfc_magic, not_magic, NotMagic) +ADD_SCENE(nfc_magic, magic_info, MagicInfo) +ADD_SCENE(nfc_magic, wipe, Wipe) +ADD_SCENE(nfc_magic, wipe_fail, WipeFail) diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c new file mode 100644 index 000000000..a19237ed4 --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c @@ -0,0 +1,34 @@ +#include "../nfc_magic_i.h" + +static bool nfc_magic_scene_file_select_is_file_suitable(NfcDevice* nfc_dev) { + return (nfc_dev->format == NfcDeviceSaveFormatMifareClassic) && + (nfc_dev->dev_data.mf_classic_data.type == MfClassicType1k) && + (nfc_dev->dev_data.nfc_data.uid_len == 4); +} + +void nfc_magic_scene_file_select_on_enter(void* context) { + NfcMagic* nfc_magic = context; + // Process file_select return + nfc_device_set_loading_callback(nfc_magic->nfc_dev, nfc_magic_show_loading_popup, nfc_magic); + + if(nfc_file_select(nfc_magic->nfc_dev)) { + if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic->nfc_dev)) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteConfirm); + } else { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrongCard); + } + } else { + scene_manager_previous_scene(nfc_magic->scene_manager); + } +} + +bool nfc_magic_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_magic_scene_file_select_on_exit(void* context) { + NfcMagic* nfc_magic = context; + nfc_device_set_loading_callback(nfc_magic->nfc_dev, NULL, nfc_magic); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_magic_info.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_magic_info.c new file mode 100644 index 000000000..e9b226b3a --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_magic_info.c @@ -0,0 +1,45 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_magic_info_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcMagic* nfc_magic = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); + } +} + +void nfc_magic_scene_magic_info_on_enter(void* context) { + NfcMagic* nfc_magic = context; + Widget* widget = nfc_magic->widget; + + notification_message(nfc_magic->notifications, &sequence_success); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Magic card detected"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_magic_info_widget_callback, nfc_magic); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); +} + +bool nfc_magic_scene_magic_info_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc_magic->scene_manager); + } + } + return consumed; +} + +void nfc_magic_scene_magic_info_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + widget_reset(nfc_magic->widget); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_not_magic.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_not_magic.c new file mode 100644 index 000000000..b87f7f383 --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_not_magic.c @@ -0,0 +1,44 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_not_magic_widget_callback(GuiButtonType result, InputType type, void* context) { + NfcMagic* nfc_magic = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); + } +} + +void nfc_magic_scene_not_magic_on_enter(void* context) { + NfcMagic* nfc_magic = context; + Widget* widget = nfc_magic->widget; + + notification_message(nfc_magic->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, "Not a magic\ncard"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_not_magic_widget_callback, nfc_magic); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); +} + +bool nfc_magic_scene_not_magic_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc_magic->scene_manager); + } + } + return consumed; +} + +void nfc_magic_scene_not_magic_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + widget_reset(nfc_magic->widget); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c new file mode 100644 index 000000000..f2984443f --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c @@ -0,0 +1,61 @@ +#include "../nfc_magic_i.h" +enum SubmenuIndex { + SubmenuIndexCheck, + SubmenuIndexWriteGen1A, + SubmenuIndexWipe, +}; + +void nfc_magic_scene_start_submenu_callback(void* context, uint32_t index) { + NfcMagic* nfc_magic = context; + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index); +} + +void nfc_magic_scene_start_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + Submenu* submenu = nfc_magic->submenu; + submenu_add_item( + submenu, + "Check Magic Tag", + SubmenuIndexCheck, + nfc_magic_scene_start_submenu_callback, + nfc_magic); + submenu_add_item( + submenu, + "Write Gen1A", + SubmenuIndexWriteGen1A, + nfc_magic_scene_start_submenu_callback, + nfc_magic); + submenu_add_item( + submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_start_submenu_callback, nfc_magic); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart)); + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); +} + +bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexCheck) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); + consumed = true; + } else if(event.event == SubmenuIndexWriteGen1A) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexWipe) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); + consumed = true; + } + scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart, event.event); + } + + return consumed; +} + +void nfc_magic_scene_start_on_exit(void* context) { + NfcMagic* nfc_magic = context; + submenu_reset(nfc_magic->submenu); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_success.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_success.c new file mode 100644 index 000000000..37441e80e --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_success.c @@ -0,0 +1,42 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_success_popup_callback(void* context) { + NfcMagic* nfc_magic = context; + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, NfcMagicCustomEventViewExit); +} + +void nfc_magic_scene_success_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + notification_message(nfc_magic->notifications, &sequence_success); + + Popup* popup = nfc_magic->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Success!", 10, 20, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc_magic); + popup_set_callback(popup, nfc_magic_scene_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); +} + +bool nfc_magic_scene_success_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcMagicCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc_magic->scene_manager, NfcMagicSceneStart); + } + } + return consumed; +} + +void nfc_magic_scene_success_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + // Clear view + popup_reset(nfc_magic->popup); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c new file mode 100644 index 000000000..1ca194286 --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c @@ -0,0 +1,90 @@ +#include "../nfc_magic_i.h" + +enum { + NfcMagicSceneWipeStateCardSearch, + NfcMagicSceneWipeStateCardFound, +}; + +bool nfc_magic_wipe_worker_callback(NfcMagicWorkerEvent event, void* context) { + furi_assert(context); + + NfcMagic* nfc_magic = context; + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event); + + return true; +} + +static void nfc_magic_scene_wipe_setup_view(NfcMagic* nfc_magic) { + Popup* popup = nfc_magic->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneWipe); + + if(state == NfcMagicSceneWipeStateCardSearch) { + popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); + popup_set_text( + nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter); + } else { + popup_set_icon(popup, 12, 23, &I_Loading_24); + popup_set_header(popup, "Wiping\nDon't move...", 52, 32, AlignLeft, AlignCenter); + } + + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); +} + +void nfc_magic_scene_wipe_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); + nfc_magic_scene_wipe_setup_view(nfc_magic); + + // Setup and start worker + nfc_magic_worker_start( + nfc_magic->worker, + NfcMagicWorkerStateWipe, + &nfc_magic->nfc_dev->dev_data, + nfc_magic_wipe_worker_callback, + nfc_magic); + nfc_magic_blink_start(nfc_magic); +} + +bool nfc_magic_scene_wipe_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcMagicWorkerEventSuccess) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess); + consumed = true; + } else if(event.event == NfcMagicWorkerEventFail) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipeFail); + consumed = true; + } else if(event.event == NfcMagicWorkerEventWrongCard) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); + consumed = true; + } else if(event.event == NfcMagicWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardFound); + nfc_magic_scene_wipe_setup_view(nfc_magic); + consumed = true; + } else if(event.event == NfcMagicWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); + nfc_magic_scene_wipe_setup_view(nfc_magic); + consumed = true; + } + } + return consumed; +} + +void nfc_magic_scene_wipe_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + nfc_magic_worker_stop(nfc_magic->worker); + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); + // Clear view + popup_reset(nfc_magic->popup); + + nfc_magic_blink_stop(nfc_magic); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c new file mode 100644 index 000000000..828b65e6c --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c @@ -0,0 +1,41 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_wipe_fail_widget_callback(GuiButtonType result, InputType type, void* context) { + NfcMagic* nfc_magic = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); + } +} + +void nfc_magic_scene_wipe_fail_on_enter(void* context) { + NfcMagic* nfc_magic = context; + Widget* widget = nfc_magic->widget; + + notification_message(nfc_magic->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Wipe failed"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wipe_fail_widget_callback, nfc_magic); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); +} + +bool nfc_magic_scene_wipe_fail_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc_magic->scene_manager); + } + } + return consumed; +} + +void nfc_magic_scene_wipe_fail_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + widget_reset(nfc_magic->widget); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write.c new file mode 100644 index 000000000..c3e6f962a --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write.c @@ -0,0 +1,90 @@ +#include "../nfc_magic_i.h" + +enum { + NfcMagicSceneWriteStateCardSearch, + NfcMagicSceneWriteStateCardFound, +}; + +bool nfc_magic_write_worker_callback(NfcMagicWorkerEvent event, void* context) { + furi_assert(context); + + NfcMagic* nfc_magic = context; + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event); + + return true; +} + +static void nfc_magic_scene_write_setup_view(NfcMagic* nfc_magic) { + Popup* popup = nfc_magic->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneWrite); + + if(state == NfcMagicSceneWriteStateCardSearch) { + popup_set_text( + nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter); + popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_icon(popup, 12, 23, &I_Loading_24); + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + } + + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); +} + +void nfc_magic_scene_write_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); + nfc_magic_scene_write_setup_view(nfc_magic); + + // Setup and start worker + nfc_magic_worker_start( + nfc_magic->worker, + NfcMagicWorkerStateWrite, + &nfc_magic->nfc_dev->dev_data, + nfc_magic_write_worker_callback, + nfc_magic); + nfc_magic_blink_start(nfc_magic); +} + +bool nfc_magic_scene_write_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcMagicWorkerEventSuccess) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess); + consumed = true; + } else if(event.event == NfcMagicWorkerEventFail) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteFail); + consumed = true; + } else if(event.event == NfcMagicWorkerEventWrongCard) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); + consumed = true; + } else if(event.event == NfcMagicWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardFound); + nfc_magic_scene_write_setup_view(nfc_magic); + consumed = true; + } else if(event.event == NfcMagicWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); + nfc_magic_scene_write_setup_view(nfc_magic); + consumed = true; + } + } + return consumed; +} + +void nfc_magic_scene_write_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + nfc_magic_worker_stop(nfc_magic->worker); + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); + // Clear view + popup_reset(nfc_magic->popup); + + nfc_magic_blink_stop(nfc_magic); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_confirm.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_confirm.c new file mode 100644 index 000000000..d31c1c194 --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_confirm.c @@ -0,0 +1,64 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_write_confirm_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcMagic* nfc_magic = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); + } +} + +void nfc_magic_scene_write_confirm_on_enter(void* context) { + NfcMagic* nfc_magic = context; + Widget* widget = nfc_magic->widget; + + widget_add_string_element(widget, 3, 0, AlignLeft, AlignTop, FontPrimary, "Risky operation"); + widget_add_text_box_element( + widget, + 0, + 13, + 128, + 54, + AlignLeft, + AlignTop, + "Writing to this card will change manufacturer block. On some cards it may not be rewritten", + false); + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Continue", + nfc_magic_scene_write_confirm_widget_callback, + nfc_magic); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Back", + nfc_magic_scene_write_confirm_widget_callback, + nfc_magic); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); +} + +bool nfc_magic_scene_write_confirm_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc_magic->scene_manager); + } else if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrite); + consumed = true; + } + } + return consumed; +} + +void nfc_magic_scene_write_confirm_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + widget_reset(nfc_magic->widget); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_fail.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_fail.c new file mode 100644 index 000000000..8a465bf61 --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_fail.c @@ -0,0 +1,58 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_write_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcMagic* nfc_magic = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); + } +} + +void nfc_magic_scene_write_fail_on_enter(void* context) { + NfcMagic* nfc_magic = context; + Widget* widget = nfc_magic->widget; + + notification_message(nfc_magic->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_magic_scene_write_fail_widget_callback, nfc_magic); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); +} + +bool nfc_magic_scene_write_fail_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc_magic->scene_manager, NfcMagicSceneStart); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc_magic->scene_manager, NfcMagicSceneStart); + } + return consumed; +} + +void nfc_magic_scene_write_fail_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + widget_reset(nfc_magic->widget); +} diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c new file mode 100644 index 000000000..69bf9eb50 --- /dev/null +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c @@ -0,0 +1,53 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_wrong_card_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcMagic* nfc_magic = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); + } +} + +void nfc_magic_scene_wrong_card_on_enter(void* context) { + NfcMagic* nfc_magic = context; + Widget* widget = nfc_magic->widget; + + notification_message(nfc_magic->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 1, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 1, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Writing is supported\nonly for 4 bytes UID\nMifare CLassic 1k"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); +} + +bool nfc_magic_scene_wrong_card_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc_magic->scene_manager); + } + } + return consumed; +} + +void nfc_magic_scene_wrong_card_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + widget_reset(nfc_magic->widget); +} diff --git a/applications/plugins/wifi_deauther/LICENSE b/applications/plugins/nrf24scan/LICENSE similarity index 100% rename from applications/plugins/wifi_deauther/LICENSE rename to applications/plugins/nrf24scan/LICENSE diff --git a/applications/plugins/nrf24scan/README.md b/applications/plugins/nrf24scan/README.md new file mode 100644 index 000000000..a44d725a5 --- /dev/null +++ b/applications/plugins/nrf24scan/README.md @@ -0,0 +1,24 @@ +# Scanner NRF24 scanner with logging and resend ability for Flipper Zero + +An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional. + + +
+ + +## PinOut from from NoComp/Frog + + +# Mousejack / NRF24 pinout by UberGuidoZ +2/A7 on FZ goes to MOSI/6 on nrf24l01
+3/A6 on FZ goes to MISO/7 on nrf24l01
+4/A4 on FZ goes to CSN/4 on nrf24l01
+5/B3 on FZ goes to SCK/5 on nrf24l01
+6/B2 on FZ goes to CE/3 on nrf24l01
+8/GND on FZ goes to GND/1 on nrf24l01
+9/3V3 on FZ goes to VCC/2 on nrf24l01
+IRQ/8 is left disconnected on nrf24l01 +![NRF_Pins](https://user-images.githubusercontent.com/57457139/178093717-39effd5c-ebe2-4253-b13c-70517d7902f9.png) +If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines! I've not tried the Plus model so it may have a bigger need for a cap. Otherwise, I haven't had any major issues. Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.) I learned if you wanna get fancy, include a 0.1 uF cap in parallel. The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. Β―\\\_(ツ)_/Β― +![NRF_Capacitor](https://user-images.githubusercontent.com/57457139/178169959-d030f9a6-d2ac-46af-af8b-470ff092c8a7.jpg) + diff --git a/applications/plugins/nrf24scan/Screenshot-1.png b/applications/plugins/nrf24scan/Screenshot-1.png new file mode 100644 index 000000000..8f7aca2a5 Binary files /dev/null and b/applications/plugins/nrf24scan/Screenshot-1.png differ diff --git a/applications/plugins/nrf24scan/Screenshot-2.png b/applications/plugins/nrf24scan/Screenshot-2.png new file mode 100644 index 000000000..0e3ebf5b2 Binary files /dev/null and b/applications/plugins/nrf24scan/Screenshot-2.png differ diff --git a/applications/plugins/nrf24scan/addr.txt b/applications/plugins/nrf24scan/addr.txt new file mode 100644 index 000000000..0e24b1c44 --- /dev/null +++ b/applications/plugins/nrf24scan/addr.txt @@ -0,0 +1,5 @@ +1 +120 +C8C801 +C8C802 +03 diff --git a/applications/plugins/nrf24scan/addr1.txt b/applications/plugins/nrf24scan/addr1.txt new file mode 100644 index 000000000..2d69d940c --- /dev/null +++ b/applications/plugins/nrf24scan/addr1.txt @@ -0,0 +1,3 @@ +1 +120 +C8C801 \ No newline at end of file diff --git a/applications/plugins/nrf24scan/addr_sniff.txt b/applications/plugins/nrf24scan/addr_sniff.txt new file mode 100644 index 000000000..2314dc90b --- /dev/null +++ b/applications/plugins/nrf24scan/addr_sniff.txt @@ -0,0 +1,4 @@ +1 +1 +0055 +00AA diff --git a/applications/plugins/nrf24scan/application.fam b/applications/plugins/nrf24scan/application.fam new file mode 100644 index 000000000..c346112e2 --- /dev/null +++ b/applications/plugins/nrf24scan/application.fam @@ -0,0 +1,20 @@ +App( + appid="Nrf24_Scanner", + name="[NRF24] Scanner", + apptype=FlipperAppType.EXTERNAL, + entry_point="nrf24scan_app", + cdefines=["APP_NRF24SCAN"], + requires=["gui"], + stack_size=2 * 1024, + order=60, + fap_icon="nrf24scan_10px.png", + fap_category="GPIO", + fap_private_libs=[ + Lib( + name="nrf24", + sources=[ + "nrf24.c", + ], + ), + ], +) diff --git a/applications/plugins/nrf24scan/lib/nrf24/nrf24.c b/applications/plugins/nrf24scan/lib/nrf24/nrf24.c new file mode 100644 index 000000000..ab206bfd6 --- /dev/null +++ b/applications/plugins/nrf24scan/lib/nrf24/nrf24.c @@ -0,0 +1,533 @@ +#include "nrf24.h" +#include +#include +#include +#include +#include + +void nrf24_init() { + furi_hal_spi_bus_handle_init(nrf24_HANDLE); + furi_hal_spi_acquire(nrf24_HANDLE); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_write(nrf24_CE_PIN, false); +} + +void nrf24_deinit() { + furi_hal_spi_release(nrf24_HANDLE); + furi_hal_spi_bus_handle_deinit(nrf24_HANDLE); + furi_hal_gpio_write(nrf24_CE_PIN, false); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void nrf24_spi_trx( + FuriHalSpiBusHandle* handle, + uint8_t* tx, + uint8_t* rx, + uint8_t size, + uint32_t timeout) { + UNUSED(timeout); + furi_hal_gpio_write(handle->cs, false); + furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT); + furi_hal_gpio_write(handle->cs, true); +} + +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { + uint8_t tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data}; + uint8_t rx[2] = {0}; + nrf24_spi_trx(handle, tx, rx, 2, nrf24_TIMEOUT); + + + FURI_LOG_D("NRF_WR", " #%02X=%02X", reg, data); + + + + return rx[0]; +} + +uint8_t + nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = W_REGISTER | (REGISTER_MASK & reg); + memcpy(&tx[1], data, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + + + + FURI_LOG_D("NRF_WR", " #%02X(%02X)=0x%02X%02X%02X%02X%02X", reg, size, data[0], data[1], data[2], data[3], data[4] ); + + + + return rx[0]; +} + +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = R_REGISTER | (REGISTER_MASK & reg); + memset(&tx[1], 0, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + memcpy(data, &rx[1], size); + return rx[0]; +} + +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_RX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_TX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle) { + uint8_t maclen; + nrf24_read_reg(handle, REG_SETUP_AW, &maclen, 1); + maclen &= 3; + return maclen + 2; +} + +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) { + assert(maclen > 1 && maclen < 6); + uint8_t status = 0; + status = nrf24_write_reg(handle, REG_SETUP_AW, maclen - 2); + return status; +} + +uint8_t nrf24_status(FuriHalSpiBusHandle* handle) { + uint8_t status; + uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)}; + nrf24_spi_trx(handle, tx, &status, 1, nrf24_TIMEOUT); + return status; +} + +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) { + uint8_t setup = 0; + uint32_t rate = 0; + nrf24_read_reg(handle, REG_RF_SETUP, &setup, 1); + setup &= 0x28; + if(setup == 0x20) + rate = 250000; // 250kbps + else if(setup == 0x08) + rate = 2000000; // 2Mbps + else if(setup == 0x00) + rate = 1000000; // 1Mbps + + return rate; +} + +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate) { + uint8_t r6 = 0; + uint8_t status = 0; + if(!rate) rate = 2000000; + + nrf24_read_reg(handle, REG_RF_SETUP, &r6, 1); // RF_SETUP register + r6 = r6 & (~0x28); // Clear rate fields. + if(rate == 2000000) + r6 = r6 | 0x08; + else if(rate == 1000000) + r6 = r6; + else if(rate == 250000) + r6 = r6 | 0x20; + + status = nrf24_write_reg(handle, REG_RF_SETUP, r6); // Write new rate. + return status; +} + +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle) { + uint8_t channel = 0; + nrf24_read_reg(handle, REG_RF_CH, &channel, 1); + return channel; +} + +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan) { + uint8_t status; + status = nrf24_write_reg(handle, REG_RF_CH, chan); + return status; +} + +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_TX_ADDR, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle) { + uint8_t len = 0; + nrf24_read_reg(handle, RX_PW_P0, &len, 1); + return len; +} + +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { + uint8_t status = 0; + status = nrf24_write_reg(handle, RX_PW_P0, len); + return status; +} + +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full) { + uint8_t status = 0; + uint8_t size = 0; + uint8_t tx_pl_wid[] = {R_RX_PL_WID, 0}; + uint8_t rx_pl_wid[] = {0, 0}; + uint8_t tx_cmd[33] = {0}; // 32 max payload size + 1 for command + uint8_t tmp_packet[33] = {0}; + + status = nrf24_status(handle); + + if(status & 0x40) { + if(full) + size = nrf24_get_packetlen(handle); + else { + nrf24_spi_trx(handle, tx_pl_wid, rx_pl_wid, 2, nrf24_TIMEOUT); + size = rx_pl_wid[1]; + } + + tx_cmd[0] = R_RX_PAYLOAD; + nrf24_spi_trx(handle, tx_cmd, tmp_packet, size + 1, nrf24_TIMEOUT); + nrf24_write_reg(handle, REG_STATUS, 0x50); // clear RX_DR, MAX_RT. + memcpy(packet, &tmp_packet[1], size); + } else if(status == 0 || (status & 0x11)) { + nrf24_flush_rx(handle); + nrf24_write_reg(handle, REG_STATUS, 0x50); // clear RX_DR, MAX_RT. + } + + *packetsize = size; + return status; +} + +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) { + uint8_t status = 0; + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(tx, 0, size + 1); + memset(rx, 0, size + 1); + + if(!ack) + tx[0] = W_TX_PAYLOAD_NOACK; + else + tx[0] = W_TX_PAYLOAD; + + memcpy(&tx[1], payload, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + nrf24_set_tx_mode(handle); + + while(!(status & (TX_DS | MAX_RT))) status = nrf24_status(handle); + + if(status & MAX_RT) nrf24_flush_tx(handle); + + nrf24_set_idle(handle); + nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); + return status & TX_DS; +} + +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg = cfg | 2; + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_delay_ms(1000); + return status; +} + +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfc; // clear bottom two bits to power down the radio + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //nr204_write_reg(handle, REG_EN_RXADDR, 0x0); + furi_hal_gpio_write(nrf24_CE_PIN, false); + return status; +} + +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + //status = nrf24_write_reg(handle, REG_CONFIG, 0x0F); // enable 2-byte CRC, PWR_UP, and PRIM_RX + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg |= 0x03; // PWR_UP, and PRIM_RX + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //nr204_write_reg(REG_EN_RXADDR, 0x03) // Set RX Pipe 0 and 1 + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(2000); + return status; +} + +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + furi_hal_gpio_write(nrf24_CE_PIN, false); + nrf24_write_reg(handle, REG_STATUS, 0x30); + //status = nrf24_write_reg(handle, REG_CONFIG, 0x0E); // enable 2-byte CRC, PWR_UP + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfe; // disable PRIM_RX + cfg |= 0x02; // PWR_UP + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(2); + return status; +} + +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa) { + assert(channel <= 125); + assert(rate == 1 || rate == 2); + if(rate == 2) + rate = 8; // 2Mbps + else + rate = 0; // 1Mbps + + nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF + nrf24_set_idle(handle); + nrf24_write_reg(handle, REG_STATUS, 0x70); // clear interrupts + if(disable_aa) + nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst + else + nrf24_write_reg(handle, REG_EN_AA, 0x1F); // Enable Shockburst + + nrf24_write_reg(handle, REG_DYNPD, 0x3F); // enable dynamic payload length on all pipes + if(noack) + nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack + else { + nrf24_write_reg(handle, REG_CONFIG, 0x0C); // 2 byte CRC + nrf24_write_reg(handle, REG_FEATURE, 0x07); // enable dyn payload and ack + nrf24_write_reg( + handle, REG_SETUP_RETR, 0x1f); // 15 retries for AA, 500us auto retransmit delay + } + + nrf24_set_idle(handle); + nrf24_flush_rx(handle); + nrf24_flush_tx(handle); + + if(maclen) nrf24_set_maclen(handle, maclen); + if(srcmac) nrf24_set_src_mac(handle, srcmac, maclen); + if(dstmac) nrf24_set_dst_mac(handle, dstmac, maclen); + + nrf24_write_reg(handle, REG_RF_CH, channel); + nrf24_write_reg(handle, REG_RF_SETUP, rate); + furi_delay_ms(200); +} + +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate) { + //uint8_t preamble[] = {0x55, 0x00}; // little endian + uint8_t preamble[] = {0xAA, 0x00}; // little endian + //uint8_t preamble[] = {0x00, 0x55}; // little endian + //uint8_t preamble[] = {0x00, 0xAA}; // little endian + nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF + nrf24_write_reg(handle, REG_STATUS, 0x70); // clear interrupts + nrf24_write_reg(handle, REG_DYNPD, 0x0); // disable shockburst + nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst + nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack + nrf24_set_maclen(handle, 2); // shortest address + nrf24_set_src_mac(handle, preamble, 2); // set src mac to preamble bits to catch everything + nrf24_set_packetlen(handle, 32); // set max packet length + nrf24_set_idle(handle); + nrf24_flush_rx(handle); + nrf24_flush_tx(handle); + nrf24_write_reg(handle, REG_RF_CH, channel); + nrf24_write_reg(handle, REG_RF_SETUP, rate); + + // prime for RX, no checksum + nrf24_write_reg(handle, REG_CONFIG, 0x03); // PWR_UP and PRIM_RX, disable AA and CRC + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(100); +} + +void hexlify(uint8_t* in, uint8_t size, char* out) { + memset(out, 0, size * 2); + for(int i = 0; i < size; i++) + snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]); +} + +uint64_t bytes_to_int64(uint8_t* bytes, uint8_t size, bool bigendian) { + uint64_t ret = 0; + for(int i = 0; i < size; i++) + if(bigendian) + ret |= bytes[i] << ((size - 1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 8; i++) { + if(bigendian) + out[i] = (val >> ((7 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian) { + uint32_t ret = 0; + for(int i = 0; i < 4; i++) + if(bigendian) + ret |= bytes[i] << ((3 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 4; i++) { + if(bigendian) + out[i] = (val >> ((3 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint64_t bytes_to_int16(uint8_t* bytes, bool bigendian) { + uint16_t ret = 0; + for(int i = 0; i < 2; i++) + if(bigendian) + ret |= bytes[i] << ((1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 2; i++) { + if(bigendian) + out[i] = (val >> ((1 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +// handle iffyness with preamble processing sometimes being a bit (literally) off +void alt_address_old(uint8_t* packet, uint8_t* altaddr) { + uint8_t macmess_hi_b[4]; + uint8_t macmess_lo_b[2]; + uint32_t macmess_hi; + uint16_t macmess_lo; + uint8_t preserved; + + // get first 6 bytes into 32-bit and 16-bit variables + memcpy(macmess_hi_b, packet, 4); + memcpy(macmess_lo_b, packet + 4, 2); + + macmess_hi = bytes_to_int32(macmess_hi_b, true); + + //preserve least 7 bits from hi that will be shifted down to lo + preserved = macmess_hi & 0x7f; + macmess_hi >>= 7; + + macmess_lo = bytes_to_int16(macmess_lo_b, true); + macmess_lo >>= 7; + macmess_lo = (preserved << 9) | macmess_lo; + int32_to_bytes(macmess_hi, macmess_hi_b, true); + int16_to_bytes(macmess_lo, macmess_lo_b, true); + memcpy(altaddr, &macmess_hi_b[1], 3); + memcpy(altaddr + 3, macmess_lo_b, 2); +} + +bool validate_address(uint8_t* addr) { + uint8_t bad[][3] = {{0x55, 0x55}, {0xAA, 0xAA}, {0x00, 0x00}, {0xFF, 0xFF}}; + for(int i = 0; i < 4; i++) + for(int j = 0; j < 2; j++) + if(!memcmp(addr + j * 2, bad[i], 2)) return false; + + return true; +} + +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address) { + bool found = false; + uint8_t packet[32] = {0}; + uint8_t packetsize; + //char printit[65]; + uint8_t status = 0; + status = nrf24_rxpacket(handle, packet, &packetsize, true); + if(status & 0x40) { + if(validate_address(packet)) { + for(int i = 0; i < maclen; i++) address[i] = packet[maclen - 1 - i]; + + /* + alt_address(packet, packet); + + for(i = 0; i < maclen; i++) + address[i + 5] = packet[maclen - 1 - i]; + */ + + //memcpy(address, packet, maclen); + //hexlify(packet, packetsize, printit); + found = true; + } + } + + return found; +} + +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit) { + uint8_t ping_packet[] = {0x0f, 0x0f, 0x0f, 0x0f}; // this can be anything, we just need an ack + uint8_t ch = max_channel + 1; // means fail + nrf24_configure(handle, rate, srcmac, dstmac, maclen, 2, false, false); + for(ch = min_channel; ch <= max_channel + 1; ch++) { + nrf24_write_reg(handle, REG_RF_CH, ch); + if(nrf24_txpacket(handle, ping_packet, 4, true)) break; + } + + if(autoinit) { + FURI_LOG_D("nrf24", "initializing radio for channel %d", ch); + nrf24_configure(handle, rate, srcmac, dstmac, maclen, ch, false, false); + return ch; + } + + return ch; +} \ No newline at end of file diff --git a/applications/plugins/nrf24scan/lib/nrf24/nrf24.h b/applications/plugins/nrf24scan/lib/nrf24/nrf24.h new file mode 100644 index 000000000..28cf8782b --- /dev/null +++ b/applications/plugins/nrf24scan/lib/nrf24/nrf24.h @@ -0,0 +1,376 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_TX_PAYLOAD_NOACK 0xB0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define RF24_NOP 0xFF + +#define REG_CONFIG 0x00 +#define REG_EN_AA 0x01 +#define REG_EN_RXADDR 0x02 +#define REG_SETUP_AW 0x03 +#define REG_SETUP_RETR 0x04 +#define REG_DYNPD 0x1C +#define REG_FEATURE 0x1D +#define REG_RF_SETUP 0x06 +#define REG_STATUS 0x07 +#define REG_RX_ADDR_P0 0x0A +#define REG_RX_ADDR_P1 0x0B +#define REG_RX_ADDR_P2 0x0C +#define REG_RX_ADDR_P3 0x0D +#define REG_RX_ADDR_P4 0x0E +#define REG_RX_ADDR_P5 0x0F +#define REG_RF_CH 0x05 +#define REG_TX_ADDR 0x10 + +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define TX_DS 0x20 +#define MAX_RT 0x10 + +#define nrf24_TIMEOUT 500 +#define nrf24_CE_PIN &gpio_ext_pb2 +#define nrf24_HANDLE &furi_hal_spi_bus_handle_external + +/* Low level API */ + +/** Write device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * + * @return device status + */ +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); + +/** Write buffer to device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * @param size - size of data to write + * + * @return device status + */ +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Read device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param[out] data - pointer to data + * + * @return device status + */ +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Power up the radio for operation + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle); + +/** Power down the radio + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle); + +/** Sets the radio to RX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle); + +/** Sets the radio to TX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle); + +/*=============================================================================================================*/ + +/* High level API */ + +/** Must call this before using any other nrf24 API + * + */ +void nrf24_init(); + +/** Must call this when we end using nrf24 device + * + */ +void nrf24_deinit(); + +/** Send flush rx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle); + +/** Send flush tx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle); + +/** Gets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return packet length in data pipe 0 + */ +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle); + +/** Sets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * @param len - length to set + * + * @return device status + */ +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len); + +/** Gets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return MAC address length + */ +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle); + +/** Sets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length to set MAC address to, must be greater than 1 and less than 6 + * + * @return MAC address length + */ +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen); + +/** Gets the current status flags from the STATUS register + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return status flags + */ +uint8_t nrf24_status(FuriHalSpiBusHandle* handle); + +/** Gets the current transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return transfer rate in bps + */ +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle); + +/** Sets the transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * @param rate - the transfer rate in bps + * + * @return device status + */ +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate); + +/** Gets the current channel + * In nrf24, the channel number is multiplied times 1MHz and added to 2400MHz to get the frequency + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return channel + */ +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle); + +/** Sets the channel + * + * @param handle - pointer to FuriHalSpiHandle + * @param frequency - the frequency in hertz + * + * @return device status + */ +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan); + +/** Gets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Gets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Reads RX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] packet - the packet contents + * @param[out] packetsize - size of the received packet + * @param full - boolean set to true, packet length is determined by RX_PW_P0 register, false it is determined by dynamic payload length command + * + * @return device status + */ +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full); + +/** Sends TX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param packet - the packet contents + * @param size - packet size + * @param ack - boolean to determine whether an ACK is required for the packet or not + * + * @return device status + */ +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack); + +/** Configure the radio + * This is not comprehensive, but covers a lot of the common configuration options that may be changed + * @param handle - pointer to FuriHalSpiHandle + * @param rate - transfer rate in Mbps (1 or 2) + * @param srcmac - source mac address + * @param dstmac - destination mac address + * @param maclen - length of mac address + * @param channel - channel to tune to + * @param noack - if true, disable auto-acknowledge + * @param disable_aa - if true, disable ShockBurst + * + */ +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa); + +/** Configures the radio for "promiscuous mode" and primes it for rx + * This is not an actual mode of the nrf24, but this function exploits a few bugs in the chip that allows it to act as if it were. + * See http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html for details. + * @param handle - pointer to FuriHalSpiHandle + * @param channel - channel to tune to + * @param rate - transfer rate in Mbps (1 or 2) + */ +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate); + +/** Listens for a packet and returns first possible address sniffed + * Call this only after calling nrf24_init_promisc_mode + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length of target mac address + * @param[out] addresses - sniffed address + * + * @return success + */ +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address); + +/** Sends ping packet on each channel for designated tx mac looking for ack + * + * @param handle - pointer to FuriHalSpiHandle + * @param srcmac - source address + * @param dstmac - destination address + * @param maclen - length of address + * @param rate - transfer rate in Mbps (1 or 2) + * @param min_channel - channel to start with + * @param max_channel - channel to end at + * @param autoinit - if true, automatically configure radio for this channel + * + * @return channel that the address is listening on, if this value is above the max_channel param, it failed + */ +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit); + +/** Converts 64 bit value into uint8_t array + * @param val - 64-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian); + +/** Converts 32 bit value into uint8_t array + * @param val - 32-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian); + +/** Converts uint8_t array into 32 bit value + * @param bytes - uint8_t array + * @param bigendian - if true, convert as big endian, otherwise little endian + * + * @return 32-bit value + */ +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/plugins/nrf24scan/nrf24scan.c b/applications/plugins/nrf24scan/nrf24scan.c new file mode 100644 index 000000000..c95a8da50 --- /dev/null +++ b/applications/plugins/nrf24scan/nrf24scan.c @@ -0,0 +1,641 @@ +// +// Written by vad7, 20.11.2022. +// ver. 1.0 +// +#include "nrf24scan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "nrf24scan" +#define MAX_CHANNEL 125 +#define MAX_ADDR 6 + +#define SCAN_APP_PATH_FOLDER "/ext/nrf24scan" +#define ADDR_FILENAME \ + "addr.txt" // File format (1 parameter per line): \ + // 0.25/1/2 - rate in Mbps \ + // 0..125 - default channel \ + // address P0 in hex (5 byte, LSB last) \ + // address P1 in hex (5 byte, LSB last) \ + // address P2, LSB in hex (1 byte) \ + // address P3, LSB in hex (1 byte) \ + // address P4, LSB in hex (1 byte) \ + // address P5, LSB in hex (1 byte) \ + // captured data in raw format, first byte = addr # 0..5 \ + // ... up to MAX_LOG_RECORDS +#define LOG_FILENAME "log.txt" +#define MAX_LOG_RECORDS 99 +#define LOG_REC_SIZE 33 // max packet size +#define VIEW_LOG_MAX_X 21 + +Nrf24Scan* nrf24scan; +uint8_t what_doing = 0; // 0 - setup, 1 - scanning +char screen_buf[64]; +char addr_file_name[32]; +uint8_t NRF_rate; // 0 - 250Kbps, 1 - 1Mbps, 2 - 2Mbps +uint8_t NRF_channel; // 0..125 +struct { + uint8_t addr_P0[5]; // MSB first + uint8_t addr_P1[5]; // MSB first + uint8_t addr_P2; // LSB only, MSB bytes equal addr_P1 + uint8_t addr_P3; // LSB only, MSB bytes equal addr_P1 + uint8_t addr_P4; // LSB only, MSB bytes equal addr_P1 + uint8_t addr_P5; // LSB only, MSB bytes equal addr_P1 + uint8_t addr_len; // 2..5 + uint8_t addr_count; +} addrs; +int8_t log_to_file = 0; // 0 - no, 1 - yes, 2 - new, -1 - only clear +uint16_t log_arr_idx; +uint16_t view_log_arr_idx = 0; +uint16_t view_log_arr_x = 0; +uint16_t last_packet_send = -1; +uint8_t last_packet_send_st = 0; +int16_t find_channel_period = 0; // sec +uint8_t menu_selected = 0; +#define menu_selected_max 5 +uint32_t start_time; + +static uint8_t GetHexVal(char hex) { + return (uint8_t)hex - ((uint8_t)hex < 58 ? 48 : ((uint8_t)hex < 97 ? 55 : 87)); +} + +// Return num bytes in array +static uint8_t ConvertHexToArray(char* hex, uint8_t* array, uint8_t maxlen) { + uint8_t len = 0; + do { + uint8_t ch = *hex++; + if(ch == 0) break; + if(ch < '0') continue; + *array++ = (GetHexVal(ch) << 4) + GetHexVal(*hex++); + len++; + } while(--maxlen); + return len; +} + +static void add_to_str_hex_bytes(char* out, char* arr, int bytes) { + if(!bytes) return; + out += strlen(out); + do { + snprintf(out, 3, "%02X", *arr++); + out += 2; + } while(--bytes); +} + +void clear_log() { + log_arr_idx = 0; + view_log_arr_idx = 0; + last_packet_send = -1; +} + +void write_to_log_file(Storage* storage) { + if(log_arr_idx == 0) return; + Stream* file_stream = file_stream_alloc(storage); + FuriString* path = furi_string_alloc(); + furi_string_set(path, SCAN_APP_PATH_FOLDER); + furi_string_cat(path, "/"); + furi_string_cat(path, LOG_FILENAME); + char* txt = malloc(LOG_REC_SIZE * 2 + 2); + if(txt == NULL) { + FURI_LOG_E(TAG, "No memory to save log!"); + } else { + if(file_stream_open( + file_stream, furi_string_get_cstr(path), FSAM_READ_WRITE, FSOM_OPEN_APPEND)) { + for(int i = 0; i < log_arr_idx; i++) { + add_to_str_hex_bytes( + txt, (char*)nrf24scan->log_arr + i * LOG_REC_SIZE, LOG_REC_SIZE); + strcat(txt, "\n"); + int len = strlen(txt); + if(stream_write(file_stream, (uint8_t*)txt, len) != len) { + FURI_LOG_I(TAG, "Failed to write bytes to log!"); + break; + } + } + } else { + FURI_LOG_E(TAG, "Failed to open log file"); + } + free(txt); + } + file_stream_close(file_stream); + furi_string_free(path); + stream_free(file_stream); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + if(plugin_state == NULL) return; + //canvas_draw_frame(canvas, 0, 0, 128, 64); // border around the edge of the screen + if(what_doing == 0) { + canvas_set_font(canvas, FontSecondary); // 8x10 font + snprintf( + screen_buf, sizeof(screen_buf), "Settings: %s", addr_file_name); // menu_selected = 0 + canvas_draw_str(canvas, 10, 10, screen_buf); + snprintf(screen_buf, sizeof(screen_buf), "Channel: %d", NRF_channel); // menu_selected = 1 + canvas_draw_str(canvas, 10, 20, screen_buf); + snprintf( + screen_buf, + sizeof(screen_buf), + "Rate: %sbps", + NRF_rate == 2 ? "2M" : + NRF_rate == 1 ? "1M" : + "250K"); // menu_selected = 2 + canvas_draw_str(canvas, 10, 30, screen_buf); + strcpy(screen_buf, "Find channel period: "); // menu_selected = 3 + if(find_channel_period == 0) + strcat(screen_buf, "off"); + else + snprintf( + screen_buf + strlen(screen_buf), sizeof(screen_buf), "%d s", find_channel_period); + canvas_draw_str(canvas, 10, 40, screen_buf); + snprintf( + screen_buf, + sizeof(screen_buf), + "Log: %s", + log_to_file == 0 ? "No" : + log_to_file == 1 ? "Yes" : + log_to_file == 2 ? "New" : + "Clear"); // menu_selected = 4 + canvas_draw_str(canvas, 10, 50, screen_buf); + snprintf( + screen_buf, + sizeof(screen_buf), + "Ok - Start read (%dp)", + addrs.addr_count); // menu_selected = 5 + canvas_draw_str(canvas, 10, 60, screen_buf); + canvas_draw_str(canvas, 0, menu_selected * 10 + 10, ">"); + } else { + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines + char ch2 = ' '; + screen_buf[0] = '\0'; + if(view_log_arr_x == 0) { + strcat(screen_buf, " "); + ch2 = '>'; + } else { + snprintf(screen_buf, sizeof(screen_buf), "<%d", view_log_arr_x); + if(view_log_arr_x < VIEW_LOG_MAX_X) ch2 = '>'; + } + snprintf( + screen_buf + strlen(screen_buf), + sizeof(screen_buf), + " Read %d channel... %c", + NRF_channel, + ch2); + canvas_draw_str(canvas, 0, 7, screen_buf); + if(log_arr_idx) { + uint16_t page = view_log_arr_idx & ~7; + for(uint8_t i = 0; i < 8 && page + i < log_arr_idx; i++) { + snprintf(screen_buf, sizeof(screen_buf), "%d: ", page + i + 1); + canvas_draw_str(canvas, 0, 14 + i * 7, screen_buf); + char* ptr = (char*)nrf24scan->log_arr + (page + i) * LOG_REC_SIZE + view_log_arr_x; + if(view_log_arr_x == 0) { + snprintf(screen_buf, sizeof(screen_buf), "%d-", *ptr + 1); + add_to_str_hex_bytes(screen_buf, ptr + 1, 10); + } else { + screen_buf[0] = '\0'; + add_to_str_hex_bytes(screen_buf, ptr + 1, 11); + } + canvas_draw_str(canvas, 13, 14 + i * 7, screen_buf); + } + canvas_draw_str( + canvas, + 10, + 14 + (view_log_arr_idx & 7) * 7, + last_packet_send != view_log_arr_idx ? ">" : + last_packet_send_st == 0 ? "!" : + "+"); + } + } + release_mutex((ValueMutex*)ctx, plugin_state); +} + +static bool select_settings_file(Stream* stream) { + DialogsApp* dialogs = furi_record_open("dialogs"); + bool result = false; + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, SCAN_APP_PATH_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL); + browser_options.hide_ext = false; + + bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); + + furi_record_close("dialogs"); + if(ret) { + if(!file_stream_open(stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + file_stream_close(stream); + FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(path)); + } else { + FURI_LOG_D(TAG, "Open file \"%s\"", furi_string_get_cstr(path)); + strncpy( + addr_file_name, + furi_string_get_cstr(path) + sizeof(SCAN_APP_PATH_FOLDER), + sizeof(addr_file_name)); + result = true; + } + } + furi_string_free(path); + return result; +} + +static bool load_settings_file(Stream* file_stream) { + size_t file_size = 0; + char* file_buf; + bool loaded = false; + file_size = stream_size(file_stream); + if(file_size == (size_t)0) { + FURI_LOG_D(TAG, "load failed. file_size: %d", file_size); + return loaded; + } + file_buf = malloc(file_size); + memset(file_buf, 0, file_size); + if(stream_read(file_stream, (uint8_t*)file_buf, file_size) == file_size) { + FURI_LOG_D(TAG, "Loading settings file"); + char* line_ptr = file_buf; + int16_t line_num = 0; + memset((uint8_t*)&addrs, 0, sizeof(addrs)); + while(line_ptr && line_ptr - file_buf < file_size) { + char* end_ptr = strstr((char*)line_ptr, "\n"); + if(end_ptr == NULL) + end_ptr = file_buf + file_size; + else + *end_ptr = '\0'; + FURI_LOG_D(TAG, " L#%d: [%d]%s", line_num, end_ptr - line_ptr, line_ptr); + if(*line_ptr == '\r') { + line_ptr = end_ptr + 1; + continue; + } + if(end_ptr - line_ptr >= LOG_REC_SIZE * 2) { // data + if(log_arr_idx < MAX_LOG_RECORDS) { + ConvertHexToArray( + line_ptr, nrf24scan->log_arr + log_arr_idx * LOG_REC_SIZE, LOG_REC_SIZE); + log_arr_idx++; + clear_log(); + } + } else if(addrs.addr_count) { + ConvertHexToArray( + line_ptr, + addrs.addr_count == 1 ? &addrs.addr_P1[0] : + addrs.addr_count == 2 ? &addrs.addr_P2 : + addrs.addr_count == 3 ? &addrs.addr_P3 : + addrs.addr_count == 4 ? &addrs.addr_P4 : + &addrs.addr_P5, + addrs.addr_count == 1 ? 5 : 1); + FURI_LOG_D(TAG, " +Addr_LSB: %s", line_ptr); + if(++addrs.addr_count == MAX_ADDR) break; + } else if(end_ptr - line_ptr < 4) { // Rate or Channel + if(line_num == 0) { // 1st line - Rate + NRF_rate = atoi(line_ptr); + FURI_LOG_D(TAG, " Rate: %d", NRF_rate); + } else if(line_num == 1) { // second line - Channel + NRF_channel = atoi(line_ptr); + FURI_LOG_D(TAG, " Ch: %d", NRF_channel); + } + } else if(end_ptr - line_ptr <= 5 * 2 + 1) { // addresses + addrs.addr_len = ConvertHexToArray(line_ptr, addrs.addr_P0, 5); + FURI_LOG_D( + TAG, + " +Addr(%d): %02X%02X%02X...", + addrs.addr_len, + addrs.addr_P0[0], + addrs.addr_P0[1], + addrs.addr_P0[2]); + loaded = true; + if(++addrs.addr_count == MAX_ADDR) break; + } + line_ptr = end_ptr + 1; + line_num++; + } + } else { + FURI_LOG_D(TAG, "load failed. file size: %d", file_size); + } + free(file_buf); + return loaded; +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void start_scanning() { + uint8_t addr[5]; + uint8_t erx_addr = (1 << 0); // Enable RX_P0 + if(addrs.addr_count == 0) return; + nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x00); // Stop nRF + nrf24_write_reg(nrf24_HANDLE, REG_STATUS, 0x70); // clear interrupts + nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, 0x0); // disable shockburst + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x00); // Disable Shockburst + nrf24_write_reg( + nrf24_HANDLE, + REG_FEATURE, + 0x01); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, Disable Dynamic Payload Length + nrf24_set_maclen(nrf24_HANDLE, addrs.addr_len); + for(int i = 0; i < addrs.addr_len; i++) addr[i] = addrs.addr_P0[addrs.addr_len - i - 1]; + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P0, &addr[0], addrs.addr_len); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P0, 32); + if(addrs.addr_count > 1) { + for(int i = 0; i < addrs.addr_len; i++) addr[i] = addrs.addr_P1[addrs.addr_len - i - 1]; + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P1, &addr[0], addrs.addr_len); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P1, 32); + erx_addr |= (1 << 1); // Enable RX_P1 + } else + nrf24_write_reg(nrf24_HANDLE, RX_PW_P1, 0); + if(addrs.addr_count > 2) { + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P2, &addrs.addr_P2, 1); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P2, 32); + erx_addr |= (1 << 2); // Enable RX_P2 + } else + nrf24_write_reg(nrf24_HANDLE, RX_PW_P2, 0); + if(addrs.addr_count > 3) { + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P3, &addrs.addr_P3, 1); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P3, 32); + erx_addr |= (1 << 3); // Enable RX_P3 + } else + nrf24_write_reg(nrf24_HANDLE, RX_PW_P3, 0); + if(addrs.addr_count > 4) { + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P4, &addrs.addr_P4, 1); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P4, 32); + erx_addr |= (1 << 4); // Enable RX_P4 + } else + nrf24_write_reg(nrf24_HANDLE, RX_PW_P4, 0); + if(addrs.addr_count > 5) { + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P5, &addrs.addr_P5, 1); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P5, 32); + erx_addr |= (1 << 5); // Enable RX_P5 + } else + nrf24_write_reg(nrf24_HANDLE, RX_PW_P5, 0); + nrf24_write_reg(nrf24_HANDLE, REG_STATUS, 0x50); // clear RX_DR, MAX_RT. + nrf24_set_idle(nrf24_HANDLE); + nrf24_flush_rx(nrf24_HANDLE); + nrf24_flush_tx(nrf24_HANDLE); + nrf24_write_reg(nrf24_HANDLE, REG_EN_RXADDR, erx_addr); + nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel); + nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, NRF_rate); + // prime for RX, no checksum + nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x03); // PWR_UP and PRIM_RX, disable AA and CRC + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(100); + + start_time = furi_get_tick(); + FURI_LOG_D(TAG, "Start scan: Ch=%d Rate=%d", NRF_channel, NRF_rate); +} + +bool nrf24_read_newpacket() { + if(nrf24scan->log_arr == NULL) return false; + bool found = false; + uint8_t packetsize; + uint8_t packet[32] = {0}; + uint8_t status = nrf24_rxpacket(nrf24_HANDLE, packet, &packetsize, true); + if(status & 0x40) { + uint8_t* ptr = nrf24scan->log_arr + log_arr_idx * LOG_REC_SIZE; + *ptr++ = (status >> 1) & 7; // pipe # + memcpy(ptr, packet, packetsize); + if(packetsize < LOG_REC_SIZE) memset(ptr + packetsize, 0, LOG_REC_SIZE - packetsize); + if(log_arr_idx < MAX_LOG_RECORDS - 1) { + log_arr_idx++; + } else { + if(log_to_file > 0) { + write_to_log_file(nrf24scan->storage); + log_arr_idx = 0; + } else { + memmove( + nrf24scan->log_arr, + nrf24scan->log_arr + LOG_REC_SIZE, + log_arr_idx * LOG_REC_SIZE); + } + } + FURI_LOG_D(TAG, "Found packet #%d pipe %d", log_arr_idx, (status >> 1) & 7); + found = true; + } + return found; +} + +bool nrf24_send_packet() { + if(log_arr_idx == 0) return false; + last_packet_send_st = nrf24_txpacket( + nrf24_HANDLE, nrf24scan->log_arr + view_log_arr_idx * LOG_REC_SIZE + 1, 32, false); + last_packet_send = view_log_arr_idx; + return last_packet_send_st; +} + +void allocate_log_array() { + nrf24scan->log_arr = malloc(LOG_REC_SIZE * MAX_LOG_RECORDS); + if(nrf24scan->log_arr == NULL) { + FURI_LOG_E(TAG, "Not enouch memory: %d", LOG_REC_SIZE * MAX_LOG_RECORDS); + strcpy(addr_file_name, "MEMORY LOW!"); + } + clear_log(); +} + +int32_t nrf24scan_app(void* p) { + UNUSED(p); + nrf24scan = malloc(sizeof(Nrf24Scan)); + nrf24scan->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + nrf24scan->plugin_state = malloc(sizeof(PluginState)); + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, nrf24scan->plugin_state, sizeof(PluginState))) { + furi_message_queue_free(nrf24scan->event_queue); + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + free(nrf24scan->plugin_state); + return 255; + } + memset((uint8_t*)&addrs, 0, sizeof(addrs)); + nrf24_init(); + + // Set system callbacks + nrf24scan->view_port = view_port_alloc(); + view_port_draw_callback_set(nrf24scan->view_port, render_callback, &state_mutex); + view_port_input_callback_set(nrf24scan->view_port, input_callback, nrf24scan->event_queue); + + // Open GUI and register view_port + nrf24scan->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(nrf24scan->gui, nrf24scan->view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + nrf24scan->storage = furi_record_open(RECORD_STORAGE); + storage_common_mkdir(nrf24scan->storage, SCAN_APP_PATH_FOLDER); + Stream* file_stream = file_stream_alloc(nrf24scan->storage); + FuriString* path = furi_string_alloc(); + furi_string_set(path, SCAN_APP_PATH_FOLDER); + furi_string_cat(path, "/"); + furi_string_cat(path, ADDR_FILENAME); + if(file_stream_open(file_stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + if(load_settings_file(file_stream)) + strcpy(addr_file_name, ADDR_FILENAME); + else + strcpy(addr_file_name, "LOAD ERROR"); + } else { + strcpy(addr_file_name, "NONE"); + } + file_stream_close(file_stream); + furi_string_free(path); + stream_free(file_stream); + allocate_log_array(); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(nrf24scan->event_queue, &event, 100); + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + switch(event.input.key) { + case InputKeyUp: + if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + if(menu_selected > 0) + menu_selected--; + else + menu_selected = menu_selected_max; + } else { + if(view_log_arr_idx > 0) view_log_arr_idx--; + } + } + break; + case InputKeyDown: + if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + if(menu_selected < menu_selected_max) + menu_selected++; + else + menu_selected = 0; + } else { + if(view_log_arr_idx < log_arr_idx - 1) view_log_arr_idx++; + } + } + break; + case InputKeyRight: + if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + switch(menu_selected) { + case 1: + NRF_channel += event.input.type == InputTypeRepeat ? 10 : 1; + if(NRF_channel > MAX_CHANNEL) NRF_channel = 0; + break; + case 2: + NRF_rate++; + if(NRF_rate > 2) NRF_rate = 0; + break; + case 3: + find_channel_period += event.input.type == InputTypeRepeat ? 10 : + 1; + break; + case 4: + if(++log_to_file > 2) log_to_file = -1; + break; + } + } else { + if(view_log_arr_x < VIEW_LOG_MAX_X) view_log_arr_x++; + } + } + break; + case InputKeyLeft: + if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + switch(menu_selected) { + case 1: + NRF_channel -= event.input.type == InputTypeRepeat ? 10 : 1; + if(NRF_channel > MAX_CHANNEL) NRF_channel = MAX_CHANNEL; + break; + case 2: + NRF_rate--; + if(NRF_rate > 2) NRF_rate = 2; + break; + case 3: + find_channel_period -= event.input.type == InputTypeRepeat ? 10 : + 1; + if(find_channel_period < 0) find_channel_period = 0; + break; + case 4: + if(--log_to_file < -1) log_to_file = 2; + break; + } + } else { + if(view_log_arr_x > 0) view_log_arr_x--; + } + } + break; + case InputKeyOk: + if(event.input.type == InputTypePress) { + if(what_doing == 0) { + if(menu_selected == 0) { // File + file_stream = file_stream_alloc(nrf24scan->storage); + if(select_settings_file(file_stream)) { + if(!load_settings_file(file_stream)) { + strcpy(addr_file_name, "LOAD ERROR"); + } else { + if(log_to_file > 0) write_to_log_file(nrf24scan->storage); + clear_log(); + } + file_stream_close(file_stream); + } + stream_free(file_stream); + } else if(menu_selected == 5) { + what_doing = !what_doing; + if(what_doing) { + if(log_to_file == 2 || log_to_file == -1) clear_log(); + start_scanning(); + } else + nrf24_set_idle(nrf24_HANDLE); + } + } else { // Send + nrf24_send_packet(); + } + } else if(event.input.type == InputTypeLong) { + what_doing = !what_doing; + if(what_doing) { + if(log_to_file == 2 || log_to_file == -1) clear_log(); + start_scanning(); + } else + nrf24_set_idle(nrf24_HANDLE); + } + break; + case InputKeyBack: + if(event.input.type == InputTypeLong) + processing = false; + else + what_doing = 0; + nrf24_set_idle(nrf24_HANDLE); + break; + default: + break; + } + } + } + + if(what_doing) { + nrf24_read_newpacket(); + if(find_channel_period && + furi_get_tick() - start_time >= (uint32_t)find_channel_period * 1000UL) { + if(++NRF_channel > MAX_CHANNEL) NRF_channel = 0; + start_scanning(); + } + } + + view_port_update(nrf24scan->view_port); + release_mutex(&state_mutex, plugin_state); + } + + nrf24_deinit(); + view_port_enabled_set(nrf24scan->view_port, false); + gui_remove_view_port(nrf24scan->gui, nrf24scan->view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_STORAGE); + view_port_free(nrf24scan->view_port); + furi_message_queue_free(nrf24scan->event_queue); + free(nrf24scan->plugin_state); + if(nrf24scan->log_arr) free(nrf24scan->log_arr); + free(nrf24scan); + return 0; +} diff --git a/applications/plugins/nrf24scan/nrf24scan.h b/applications/plugins/nrf24scan/nrf24scan.h new file mode 100644 index 000000000..c4e2b5d6f --- /dev/null +++ b/applications/plugins/nrf24scan/nrf24scan.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + int x; + int y; +} PluginState; + +typedef struct { + Gui* gui; + FuriMessageQueue* event_queue; + PluginState* plugin_state; + ViewPort* view_port; + Storage* storage; + uint8_t* log_arr; +} Nrf24Scan; diff --git a/applications/plugins/nrf24scan/nrf24scan_10px.png b/applications/plugins/nrf24scan/nrf24scan_10px.png new file mode 100644 index 000000000..348b35eca Binary files /dev/null and b/applications/plugins/nrf24scan/nrf24scan_10px.png differ diff --git a/applications/plugins/nrfsniff/application.fam b/applications/plugins/nrfsniff/application.fam index b5f7c1d79..e55ab3e9c 100644 --- a/applications/plugins/nrfsniff/application.fam +++ b/applications/plugins/nrfsniff/application.fam @@ -9,4 +9,12 @@ App( order=60, fap_icon="nrfsniff_10px.png", fap_category="GPIO", + fap_private_libs=[ + Lib( + name="nrf24", + sources=[ + "nrf24.c", + ], + ), + ], ) diff --git a/applications/plugins/nrfsniff/lib/nrf24/nrf24.c b/applications/plugins/nrfsniff/lib/nrf24/nrf24.c new file mode 100644 index 000000000..8b3776445 --- /dev/null +++ b/applications/plugins/nrfsniff/lib/nrf24/nrf24.c @@ -0,0 +1,520 @@ +#include "nrf24.h" +#include +#include +#include +#include +#include + +void nrf24_init() { + furi_hal_spi_bus_handle_init(nrf24_HANDLE); + furi_hal_spi_acquire(nrf24_HANDLE); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_write(nrf24_CE_PIN, false); +} + +void nrf24_deinit() { + furi_hal_spi_release(nrf24_HANDLE); + furi_hal_spi_bus_handle_deinit(nrf24_HANDLE); + furi_hal_gpio_write(nrf24_CE_PIN, false); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void nrf24_spi_trx( + FuriHalSpiBusHandle* handle, + uint8_t* tx, + uint8_t* rx, + uint8_t size, + uint32_t timeout) { + UNUSED(timeout); + furi_hal_gpio_write(handle->cs, false); + furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT); + furi_hal_gpio_write(handle->cs, true); +} + +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { + uint8_t tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data}; + uint8_t rx[2] = {0}; + nrf24_spi_trx(handle, tx, rx, 2, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t + nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = W_REGISTER | (REGISTER_MASK & reg); + memcpy(&tx[1], data, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = R_REGISTER | (REGISTER_MASK & reg); + memset(&tx[1], 0, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + memcpy(data, &rx[1], size); + return rx[0]; +} + +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_RX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_TX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle) { + uint8_t maclen; + nrf24_read_reg(handle, REG_SETUP_AW, &maclen, 1); + maclen &= 3; + return maclen + 2; +} + +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) { + assert(maclen > 1 && maclen < 6); + uint8_t status = 0; + status = nrf24_write_reg(handle, REG_SETUP_AW, maclen - 2); + return status; +} + +uint8_t nrf24_status(FuriHalSpiBusHandle* handle) { + uint8_t status; + uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)}; + nrf24_spi_trx(handle, tx, &status, 1, nrf24_TIMEOUT); + return status; +} + +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) { + uint8_t setup = 0; + uint32_t rate = 0; + nrf24_read_reg(handle, REG_RF_SETUP, &setup, 1); + setup &= 0x28; + if(setup == 0x20) + rate = 250000; // 250kbps + else if(setup == 0x08) + rate = 2000000; // 2Mbps + else if(setup == 0x00) + rate = 1000000; // 1Mbps + + return rate; +} + +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate) { + uint8_t r6 = 0; + uint8_t status = 0; + if(!rate) rate = 2000000; + + nrf24_read_reg(handle, REG_RF_SETUP, &r6, 1); // RF_SETUP register + r6 = r6 & (~0x28); // Clear rate fields. + if(rate == 2000000) + r6 = r6 | 0x08; + else if(rate == 1000000) + r6 = r6; + else if(rate == 250000) + r6 = r6 | 0x20; + + status = nrf24_write_reg(handle, REG_RF_SETUP, r6); // Write new rate. + return status; +} + +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle) { + uint8_t channel = 0; + nrf24_read_reg(handle, REG_RF_CH, &channel, 1); + return channel; +} + +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan) { + uint8_t status; + status = nrf24_write_reg(handle, REG_RF_CH, chan); + return status; +} + +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_TX_ADDR, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle) { + uint8_t len = 0; + nrf24_read_reg(handle, RX_PW_P0, &len, 1); + return len; +} + +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { + uint8_t status = 0; + status = nrf24_write_reg(handle, RX_PW_P0, len); + return status; +} + +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full) { + uint8_t status = 0; + uint8_t size = 0; + uint8_t tx_pl_wid[] = {R_RX_PL_WID, 0}; + uint8_t rx_pl_wid[] = {0, 0}; + uint8_t tx_cmd[33] = {0}; // 32 max payload size + 1 for command + uint8_t tmp_packet[33] = {0}; + + status = nrf24_status(handle); + + if(status & 0x40) { + if(full) + size = nrf24_get_packetlen(handle); + else { + nrf24_spi_trx(handle, tx_pl_wid, rx_pl_wid, 2, nrf24_TIMEOUT); + size = rx_pl_wid[1]; + } + + tx_cmd[0] = R_RX_PAYLOAD; + nrf24_spi_trx(handle, tx_cmd, tmp_packet, size + 1, nrf24_TIMEOUT); + nrf24_write_reg(handle, REG_STATUS, 0x40); // clear bit. + memcpy(packet, &tmp_packet[1], size); + } else if(status == 0) { + nrf24_flush_rx(handle); + nrf24_write_reg(handle, REG_STATUS, 0x40); // clear bit. + } + + *packetsize = size; + return status; +} + +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) { + uint8_t status = 0; + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(tx, 0, size + 1); + memset(rx, 0, size + 1); + + if(!ack) + tx[0] = W_TX_PAYLOAD_NOACK; + else + tx[0] = W_TX_PAYLOAD; + + memcpy(&tx[1], payload, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + nrf24_set_tx_mode(handle); + + while(!(status & (TX_DS | MAX_RT))) status = nrf24_status(handle); + + if(status & MAX_RT) nrf24_flush_tx(handle); + + nrf24_set_idle(handle); + nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); + return status & TX_DS; +} + +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg = cfg | 2; + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_delay_ms(5000); + return status; +} + +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfc; // clear bottom two bits to power down the radio + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //nr204_write_reg(handle, REG_EN_RXADDR, 0x0); + furi_hal_gpio_write(nrf24_CE_PIN, false); + return status; +} + +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + //status = nrf24_write_reg(handle, REG_CONFIG, 0x0F); // enable 2-byte CRC, PWR_UP, and PRIM_RX + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg |= 0x03; // PWR_UP, and PRIM_RX + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //nr204_write_reg(REG_EN_RXADDR, 0x03) // Set RX Pipe 0 and 1 + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(2000); + return status; +} + +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + furi_hal_gpio_write(nrf24_CE_PIN, false); + nrf24_write_reg(handle, REG_STATUS, 0x30); + //status = nrf24_write_reg(handle, REG_CONFIG, 0x0E); // enable 2-byte CRC, PWR_UP + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfe; // disable PRIM_RX + cfg |= 0x02; // PWR_UP + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(2); + return status; +} + +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa) { + assert(channel <= 125); + assert(rate == 1 || rate == 2); + if(rate == 2) + rate = 8; // 2Mbps + else + rate = 0; // 1Mbps + + nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF + nrf24_set_idle(handle); + nrf24_write_reg(handle, REG_STATUS, 0x1c); // clear interrupts + if(disable_aa) + nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst + else + nrf24_write_reg(handle, REG_EN_AA, 0x1F); // Enable Shockburst + + nrf24_write_reg(handle, REG_DYNPD, 0x3F); // enable dynamic payload length on all pipes + if(noack) + nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack + else { + nrf24_write_reg(handle, REG_CONFIG, 0x0C); // 2 byte CRC + nrf24_write_reg(handle, REG_FEATURE, 0x07); // enable dyn payload and ack + nrf24_write_reg( + handle, REG_SETUP_RETR, 0x1f); // 15 retries for AA, 500us auto retransmit delay + } + + nrf24_set_idle(handle); + nrf24_flush_rx(handle); + nrf24_flush_tx(handle); + + if(maclen) nrf24_set_maclen(handle, maclen); + if(srcmac) nrf24_set_src_mac(handle, srcmac, maclen); + if(dstmac) nrf24_set_dst_mac(handle, dstmac, maclen); + + nrf24_write_reg(handle, REG_RF_CH, channel); + nrf24_write_reg(handle, REG_RF_SETUP, rate); + furi_delay_ms(200); +} + +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate) { + //uint8_t preamble[] = {0x55, 0x00}; // little endian + uint8_t preamble[] = {0xAA, 0x00}; // little endian + //uint8_t preamble[] = {0x00, 0x55}; // little endian + //uint8_t preamble[] = {0x00, 0xAA}; // little endian + nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF + nrf24_write_reg(handle, REG_STATUS, 0x1c); // clear interrupts + nrf24_write_reg(handle, REG_DYNPD, 0x0); // disable shockburst + nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst + nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack + nrf24_set_maclen(handle, 2); // shortest address + nrf24_set_src_mac(handle, preamble, 2); // set src mac to preamble bits to catch everything + nrf24_set_packetlen(handle, 32); // set max packet length + nrf24_set_idle(handle); + nrf24_flush_rx(handle); + nrf24_flush_tx(handle); + nrf24_write_reg(handle, REG_RF_CH, channel); + nrf24_write_reg(handle, REG_RF_SETUP, rate); + + // prime for RX, no checksum + nrf24_write_reg(handle, REG_CONFIG, 0x03); // PWR_UP and PRIM_RX, disable AA and CRC + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(100); +} + +void hexlify(uint8_t* in, uint8_t size, char* out) { + memset(out, 0, size * 2); + for(int i = 0; i < size; i++) + snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]); +} + +uint64_t bytes_to_int64(uint8_t* bytes, uint8_t size, bool bigendian) { + uint64_t ret = 0; + for(int i = 0; i < size; i++) + if(bigendian) + ret |= bytes[i] << ((size - 1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 8; i++) { + if(bigendian) + out[i] = (val >> ((7 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian) { + uint32_t ret = 0; + for(int i = 0; i < 4; i++) + if(bigendian) + ret |= bytes[i] << ((3 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 4; i++) { + if(bigendian) + out[i] = (val >> ((3 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint64_t bytes_to_int16(uint8_t* bytes, bool bigendian) { + uint16_t ret = 0; + for(int i = 0; i < 2; i++) + if(bigendian) + ret |= bytes[i] << ((1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 2; i++) { + if(bigendian) + out[i] = (val >> ((1 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +// handle iffyness with preamble processing sometimes being a bit (literally) off +void alt_address_old(uint8_t* packet, uint8_t* altaddr) { + uint8_t macmess_hi_b[4]; + uint8_t macmess_lo_b[2]; + uint32_t macmess_hi; + uint16_t macmess_lo; + uint8_t preserved; + + // get first 6 bytes into 32-bit and 16-bit variables + memcpy(macmess_hi_b, packet, 4); + memcpy(macmess_lo_b, packet + 4, 2); + + macmess_hi = bytes_to_int32(macmess_hi_b, true); + + //preserve least 7 bits from hi that will be shifted down to lo + preserved = macmess_hi & 0x7f; + macmess_hi >>= 7; + + macmess_lo = bytes_to_int16(macmess_lo_b, true); + macmess_lo >>= 7; + macmess_lo = (preserved << 9) | macmess_lo; + int32_to_bytes(macmess_hi, macmess_hi_b, true); + int16_to_bytes(macmess_lo, macmess_lo_b, true); + memcpy(altaddr, &macmess_hi_b[1], 3); + memcpy(altaddr + 3, macmess_lo_b, 2); +} + +bool validate_address(uint8_t* addr) { + uint8_t bad[][3] = {{0x55, 0x55}, {0xAA, 0xAA}, {0x00, 0x00}, {0xFF, 0xFF}}; + for(int i = 0; i < 4; i++) + for(int j = 0; j < 2; j++) + if(!memcmp(addr + j * 2, bad[i], 2)) return false; + + return true; +} + +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address) { + bool found = false; + uint8_t packet[32] = {0}; + uint8_t packetsize; + //char printit[65]; + uint8_t status = 0; + status = nrf24_rxpacket(handle, packet, &packetsize, true); + if(status & 0x40) { + if(validate_address(packet)) { + for(int i = 0; i < maclen; i++) address[i] = packet[maclen - 1 - i]; + + /* + alt_address(packet, packet); + + for(i = 0; i < maclen; i++) + address[i + 5] = packet[maclen - 1 - i]; + */ + + //memcpy(address, packet, maclen); + //hexlify(packet, packetsize, printit); + found = true; + } + } + + return found; +} + +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit) { + uint8_t ping_packet[] = {0x0f, 0x0f, 0x0f, 0x0f}; // this can be anything, we just need an ack + uint8_t ch = max_channel + 1; // means fail + nrf24_configure(handle, rate, srcmac, dstmac, maclen, 2, false, false); + for(ch = min_channel; ch <= max_channel + 1; ch++) { + nrf24_write_reg(handle, REG_RF_CH, ch); + if(nrf24_txpacket(handle, ping_packet, 4, true)) break; + } + + if(autoinit) { + FURI_LOG_D("nrf24", "initializing radio for channel %d", ch); + nrf24_configure(handle, rate, srcmac, dstmac, maclen, ch, false, false); + return ch; + } + + return ch; +} \ No newline at end of file diff --git a/applications/plugins/nrfsniff/lib/nrf24/nrf24.h b/applications/plugins/nrfsniff/lib/nrf24/nrf24.h new file mode 100644 index 000000000..3cfcfe089 --- /dev/null +++ b/applications/plugins/nrfsniff/lib/nrf24/nrf24.h @@ -0,0 +1,366 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_TX_PAYLOAD_NOACK 0xB0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define RF24_NOP 0xFF + +#define REG_CONFIG 0x00 +#define REG_EN_AA 0x01 +#define REG_EN_RXADDR 0x02 +#define REG_SETUP_AW 0x03 +#define REG_SETUP_RETR 0x04 +#define REG_DYNPD 0x1C +#define REG_FEATURE 0x1D +#define REG_RF_SETUP 0x06 +#define REG_STATUS 0x07 +#define REG_RX_ADDR_P0 0x0A +#define REG_RF_CH 0x05 +#define REG_TX_ADDR 0x10 + +#define RX_PW_P0 0x11 +#define TX_DS 0x20 +#define MAX_RT 0x10 + +#define nrf24_TIMEOUT 500 +#define nrf24_CE_PIN &gpio_ext_pb2 +#define nrf24_HANDLE &furi_hal_spi_bus_handle_external + +/* Low level API */ + +/** Write device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * + * @return device status + */ +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); + +/** Write buffer to device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * @param size - size of data to write + * + * @return device status + */ +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Read device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param[out] data - pointer to data + * + * @return device status + */ +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Power up the radio for operation + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle); + +/** Power down the radio + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle); + +/** Sets the radio to RX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle); + +/** Sets the radio to TX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle); + +/*=============================================================================================================*/ + +/* High level API */ + +/** Must call this before using any other nrf24 API + * + */ +void nrf24_init(); + +/** Must call this when we end using nrf24 device + * + */ +void nrf24_deinit(); + +/** Send flush rx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle); + +/** Send flush tx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle); + +/** Gets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return packet length in data pipe 0 + */ +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle); + +/** Sets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * @param len - length to set + * + * @return device status + */ +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len); + +/** Gets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return MAC address length + */ +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle); + +/** Sets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length to set MAC address to, must be greater than 1 and less than 6 + * + * @return MAC address length + */ +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen); + +/** Gets the current status flags from the STATUS register + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return status flags + */ +uint8_t nrf24_status(FuriHalSpiBusHandle* handle); + +/** Gets the current transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return transfer rate in bps + */ +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle); + +/** Sets the transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * @param rate - the transfer rate in bps + * + * @return device status + */ +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate); + +/** Gets the current channel + * In nrf24, the channel number is multiplied times 1MHz and added to 2400MHz to get the frequency + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return channel + */ +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle); + +/** Sets the channel + * + * @param handle - pointer to FuriHalSpiHandle + * @param frequency - the frequency in hertz + * + * @return device status + */ +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan); + +/** Gets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Gets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Reads RX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] packet - the packet contents + * @param[out] packetsize - size of the received packet + * @param full - boolean set to true, packet length is determined by RX_PW_P0 register, false it is determined by dynamic payload length command + * + * @return device status + */ +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full); + +/** Sends TX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param packet - the packet contents + * @param size - packet size + * @param ack - boolean to determine whether an ACK is required for the packet or not + * + * @return device status + */ +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack); + +/** Configure the radio + * This is not comprehensive, but covers a lot of the common configuration options that may be changed + * @param handle - pointer to FuriHalSpiHandle + * @param rate - transfer rate in Mbps (1 or 2) + * @param srcmac - source mac address + * @param dstmac - destination mac address + * @param maclen - length of mac address + * @param channel - channel to tune to + * @param noack - if true, disable auto-acknowledge + * @param disable_aa - if true, disable ShockBurst + * + */ +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa); + +/** Configures the radio for "promiscuous mode" and primes it for rx + * This is not an actual mode of the nrf24, but this function exploits a few bugs in the chip that allows it to act as if it were. + * See http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html for details. + * @param handle - pointer to FuriHalSpiHandle + * @param channel - channel to tune to + * @param rate - transfer rate in Mbps (1 or 2) + */ +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate); + +/** Listens for a packet and returns first possible address sniffed + * Call this only after calling nrf24_init_promisc_mode + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length of target mac address + * @param[out] addresses - sniffed address + * + * @return success + */ +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address); + +/** Sends ping packet on each channel for designated tx mac looking for ack + * + * @param handle - pointer to FuriHalSpiHandle + * @param srcmac - source address + * @param dstmac - destination address + * @param maclen - length of address + * @param rate - transfer rate in Mbps (1 or 2) + * @param min_channel - channel to start with + * @param max_channel - channel to end at + * @param autoinit - if true, automatically configure radio for this channel + * + * @return channel that the address is listening on, if this value is above the max_channel param, it failed + */ +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit); + +/** Converts 64 bit value into uint8_t array + * @param val - 64-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian); + +/** Converts 32 bit value into uint8_t array + * @param val - 32-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian); + +/** Converts uint8_t array into 32 bit value + * @param bytes - uint8_t array + * @param bigendian - if true, convert as big endian, otherwise little endian + * + * @return 32-bit value + */ +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/plugins/nrfsniff/nrfsniff.c b/applications/plugins/nrfsniff/nrfsniff.c index 2ad9287b9..ce2836152 100644 --- a/applications/plugins/nrfsniff/nrfsniff.c +++ b/applications/plugins/nrfsniff/nrfsniff.c @@ -400,6 +400,8 @@ int32_t nrfsniff_app(void* p) { case InputKeyBack: if(event.input.type == InputTypeLong) processing = false; break; + default: + break; } } } @@ -444,7 +446,7 @@ int32_t nrfsniff_app(void* p) { sample_time = DEFAULT_SAMPLE_TIME; target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps) sniffing_state = false; - furi_hal_spi_release(nrf24_HANDLE); + nrf24_deinit(); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); diff --git a/applications/plugins/ocarina/README.md b/applications/plugins/ocarina/README.md new file mode 100644 index 000000000..1fcfd00fa --- /dev/null +++ b/applications/plugins/ocarina/README.md @@ -0,0 +1,4 @@ +# flipperzero-ocarina +A basic Ocarina (of Time) for the Flipper Zero. + +Controls are the same as the N64 version of the Ocarina of Time, the Ok button takes the place of the A button diff --git a/applications/plugins/ocarina/application.fam b/applications/plugins/ocarina/application.fam new file mode 100644 index 000000000..192cb2f16 --- /dev/null +++ b/applications/plugins/ocarina/application.fam @@ -0,0 +1,13 @@ +App( + appid="Ocarina", + name="Ocarina", + apptype=FlipperAppType.EXTERNAL, + entry_point="ocarina_app", + cdefines=["APP_OCARINA"], + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_icon="icons/music_10px.png", + fap_category="Music", + fap_icon_assets="icons", +) diff --git a/applications/plugins/ocarina/icons/music_10px.png b/applications/plugins/ocarina/icons/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/ocarina/icons/music_10px.png differ diff --git a/applications/plugins/ocarina/ocarina.c b/applications/plugins/ocarina/ocarina.c new file mode 100644 index 000000000..013f81ab8 --- /dev/null +++ b/applications/plugins/ocarina/ocarina.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include + +#define NOTE_UP 587.33f +#define NOTE_LEFT 493.88f +#define NOTE_RIGHT 440.00f +#define NOTE_DOWN 349.23 +#define NOTE_OK 293.66f + +typedef struct { + FuriMutex* model_mutex; + + FuriMessageQueue* event_queue; + + ViewPort* view_port; + Gui* gui; +} Ocarina; + +void draw_callback(Canvas* canvas, void* ctx) { + Ocarina* ocarina = ctx; + furi_check(furi_mutex_acquire(ocarina->model_mutex, FuriWaitForever) == FuriStatusOk); + + //canvas_draw_box(canvas, ocarina->model->x, ocarina->model->y, 4, 4); + canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_draw_str(canvas, 50, 10, "Ocarina"); + canvas_draw_str(canvas, 30, 20, "OK button for A"); + + furi_mutex_release(ocarina->model_mutex); +} + +void input_callback(InputEvent* input, void* ctx) { + Ocarina* ocarina = ctx; + // Puts input onto event queue with priority 0, and waits until completion. + furi_message_queue_put(ocarina->event_queue, input, FuriWaitForever); +} + +Ocarina* ocarina_alloc() { + Ocarina* instance = malloc(sizeof(Ocarina)); + + instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + instance->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, draw_callback, instance); + view_port_input_callback_set(instance->view_port, input_callback, instance); + + instance->gui = furi_record_open("gui"); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + return instance; +} + +void ocarina_free(Ocarina* instance) { + view_port_enabled_set(instance->view_port, false); // Disabsles our ViewPort + gui_remove_view_port(instance->gui, instance->view_port); // Removes our ViewPort from the Gui + furi_record_close("gui"); // Closes the gui record + view_port_free(instance->view_port); // Frees memory allocated by view_port_alloc + furi_message_queue_free(instance->event_queue); + + furi_mutex_free(instance->model_mutex); + + furi_hal_speaker_stop(); + + free(instance); +} + +int32_t ocarina_app(void* p) { + UNUSED(p); + + Ocarina* ocarina = ocarina_alloc(); + + InputEvent event; + for(bool processing = true; processing;) { + // Pops a message off the queue and stores it in `event`. + // No message priority denoted by NULL, and 100 ticks of timeout. + FuriStatus status = furi_message_queue_get(ocarina->event_queue, &event, 100); + furi_check(furi_mutex_acquire(ocarina->model_mutex, FuriWaitForever) == FuriStatusOk); + + float volume = 1.0f; + if(status == FuriStatusOk) { + if(event.type == InputTypePress) { + switch(event.key) { + case InputKeyUp: + furi_hal_speaker_start(NOTE_UP, volume); + break; + case InputKeyDown: + furi_hal_speaker_start(NOTE_DOWN, volume); + break; + case InputKeyLeft: + furi_hal_speaker_start(NOTE_LEFT, volume); + break; + case InputKeyRight: + furi_hal_speaker_start(NOTE_RIGHT, volume); + break; + case InputKeyOk: + furi_hal_speaker_start(NOTE_OK, volume); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } else if(event.type == InputTypeRelease) { + furi_hal_speaker_stop(); + } + } + + furi_mutex_release(ocarina->model_mutex); + view_port_update(ocarina->view_port); // signals our draw callback + } + ocarina_free(ocarina); + return 0; +} diff --git a/applications/plugins/passgen/LICENSE b/applications/plugins/passgen/LICENSE new file mode 100644 index 000000000..85e363072 --- /dev/null +++ b/applications/plugins/passgen/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Skurydin Alexey + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/passgen/README.md b/applications/plugins/passgen/README.md new file mode 100644 index 000000000..fbeb41680 --- /dev/null +++ b/applications/plugins/passgen/README.md @@ -0,0 +1,4 @@ +# flipper_passgen +This is a simple Password Generator plugin (**fap**) for the [Flipper Zero](https://www.flipperzero.one). + +![preview](images/preview.png) \ No newline at end of file diff --git a/applications/plugins/passgen/application.fam b/applications/plugins/passgen/application.fam new file mode 100644 index 000000000..89b7b9d33 --- /dev/null +++ b/applications/plugins/passgen/application.fam @@ -0,0 +1,12 @@ +App( + appid="passgen", + name="Password Generator", + apptype=FlipperAppType.PLUGIN, + entry_point="passgenapp", + requires=[ + "gui", + ], + fap_category="Tools", + fap_icon="icons/passgen_icon.png", + fap_icon_assets="icons", +) \ No newline at end of file diff --git a/applications/plugins/passgen/icons/Horizontal_arrow_9x7.png b/applications/plugins/passgen/icons/Horizontal_arrow_9x7.png new file mode 100644 index 000000000..caca88718 Binary files /dev/null and b/applications/plugins/passgen/icons/Horizontal_arrow_9x7.png differ diff --git a/applications/plugins/passgen/icons/Ok_btn_9x9.png b/applications/plugins/passgen/icons/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/passgen/icons/Ok_btn_9x9.png differ diff --git a/applications/plugins/passgen/icons/Pin_back_arrow_10x8.png b/applications/plugins/passgen/icons/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/applications/plugins/passgen/icons/Pin_back_arrow_10x8.png differ diff --git a/applications/plugins/passgen/icons/Vertical_arrow_7x9.png b/applications/plugins/passgen/icons/Vertical_arrow_7x9.png new file mode 100644 index 000000000..b889fc8f3 Binary files /dev/null and b/applications/plugins/passgen/icons/Vertical_arrow_7x9.png differ diff --git a/applications/plugins/passgen/icons/passgen_icon.png b/applications/plugins/passgen/icons/passgen_icon.png new file mode 100644 index 000000000..1ed4f77fc Binary files /dev/null and b/applications/plugins/passgen/icons/passgen_icon.png differ diff --git a/applications/plugins/passgen/images/preview.png b/applications/plugins/passgen/images/preview.png new file mode 100644 index 000000000..3224b0a8d Binary files /dev/null and b/applications/plugins/passgen/images/preview.png differ diff --git a/applications/plugins/passgen/passgen.c b/applications/plugins/passgen/passgen.c new file mode 100644 index 000000000..12cdc10fb --- /dev/null +++ b/applications/plugins/passgen/passgen.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include + +#define PASSGEN_MAX_LENGTH 16 +#define PASSGEN_CHARACTERS_LENGTH (26 * 4) + +#define PASSGEN_DIGITS "0123456789" +#define PASSGEN_LETTERS_LOW "abcdefghijklmnopqrstuvwxyz" +#define PASSGEN_LETTERS_UP "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define PASSGEN_SPECIAL "!#$%^&*.-_" + +typedef enum PassGen_Alphabet { + Digits = 1, + Lowercase = 2, + + Uppercase = 4, + Special = 8, + + DigitsLower = Digits | Lowercase, + DigitsAllLetters = Digits | Lowercase | Uppercase, + Mixed = DigitsAllLetters | Special +} PassGen_Alphabet; + +const int AlphabetLevels[] = {Digits, Lowercase, DigitsLower, DigitsAllLetters, Mixed}; +const char* AlphabetLevelNames[] = {"1234", "abcd", "ab12", "Ab12", "Ab1#"}; +const int AlphabetLevelsCount = sizeof(AlphabetLevels) / sizeof(int); + +const NotificationSequence PassGen_Alert_vibro = { + &message_vibro_on, + &message_blue_255, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +typedef struct { + FuriMessageQueue* input_queue; + ViewPort* view_port; + Gui* gui; + FuriMutex** mutex; + NotificationApp* notify; + char password[PASSGEN_MAX_LENGTH + 1]; + char alphabet[PASSGEN_CHARACTERS_LENGTH + 1]; + int length; + int level; +} PassGen; + +void state_free(PassGen* app) { + gui_remove_view_port(app->gui, app->view_port); + furi_record_close(RECORD_GUI); + view_port_free(app->view_port); + furi_message_queue_free(app->input_queue); + furi_mutex_free(app->mutex); + furi_record_close(RECORD_NOTIFICATION); + free(app); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + PassGen* app = ctx; + if(input_event->type == InputTypeShort) { + furi_message_queue_put(app->input_queue, input_event, 0); + } +} + +static void render_callback(Canvas* canvas, void* ctx) { + char str_length[8]; + PassGen* app = ctx; + furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk); + + canvas_clear(canvas); + canvas_draw_box(canvas, 0, 0, 128, 14); + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 11, "Password Generator"); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignCenter, app->password); + + // Navigation menu: + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 96, 52, &I_Pin_back_arrow_10x8); + canvas_draw_str(canvas, 108, 60, "Exit"); + + canvas_draw_icon(canvas, 54, 52, &I_Vertical_arrow_7x9); + canvas_draw_str(canvas, 64, 60, AlphabetLevelNames[app->level]); + + snprintf(str_length, sizeof(str_length), "Len: %d", app->length); + canvas_draw_icon(canvas, 4, 53, &I_Horizontal_arrow_9x7); + canvas_draw_str(canvas, 15, 60, str_length); + + furi_mutex_release(app->mutex); +} + +void build_alphabet(PassGen* app) { + PassGen_Alphabet mode = AlphabetLevels[app->level]; + app->alphabet[0] = '\0'; + if((mode & Digits) != 0) strcat(app->alphabet, PASSGEN_DIGITS); + if((mode & Lowercase) != 0) strcat(app->alphabet, PASSGEN_LETTERS_LOW); + if((mode & Uppercase) != 0) strcat(app->alphabet, PASSGEN_LETTERS_UP); + if((mode & Special) != 0) strcat(app->alphabet, PASSGEN_SPECIAL); +} + +PassGen* state_init() { + PassGen* app = malloc(sizeof(PassGen)); + app->length = 8; + app->level = 2; + build_alphabet(app); + app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + app->view_port = view_port_alloc(); + app->gui = furi_record_open(RECORD_GUI); + app->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + view_port_input_callback_set(app->view_port, input_callback, app); + view_port_draw_callback_set(app->view_port, render_callback, app); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + app->notify = furi_record_open(RECORD_NOTIFICATION); + + return app; +} + +void generate(PassGen* app) { + int hi = strlen(app->alphabet); + for(int i = 0; i < app->length; i++) { + int x = rand() % hi; + app->password[i] = app->alphabet[x]; + } + app->password[app->length] = '\0'; +} + +void update_password(PassGen* app, bool vibro) { + generate(app); + + if(vibro) + notification_message(app->notify, &PassGen_Alert_vibro); + else + notification_message(app->notify, &sequence_blink_blue_100); + view_port_update(app->view_port); +} + +int32_t passgenapp(void) { + PassGen* app = state_init(); + generate(app); + + while(1) { + InputEvent input; + while(furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk) { + furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk); + + if(input.type == InputTypeShort) { + switch(input.key) { + case InputKeyBack: + furi_mutex_release(app->mutex); + state_free(app); + return 0; + case InputKeyDown: + if(app->level > 0) { + app->level--; + build_alphabet(app); + update_password(app, false); + } else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyUp: + if(app->level < AlphabetLevelsCount - 1) { + app->level++; + build_alphabet(app); + update_password(app, false); + } else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyLeft: + if(app->length > 1) { + app->length--; + update_password(app, false); + } else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyRight: + if(app->length < PASSGEN_MAX_LENGTH) { + app->length++; + update_password(app, false); + } else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyOk: + update_password(app, true); + break; + default: + break; + } + } + furi_mutex_release(app->mutex); + } + } + state_free(app); + return 0; +} diff --git a/applications/plugins/picopass/125_10px.png b/applications/plugins/picopass/125_10px.png new file mode 100644 index 000000000..ce01284a2 Binary files /dev/null and b/applications/plugins/picopass/125_10px.png differ diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam index 76b9712c4..6cec00d8d 100644 --- a/applications/plugins/picopass/application.fam +++ b/applications/plugins/picopass/application.fam @@ -9,7 +9,7 @@ App( ], stack_size=4 * 1024, order=175, - fap_icon="../../../assets/icons/Archive/125_10px.png", + fap_icon="125_10px.png", fap_category="Tools", fap_libs=["mbedtls"], fap_private_libs=[ @@ -17,4 +17,5 @@ App( name="loclass", ), ], + fap_icon_assets="icons", ) diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.c b/applications/plugins/picopass/helpers/iclass_elite_dict.c new file mode 100644 index 000000000..455eb23c1 --- /dev/null +++ b/applications/plugins/picopass/helpers/iclass_elite_dict.c @@ -0,0 +1,151 @@ +#include "iclass_elite_dict.h" + +#include +#include + +#define ICLASS_ELITE_DICT_FLIPPER_PATH EXT_PATH("picopass/assets/iclass_elite_dict.txt") +#define ICLASS_ELITE_DICT_USER_PATH EXT_PATH("picopass/assets/iclass_elite_dict_user.txt") + +#define TAG "IclassEliteDict" + +#define ICLASS_ELITE_KEY_LINE_LEN (17) +#define ICLASS_ELITE_KEY_LEN (8) + +struct IclassEliteDict { + Stream* stream; + uint32_t total_keys; +}; + +bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool dict_present = false; + if(dict_type == IclassEliteDictTypeFlipper) { + dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) == + FSE_OK; + } else if(dict_type == IclassEliteDictTypeUser) { + dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK; + } + + furi_record_close(RECORD_STORAGE); + + return dict_present; +} + +IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) { + IclassEliteDict* dict = malloc(sizeof(IclassEliteDict)); + Storage* storage = furi_record_open(RECORD_STORAGE); + dict->stream = buffered_file_stream_alloc(storage); + furi_record_close(RECORD_STORAGE); + FuriString* next_line = furi_string_alloc(); + + bool dict_loaded = false; + do { + if(dict_type == IclassEliteDictTypeFlipper) { + if(!buffered_file_stream_open( + dict->stream, ICLASS_ELITE_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(dict->stream); + break; + } + } else if(dict_type == IclassEliteDictTypeUser) { + if(!buffered_file_stream_open( + dict->stream, ICLASS_ELITE_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { + buffered_file_stream_close(dict->stream); + break; + } + } + + // Read total amount of keys + while(true) { + if(!stream_read_line(dict->stream, next_line)) break; + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; + dict->total_keys++; + } + furi_string_reset(next_line); + stream_rewind(dict->stream); + + dict_loaded = true; + FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); + } while(false); + + if(!dict_loaded) { + buffered_file_stream_close(dict->stream); + free(dict); + dict = NULL; + } + + furi_string_free(next_line); + + return dict; +} + +void iclass_elite_dict_free(IclassEliteDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + buffered_file_stream_close(dict->stream); + stream_free(dict->stream); + free(dict); +} + +uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict) { + furi_assert(dict); + + return dict->total_keys; +} + +bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + uint8_t key_byte_tmp = 0; + FuriString* next_line = furi_string_alloc(); + + bool key_read = false; + *key = 0ULL; + while(!key_read) { + if(!stream_read_line(dict->stream, next_line)) break; + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; + for(uint8_t i = 0; i < ICLASS_ELITE_KEY_LEN * 2; i += 2) { + args_char_to_hex( + furi_string_get_char(next_line, i), + furi_string_get_char(next_line, i + 1), + &key_byte_tmp); + key[i / 2] = key_byte_tmp; + } + key_read = true; + } + + furi_string_free(next_line); + return key_read; +} + +bool iclass_elite_dict_rewind(IclassEliteDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + return stream_rewind(dict->stream); +} + +bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + FuriString* key_str = furi_string_alloc(); + for(size_t i = 0; i < 6; i++) { + furi_string_cat_printf(key_str, "%02X", key[i]); + } + furi_string_cat_printf(key_str, "\n"); + + bool key_added = false; + do { + if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; + if(!stream_insert_string(dict->stream, key_str)) break; + key_added = true; + } while(false); + + furi_string_free(key_str); + return key_added; +} \ No newline at end of file diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.h b/applications/plugins/picopass/helpers/iclass_elite_dict.h new file mode 100644 index 000000000..e5ec8dfcb --- /dev/null +++ b/applications/plugins/picopass/helpers/iclass_elite_dict.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include + +typedef enum { + IclassEliteDictTypeUser, + IclassEliteDictTypeFlipper, +} IclassEliteDictType; + +typedef struct IclassEliteDict IclassEliteDict; + +bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type); + +IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type); + +void iclass_elite_dict_free(IclassEliteDict* dict); + +uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict); + +bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key); + +bool iclass_elite_dict_rewind(IclassEliteDict* dict); + +bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key); \ No newline at end of file diff --git a/applications/plugins/picopass/icons/DolphinMafia_115x62.png b/applications/plugins/picopass/icons/DolphinMafia_115x62.png new file mode 100644 index 000000000..66fdb40ff Binary files /dev/null and b/applications/plugins/picopass/icons/DolphinMafia_115x62.png differ diff --git a/applications/plugins/picopass/icons/DolphinNice_96x59.png b/applications/plugins/picopass/icons/DolphinNice_96x59.png new file mode 100644 index 000000000..a299d3630 Binary files /dev/null and b/applications/plugins/picopass/icons/DolphinNice_96x59.png differ diff --git a/applications/plugins/picopass/icons/Nfc_10px.png b/applications/plugins/picopass/icons/Nfc_10px.png new file mode 100644 index 000000000..6bc027111 Binary files /dev/null and b/applications/plugins/picopass/icons/Nfc_10px.png differ diff --git a/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png b/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png new file mode 100644 index 000000000..e1f5f9f80 Binary files /dev/null and b/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png differ diff --git a/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png b/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png new file mode 100644 index 000000000..380a970d9 Binary files /dev/null and b/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png differ diff --git a/applications/plugins/picopass/lib/loclass/optimized_elite.c b/applications/plugins/picopass/lib/loclass/optimized_elite.c index c175f3986..34e987060 100644 --- a/applications/plugins/picopass/lib/loclass/optimized_elite.c +++ b/applications/plugins/picopass/lib/loclass/optimized_elite.c @@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8 * @param loclass_hash1 loclass_hash1 * @param key_sel output key_sel=h[loclass_hash1[i]] */ -void hash2(uint8_t* key64, uint8_t* outp_keytable) { +void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) { /** *Expected: * High Security Key Table diff --git a/applications/plugins/picopass/picopass_device.c b/applications/plugins/picopass/picopass_device.c index b6e69cc21..6403da922 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_device.h b/applications/plugins/picopass/picopass_device.h index ed6bb4781..26f215941 100644 --- a/applications/plugins/picopass/picopass_device.h +++ b/applications/plugins/picopass/picopass_device.h @@ -9,6 +9,7 @@ #include "rfal_picopass.h" #include #include +#include "helpers/iclass_elite_dict.h" #define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_READER_DATA_MAX_SIZE 64 @@ -49,6 +50,7 @@ typedef struct { bool se_enabled; bool sio; bool biometrics; + uint8_t key[8]; uint8_t pin_length; PicopassEncryption encryption; uint8_t credential[8]; diff --git a/applications/plugins/picopass/picopass_i.h b/applications/plugins/picopass/picopass_i.h index 8e011f222..8f954c83c 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/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index bb6ccd862..0abfd74cb 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/applications/plugins/picopass/picopass_worker.c @@ -1,5 +1,7 @@ #include "picopass_worker_i.h" +#include + #define TAG "PicopassWorker" const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; @@ -23,11 +25,8 @@ PicopassWorker* picopass_worker_alloc() { PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker)); // Worker thread attributes - picopass_worker->thread = furi_thread_alloc(); - furi_thread_set_name(picopass_worker->thread, "PicopassWorker"); - furi_thread_set_stack_size(picopass_worker->thread, 8192); - furi_thread_set_callback(picopass_worker->thread, picopass_worker_task); - furi_thread_set_context(picopass_worker->thread, picopass_worker); + picopass_worker->thread = + furi_thread_alloc_ex("PicopassWorker", 8192, picopass_worker_task, picopass_worker); picopass_worker->callback = NULL; picopass_worker->context = NULL; @@ -176,7 +175,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { return ERR_NONE; } -ReturnCode picopass_read_card(PicopassBlock* AA1) { +ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { rfalPicoPassReadCheckRes rcRes; rfalPicoPassCheckRes chkRes; @@ -197,10 +196,68 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { loclass_opt_doReaderMAC(ccnr, div_key, mac); err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); - return err; + if(err == ERR_NONE) { + return ERR_NONE; } + FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + + FURI_LOG_E(TAG, "Starting dictionary attack"); + + size_t index = 0; + uint8_t key[PICOPASS_BLOCK_LEN] = {0}; + + if(!iclass_elite_dict_check_presence(IclassEliteDictTypeFlipper)) { + FURI_LOG_E(TAG, "Dictionary not found"); + return ERR_PARAM; + } + + IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper); + if(!dict) { + FURI_LOG_E(TAG, "Dictionary not allocated"); + return ERR_PARAM; + } + + FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict)); + while(iclass_elite_dict_get_next_key(dict, key)) { + FURI_LOG_D( + TAG, + "Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x", + index++, + key[0], + key[1], + key[2], + key[3], + key[4], + key[5], + key[6], + key[7]); + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + loclass_iclass_calc_div_key(AA1[PICOPASS_CSN_BLOCK_INDEX].data, key, div_key, true); + loclass_opt_doReaderMAC(ccnr, div_key, mac); + + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err == ERR_NONE) { + memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + break; + } + } + + if(dict) { + iclass_elite_dict_free(dict); + } + + return err; +} + +ReturnCode picopass_read_card(PicopassBlock* AA1) { + ReturnCode err; size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : @@ -352,28 +409,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); if(pacs->se_enabled) { FURI_LOG_D(TAG, "SE enabled"); + nextState = PicopassWorkerEventFail; } - err = picopass_read_card(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_card error %d", err); - nextState = PicopassWorkerEventFail; + if(nextState == PicopassWorkerEventSuccess) { + err = picopass_auth(AA1, pacs); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_try_auth error %d", err); + nextState = PicopassWorkerEventFail; + } + } + + if(nextState == PicopassWorkerEventSuccess) { + err = picopass_read_card(AA1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_read_card error %d", err); + nextState = PicopassWorkerEventFail; + } } if(nextState == PicopassWorkerEventSuccess) { err = picopass_device_parse_credential(AA1, pacs); - } - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); - nextState = PicopassWorkerEventFail; + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); + nextState = PicopassWorkerEventFail; + } } if(nextState == PicopassWorkerEventSuccess) { err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); - } - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); - nextState = PicopassWorkerEventFail; + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); + nextState = PicopassWorkerEventFail; + } } // Notify caller and exit diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/plugins/picopass/picopass_worker_i.h index 9b215fb74..ded40e6c6 100644 --- a/applications/plugins/picopass/picopass_worker_i.h +++ b/applications/plugins/picopass/picopass_worker_i.h @@ -9,8 +9,6 @@ #include #include -#include -#include #include #include @@ -18,6 +16,7 @@ struct PicopassWorker { FuriThread* thread; Storage* storage; + Stream* dict_stream; PicopassDeviceData* dev_data; PicopassWorkerCallback callback; 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/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index 37f1db4f2..bb170ac45 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -15,12 +15,10 @@ void picopass_scene_read_card_success_widget_callback( void picopass_scene_read_card_success_on_enter(void* context) { Picopass* picopass = context; - FuriString* credential_str; - FuriString* wiegand_str; - FuriString* sio_str; - credential_str = furi_string_alloc(); - wiegand_str = furi_string_alloc(); - sio_str = furi_string_alloc(); + FuriString* csn_str = furi_string_alloc_set("CSN:"); + FuriString* credential_str = furi_string_alloc(); + FuriString* wiegand_str = furi_string_alloc(); + FuriString* sio_str = furi_string_alloc(); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); @@ -28,10 +26,18 @@ void picopass_scene_read_card_success_on_enter(void* context) { notification_message(picopass->notifications, &sequence_success); // Setup view + PicopassBlock* AA1 = picopass->dev->dev_data.AA1; PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - if(pacs->record.bitLength == 0) { + uint8_t csn[PICOPASS_BLOCK_LEN]; + memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(csn_str, " %02X", csn[i]); + } + + // Neither of these are valid. Indicates the block was all 0x00 or all 0xff + if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { furi_string_cat_printf(wiegand_str, "Read Failed"); if(pacs->se_enabled) { @@ -79,18 +85,21 @@ void picopass_scene_read_card_success_on_enter(void* context) { } widget_add_string_element( - widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); + widget_add_string_element( + widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); widget_add_string_element( widget, 64, - 32, + 36, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(credential_str)); widget_add_string_element( - widget, 64, 42, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + furi_string_free(csn_str); furi_string_free(credential_str); furi_string_free(wiegand_str); furi_string_free(sio_str); diff --git a/applications/plugins/playlist/application.fam b/applications/plugins/playlist/application.fam index 5c7fcc10b..31acd8ea2 100644 --- a/applications/plugins/playlist/application.fam +++ b/applications/plugins/playlist/application.fam @@ -9,4 +9,5 @@ App( order=200, fap_icon="playlist_10px.png", fap_category="Tools", + fap_icon_assets="images", ) diff --git a/applications/plugins/playlist/images/ButtonRight_4x7.png b/applications/plugins/playlist/images/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/applications/plugins/playlist/images/ButtonRight_4x7.png differ diff --git a/applications/plugins/playlist/images/sub1_10px.png b/applications/plugins/playlist/images/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/applications/plugins/playlist/images/sub1_10px.png differ diff --git a/applications/plugins/playlist/playlist.c b/applications/plugins/playlist/playlist.c index c3ab4c6cf..ecf2f2817 100644 --- a/applications/plugins/playlist/playlist.c +++ b/applications/plugins/playlist/playlist.c @@ -6,8 +6,9 @@ #include #include -#include +#include +#include #include #include @@ -158,6 +159,7 @@ static int playlist_worker_process( // (try to) send file SubGhzEnvironment* environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, furi_string_get_cstr(protocol)); @@ -589,6 +591,8 @@ static void render_callback(Canvas* canvas, void* ctx) { } } break; + default: + break; } furi_string_free(temp_str); diff --git a/applications/plugins/pomodoro/LICENSE b/applications/plugins/pomodoro/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/applications/plugins/pomodoro/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/applications/plugins/pomodoro/README.md b/applications/plugins/pomodoro/README.md new file mode 100644 index 000000000..e8e7491f5 --- /dev/null +++ b/applications/plugins/pomodoro/README.md @@ -0,0 +1,34 @@ +# flipperzero_pomodoro + +The Pomodoro Technique is a time management method developed by Francesco Cirillo in the late 1980s.[1] It uses a kitchen timer to break work into intervals, typically 25 minutes in length, separated by short breaks. Each interval is known as a pomodoro, from the Italian word for tomato, after the tomato-shaped kitchen timer Cirillo used as a university student. + +Flipper Zero is a portable Tamagotchi-like multi-functional device developed for interaction with access control systems. The device is able to read, copy, and emulate radio-frequency (RFID) tags, radio remotes, and digital access keys. + +## Pomodoro timer application for Flipper Zero + +Three timers available: + +- classic 25 min work, 5 min rest +- long 50 min work, 10 min rest +- sprint 10 min work, 2 min rest + +With tomato counter + +Plays sound alerts + +Has built-in clocks + +Screenshots: + +![](./misc/1.png) + +![](./misc/2.png) + +![](./misc/3.png) + +![](./misc/4.png) + +![](./misc/5.png) + + +Compatible with firmware v. F81999EA from 14 Oct. 2022 diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam new file mode 100644 index 000000000..e73427fc8 --- /dev/null +++ b/applications/plugins/pomodoro/application.fam @@ -0,0 +1,15 @@ +App( + appid="Pomodoro_Timer", + name="Pomodoro Timer", + apptype=FlipperAppType.EXTERNAL, + entry_point="pomodoro_app", + stack_size=1 * 1024, + cdefines=["APP_POMODORO"], + requires=[ + "gui", + ], + order=10, + fap_icon="pomodoro_timer.png", + fap_category="Tools", + fap_icon_assets="icons", +) diff --git a/applications/plugins/pomodoro/icons/ButtonLeft_4x7.png b/applications/plugins/pomodoro/icons/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/applications/plugins/pomodoro/icons/ButtonLeft_4x7.png differ diff --git a/applications/plugins/pomodoro/icons/Ok_btn_9x9.png b/applications/plugins/pomodoro/icons/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/pomodoro/icons/Ok_btn_9x9.png differ diff --git a/applications/plugins/pomodoro/icons/Pin_back_arrow_10x8.png b/applications/plugins/pomodoro/icons/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/applications/plugins/pomodoro/icons/Pin_back_arrow_10x8.png differ diff --git a/applications/plugins/pomodoro/icons/Space_65x18.png b/applications/plugins/pomodoro/icons/Space_65x18.png new file mode 100644 index 000000000..b60ae5097 Binary files /dev/null and b/applications/plugins/pomodoro/icons/Space_65x18.png differ diff --git a/applications/plugins/pomodoro/misc/1.png b/applications/plugins/pomodoro/misc/1.png new file mode 100644 index 000000000..e8543a255 Binary files /dev/null and b/applications/plugins/pomodoro/misc/1.png differ diff --git a/applications/plugins/pomodoro/misc/2.png b/applications/plugins/pomodoro/misc/2.png new file mode 100644 index 000000000..8b5f28476 Binary files /dev/null and b/applications/plugins/pomodoro/misc/2.png differ diff --git a/applications/plugins/pomodoro/misc/3.png b/applications/plugins/pomodoro/misc/3.png new file mode 100644 index 000000000..32473be3c Binary files /dev/null and b/applications/plugins/pomodoro/misc/3.png differ diff --git a/applications/plugins/pomodoro/misc/4.png b/applications/plugins/pomodoro/misc/4.png new file mode 100644 index 000000000..0b48b9fdc Binary files /dev/null and b/applications/plugins/pomodoro/misc/4.png differ diff --git a/applications/plugins/pomodoro/misc/5.png b/applications/plugins/pomodoro/misc/5.png new file mode 100644 index 000000000..1a53bc074 Binary files /dev/null and b/applications/plugins/pomodoro/misc/5.png differ diff --git a/applications/plugins/pomodoro/pomodoro.c b/applications/plugins/pomodoro/pomodoro.c new file mode 100644 index 000000000..5b1db1984 --- /dev/null +++ b/applications/plugins/pomodoro/pomodoro.c @@ -0,0 +1,164 @@ +#include "pomodoro.h" +#include + +#define TAG "PomodoroApp" + +enum PomodoroDebugSubmenuIndex { + PomodoroSubmenuIndex10, + PomodoroSubmenuIndex25, + PomodoroSubmenuIndex50, +}; + +void pomodoro_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + Pomodoro* app = context; + if(index == PomodoroSubmenuIndex10) { + app->view_id = PomodoroView10; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView10); + } + if(index == PomodoroSubmenuIndex25) { + app->view_id = PomodoroView25; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView25); + } + if(index == PomodoroSubmenuIndex50) { + app->view_id = PomodoroView50; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView50); + } +} + +void pomodoro_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + Pomodoro* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_stop(app->view_dispatcher); + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroViewSubmenu); + } +} + +uint32_t pomodoro_exit_confirm_view(void* context) { + UNUSED(context); + return PomodoroViewExitConfirm; +} + +uint32_t pomodoro_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +Pomodoro* pomodoro_app_alloc() { + Pomodoro* app = malloc(sizeof(Pomodoro)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // Notifications + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu view + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, + "Classic: 25 work 5 rest", + PomodoroSubmenuIndex25, + pomodoro_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Long: 50 work 10 rest", + PomodoroSubmenuIndex50, + pomodoro_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Sprint: 10 work 2 rest", + PomodoroSubmenuIndex10, + pomodoro_submenu_callback, + app); + view_set_previous_callback(submenu_get_view(app->submenu), pomodoro_exit); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroViewSubmenu, submenu_get_view(app->submenu)); + + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, pomodoro_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // 25 minutes view + app->pomodoro_25 = pomodoro_25_alloc(); + view_set_previous_callback(pomodoro_25_get_view(app->pomodoro_25), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView25, pomodoro_25_get_view(app->pomodoro_25)); + + // 50 minutes view + app->pomodoro_50 = pomodoro_50_alloc(); + view_set_previous_callback(pomodoro_50_get_view(app->pomodoro_50), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView50, pomodoro_50_get_view(app->pomodoro_50)); + + // 10 minutes view + app->pomodoro_10 = pomodoro_10_alloc(); + view_set_previous_callback(pomodoro_10_get_view(app->pomodoro_10), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView10, pomodoro_10_get_view(app->pomodoro_10)); + + // TODO switch to menu after Media is done + app->view_id = PomodoroViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + + return app; +} + +void pomodoro_app_free(Pomodoro* app) { + furi_assert(app); + + // Reset notification + notification_internal_message(app->notifications, &sequence_reset_blue); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, PomodoroViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView25); + pomodoro_25_free(app->pomodoro_25); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView50); + pomodoro_50_free(app->pomodoro_50); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView10); + pomodoro_10_free(app->pomodoro_10); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Free rest + free(app); +} + +int32_t pomodoro_app(void* p) { + UNUSED(p); + // Switch profile to Hid + Pomodoro* app = pomodoro_app_alloc(); + + view_dispatcher_run(app->view_dispatcher); + + pomodoro_app_free(app); + + return 0; +} diff --git a/applications/plugins/pomodoro/pomodoro.h b/applications/plugins/pomodoro/pomodoro.h new file mode 100644 index 000000000..53dedb8e3 --- /dev/null +++ b/applications/plugins/pomodoro/pomodoro.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include "pomodoro_timer.h" +#include "views/pomodoro_10.h" +#include "views/pomodoro_25.h" +#include "views/pomodoro_50.h" + +typedef struct { + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + DialogEx* dialog; + PomodoroTimer* pomodoro_10; + PomodoroTimer* pomodoro_25; + PomodoroTimer* pomodoro_50; + uint32_t view_id; +} Pomodoro; + +typedef enum { + PomodoroViewSubmenu, + PomodoroView10, + PomodoroView25, + PomodoroView50, + PomodoroViewExitConfirm, +} PomodoroView; diff --git a/applications/plugins/pomodoro/pomodoro_timer.c b/applications/plugins/pomodoro/pomodoro_timer.c new file mode 100644 index 000000000..0fba5db42 --- /dev/null +++ b/applications/plugins/pomodoro/pomodoro_timer.c @@ -0,0 +1,242 @@ +#include "pomodoro_timer.h" +#include +#include +#include +#include +#include + +const NotificationSequence sequence_finish = { + &message_display_backlight_on, + &message_green_255, + &message_vibro_on, + &message_note_c5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_e5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_g5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_b5, + &message_delay_250, + &message_vibro_off, + &message_vibro_on, + &message_note_c6, + &message_delay_250, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_rest = { + &message_display_backlight_on, + &message_red_255, + &message_vibro_on, + &message_note_c6, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_b5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_g5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_e5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_c5, + &message_delay_250, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event) { + with_view_model( + pomodoro_timer->view, + PomodoroTimerModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyOk) { + model->ok_pressed = true; + } else if(event->key == InputKeyLeft) { + model->reset_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyOk) { + model->ok_pressed = false; + + // START/STOP TIMER + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t current_timestamp = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + // STARTED -> PAUSED + if(model->timer_running) { + // Update stopped seconds + model->timer_stopped_seconds = + current_timestamp - model->timer_start_timestamp; + } else if(!model->time_passed) { + // INITIAL -> STARTED + model->timer_start_timestamp = current_timestamp; + model->rest_running = false; + } else { + // PAUSED -> STARTED + model->timer_start_timestamp = + current_timestamp - model->timer_stopped_seconds; + } + model->timer_running = !model->timer_running; + } else if(event->key == InputKeyLeft) { + if(!model->timer_running) { + furi_record_close(RECORD_NOTIFICATION); + model->timer_stopped_seconds = 0; + model->timer_start_timestamp = 0; + model->time_passed = 0; + model->timer_running = false; + } + model->reset_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + } + }, + true); +} + +void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest) { + furi_assert(context); + PomodoroTimerModel* model = context; + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t current_timestamp = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + // Header + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Pomodoro"); + + canvas_draw_icon(canvas, 68, 1, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 127, 1, AlignRight, AlignTop, "Hold to exit"); + + // Start/Pause/Continue + int txt_main_y = 34; + canvas_draw_icon(canvas, 63, 23, &I_Space_65x18); // button + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 66, 25, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + if(model->timer_running) { + model->time_passed = current_timestamp - model->timer_start_timestamp; + elements_multiline_text_aligned(canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Pause"); + canvas_draw_box(canvas, 71, 27, 2, 8); + canvas_draw_box(canvas, 75, 27, 2, 8); + } else { + if(model->time_passed) { + elements_multiline_text_aligned( + canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Continue"); + } else { + elements_multiline_text_aligned( + canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Start"); + } + canvas_draw_icon(canvas, 70, 26, &I_Ok_btn_9x9); // OK icon + } + canvas_set_color(canvas, ColorBlack); + + // Reset + if(!model->timer_running && model->time_passed) { + canvas_draw_icon(canvas, 63, 46, &I_Space_65x18); + if(model->reset_pressed) { + elements_slightly_rounded_box(canvas, 66, 48, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 72, 50, &I_ButtonLeft_4x7); + elements_multiline_text_aligned(canvas, 83, 57, AlignLeft, AlignBottom, "Reset"); + canvas_set_color(canvas, ColorBlack); + } + + char buffer[64]; + + // Time to work + int total_time_left = (max_seconds - (uint32_t)model->time_passed); + int minutes_left = total_time_left / 60; + int seconds_left = total_time_left % 60; + canvas_set_font(canvas, FontBigNumbers); + + // Play sound + if(total_time_left == 0 && !model->sound_playing) { + model->sound_playing = true; + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_finish); + } + if(total_time_left < 0) { + model->timer_running = false; + model->time_passed = 0; + model->sound_playing = false; + + model->rest_running = true; + model->rest_start_timestamp = current_timestamp; + seconds_left = 0; + model->counter += 1; + } + if(!model->rest_running) { + snprintf(buffer, sizeof(buffer), "%02d:%02d", minutes_left, seconds_left); + canvas_draw_str(canvas, 0, 39, buffer); + } + if(model->timer_running) { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 50, AlignLeft, AlignTop, "Time to work"); + } + + // Time to rest + if(model->rest_running && !model->timer_running) { + canvas_set_font(canvas, FontBigNumbers); + int rest_passed = current_timestamp - model->rest_start_timestamp; + int rest_total_time_left = (max_seconds_rest - rest_passed); + int rest_minutes_left = rest_total_time_left / 60; + int rest_seconds_left = rest_total_time_left % 60; + + // Play sound + if(rest_total_time_left == 0 && !model->sound_playing) { + model->sound_playing = true; + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_rest); + } + if(rest_total_time_left < 0) { + rest_seconds_left = 0; + model->rest_running = false; + model->sound_playing = false; + } + snprintf(buffer, sizeof(buffer), "%02d:%02d", rest_minutes_left, rest_seconds_left); + canvas_draw_str(canvas, 0, 60, buffer); + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 27, AlignLeft, AlignTop, "Have a rest"); + } + + // Clocks + canvas_set_font(canvas, FontSecondary); + snprintf( + buffer, + sizeof(buffer), + "%02ld:%02ld:%02ld", + ((uint32_t)current_timestamp % (60 * 60 * 24)) / (60 * 60), + ((uint32_t)current_timestamp % (60 * 60)) / 60, + (uint32_t)current_timestamp % 60); + canvas_draw_str(canvas, 0, 20, buffer); + + // Tomato counter + if(model->counter > 5) { + model->counter = 1; + } + for(int i = 0; i < model->counter; ++i) { + canvas_draw_disc(canvas, 122 - i * 10, 15, 4); + } +} diff --git a/applications/plugins/pomodoro/pomodoro_timer.h b/applications/plugins/pomodoro/pomodoro_timer.h new file mode 100644 index 000000000..284f0a6c6 --- /dev/null +++ b/applications/plugins/pomodoro/pomodoro_timer.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +typedef struct PomodoroTimer PomodoroTimer; + +struct PomodoroTimer { + View* view; +}; + +typedef struct PomodoroTimerModel PomodoroTimerModel; + +struct PomodoroTimerModel { + bool ok_pressed; + bool reset_pressed; + bool back_pressed; + bool connected; + bool timer_running; + bool rest_running; + bool sound_playing; + uint32_t timer_start_timestamp; + uint32_t timer_stopped_seconds; + uint32_t time_passed; + uint32_t rest_start_timestamp; + int counter; +}; + +void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event); + +void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest); diff --git a/applications/plugins/pomodoro/pomodoro_timer.png b/applications/plugins/pomodoro/pomodoro_timer.png new file mode 100644 index 000000000..b25c2718e Binary files /dev/null and b/applications/plugins/pomodoro/pomodoro_timer.png differ diff --git a/applications/plugins/pomodoro/views/pomodoro_10.c b/applications/plugins/pomodoro/views/pomodoro_10.c new file mode 100644 index 000000000..f11f96d9f --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_10.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_10.h" +#include +#include +#include +#include + +static void pomodoro_10_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 10; + int max_seconds_rest = 60 * 2; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_10_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_10 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_10, event); + return true; + } +} + +PomodoroTimer* pomodoro_10_alloc() { + PomodoroTimer* pomodoro_10 = malloc(sizeof(PomodoroTimer)); + pomodoro_10->view = view_alloc(); + view_set_context(pomodoro_10->view, pomodoro_10); + view_allocate_model(pomodoro_10->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_10->view, pomodoro_10_draw_callback); + view_set_input_callback(pomodoro_10->view, pomodoro_10_input_callback); + + return pomodoro_10; +} + +void pomodoro_10_free(PomodoroTimer* pomodoro_10) { + furi_assert(pomodoro_10); + view_free(pomodoro_10->view); + free(pomodoro_10); +} + +View* pomodoro_10_get_view(PomodoroTimer* pomodoro_10) { + furi_assert(pomodoro_10); + return pomodoro_10->view; +} diff --git a/applications/plugins/pomodoro/views/pomodoro_10.h b/applications/plugins/pomodoro/views/pomodoro_10.h new file mode 100644 index 000000000..8f27e6bd6 --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_10.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_10_alloc(); + +void pomodoro_10_free(PomodoroTimer* pomodoro_10); + +View* pomodoro_10_get_view(PomodoroTimer* pomodoro_10); diff --git a/applications/plugins/pomodoro/views/pomodoro_25.c b/applications/plugins/pomodoro/views/pomodoro_25.c new file mode 100644 index 000000000..01c5a7125 --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_25.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_25.h" +#include +#include +#include +#include + +static void pomodoro_25_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 25; + int max_seconds_rest = 60 * 5; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_25_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_25 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_25, event); + return true; + } +} + +PomodoroTimer* pomodoro_25_alloc() { + PomodoroTimer* pomodoro_25 = malloc(sizeof(PomodoroTimer)); + pomodoro_25->view = view_alloc(); + view_set_context(pomodoro_25->view, pomodoro_25); + view_allocate_model(pomodoro_25->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_25->view, pomodoro_25_draw_callback); + view_set_input_callback(pomodoro_25->view, pomodoro_25_input_callback); + + return pomodoro_25; +} + +void pomodoro_25_free(PomodoroTimer* pomodoro_25) { + furi_assert(pomodoro_25); + view_free(pomodoro_25->view); + free(pomodoro_25); +} + +View* pomodoro_25_get_view(PomodoroTimer* pomodoro_25) { + furi_assert(pomodoro_25); + return pomodoro_25->view; +} diff --git a/applications/plugins/pomodoro/views/pomodoro_25.h b/applications/plugins/pomodoro/views/pomodoro_25.h new file mode 100644 index 000000000..c3eb43976 --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_25.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_25_alloc(); + +void pomodoro_25_free(PomodoroTimer* pomodoro_25); + +View* pomodoro_25_get_view(PomodoroTimer* pomodoro_25); diff --git a/applications/plugins/pomodoro/views/pomodoro_50.c b/applications/plugins/pomodoro/views/pomodoro_50.c new file mode 100644 index 000000000..74f89122a --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_50.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_50.h" +#include +#include +#include +#include + +static void pomodoro_50_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 50; + int max_seconds_rest = 60 * 10; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_50_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_50 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_50, event); + return true; + } +} + +PomodoroTimer* pomodoro_50_alloc() { + PomodoroTimer* pomodoro_50 = malloc(sizeof(PomodoroTimer)); + pomodoro_50->view = view_alloc(); + view_set_context(pomodoro_50->view, pomodoro_50); + view_allocate_model(pomodoro_50->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_50->view, pomodoro_50_draw_callback); + view_set_input_callback(pomodoro_50->view, pomodoro_50_input_callback); + + return pomodoro_50; +} + +void pomodoro_50_free(PomodoroTimer* pomodoro_50) { + furi_assert(pomodoro_50); + view_free(pomodoro_50->view); + free(pomodoro_50); +} + +View* pomodoro_50_get_view(PomodoroTimer* pomodoro_50) { + furi_assert(pomodoro_50); + return pomodoro_50->view; +} diff --git a/applications/plugins/pomodoro/views/pomodoro_50.h b/applications/plugins/pomodoro/views/pomodoro_50.h new file mode 100644 index 000000000..e0246d2d2 --- /dev/null +++ b/applications/plugins/pomodoro/views/pomodoro_50.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_50_alloc(); + +void pomodoro_50_free(PomodoroTimer* pomodoro_50); + +View* pomodoro_50_get_view(PomodoroTimer* pomodoro_50); diff --git a/applications/plugins/sam/application.fam b/applications/plugins/sam/application.fam new file mode 100644 index 000000000..c35f67856 --- /dev/null +++ b/applications/plugins/sam/application.fam @@ -0,0 +1,45 @@ +App( + appid="SAM", + name="SAM AYBABTU", + apptype=FlipperAppType.PLUGIN, + entry_point="sam_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + order=20, + fap_icon="icons/music_10px.png", + fap_category="Music", + fap_icon_assets="icons", +) +App( + appid="SAM_YES", + name="SAM YES", + apptype=FlipperAppType.PLUGIN, + entry_point="sam_app_yes", + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + order=20, + fap_icon="icons/music_10px.png", + fap_category="Music", + fap_icon_assets="icons", +) +App( + appid="SAM_NO", + name="SAM NO", + apptype=FlipperAppType.PLUGIN, + entry_point="sam_app_no", + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + order=20, + fap_icon="icons/music_10px.png", + fap_category="Music", + fap_icon_assets="icons", +) diff --git a/applications/plugins/sam/icons/music_10px.png b/applications/plugins/sam/icons/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/sam/icons/music_10px.png differ diff --git a/applications/plugins/sam/sam_app.cpp b/applications/plugins/sam/sam_app.cpp new file mode 100644 index 000000000..4b218d1f7 --- /dev/null +++ b/applications/plugins/sam/sam_app.cpp @@ -0,0 +1,32 @@ +#include +#include "stm32_sam.h" +// WOULD BE COOL IF SOMEONE MADE A TEXT ENTRY SCREEN TO HAVE IT READ WHAT IS ENTERED TO TEXT +STM32SAM voice; + +extern "C" int32_t sam_app(void* p) { + UNUSED(p); + + voice.begin(); + voice.say( + "All your base are belong to us. You have no chance to survive make your time. ha. ha. ha. GOOD BYE. "); + + return 0; +} + +extern "C" int32_t sam_app_yes(void* p) { + UNUSED(p); + + voice.begin(); + voice.say("Yes"); + + return 0; +} + +extern "C" int32_t sam_app_no(void* p) { + UNUSED(p); + + voice.begin(); + voice.say("No"); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/sam/stm32_sam.cpp b/applications/plugins/sam/stm32_sam.cpp new file mode 100644 index 000000000..16f6fcaab --- /dev/null +++ b/applications/plugins/sam/stm32_sam.cpp @@ -0,0 +1,5704 @@ + +#include "stm32_sam.h" +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// All +// +//////////////////////////////////////////////////////////////////////////////////////////// + +char input[256 + 1] = {0}; //tab39445 +//standard sam sound + +unsigned char wait1 = 7; +unsigned char wait2 = 6; + +unsigned char A, X, Y; +unsigned char mem44; +unsigned char mem47; +unsigned char mem49; +unsigned char mem39; +unsigned char mem50; +unsigned char mem51; +unsigned char mem53; +unsigned char mem56; +unsigned char mem59 = 0; + +unsigned char phonemeIndexOutput[60]; //tab47296 +unsigned char stressOutput[60]; //tab47365 +unsigned char phonemeLengthOutput[60]; //tab47416 + +// contains the soundbuffer position +int bufferpos; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam Tabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//tab40672 +const unsigned char stressInputTable[] = {'*', '1', '2', '3', '4', '5', '6', '7', '8'}; + +//tab40682 +const unsigned char signInputTable1[] = { + ' ', '.', '?', ',', '-', 'I', 'I', 'E', 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', 'U', + 'O', 'R', 'L', 'W', 'Y', 'W', 'R', 'L', 'W', 'Y', 'M', 'N', 'N', 'D', 'Q', 'S', 'S', + 'F', 'T', '/', '/', 'Z', 'Z', 'V', 'D', 'C', '*', 'J', '*', '*', '*', 'E', 'A', 'O', + 'A', 'O', 'U', 'B', '*', '*', 'D', '*', '*', 'G', '*', '*', 'G', '*', '*', 'P', '*', + '*', 'T', '*', '*', 'K', '*', '*', 'K', '*', '*', 'U', 'U', 'U'}; + +//tab40763 +const unsigned char signInputTable2[] = { + '*', '*', '*', '*', '*', 'Y', 'H', 'H', 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', 'X', + 'H', 'X', 'X', 'X', 'X', 'H', '*', '*', '*', '*', '*', '*', 'X', 'X', '*', '*', 'H', + '*', 'H', 'H', 'X', '*', 'H', '*', 'H', 'H', '*', '*', '*', '*', '*', 'Y', 'Y', 'Y', + 'W', 'W', 'W', '*', '*', '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', '*', '*', + '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', 'L', 'M', 'N'}; + +//loc_9F8C +const unsigned char flags[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x84, 0x84, 0xA4, + 0xA4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4C, + 0x4C, 0x4C, 0x48, 0x4C, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, 0x44, 0x44, + 0x48, 0x40, 0x4C, 0x44, 0x00, 0x00, 0xB4, 0xB4, 0xB4, 0x94, 0x94, 0x94, 0x4E, 0x4E, + 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4B, 0x4B, 0x4B, 0x4B, + 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x80, 0xC1, 0xC1 + +}; + +//??? flags overlap flags2 +//loc_9FDA +const unsigned char flags2[] = { + 0x80, 0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x08, 0x0C, 0x08, 0x04, 0x40, + 0x24, 0x20, 0x20, 0x24, 0x00, 0x00, 0x24, 0x20, 0x20, 0x24, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +//tab45616??? +const unsigned char phonemeStressedLengthTable[] = { + 0x00, 0x12, 0x12, 0x12, 8, 0xB, 9, 0xB, 0xE, 0xF, 0xB, 0x10, 0xC, 6, 6, 0xE, + 0xC, 0xE, 0xC, 0xB, 8, 8, 0xB, 0xA, 9, 8, 8, 8, 8, 8, 3, 5, + 2, 2, 2, 2, 2, 2, 6, 6, 8, 6, 6, 2, 9, 4, 2, 1, + 0xE, 0xF, 0xF, 0xF, 0xE, 0xE, 8, 2, 2, 7, 2, 1, 7, 2, 2, 7, + 2, 2, 8, 2, 2, 6, 2, 2, 7, 2, 4, 7, 1, 4, 5, 5}; + +//tab45536??? +const unsigned char phonemeLengthTable[] = { + 0, 0x12, 0x12, 0x12, 8, 8, 8, 8, 8, 0xB, 6, 0xC, 0xA, 5, 5, 0xB, 0xA, 0xA, 0xA, 9, + 8, 7, 9, 7, 6, 8, 6, 7, 7, 7, 2, 5, 2, 2, 2, 2, 2, 2, 6, 6, + 7, 6, 6, 2, 8, 3, 1, 0x1E, 0xD, 0xC, 0xC, 0xC, 0xE, 9, 6, 1, 2, 5, 1, 1, + 6, 1, 2, 6, 1, 2, 8, 2, 2, 4, 2, 2, 6, 1, 4, 6, 1, 4, 0xC7, 0xFF}; + +/* + + Ind | phoneme | flags | + -----|---------|----------| + 0 | * | 00000000 | + 1 | .* | 00000000 | + 2 | ?* | 00000000 | + 3 | ,* | 00000000 | + 4 | -* | 00000000 | + + VOWELS + 5 | IY | 10100100 | + 6 | IH | 10100100 | + 7 | EH | 10100100 | + 8 | AE | 10100100 | + 9 | AA | 10100100 | + 10 | AH | 10100100 | + 11 | AO | 10000100 | + 17 | OH | 10000100 | + 12 | UH | 10000100 | + 16 | UX | 10000100 | + 15 | ER | 10000100 | + 13 | AX | 10100100 | + 14 | IX | 10100100 | + + DIPHTONGS + 48 | EY | 10110100 | + 49 | AY | 10110100 | + 50 | OY | 10110100 | + 51 | AW | 10010100 | + 52 | OW | 10010100 | + 53 | UW | 10010100 | + + + 21 | YX | 10000100 | + 20 | WX | 10000100 | + 18 | RX | 10000100 | + 19 | LX | 10000100 | + 37 | /X | 01000000 | + 30 | DX | 01001000 | + + + 22 | WH | 01000100 | + + + VOICED CONSONANTS + 23 | R* | 01000100 | + 24 | L* | 01000100 | + 25 | W* | 01000100 | + 26 | Y* | 01000100 | + 27 | M* | 01001100 | + 28 | N* | 01001100 | + 29 | NX | 01001100 | + 54 | B* | 01001110 | + 57 | D* | 01001110 | + 60 | G* | 01001110 | + 44 | J* | 01001100 | + 38 | Z* | 01000100 | + 39 | ZH | 01000100 | + 40 | V* | 01000100 | + 41 | DH | 01000100 | + + unvoiced CONSONANTS + 32 | S* | 01000000 | + 33 | SH | 01000000 | + 34 | F* | 01000000 | + 35 | TH | 01000000 | + 66 | P* | 01001011 | + 69 | T* | 01001011 | + 72 | K* | 01001011 | + 42 | CH | 01001000 | + 36 | /H | 01000000 | + + 43 | ** | 01000000 | + 45 | ** | 01000100 | + 46 | ** | 00000000 | + 47 | ** | 00000000 | + + + 55 | ** | 01001110 | + 56 | ** | 01001110 | + 58 | ** | 01001110 | + 59 | ** | 01001110 | + 61 | ** | 01001110 | + 62 | ** | 01001110 | + 63 | GX | 01001110 | + 64 | ** | 01001110 | + 65 | ** | 01001110 | + 67 | ** | 01001011 | + 68 | ** | 01001011 | + 70 | ** | 01001011 | + 71 | ** | 01001011 | + 73 | ** | 01001011 | + 74 | ** | 01001011 | + 75 | KX | 01001011 | + 76 | ** | 01001011 | + 77 | ** | 01001011 | + + + SPECIAL + 78 | UL | 10000000 | + 79 | UM | 11000001 | + 80 | UN | 11000001 | + 31 | Q* | 01001100 | + +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// RenderTabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +const unsigned char tab48426[5] = {0x18, 0x1A, 0x17, 0x17, 0x17}; + +const unsigned char tab47492[] = {0, 0, 0xE0, 0xE6, 0xEC, 0xF3, 0xF9, 0, 6, 0xC, 6}; + +const unsigned char amplitudeRescale[] = { + 0, + 1, + 2, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 6, + 8, + 9, + 0xB, + 0xD, + 0xF, + 0 //17 elements? +}; + +// Used to decide which phoneme's blend lengths. The candidate with the lower score is selected. +// tab45856 +const unsigned char blendRank[] = {0, 0x1F, 0x1F, 0x1F, 0x1F, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 5, 5, 2, 0xA, 2, 8, + 5, 5, 0xB, 0xA, 9, 8, 8, 0xA0, 8, 8, + 0x17, 0x1F, 0x12, 0x12, 0x12, 0x12, 0x1E, 0x1E, 0x14, 0x14, + 0x14, 0x14, 0x17, 0x17, 0x1A, 0x1A, 0x1D, 0x1D, 2, 2, + 2, 2, 2, 2, 0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, + 0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, 0x17, 0x1D, 0x17, 0x17, + 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x17}; + +// Number of frames at the end of a phoneme devoted to interpolating to next phoneme's final value +//tab45696 +const unsigned char outBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 3, 2, 4, 4, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 1, 0, 1, 0, 5, + 5, 5, 5, 5, 4, 4, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, + 0, 1, 2, 0, 2, 2, 0, 1, 3, 0, 2, 3, 0, 2, 0xA0, 0xA0}; + +// Number of frames at beginning of a phoneme devoted to interpolating to phoneme's final value +// tab45776 +const unsigned char inBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 1, 2, 3, 2, 1, + 3, 3, 3, 3, 1, 1, 3, 3, 3, 2, 2, 3, 2, 3, 0, 0, + 5, 5, 5, 5, 4, 4, 2, 0, 2, 2, 0, 3, 2, 0, 4, 2, + 0, 3, 2, 0, 2, 2, 0, 2, 3, 0, 3, 3, 0, 3, 0xB0, 0xA0}; + +// Looks like it's used as bit flags +// High bits masked by 248 (11111000) +// +// 32: S* 241 11110001 +// 33: SH 226 11100010 +// 34: F* 211 11010011 +// 35: TH 187 10111011 +// 36: /H 124 01111100 +// 37: /X 149 10010101 +// 38: Z* 1 00000001 +// 39: ZH 2 00000010 +// 40: V* 3 00000011 +// 41: DH 3 00000011 +// 43: ** 114 01110010 +// 45: ** 2 00000010 +// 67: ** 27 00011011 +// 70: ** 25 00011001 +// tab45936 +const unsigned char sampledConsonantFlags[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xF1, 0xE2, 0xD3, 0xBB, 0x7C, 0x95, 1, 2, + 3, 3, 0, 0x72, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1B, 0, 0, 0x19, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +//tab45056 +unsigned char freq1data[] = { + 0x00, 0x13, 0x13, 0x13, 0x13, 0xA, 0xE, 0x12, 0x18, 0x1A, 0x16, 0x14, 0x10, 0x14, 0xE, 0x12, + 0xE, 0x12, 0x12, 0x10, 0xC, 0xE, 0xA, 0x12, 0xE, 0xA, 8, 6, 6, 6, 6, 0x11, + 6, 6, 6, 6, 0xE, 0x10, 9, 0xA, 8, 0xA, 6, 6, 6, 5, 6, 0, + 0x12, 0x1A, 0x14, 0x1A, 0x12, 0xC, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 0xA, 0xA, 6, 6, 6, 0x2C, 0x13}; + +//tab451356 +unsigned char freq2data[] = {0x00, 0x43, 0x43, 0x43, 0x43, 0x54, 0x48, 0x42, 0x3E, 0x28, + 0x2C, 0x1E, 0x24, 0x2C, 0x48, 0x30, 0x24, 0x1E, 0x32, 0x24, + 0x1C, 0x44, 0x18, 0x32, 0x1E, 0x18, 0x52, 0x2E, 0x36, 0x56, + 0x36, 0x43, 0x49, 0x4F, 0x1A, 0x42, 0x49, 0x25, 0x33, 0x42, + 0x28, 0x2F, 0x4F, 0x4F, 0x42, 0x4F, 0x6E, 0x00, 0x48, 0x26, + 0x1E, 0x2A, 0x1E, 0x22, 0x1A, 0x1A, 0x1A, 0x42, 0x42, 0x42, + 0x6E, 0x6E, 0x6E, 0x54, 0x54, 0x54, 0x1A, 0x1A, 0x1A, 0x42, + 0x42, 0x42, 0x6D, 0x56, 0x6D, 0x54, 0x54, 0x54, 0x7F, 0x7F}; +//tab45216 +unsigned char freq3data[] = {0x00, 0x5B, 0x5B, 0x5B, 0x5B, 0x6E, 0x5D, 0x5B, 0x58, 0x59, + 0x57, 0x58, 0x52, 0x59, 0x5D, 0x3E, 0x52, 0x58, 0x3E, 0x6E, + 0x50, 0x5D, 0x5A, 0x3C, 0x6E, 0x5A, 0x6E, 0x51, 0x79, 0x65, + 0x79, 0x5B, 0x63, 0x6A, 0x51, 0x79, 0x5D, 0x52, 0x5D, 0x67, + 0x4C, 0x5D, 0x65, 0x65, 0x79, 0x65, 0x79, 0x00, 0x5A, 0x58, + 0x58, 0x58, 0x58, 0x52, 0x51, 0x51, 0x51, 0x79, 0x79, 0x79, + 0x70, 0x6E, 0x6E, 0x5E, 0x5E, 0x5E, 0x51, 0x51, 0x51, 0x79, + 0x79, 0x79, 0x65, 0x65, 0x70, 0x5E, 0x5E, 0x5E, 0x08, 0x01}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Reciter +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char inputtemp[256]; // secure copy of input tab36096 + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Render +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//timetable for more accurate c64 simulation +int timetable[5][5] = { + {162, 167, 167, 127, 128}, + {226, 60, 60, 0, 0}, + {225, 60, 59, 0, 0}, + {200, 0, 0, 54, 55}, + {199, 0, 0, 54, 54}}; + +unsigned oldtimetableindex; + +const unsigned char ampl1data[] = {0, 0, 0, 0, 0, 0xD, 0xD, 0xE, 0xF, 0xF, 0xF, 0xF, + 0xF, 0xC, 0xD, 0xC, 0xF, 0xF, 0xD, 0xD, 0xD, 0xE, 0xD, 0xC, + 0xD, 0xD, 0xD, 0xC, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0xB, 0xB, 0xB, 0xB, 0, 0, 1, 0xB, 0, 2, + 0xE, 0xF, 0xF, 0xF, 0xF, 0xD, 2, 4, 0, 2, 4, 0, + 1, 4, 0, 1, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0xC, 0, 0, 0, 0, 0xF, 0xF}; + +const unsigned char ampl2data[] = { + 0, 0, 0, 0, 0, 0xA, 0xB, 0xD, 0xE, 0xD, 0xC, 0xC, 0xB, 9, 0xB, 0xB, 0xC, 0xC, 0xC, 8, + 8, 0xC, 8, 0xA, 8, 8, 0xA, 3, 9, 6, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, + 3, 4, 0, 0, 0, 5, 0xA, 2, 0xE, 0xD, 0xC, 0xD, 0xC, 8, 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0xA, 0, 0, 0xA, 0, 0, 0}; + +const unsigned char ampl3data[] = {0, 0, 0, 0, 0, 8, 7, 8, 8, 1, 1, 0, 1, 0, 7, 5, + 1, 0, 6, 1, 0, 7, 0, 5, 1, 0, 8, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0xE, 1, + 9, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 5, 0, 0x13, 0x10}; + +//tab42240 +const signed char sinus[256] = { + 0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, + 49, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, + 90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, 116, + 117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127, + 127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 122, 121, 120, 118, + 117, 116, 115, 113, 112, 111, 109, 107, 106, 104, 102, 100, 98, 96, 94, 92, + 90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51, + 49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 12, 9, 6, 3, + 0, -3, -6, -9, -12, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46, + -49, -51, -54, -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88, + -90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, -112, -113, -115, -116, + -117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127, + -127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118, + -117, -116, -115, -113, -112, -111, -109, -107, -106, -104, -102, -100, -98, -96, -94, -92, + -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51, + -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3}; + +//tab42496 +const unsigned char rectangle[] = { + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70}; + +//random data ? +const unsigned char sampleTable[0x500] = { + //00 + + 0x38, + 0x84, + 0x6B, + 0x19, + 0xC6, + 0x63, + 0x18, + 0x86, + 0x73, + 0x98, + 0xC6, + 0xB1, + 0x1C, + 0xCA, + 0x31, + 0x8C, + 0xC7, + 0x31, + 0x88, + 0xC2, + 0x30, + 0x98, + 0x46, + 0x31, + 0x18, + 0xC6, + 0x35, + 0xC, + 0xCA, + 0x31, + 0xC, + 0xC6 + //20 + , + 0x21, + 0x10, + 0x24, + 0x69, + 0x12, + 0xC2, + 0x31, + 0x14, + 0xC4, + 0x71, + 8, + 0x4A, + 0x22, + 0x49, + 0xAB, + 0x6A, + 0xA8, + 0xAC, + 0x49, + 0x51, + 0x32, + 0xD5, + 0x52, + 0x88, + 0x93, + 0x6C, + 0x94, + 0x22, + 0x15, + 0x54, + 0xD2, + 0x25 + //40 + , + 0x96, + 0xD4, + 0x50, + 0xA5, + 0x46, + 0x21, + 8, + 0x85, + 0x6B, + 0x18, + 0xC4, + 0x63, + 0x10, + 0xCE, + 0x6B, + 0x18, + 0x8C, + 0x71, + 0x19, + 0x8C, + 0x63, + 0x35, + 0xC, + 0xC6, + 0x33, + 0x99, + 0xCC, + 0x6C, + 0xB5, + 0x4E, + 0xA2, + 0x99 + //60 + , + 0x46, + 0x21, + 0x28, + 0x82, + 0x95, + 0x2E, + 0xE3, + 0x30, + 0x9C, + 0xC5, + 0x30, + 0x9C, + 0xA2, + 0xB1, + 0x9C, + 0x67, + 0x31, + 0x88, + 0x66, + 0x59, + 0x2C, + 0x53, + 0x18, + 0x84, + 0x67, + 0x50, + 0xCA, + 0xE3, + 0xA, + 0xAC, + 0xAB, + 0x30 + //80 + , + 0xAC, + 0x62, + 0x30, + 0x8C, + 0x63, + 0x10, + 0x94, + 0x62, + 0xB1, + 0x8C, + 0x82, + 0x28, + 0x96, + 0x33, + 0x98, + 0xD6, + 0xB5, + 0x4C, + 0x62, + 0x29, + 0xA5, + 0x4A, + 0xB5, + 0x9C, + 0xC6, + 0x31, + 0x14, + 0xD6, + 0x38, + 0x9C, + 0x4B, + 0xB4 + //A0 + , + 0x86, + 0x65, + 0x18, + 0xAE, + 0x67, + 0x1C, + 0xA6, + 0x63, + 0x19, + 0x96, + 0x23, + 0x19, + 0x84, + 0x13, + 8, + 0xA6, + 0x52, + 0xAC, + 0xCA, + 0x22, + 0x89, + 0x6E, + 0xAB, + 0x19, + 0x8C, + 0x62, + 0x34, + 0xC4, + 0x62, + 0x19, + 0x86, + 0x63 + //C0 + , + 0x18, + 0xC4, + 0x23, + 0x58, + 0xD6, + 0xA3, + 0x50, + 0x42, + 0x54, + 0x4A, + 0xAD, + 0x4A, + 0x25, + 0x11, + 0x6B, + 0x64, + 0x89, + 0x4A, + 0x63, + 0x39, + 0x8A, + 0x23, + 0x31, + 0x2A, + 0xEA, + 0xA2, + 0xA9, + 0x44, + 0xC5, + 0x12, + 0xCD, + 0x42 + //E0 + , + 0x34, + 0x8C, + 0x62, + 0x18, + 0x8C, + 0x63, + 0x11, + 0x48, + 0x66, + 0x31, + 0x9D, + 0x44, + 0x33, + 0x1D, + 0x46, + 0x31, + 0x9C, + 0xC6, + 0xB1, + 0xC, + 0xCD, + 0x32, + 0x88, + 0xC4, + 0x73, + 0x18, + 0x86, + 0x73, + 8, + 0xD6, + 0x63, + 0x58 + //100 + , + 7, + 0x81, + 0xE0, + 0xF0, + 0x3C, + 7, + 0x87, + 0x90, + 0x3C, + 0x7C, + 0xF, + 0xC7, + 0xC0, + 0xC0, + 0xF0, + 0x7C, + 0x1E, + 7, + 0x80, + 0x80, + 0, + 0x1C, + 0x78, + 0x70, + 0xF1, + 0xC7, + 0x1F, + 0xC0, + 0xC, + 0xFE, + 0x1C, + 0x1F + //120 + , + 0x1F, + 0xE, + 0xA, + 0x7A, + 0xC0, + 0x71, + 0xF2, + 0x83, + 0x8F, + 3, + 0xF, + 0xF, + 0xC, + 0, + 0x79, + 0xF8, + 0x61, + 0xE0, + 0x43, + 0xF, + 0x83, + 0xE7, + 0x18, + 0xF9, + 0xC1, + 0x13, + 0xDA, + 0xE9, + 0x63, + 0x8F, + 0xF, + 0x83 + //140 + , + 0x83, + 0x87, + 0xC3, + 0x1F, + 0x3C, + 0x70, + 0xF0, + 0xE1, + 0xE1, + 0xE3, + 0x87, + 0xB8, + 0x71, + 0xE, + 0x20, + 0xE3, + 0x8D, + 0x48, + 0x78, + 0x1C, + 0x93, + 0x87, + 0x30, + 0xE1, + 0xC1, + 0xC1, + 0xE4, + 0x78, + 0x21, + 0x83, + 0x83, + 0xC3 + //160 + , + 0x87, + 6, + 0x39, + 0xE5, + 0xC3, + 0x87, + 7, + 0xE, + 0x1C, + 0x1C, + 0x70, + 0xF4, + 0x71, + 0x9C, + 0x60, + 0x36, + 0x32, + 0xC3, + 0x1E, + 0x3C, + 0xF3, + 0x8F, + 0xE, + 0x3C, + 0x70, + 0xE3, + 0xC7, + 0x8F, + 0xF, + 0xF, + 0xE, + 0x3C + //180 + , + 0x78, + 0xF0, + 0xE3, + 0x87, + 6, + 0xF0, + 0xE3, + 7, + 0xC1, + 0x99, + 0x87, + 0xF, + 0x18, + 0x78, + 0x70, + 0x70, + 0xFC, + 0xF3, + 0x10, + 0xB1, + 0x8C, + 0x8C, + 0x31, + 0x7C, + 0x70, + 0xE1, + 0x86, + 0x3C, + 0x64, + 0x6C, + 0xB0, + 0xE1 + //1A0 + , + 0xE3, + 0xF, + 0x23, + 0x8F, + 0xF, + 0x1E, + 0x3E, + 0x38, + 0x3C, + 0x38, + 0x7B, + 0x8F, + 7, + 0xE, + 0x3C, + 0xF4, + 0x17, + 0x1E, + 0x3C, + 0x78, + 0xF2, + 0x9E, + 0x72, + 0x49, + 0xE3, + 0x25, + 0x36, + 0x38, + 0x58, + 0x39, + 0xE2, + 0xDE + //1C0 + , + 0x3C, + 0x78, + 0x78, + 0xE1, + 0xC7, + 0x61, + 0xE1, + 0xE1, + 0xB0, + 0xF0, + 0xF0, + 0xC3, + 0xC7, + 0xE, + 0x38, + 0xC0, + 0xF0, + 0xCE, + 0x73, + 0x73, + 0x18, + 0x34, + 0xB0, + 0xE1, + 0xC7, + 0x8E, + 0x1C, + 0x3C, + 0xF8, + 0x38, + 0xF0, + 0xE1 + //1E0 + , + 0xC1, + 0x8B, + 0x86, + 0x8F, + 0x1C, + 0x78, + 0x70, + 0xF0, + 0x78, + 0xAC, + 0xB1, + 0x8F, + 0x39, + 0x31, + 0xDB, + 0x38, + 0x61, + 0xC3, + 0xE, + 0xE, + 0x38, + 0x78, + 0x73, + 0x17, + 0x1E, + 0x39, + 0x1E, + 0x38, + 0x64, + 0xE1, + 0xF1, + 0xC1 + //200 + , + 0x4E, + 0xF, + 0x40, + 0xA2, + 2, + 0xC5, + 0x8F, + 0x81, + 0xA1, + 0xFC, + 0x12, + 8, + 0x64, + 0xE0, + 0x3C, + 0x22, + 0xE0, + 0x45, + 7, + 0x8E, + 0xC, + 0x32, + 0x90, + 0xF0, + 0x1F, + 0x20, + 0x49, + 0xE0, + 0xF8, + 0xC, + 0x60, + 0xF0 + //220 + , + 0x17, + 0x1A, + 0x41, + 0xAA, + 0xA4, + 0xD0, + 0x8D, + 0x12, + 0x82, + 0x1E, + 0x1E, + 3, + 0xF8, + 0x3E, + 3, + 0xC, + 0x73, + 0x80, + 0x70, + 0x44, + 0x26, + 3, + 0x24, + 0xE1, + 0x3E, + 4, + 0x4E, + 4, + 0x1C, + 0xC1, + 9, + 0xCC + //240 + , + 0x9E, + 0x90, + 0x21, + 7, + 0x90, + 0x43, + 0x64, + 0xC0, + 0xF, + 0xC6, + 0x90, + 0x9C, + 0xC1, + 0x5B, + 3, + 0xE2, + 0x1D, + 0x81, + 0xE0, + 0x5E, + 0x1D, + 3, + 0x84, + 0xB8, + 0x2C, + 0xF, + 0x80, + 0xB1, + 0x83, + 0xE0, + 0x30, + 0x41 + //260 + , + 0x1E, + 0x43, + 0x89, + 0x83, + 0x50, + 0xFC, + 0x24, + 0x2E, + 0x13, + 0x83, + 0xF1, + 0x7C, + 0x4C, + 0x2C, + 0xC9, + 0xD, + 0x83, + 0xB0, + 0xB5, + 0x82, + 0xE4, + 0xE8, + 6, + 0x9C, + 7, + 0xA0, + 0x99, + 0x1D, + 7, + 0x3E, + 0x82, + 0x8F + //280 + , + 0x70, + 0x30, + 0x74, + 0x40, + 0xCA, + 0x10, + 0xE4, + 0xE8, + 0xF, + 0x92, + 0x14, + 0x3F, + 6, + 0xF8, + 0x84, + 0x88, + 0x43, + 0x81, + 0xA, + 0x34, + 0x39, + 0x41, + 0xC6, + 0xE3, + 0x1C, + 0x47, + 3, + 0xB0, + 0xB8, + 0x13, + 0xA, + 0xC2 + //2A0 + , + 0x64, + 0xF8, + 0x18, + 0xF9, + 0x60, + 0xB3, + 0xC0, + 0x65, + 0x20, + 0x60, + 0xA6, + 0x8C, + 0xC3, + 0x81, + 0x20, + 0x30, + 0x26, + 0x1E, + 0x1C, + 0x38, + 0xD3, + 1, + 0xB0, + 0x26, + 0x40, + 0xF4, + 0xB, + 0xC3, + 0x42, + 0x1F, + 0x85, + 0x32 + //2C0 + , + 0x26, + 0x60, + 0x40, + 0xC9, + 0xCB, + 1, + 0xEC, + 0x11, + 0x28, + 0x40, + 0xFA, + 4, + 0x34, + 0xE0, + 0x70, + 0x4C, + 0x8C, + 0x1D, + 7, + 0x69, + 3, + 0x16, + 0xC8, + 4, + 0x23, + 0xE8, + 0xC6, + 0x9A, + 0xB, + 0x1A, + 3, + 0xE0 + //2E0 + , + 0x76, + 6, + 5, + 0xCF, + 0x1E, + 0xBC, + 0x58, + 0x31, + 0x71, + 0x66, + 0, + 0xF8, + 0x3F, + 4, + 0xFC, + 0xC, + 0x74, + 0x27, + 0x8A, + 0x80, + 0x71, + 0xC2, + 0x3A, + 0x26, + 6, + 0xC0, + 0x1F, + 5, + 0xF, + 0x98, + 0x40, + 0xAE + //300 + , + 1, + 0x7F, + 0xC0, + 7, + 0xFF, + 0, + 0xE, + 0xFE, + 0, + 3, + 0xDF, + 0x80, + 3, + 0xEF, + 0x80, + 0x1B, + 0xF1, + 0xC2, + 0, + 0xE7, + 0xE0, + 0x18, + 0xFC, + 0xE0, + 0x21, + 0xFC, + 0x80, + 0x3C, + 0xFC, + 0x40, + 0xE, + 0x7E + //320 + , + 0, + 0x3F, + 0x3E, + 0, + 0xF, + 0xFE, + 0, + 0x1F, + 0xFF, + 0, + 0x3E, + 0xF0, + 7, + 0xFC, + 0, + 0x7E, + 0x10, + 0x3F, + 0xFF, + 0, + 0x3F, + 0x38, + 0xE, + 0x7C, + 1, + 0x87, + 0xC, + 0xFC, + 0xC7, + 0, + 0x3E, + 4 + //340 + , + 0xF, + 0x3E, + 0x1F, + 0xF, + 0xF, + 0x1F, + 0xF, + 2, + 0x83, + 0x87, + 0xCF, + 3, + 0x87, + 0xF, + 0x3F, + 0xC0, + 7, + 0x9E, + 0x60, + 0x3F, + 0xC0, + 3, + 0xFE, + 0, + 0x3F, + 0xE0, + 0x77, + 0xE1, + 0xC0, + 0xFE, + 0xE0, + 0xC3 + //360 + , + 0xE0, + 1, + 0xDF, + 0xF8, + 3, + 7, + 0, + 0x7E, + 0x70, + 0, + 0x7C, + 0x38, + 0x18, + 0xFE, + 0xC, + 0x1E, + 0x78, + 0x1C, + 0x7C, + 0x3E, + 0xE, + 0x1F, + 0x1E, + 0x1E, + 0x3E, + 0, + 0x7F, + 0x83, + 7, + 0xDB, + 0x87, + 0x83 + //380 + , + 7, + 0xC7, + 7, + 0x10, + 0x71, + 0xFF, + 0, + 0x3F, + 0xE2, + 1, + 0xE0, + 0xC1, + 0xC3, + 0xE1, + 0, + 0x7F, + 0xC0, + 5, + 0xF0, + 0x20, + 0xF8, + 0xF0, + 0x70, + 0xFE, + 0x78, + 0x79, + 0xF8, + 2, + 0x3F, + 0xC, + 0x8F, + 3 + //3a0 + , + 0xF, + 0x9F, + 0xE0, + 0xC1, + 0xC7, + 0x87, + 3, + 0xC3, + 0xC3, + 0xB0, + 0xE1, + 0xE1, + 0xC1, + 0xE3, + 0xE0, + 0x71, + 0xF0, + 0, + 0xFC, + 0x70, + 0x7C, + 0xC, + 0x3E, + 0x38, + 0xE, + 0x1C, + 0x70, + 0xC3, + 0xC7, + 3, + 0x81, + 0xC1 + //3c0 + , + 0xC7, + 0xE7, + 0, + 0xF, + 0xC7, + 0x87, + 0x19, + 9, + 0xEF, + 0xC4, + 0x33, + 0xE0, + 0xC1, + 0xFC, + 0xF8, + 0x70, + 0xF0, + 0x78, + 0xF8, + 0xF0, + 0x61, + 0xC7, + 0, + 0x1F, + 0xF8, + 1, + 0x7C, + 0xF8, + 0xF0, + 0x78, + 0x70, + 0x3C + //3e0 + , + 0x7C, + 0xCE, + 0xE, + 0x21, + 0x83, + 0xCF, + 8, + 7, + 0x8F, + 8, + 0xC1, + 0x87, + 0x8F, + 0x80, + 0xC7, + 0xE3, + 0, + 7, + 0xF8, + 0xE0, + 0xEF, + 0, + 0x39, + 0xF7, + 0x80, + 0xE, + 0xF8, + 0xE1, + 0xE3, + 0xF8, + 0x21, + 0x9F + //400 + , + 0xC0, + 0xFF, + 3, + 0xF8, + 7, + 0xC0, + 0x1F, + 0xF8, + 0xC4, + 4, + 0xFC, + 0xC4, + 0xC1, + 0xBC, + 0x87, + 0xF0, + 0xF, + 0xC0, + 0x7F, + 5, + 0xE0, + 0x25, + 0xEC, + 0xC0, + 0x3E, + 0x84, + 0x47, + 0xF0, + 0x8E, + 3, + 0xF8, + 3 + //420 + , + 0xFB, + 0xC0, + 0x19, + 0xF8, + 7, + 0x9C, + 0xC, + 0x17, + 0xF8, + 7, + 0xE0, + 0x1F, + 0xA1, + 0xFC, + 0xF, + 0xFC, + 1, + 0xF0, + 0x3F, + 0, + 0xFE, + 3, + 0xF0, + 0x1F, + 0, + 0xFD, + 0, + 0xFF, + 0x88, + 0xD, + 0xF9, + 1 + //440 + , + 0xFF, + 0, + 0x70, + 7, + 0xC0, + 0x3E, + 0x42, + 0xF3, + 0xD, + 0xC4, + 0x7F, + 0x80, + 0xFC, + 7, + 0xF0, + 0x5E, + 0xC0, + 0x3F, + 0, + 0x78, + 0x3F, + 0x81, + 0xFF, + 1, + 0xF8, + 1, + 0xC3, + 0xE8, + 0xC, + 0xE4, + 0x64, + 0x8F + ////460 + , + 0xE4, + 0xF, + 0xF0, + 7, + 0xF0, + 0xC2, + 0x1F, + 0, + 0x7F, + 0xC0, + 0x6F, + 0x80, + 0x7E, + 3, + 0xF8, + 7, + 0xF0, + 0x3F, + 0xC0, + 0x78, + 0xF, + 0x82, + 7, + 0xFE, + 0x22, + 0x77, + 0x70, + 2, + 0x76, + 3, + 0xFE, + 0 + //480 + , + 0xFE, + 0x67, + 0, + 0x7C, + 0xC7, + 0xF1, + 0x8E, + 0xC6, + 0x3B, + 0xE0, + 0x3F, + 0x84, + 0xF3, + 0x19, + 0xD8, + 3, + 0x99, + 0xFC, + 9, + 0xB8, + 0xF, + 0xF8, + 0, + 0x9D, + 0x24, + 0x61, + 0xF9, + 0xD, + 0, + 0xFD, + 3, + 0xF0 + //4a0 + , + 0x1F, + 0x90, + 0x3F, + 1, + 0xF8, + 0x1F, + 0xD0, + 0xF, + 0xF8, + 0x37, + 1, + 0xF8, + 7, + 0xF0, + 0xF, + 0xC0, + 0x3F, + 0, + 0xFE, + 3, + 0xF8, + 0xF, + 0xC0, + 0x3F, + 0, + 0xFA, + 3, + 0xF0, + 0xF, + 0x80, + 0xFF, + 1 + //4c0 + , + 0xB8, + 7, + 0xF0, + 1, + 0xFC, + 1, + 0xBC, + 0x80, + 0x13, + 0x1E, + 0, + 0x7F, + 0xE1, + 0x40, + 0x7F, + 0xA0, + 0x7F, + 0xB0, + 0, + 0x3F, + 0xC0, + 0x1F, + 0xC0, + 0x38, + 0xF, + 0xF0, + 0x1F, + 0x80, + 0xFF, + 1, + 0xFC, + 3 + //4e0 + , + 0xF1, + 0x7E, + 1, + 0xFE, + 1, + 0xF0, + 0xFF, + 0, + 0x7F, + 0xC0, + 0x1D, + 7, + 0xF0, + 0xF, + 0xC0, + 0x7E, + 6, + 0xE0, + 7, + 0xE0, + 0xF, + 0xF8, + 6, + 0xC1, + 0xFE, + 1, + 0xFC, + 3, + 0xE0, + 0xF, + 0, + 0xFC}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Render +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char pitches[256]; // tab43008 + +unsigned char frequency1[256]; +unsigned char frequency2[256]; +unsigned char frequency3[256]; + +unsigned char amplitude1[256]; +unsigned char amplitude2[256]; +unsigned char amplitude3[256]; + +unsigned char sampledConsonantFlag[256]; // tab44800 + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char stress[256]; //numbers from 0 to 8 +unsigned char phonemeLength[256]; //tab40160 +unsigned char phonemeindex[256]; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// ReciterTabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//some flags +const unsigned char tab36376[] = { + 0, 0, 0, 0, 0, 0, 0, 0, // 0-7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 130, // ' ', '!' + 0, 0, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 2, 2, 192, 168, 176, 172, 192, 160, 184, // '@', 'A' + 160, 192, 188, 160, 172, 168, 172, 192, 160, 160, 172, 180, 164, 192, 168, 168, + 176, 192, 188, 0, 0, 0, 2, 0, // 'X', 'Y', 'Z', '[', + 32, 32, 155, 32, 192, 185, 32, 205, 163, 76, 138, 142}; + +const unsigned char rules[] = { + ']', 'A' | 0x80, ' ', '(', 'A', '.', ')', '=', + 'E', 'H', '4', 'Y', '.', ' ' | 0x80, '(', 'A', + ')', ' ', '=', 'A', 'H' | 0x80, ' ', '(', 'A', + 'R', 'E', ')', ' ', '=', 'A', 'A', 'R' | 0x80, + ' ', '(', 'A', 'R', ')', 'O', '=', 'A', + 'X', 'R' | 0x80, '(', 'A', 'R', ')', '#', '=', + 'E', 'H', '4', 'R' | 0x80, ' ', '^', '(', 'A', + 'S', ')', '#', '=', 'E', 'Y', '4', 'S' | 0x80, + '(', 'A', ')', 'W', 'A', '=', 'A', 'X' | 0x80, + '(', 'A', 'W', ')', '=', 'A', 'O', '5' | 0x80, + ' ', ':', '(', 'A', 'N', 'Y', ')', '=', + 'E', 'H', '4', 'N', 'I', 'Y' | 0x80, '(', 'A', + ')', '^', '+', '#', '=', 'E', 'Y', '5' | 0x80, + '#', ':', '(', 'A', 'L', 'L', 'Y', ')', + '=', 'U', 'L', 'I', 'Y' | 0x80, ' ', '(', 'A', + 'L', ')', '#', '=', 'U', 'L' | 0x80, '(', 'A', + 'G', 'A', 'I', 'N', ')', '=', 'A', 'X', + 'G', 'E', 'H', '4', 'N' | 0x80, '#', ':', '(', + 'A', 'G', ')', 'E', '=', 'I', 'H', 'J' | 0x80, + '(', 'A', ')', '^', '%', '=', 'E', 'Y' | 0x80, + '(', 'A', ')', '^', '+', ':', '#', '=', + 'A', 'E' | 0x80, ' ', ':', '(', 'A', ')', '^', + '+', ' ', '=', 'E', 'Y', '4' | 0x80, ' ', '(', + 'A', 'R', 'R', ')', '=', 'A', 'X', 'R' | 0x80, + '(', 'A', 'R', 'R', ')', '=', 'A', 'E', + '4', 'R' | 0x80, ' ', '^', '(', 'A', 'R', ')', + ' ', '=', 'A', 'A', '5', 'R' | 0x80, '(', 'A', + 'R', ')', '=', 'A', 'A', '5', 'R' | 0x80, '(', + 'A', 'I', 'R', ')', '=', 'E', 'H', '4', + 'R' | 0x80, '(', 'A', 'I', ')', '=', 'E', 'Y', + '4' | 0x80, '(', 'A', 'Y', ')', '=', 'E', 'Y', + '5' | 0x80, '(', 'A', 'U', ')', '=', 'A', 'O', + '4' | 0x80, '#', ':', '(', 'A', 'L', ')', ' ', + '=', 'U', 'L' | 0x80, '#', ':', '(', 'A', 'L', + 'S', ')', ' ', '=', 'U', 'L', 'Z' | 0x80, '(', + 'A', 'L', 'K', ')', '=', 'A', 'O', '4', + 'K' | 0x80, '(', 'A', 'L', ')', '^', '=', 'A', + 'O', 'L' | 0x80, ' ', ':', '(', 'A', 'B', 'L', + 'E', ')', '=', 'E', 'Y', '4', 'B', 'U', + 'L' | 0x80, '(', 'A', 'B', 'L', 'E', ')', '=', + 'A', 'X', 'B', 'U', 'L' | 0x80, '(', 'A', ')', + 'V', 'O', '=', 'E', 'Y', '4' | 0x80, '(', 'A', + 'N', 'G', ')', '+', '=', 'E', 'Y', '4', + 'N', 'J' | 0x80, '(', 'A', 'T', 'A', 'R', 'I', + ')', '=', 'A', 'H', 'T', 'A', 'A', '4', + 'R', 'I', 'Y' | 0x80, '(', 'A', ')', 'T', 'O', + 'M', '=', 'A', 'E' | 0x80, '(', 'A', ')', 'T', + 'T', 'I', '=', 'A', 'E' | 0x80, ' ', '(', 'A', + 'T', ')', ' ', '=', 'A', 'E', 'T' | 0x80, ' ', + '(', 'A', ')', 'T', '=', 'A', 'H' | 0x80, '(', + 'A', ')', '=', 'A', 'E' | 0x80, + + ']', 'B' | 0x80, ' ', '(', 'B', ')', ' ', '=', + 'B', 'I', 'Y', '4' | 0x80, ' ', '(', 'B', 'E', + ')', '^', '#', '=', 'B', 'I', 'H' | 0x80, '(', + 'B', 'E', 'I', 'N', 'G', ')', '=', 'B', + 'I', 'Y', '4', 'I', 'H', 'N', 'X' | 0x80, ' ', + '(', 'B', 'O', 'T', 'H', ')', ' ', '=', + 'B', 'O', 'W', '4', 'T', 'H' | 0x80, ' ', '(', + 'B', 'U', 'S', ')', '#', '=', 'B', 'I', + 'H', '4', 'Z' | 0x80, '(', 'B', 'R', 'E', 'A', + 'K', ')', '=', 'B', 'R', 'E', 'Y', '5', + 'K' | 0x80, '(', 'B', 'U', 'I', 'L', ')', '=', + 'B', 'I', 'H', '4', 'L' | 0x80, '(', 'B', ')', + '=', 'B' | 0x80, + + ']', 'C' | 0x80, ' ', '(', 'C', ')', ' ', '=', + 'S', 'I', 'Y', '4' | 0x80, ' ', '(', 'C', 'H', + ')', '^', '=', 'K' | 0x80, '^', 'E', '(', 'C', + 'H', ')', '=', 'K' | 0x80, '(', 'C', 'H', 'A', + ')', 'R', '#', '=', 'K', 'E', 'H', '5' | 0x80, + '(', 'C', 'H', ')', '=', 'C', 'H' | 0x80, ' ', + 'S', '(', 'C', 'I', ')', '#', '=', 'S', + 'A', 'Y', '4' | 0x80, '(', 'C', 'I', ')', 'A', + '=', 'S', 'H' | 0x80, '(', 'C', 'I', ')', 'O', + '=', 'S', 'H' | 0x80, '(', 'C', 'I', ')', 'E', + 'N', '=', 'S', 'H' | 0x80, '(', 'C', 'I', 'T', + 'Y', ')', '=', 'S', 'I', 'H', 'T', 'I', + 'Y' | 0x80, '(', 'C', ')', '+', '=', 'S' | 0x80, '(', + 'C', 'K', ')', '=', 'K' | 0x80, '(', 'C', 'O', + 'M', 'M', 'O', 'D', 'O', 'R', 'E', ')', + '=', 'K', 'A', 'A', '4', 'M', 'A', 'H', + 'D', 'O', 'H', 'R' | 0x80, '(', 'C', 'O', 'M', + ')', '=', 'K', 'A', 'H', 'M' | 0x80, '(', 'C', + 'U', 'I', 'T', ')', '=', 'K', 'I', 'H', + 'T' | 0x80, '(', 'C', 'R', 'E', 'A', ')', '=', + 'K', 'R', 'I', 'Y', 'E', 'Y' | 0x80, '(', 'C', + ')', '=', 'K' | 0x80, + + ']', 'D' | 0x80, ' ', '(', 'D', ')', ' ', '=', + 'D', 'I', 'Y', '4' | 0x80, ' ', '(', 'D', 'R', + '.', ')', ' ', '=', 'D', 'A', 'A', '4', + 'K', 'T', 'E', 'R' | 0x80, '#', ':', '(', 'D', + 'E', 'D', ')', ' ', '=', 'D', 'I', 'H', + 'D' | 0x80, '.', 'E', '(', 'D', ')', ' ', '=', + 'D' | 0x80, '#', ':', '^', 'E', '(', 'D', ')', + ' ', '=', 'T' | 0x80, ' ', '(', 'D', 'E', ')', + '^', '#', '=', 'D', 'I', 'H' | 0x80, ' ', '(', + 'D', 'O', ')', ' ', '=', 'D', 'U', 'W' | 0x80, + ' ', '(', 'D', 'O', 'E', 'S', ')', '=', + 'D', 'A', 'H', 'Z' | 0x80, '(', 'D', 'O', 'N', + 'E', ')', ' ', '=', 'D', 'A', 'H', '5', + 'N' | 0x80, '(', 'D', 'O', 'I', 'N', 'G', ')', + '=', 'D', 'U', 'W', '4', 'I', 'H', 'N', + 'X' | 0x80, ' ', '(', 'D', 'O', 'W', ')', '=', + 'D', 'A', 'W' | 0x80, '#', '(', 'D', 'U', ')', + 'A', '=', 'J', 'U', 'W' | 0x80, '#', '(', 'D', + 'U', ')', '^', '#', '=', 'J', 'A', 'X' | 0x80, + '(', 'D', ')', '=', 'D' | 0x80, + + ']', 'E' | 0x80, ' ', '(', 'E', ')', ' ', '=', + 'I', 'Y', 'I', 'Y', '4' | 0x80, '#', ':', '(', + 'E', ')', ' ', '=' | 0x80, '\'', ':', '^', '(', + 'E', ')', ' ', '=' | 0x80, ' ', ':', '(', 'E', + ')', ' ', '=', 'I', 'Y' | 0x80, '#', '(', 'E', + 'D', ')', ' ', '=', 'D' | 0x80, '#', ':', '(', + 'E', ')', 'D', ' ', '=' | 0x80, '(', 'E', 'V', + ')', 'E', 'R', '=', 'E', 'H', '4', 'V' | 0x80, + '(', 'E', ')', '^', '%', '=', 'I', 'Y', + '4' | 0x80, '(', 'E', 'R', 'I', ')', '#', '=', + 'I', 'Y', '4', 'R', 'I', 'Y' | 0x80, '(', 'E', + 'R', 'I', ')', '=', 'E', 'H', '4', 'R', + 'I', 'H' | 0x80, '#', ':', '(', 'E', 'R', ')', + '#', '=', 'E', 'R' | 0x80, '(', 'E', 'R', 'R', + 'O', 'R', ')', '=', 'E', 'H', '4', 'R', + 'O', 'H', 'R' | 0x80, '(', 'E', 'R', 'A', 'S', + 'E', ')', '=', 'I', 'H', 'R', 'E', 'Y', + '5', 'S' | 0x80, '(', 'E', 'R', ')', '#', '=', + 'E', 'H', 'R' | 0x80, '(', 'E', 'R', ')', '=', + 'E', 'R' | 0x80, ' ', '(', 'E', 'V', 'E', 'N', + ')', '=', 'I', 'Y', 'V', 'E', 'H', 'N' | 0x80, + '#', ':', '(', 'E', ')', 'W', '=' | 0x80, '@', + '(', 'E', 'W', ')', '=', 'U', 'W' | 0x80, '(', + 'E', 'W', ')', '=', 'Y', 'U', 'W' | 0x80, '(', + 'E', ')', 'O', '=', 'I', 'Y' | 0x80, '#', ':', + '&', '(', 'E', 'S', ')', ' ', '=', 'I', + 'H', 'Z' | 0x80, '#', ':', '(', 'E', ')', 'S', + ' ', '=' | 0x80, '#', ':', '(', 'E', 'L', 'Y', + ')', ' ', '=', 'L', 'I', 'Y' | 0x80, '#', ':', + '(', 'E', 'M', 'E', 'N', 'T', ')', '=', + 'M', 'E', 'H', 'N', 'T' | 0x80, '(', 'E', 'F', + 'U', 'L', ')', '=', 'F', 'U', 'H', 'L' | 0x80, + '(', 'E', 'E', ')', '=', 'I', 'Y', '4' | 0x80, + '(', 'E', 'A', 'R', 'N', ')', '=', 'E', + 'R', '5', 'N' | 0x80, ' ', '(', 'E', 'A', 'R', + ')', '^', '=', 'E', 'R', '5' | 0x80, '(', 'E', + 'A', 'D', ')', '=', 'E', 'H', 'D' | 0x80, '#', + ':', '(', 'E', 'A', ')', ' ', '=', 'I', + 'Y', 'A', 'X' | 0x80, '(', 'E', 'A', ')', 'S', + 'U', '=', 'E', 'H', '5' | 0x80, '(', 'E', 'A', + ')', '=', 'I', 'Y', '5' | 0x80, '(', 'E', 'I', + 'G', 'H', ')', '=', 'E', 'Y', '4' | 0x80, '(', + 'E', 'I', ')', '=', 'I', 'Y', '4' | 0x80, ' ', + '(', 'E', 'Y', 'E', ')', '=', 'A', 'Y', + '4' | 0x80, '(', 'E', 'Y', ')', '=', 'I', 'Y' | 0x80, + '(', 'E', 'U', ')', '=', 'Y', 'U', 'W', + '5' | 0x80, '(', 'E', 'Q', 'U', 'A', 'L', ')', + '=', 'I', 'Y', '4', 'K', 'W', 'U', 'L' | 0x80, + '(', 'E', ')', '=', 'E', 'H' | 0x80, + + ']', 'F' | 0x80, ' ', '(', 'F', ')', ' ', '=', + 'E', 'H', '4', 'F' | 0x80, '(', 'F', 'U', 'L', + ')', '=', 'F', 'U', 'H', 'L' | 0x80, '(', 'F', + 'R', 'I', 'E', 'N', 'D', ')', '=', 'F', + 'R', 'E', 'H', '5', 'N', 'D' | 0x80, '(', 'F', + 'A', 'T', 'H', 'E', 'R', ')', '=', 'F', + 'A', 'A', '4', 'D', 'H', 'E', 'R' | 0x80, '(', + 'F', ')', 'F', '=' | 0x80, '(', 'F', ')', '=', + 'F' | 0x80, + + ']', 'G' | 0x80, ' ', '(', 'G', ')', ' ', '=', + 'J', 'I', 'Y', '4' | 0x80, '(', 'G', 'I', 'V', + ')', '=', 'G', 'I', 'H', '5', 'V' | 0x80, ' ', + '(', 'G', ')', 'I', '^', '=', 'G' | 0x80, '(', + 'G', 'E', ')', 'T', '=', 'G', 'E', 'H', + '5' | 0x80, 'S', 'U', '(', 'G', 'G', 'E', 'S', + ')', '=', 'G', 'J', 'E', 'H', '4', 'S' | 0x80, + '(', 'G', 'G', ')', '=', 'G' | 0x80, ' ', 'B', + '#', '(', 'G', ')', '=', 'G' | 0x80, '(', 'G', + ')', '+', '=', 'J' | 0x80, '(', 'G', 'R', 'E', + 'A', 'T', ')', '=', 'G', 'R', 'E', 'Y', + '4', 'T' | 0x80, '(', 'G', 'O', 'N', ')', 'E', + '=', 'G', 'A', 'O', '5', 'N' | 0x80, '#', '(', + 'G', 'H', ')', '=' | 0x80, ' ', '(', 'G', 'N', + ')', '=', 'N' | 0x80, '(', 'G', ')', '=', 'G' | 0x80, + + ']', 'H' | 0x80, ' ', '(', 'H', ')', ' ', '=', + 'E', 'Y', '4', 'C', 'H' | 0x80, ' ', '(', 'H', + 'A', 'V', ')', '=', '/', 'H', 'A', 'E', + '6', 'V' | 0x80, ' ', '(', 'H', 'E', 'R', 'E', + ')', '=', '/', 'H', 'I', 'Y', 'R' | 0x80, ' ', + '(', 'H', 'O', 'U', 'R', ')', '=', 'A', + 'W', '5', 'E', 'R' | 0x80, '(', 'H', 'O', 'W', + ')', '=', '/', 'H', 'A', 'W' | 0x80, '(', 'H', + ')', '#', '=', '/', 'H' | 0x80, '(', 'H', ')', + '=' | 0x80, + + ']', 'I' | 0x80, ' ', '(', 'I', 'N', ')', '=', + 'I', 'H', 'N' | 0x80, ' ', '(', 'I', ')', ' ', + '=', 'A', 'Y', '4' | 0x80, '(', 'I', ')', ' ', + '=', 'A', 'Y' | 0x80, '(', 'I', 'N', ')', 'D', + '=', 'A', 'Y', '5', 'N' | 0x80, 'S', 'E', 'M', + '(', 'I', ')', '=', 'I', 'Y' | 0x80, ' ', 'A', + 'N', 'T', '(', 'I', ')', '=', 'A', 'Y' | 0x80, + '(', 'I', 'E', 'R', ')', '=', 'I', 'Y', + 'E', 'R' | 0x80, '#', ':', 'R', '(', 'I', 'E', + 'D', ')', ' ', '=', 'I', 'Y', 'D' | 0x80, '(', + 'I', 'E', 'D', ')', ' ', '=', 'A', 'Y', + '5', 'D' | 0x80, '(', 'I', 'E', 'N', ')', '=', + 'I', 'Y', 'E', 'H', 'N' | 0x80, '(', 'I', 'E', + ')', 'T', '=', 'A', 'Y', '4', 'E', 'H' | 0x80, + '(', 'I', '\'', ')', '=', 'A', 'Y', '5' | 0x80, + ' ', ':', '(', 'I', ')', '^', '%', '=', + 'A', 'Y', '5' | 0x80, ' ', ':', '(', 'I', 'E', + ')', ' ', '=', 'A', 'Y', '4' | 0x80, '(', 'I', + ')', '%', '=', 'I', 'Y' | 0x80, '(', 'I', 'E', + ')', '=', 'I', 'Y', '4' | 0x80, ' ', '(', 'I', + 'D', 'E', 'A', ')', '=', 'A', 'Y', 'D', + 'I', 'Y', '5', 'A', 'H' | 0x80, '(', 'I', ')', + '^', '+', ':', '#', '=', 'I', 'H' | 0x80, '(', + 'I', 'R', ')', '#', '=', 'A', 'Y', 'R' | 0x80, + '(', 'I', 'Z', ')', '%', '=', 'A', 'Y', + 'Z' | 0x80, '(', 'I', 'S', ')', '%', '=', 'A', + 'Y', 'Z' | 0x80, 'I', '^', '(', 'I', ')', '^', + '#', '=', 'I', 'H' | 0x80, '+', '^', '(', 'I', + ')', '^', '+', '=', 'A', 'Y' | 0x80, '#', ':', + '^', '(', 'I', ')', '^', '+', '=', 'I', + 'H' | 0x80, '(', 'I', ')', '^', '+', '=', 'A', + 'Y' | 0x80, '(', 'I', 'R', ')', '=', 'E', 'R' | 0x80, + '(', 'I', 'G', 'H', ')', '=', 'A', 'Y', + '4' | 0x80, '(', 'I', 'L', 'D', ')', '=', 'A', + 'Y', '5', 'L', 'D' | 0x80, ' ', '(', 'I', 'G', + 'N', ')', '=', 'I', 'H', 'G', 'N' | 0x80, '(', + 'I', 'G', 'N', ')', ' ', '=', 'A', 'Y', + '4', 'N' | 0x80, '(', 'I', 'G', 'N', ')', '^', + '=', 'A', 'Y', '4', 'N' | 0x80, '(', 'I', 'G', + 'N', ')', '%', '=', 'A', 'Y', '4', 'N' | 0x80, + '(', 'I', 'C', 'R', 'O', ')', '=', 'A', + 'Y', '4', 'K', 'R', 'O', 'H' | 0x80, '(', 'I', + 'Q', 'U', 'E', ')', '=', 'I', 'Y', '4', + 'K' | 0x80, '(', 'I', ')', '=', 'I', 'H' | 0x80, + + ']', 'J' | 0x80, ' ', '(', 'J', ')', ' ', '=', + 'J', 'E', 'Y', '4' | 0x80, '(', 'J', ')', '=', + 'J' | 0x80, + + ']', 'K' | 0x80, ' ', '(', 'K', ')', ' ', '=', + 'K', 'E', 'Y', '4' | 0x80, ' ', '(', 'K', ')', + 'N', '=' | 0x80, '(', 'K', ')', '=', 'K' | 0x80, + + ']', 'L' | 0x80, ' ', '(', 'L', ')', ' ', '=', + 'E', 'H', '4', 'L' | 0x80, '(', 'L', 'O', ')', + 'C', '#', '=', 'L', 'O', 'W' | 0x80, 'L', '(', + 'L', ')', '=' | 0x80, '#', ':', '^', '(', 'L', + ')', '%', '=', 'U', 'L' | 0x80, '(', 'L', 'E', + 'A', 'D', ')', '=', 'L', 'I', 'Y', 'D' | 0x80, + ' ', '(', 'L', 'A', 'U', 'G', 'H', ')', + '=', 'L', 'A', 'E', '4', 'F' | 0x80, '(', 'L', + ')', '=', 'L' | 0x80, + + ']', 'M' | 0x80, ' ', '(', 'M', ')', ' ', '=', + 'E', 'H', '4', 'M' | 0x80, ' ', '(', 'M', 'R', + '.', ')', ' ', '=', 'M', 'I', 'H', '4', + 'S', 'T', 'E', 'R' | 0x80, ' ', '(', 'M', 'S', + '.', ')', '=', 'M', 'I', 'H', '5', 'Z' | 0x80, + ' ', '(', 'M', 'R', 'S', '.', ')', ' ', + '=', 'M', 'I', 'H', '4', 'S', 'I', 'X', + 'Z' | 0x80, '(', 'M', 'O', 'V', ')', '=', 'M', + 'U', 'W', '4', 'V' | 0x80, '(', 'M', 'A', 'C', + 'H', 'I', 'N', ')', '=', 'M', 'A', 'H', + 'S', 'H', 'I', 'Y', '5', 'N' | 0x80, 'M', '(', + 'M', ')', '=' | 0x80, '(', 'M', ')', '=', 'M' | 0x80, + + ']', 'N' | 0x80, ' ', '(', 'N', ')', ' ', '=', + 'E', 'H', '4', 'N' | 0x80, 'E', '(', 'N', 'G', + ')', '+', '=', 'N', 'J' | 0x80, '(', 'N', 'G', + ')', 'R', '=', 'N', 'X', 'G' | 0x80, '(', 'N', + 'G', ')', '#', '=', 'N', 'X', 'G' | 0x80, '(', + 'N', 'G', 'L', ')', '%', '=', 'N', 'X', + 'G', 'U', 'L' | 0x80, '(', 'N', 'G', ')', '=', + 'N', 'X' | 0x80, '(', 'N', 'K', ')', '=', 'N', + 'X', 'K' | 0x80, ' ', '(', 'N', 'O', 'W', ')', + ' ', '=', 'N', 'A', 'W', '4' | 0x80, 'N', '(', + 'N', ')', '=' | 0x80, '(', 'N', 'O', 'N', ')', + 'E', '=', 'N', 'A', 'H', '4', 'N' | 0x80, '(', + 'N', ')', '=', 'N' | 0x80, + + ']', 'O' | 0x80, ' ', '(', 'O', ')', ' ', '=', + 'O', 'H', '4', 'W' | 0x80, '(', 'O', 'F', ')', + ' ', '=', 'A', 'H', 'V' | 0x80, ' ', '(', 'O', + 'H', ')', ' ', '=', 'O', 'W', '5' | 0x80, '(', + 'O', 'R', 'O', 'U', 'G', 'H', ')', '=', + 'E', 'R', '4', 'O', 'W' | 0x80, '#', ':', '(', + 'O', 'R', ')', ' ', '=', 'E', 'R' | 0x80, '#', + ':', '(', 'O', 'R', 'S', ')', ' ', '=', + 'E', 'R', 'Z' | 0x80, '(', 'O', 'R', ')', '=', + 'A', 'O', 'R' | 0x80, ' ', '(', 'O', 'N', 'E', + ')', '=', 'W', 'A', 'H', 'N' | 0x80, '#', '(', + 'O', 'N', 'E', ')', ' ', '=', 'W', 'A', + 'H', 'N' | 0x80, '(', 'O', 'W', ')', '=', 'O', + 'W' | 0x80, ' ', '(', 'O', 'V', 'E', 'R', ')', + '=', 'O', 'W', '5', 'V', 'E', 'R' | 0x80, 'P', + 'R', '(', 'O', ')', 'V', '=', 'U', 'W', + '4' | 0x80, '(', 'O', 'V', ')', '=', 'A', 'H', + '4', 'V' | 0x80, '(', 'O', ')', '^', '%', '=', + 'O', 'W', '5' | 0x80, '(', 'O', ')', '^', 'E', + 'N', '=', 'O', 'W' | 0x80, '(', 'O', ')', '^', + 'I', '#', '=', 'O', 'W', '5' | 0x80, '(', 'O', + 'L', ')', 'D', '=', 'O', 'W', '4', 'L' | 0x80, + '(', 'O', 'U', 'G', 'H', 'T', ')', '=', + 'A', 'O', '5', 'T' | 0x80, '(', 'O', 'U', 'G', + 'H', ')', '=', 'A', 'H', '5', 'F' | 0x80, ' ', + '(', 'O', 'U', ')', '=', 'A', 'W' | 0x80, 'H', + '(', 'O', 'U', ')', 'S', '#', '=', 'A', + 'W', '4' | 0x80, '(', 'O', 'U', 'S', ')', '=', + 'A', 'X', 'S' | 0x80, '(', 'O', 'U', 'R', ')', + '=', 'O', 'H', 'R' | 0x80, '(', 'O', 'U', 'L', + 'D', ')', '=', 'U', 'H', '5', 'D' | 0x80, '(', + 'O', 'U', ')', '^', 'L', '=', 'A', 'H', + '5' | 0x80, '(', 'O', 'U', 'P', ')', '=', 'U', + 'W', '5', 'P' | 0x80, '(', 'O', 'U', ')', '=', + 'A', 'W' | 0x80, '(', 'O', 'Y', ')', '=', 'O', + 'Y' | 0x80, '(', 'O', 'I', 'N', 'G', ')', '=', + 'O', 'W', '4', 'I', 'H', 'N', 'X' | 0x80, '(', + 'O', 'I', ')', '=', 'O', 'Y', '5' | 0x80, '(', + 'O', 'O', 'R', ')', '=', 'O', 'H', '5', + 'R' | 0x80, '(', 'O', 'O', 'K', ')', '=', 'U', + 'H', '5', 'K' | 0x80, 'F', '(', 'O', 'O', 'D', + ')', '=', 'U', 'W', '5', 'D' | 0x80, 'L', '(', + 'O', 'O', 'D', ')', '=', 'A', 'H', '5', + 'D' | 0x80, 'M', '(', 'O', 'O', 'D', ')', '=', + 'U', 'W', '5', 'D' | 0x80, '(', 'O', 'O', 'D', + ')', '=', 'U', 'H', '5', 'D' | 0x80, 'F', '(', + 'O', 'O', 'T', ')', '=', 'U', 'H', '5', + 'T' | 0x80, '(', 'O', 'O', ')', '=', 'U', 'W', + '5' | 0x80, '(', 'O', '\'', ')', '=', 'O', 'H' | 0x80, + '(', 'O', ')', 'E', '=', 'O', 'W' | 0x80, '(', + 'O', ')', ' ', '=', 'O', 'W' | 0x80, '(', 'O', + 'A', ')', '=', 'O', 'W', '4' | 0x80, ' ', '(', + 'O', 'N', 'L', 'Y', ')', '=', 'O', 'W', + '4', 'N', 'L', 'I', 'Y' | 0x80, ' ', '(', 'O', + 'N', 'C', 'E', ')', '=', 'W', 'A', 'H', + '4', 'N', 'S' | 0x80, '(', 'O', 'N', '\'', 'T', + ')', '=', 'O', 'W', '4', 'N', 'T' | 0x80, 'C', + '(', 'O', ')', 'N', '=', 'A', 'A' | 0x80, '(', + 'O', ')', 'N', 'G', '=', 'A', 'O' | 0x80, ' ', + ':', '^', '(', 'O', ')', 'N', '=', 'A', + 'H' | 0x80, 'I', '(', 'O', 'N', ')', '=', 'U', + 'N' | 0x80, '#', ':', '(', 'O', 'N', ')', '=', + 'U', 'N' | 0x80, '#', '^', '(', 'O', 'N', ')', + '=', 'U', 'N' | 0x80, '(', 'O', ')', 'S', 'T', + '=', 'O', 'W' | 0x80, '(', 'O', 'F', ')', '^', + '=', 'A', 'O', '4', 'F' | 0x80, '(', 'O', 'T', + 'H', 'E', 'R', ')', '=', 'A', 'H', '5', + 'D', 'H', 'E', 'R' | 0x80, 'R', '(', 'O', ')', + 'B', '=', 'R', 'A', 'A' | 0x80, '^', 'R', '(', + 'O', ')', ':', '#', '=', 'O', 'W', '5' | 0x80, + '(', 'O', 'S', 'S', ')', ' ', '=', 'A', + 'O', '5', 'S' | 0x80, '#', ':', '^', '(', 'O', + 'M', ')', '=', 'A', 'H', 'M' | 0x80, '(', 'O', + ')', '=', 'A', 'A' | 0x80, + + ']', 'P' | 0x80, ' ', '(', 'P', ')', ' ', '=', + 'P', 'I', 'Y', '4' | 0x80, '(', 'P', 'H', ')', + '=', 'F' | 0x80, '(', 'P', 'E', 'O', 'P', 'L', + ')', '=', 'P', 'I', 'Y', '5', 'P', 'U', + 'L' | 0x80, '(', 'P', 'O', 'W', ')', '=', 'P', + 'A', 'W', '4' | 0x80, '(', 'P', 'U', 'T', ')', + ' ', '=', 'P', 'U', 'H', 'T' | 0x80, '(', 'P', + ')', 'P', '=' | 0x80, '(', 'P', ')', 'S', '=' | 0x80, + '(', 'P', ')', 'N', '=' | 0x80, '(', 'P', 'R', + 'O', 'F', '.', ')', '=', 'P', 'R', 'O', + 'H', 'F', 'E', 'H', '4', 'S', 'E', 'R' | 0x80, + '(', 'P', ')', '=', 'P' | 0x80, + + ']', 'Q' | 0x80, ' ', '(', 'Q', ')', ' ', '=', + 'K', 'Y', 'U', 'W', '4' | 0x80, '(', 'Q', 'U', + 'A', 'R', ')', '=', 'K', 'W', 'O', 'H', + '5', 'R' | 0x80, '(', 'Q', 'U', ')', '=', 'K', + 'W' | 0x80, '(', 'Q', ')', '=', 'K' | 0x80, ']', 'R' | 0x80, + ' ', '(', 'R', ')', ' ', '=', 'A', 'A', + '5', 'R' | 0x80, ' ', '(', 'R', 'E', ')', '^', + '#', '=', 'R', 'I', 'Y' | 0x80, '(', 'R', ')', + 'R', '=' | 0x80, '(', 'R', ')', '=', 'R' | 0x80, + + ']', 'S' | 0x80, ' ', '(', 'S', ')', ' ', '=', + 'E', 'H', '4', 'S' | 0x80, '(', 'S', 'H', ')', + '=', 'S', 'H' | 0x80, '#', '(', 'S', 'I', 'O', + 'N', ')', '=', 'Z', 'H', 'U', 'N' | 0x80, '(', + 'S', 'O', 'M', 'E', ')', '=', 'S', 'A', + 'H', 'M' | 0x80, '#', '(', 'S', 'U', 'R', ')', + '#', '=', 'Z', 'H', 'E', 'R' | 0x80, '(', 'S', + 'U', 'R', ')', '#', '=', 'S', 'H', 'E', + 'R' | 0x80, '#', '(', 'S', 'U', ')', '#', '=', + 'Z', 'H', 'U', 'W' | 0x80, '#', '(', 'S', 'S', + 'U', ')', '#', '=', 'S', 'H', 'U', 'W' | 0x80, + '#', '(', 'S', 'E', 'D', ')', '=', 'Z', + 'D' | 0x80, '#', '(', 'S', ')', '#', '=', 'Z' | 0x80, + '(', 'S', 'A', 'I', 'D', ')', '=', 'S', + 'E', 'H', 'D' | 0x80, '^', '(', 'S', 'I', 'O', + 'N', ')', '=', 'S', 'H', 'U', 'N' | 0x80, '(', + 'S', ')', 'S', '=' | 0x80, '.', '(', 'S', ')', + ' ', '=', 'Z' | 0x80, '#', ':', '.', 'E', '(', + 'S', ')', ' ', '=', 'Z' | 0x80, '#', ':', '^', + '#', '(', 'S', ')', ' ', '=', 'S' | 0x80, 'U', + '(', 'S', ')', ' ', '=', 'S' | 0x80, ' ', ':', + '#', '(', 'S', ')', ' ', '=', 'Z' | 0x80, '#', + '#', '(', 'S', ')', ' ', '=', 'Z' | 0x80, ' ', + '(', 'S', 'C', 'H', ')', '=', 'S', 'K' | 0x80, + '(', 'S', ')', 'C', '+', '=' | 0x80, '#', '(', + 'S', 'M', ')', '=', 'Z', 'U', 'M' | 0x80, '#', + '(', 'S', 'N', ')', '\'', '=', 'Z', 'U', + 'M' | 0x80, '(', 'S', 'T', 'L', 'E', ')', '=', + 'S', 'U', 'L' | 0x80, '(', 'S', ')', '=', 'S' | 0x80, + + ']', 'T' | 0x80, ' ', '(', 'T', ')', ' ', '=', + 'T', 'I', 'Y', '4' | 0x80, ' ', '(', 'T', 'H', + 'E', ')', ' ', '#', '=', 'D', 'H', 'I', + 'Y' | 0x80, ' ', '(', 'T', 'H', 'E', ')', ' ', + '=', 'D', 'H', 'A', 'X' | 0x80, '(', 'T', 'O', + ')', ' ', '=', 'T', 'U', 'X' | 0x80, ' ', '(', + 'T', 'H', 'A', 'T', ')', '=', 'D', 'H', + 'A', 'E', 'T' | 0x80, ' ', '(', 'T', 'H', 'I', + 'S', ')', ' ', '=', 'D', 'H', 'I', 'H', + 'S' | 0x80, ' ', '(', 'T', 'H', 'E', 'Y', ')', + '=', 'D', 'H', 'E', 'Y' | 0x80, ' ', '(', 'T', + 'H', 'E', 'R', 'E', ')', '=', 'D', 'H', + 'E', 'H', 'R' | 0x80, '(', 'T', 'H', 'E', 'R', + ')', '=', 'D', 'H', 'E', 'R' | 0x80, '(', 'T', + 'H', 'E', 'I', 'R', ')', '=', 'D', 'H', + 'E', 'H', 'R' | 0x80, ' ', '(', 'T', 'H', 'A', + 'N', ')', ' ', '=', 'D', 'H', 'A', 'E', + 'N' | 0x80, ' ', '(', 'T', 'H', 'E', 'M', ')', + ' ', '=', 'D', 'H', 'A', 'E', 'N' | 0x80, '(', + 'T', 'H', 'E', 'S', 'E', ')', ' ', '=', + 'D', 'H', 'I', 'Y', 'Z' | 0x80, ' ', '(', 'T', + 'H', 'E', 'N', ')', '=', 'D', 'H', 'E', + 'H', 'N' | 0x80, '(', 'T', 'H', 'R', 'O', 'U', + 'G', 'H', ')', '=', 'T', 'H', 'R', 'U', + 'W', '4' | 0x80, '(', 'T', 'H', 'O', 'S', 'E', + ')', '=', 'D', 'H', 'O', 'H', 'Z' | 0x80, '(', + 'T', 'H', 'O', 'U', 'G', 'H', ')', ' ', + '=', 'D', 'H', 'O', 'W' | 0x80, '(', 'T', 'O', + 'D', 'A', 'Y', ')', '=', 'T', 'U', 'X', + 'D', 'E', 'Y' | 0x80, '(', 'T', 'O', 'M', 'O', + ')', 'R', 'R', 'O', 'W', '=', 'T', 'U', + 'M', 'A', 'A', '5' | 0x80, '(', 'T', 'O', ')', + 'T', 'A', 'L', '=', 'T', 'O', 'W', '5' | 0x80, + ' ', '(', 'T', 'H', 'U', 'S', ')', '=', + 'D', 'H', 'A', 'H', '4', 'S' | 0x80, '(', 'T', + 'H', ')', '=', 'T', 'H' | 0x80, '#', ':', '(', + 'T', 'E', 'D', ')', '=', 'T', 'I', 'X', + 'D' | 0x80, 'S', '(', 'T', 'I', ')', '#', 'N', + '=', 'C', 'H' | 0x80, '(', 'T', 'I', ')', 'O', + '=', 'S', 'H' | 0x80, '(', 'T', 'I', ')', 'A', + '=', 'S', 'H' | 0x80, '(', 'T', 'I', 'E', 'N', + ')', '=', 'S', 'H', 'U', 'N' | 0x80, '(', 'T', + 'U', 'R', ')', '#', '=', 'C', 'H', 'E', + 'R' | 0x80, '(', 'T', 'U', ')', 'A', '=', 'C', + 'H', 'U', 'W' | 0x80, ' ', '(', 'T', 'W', 'O', + ')', '=', 'T', 'U', 'W' | 0x80, '&', '(', 'T', + ')', 'E', 'N', ' ', '=' | 0x80, '(', 'T', ')', + '=', 'T' | 0x80, + + ']', 'U' | 0x80, ' ', '(', 'U', ')', ' ', '=', + 'Y', 'U', 'W', '4' | 0x80, ' ', '(', 'U', 'N', + ')', 'I', '=', 'Y', 'U', 'W', 'N' | 0x80, ' ', + '(', 'U', 'N', ')', '=', 'A', 'H', 'N' | 0x80, + ' ', '(', 'U', 'P', 'O', 'N', ')', '=', + 'A', 'X', 'P', 'A', 'O', 'N' | 0x80, '@', '(', + 'U', 'R', ')', '#', '=', 'U', 'H', '4', + 'R' | 0x80, '(', 'U', 'R', ')', '#', '=', 'Y', + 'U', 'H', '4', 'R' | 0x80, '(', 'U', 'R', ')', + '=', 'E', 'R' | 0x80, '(', 'U', ')', '^', ' ', + '=', 'A', 'H' | 0x80, '(', 'U', ')', '^', '^', + '=', 'A', 'H', '5' | 0x80, '(', 'U', 'Y', ')', + '=', 'A', 'Y', '5' | 0x80, ' ', 'G', '(', 'U', + ')', '#', '=' | 0x80, 'G', '(', 'U', ')', '%', + '=' | 0x80, 'G', '(', 'U', ')', '#', '=', 'W' | 0x80, + '#', 'N', '(', 'U', ')', '=', 'Y', 'U', + 'W' | 0x80, '@', '(', 'U', ')', '=', 'U', 'W' | 0x80, + '(', 'U', ')', '=', 'Y', 'U', 'W' | 0x80, + + ']', 'V' | 0x80, ' ', '(', 'V', ')', ' ', '=', + 'V', 'I', 'Y', '4' | 0x80, '(', 'V', 'I', 'E', + 'W', ')', '=', 'V', 'Y', 'U', 'W', '5' | 0x80, + '(', 'V', ')', '=', 'V' | 0x80, + + ']', 'W' | 0x80, ' ', '(', 'W', ')', ' ', '=', + 'D', 'A', 'H', '4', 'B', 'U', 'L', 'Y', + 'U', 'W' | 0x80, ' ', '(', 'W', 'E', 'R', 'E', + ')', '=', 'W', 'E', 'R' | 0x80, '(', 'W', 'A', + ')', 'S', 'H', '=', 'W', 'A', 'A' | 0x80, '(', + 'W', 'A', ')', 'S', 'T', '=', 'W', 'E', + 'Y' | 0x80, '(', 'W', 'A', ')', 'S', '=', 'W', + 'A', 'H' | 0x80, '(', 'W', 'A', ')', 'T', '=', + 'W', 'A', 'A' | 0x80, '(', 'W', 'H', 'E', 'R', + 'E', ')', '=', 'W', 'H', 'E', 'H', 'R' | 0x80, + '(', 'W', 'H', 'A', 'T', ')', '=', 'W', + 'H', 'A', 'H', 'T' | 0x80, '(', 'W', 'H', 'O', + 'L', ')', '=', '/', 'H', 'O', 'W', 'L' | 0x80, + '(', 'W', 'H', 'O', ')', '=', '/', 'H', + 'U', 'W' | 0x80, '(', 'W', 'H', ')', '=', 'W', + 'H' | 0x80, '(', 'W', 'A', 'R', ')', '#', '=', + 'W', 'E', 'H', 'R' | 0x80, '(', 'W', 'A', 'R', + ')', '=', 'W', 'A', 'O', 'R' | 0x80, '(', 'W', + 'O', 'R', ')', '^', '=', 'W', 'E', 'R' | 0x80, + '(', 'W', 'R', ')', '=', 'R' | 0x80, '(', 'W', + 'O', 'M', ')', 'A', '=', 'W', 'U', 'H', + 'M' | 0x80, '(', 'W', 'O', 'M', ')', 'E', '=', + 'W', 'I', 'H', 'M' | 0x80, '(', 'W', 'E', 'A', + ')', 'R', '=', 'W', 'E', 'H' | 0x80, '(', 'W', + 'A', 'N', 'T', ')', '=', 'W', 'A', 'A', + '5', 'N', 'T' | 0x80, 'A', 'N', 'S', '(', 'W', + 'E', 'R', ')', '=', 'E', 'R' | 0x80, '(', 'W', + ')', '=', 'W' | 0x80, + + ']', 'X' | 0x80, ' ', '(', 'X', ')', ' ', '=', + 'E', 'H', '4', 'K', 'R' | 0x80, ' ', '(', 'X', + ')', '=', 'Z' | 0x80, '(', 'X', ')', '=', 'K', + 'S' | 0x80, + + ']', 'Y' | 0x80, ' ', '(', 'Y', ')', ' ', '=', + 'W', 'A', 'Y', '4' | 0x80, '(', 'Y', 'O', 'U', + 'N', 'G', ')', '=', 'Y', 'A', 'H', 'N', + 'X' | 0x80, ' ', '(', 'Y', 'O', 'U', 'R', ')', + '=', 'Y', 'O', 'H', 'R' | 0x80, ' ', '(', 'Y', + 'O', 'U', ')', '=', 'Y', 'U', 'W' | 0x80, ' ', + '(', 'Y', 'E', 'S', ')', '=', 'Y', 'E', + 'H', 'S' | 0x80, ' ', '(', 'Y', ')', '=', 'Y' | 0x80, + 'F', '(', 'Y', ')', '=', 'A', 'Y' | 0x80, 'P', + 'S', '(', 'Y', 'C', 'H', ')', '=', 'A', + 'Y', 'K' | 0x80, '#', ':', '^', '(', 'Y', ')', + '=', 'I', 'Y' | 0x80, '#', ':', '^', '(', 'Y', + ')', 'I', '=', 'I', 'Y' | 0x80, ' ', ':', '(', + 'Y', ')', ' ', '=', 'A', 'Y' | 0x80, ' ', ':', + '(', 'Y', ')', '#', '=', 'A', 'Y' | 0x80, ' ', + ':', '(', 'Y', ')', '^', '+', ':', '#', + '=', 'I', 'H' | 0x80, ' ', ':', '(', 'Y', ')', + '^', '#', '=', 'A', 'Y' | 0x80, '(', 'Y', ')', + '=', 'I', 'H' | 0x80, + + ']', 'Z' | 0x80, ' ', '(', 'Z', ')', ' ', '=', + 'Z', 'I', 'Y', '4' | 0x80, '(', 'Z', ')', '=', + 'Z' | 0x80, 'j' | 0x80}; + +const unsigned char rules2[] = { + '(', 'A', ')', '=' | 0x80, '(', '!', ')', '=', + '.' | 0x80, '(', '"', ')', ' ', '=', '-', 'A', + 'H', '5', 'N', 'K', 'W', 'O', 'W', 'T', + '-' | 0x80, '(', '"', ')', '=', 'K', 'W', 'O', + 'W', '4', 'T', '-' | 0x80, '(', '#', ')', '=', + ' ', 'N', 'A', 'H', '4', 'M', 'B', 'E', + 'R' | 0x80, '(', '$', ')', '=', ' ', 'D', 'A', + 'A', '4', 'L', 'E', 'R' | 0x80, '(', '%', ')', + '=', ' ', 'P', 'E', 'R', 'S', 'E', 'H', + '4', 'N', 'T' | 0x80, '(', '&', ')', '=', ' ', + 'A', 'E', 'N', 'D' | 0x80, '(', '\'', ')', '=' | 0x80, + '(', '*', ')', '=', ' ', 'A', 'E', '4', + 'S', 'T', 'E', 'R', 'I', 'H', 'S', 'K' | 0x80, + '(', '+', ')', '=', ' ', 'P', 'L', 'A', + 'H', '4', 'S' | 0x80, '(', ',', ')', '=', ',' | 0x80, + ' ', '(', '-', ')', ' ', '=', '-' | 0x80, '(', + '-', ')', '=' | 0x80, '(', '.', ')', '=', ' ', + 'P', 'O', 'Y', 'N', 'T' | 0x80, '(', '/', ')', + '=', ' ', 'S', 'L', 'A', 'E', '4', 'S', + 'H' | 0x80, '(', '0', ')', '=', ' ', 'Z', 'I', + 'Y', '4', 'R', 'O', 'W' | 0x80, ' ', '(', '1', + 'S', 'T', ')', '=', 'F', 'E', 'R', '4', + 'S', 'T' | 0x80, ' ', '(', '1', '0', 'T', 'H', + ')', '=', 'T', 'E', 'H', '4', 'N', 'T', + 'H' | 0x80, '(', '1', ')', '=', ' ', 'W', 'A', + 'H', '4', 'N' | 0x80, ' ', '(', '2', 'N', 'D', + ')', '=', 'S', 'E', 'H', '4', 'K', 'U', + 'N', 'D' | 0x80, '(', '2', ')', '=', ' ', 'T', + 'U', 'W', '4' | 0x80, ' ', '(', '3', 'R', 'D', + ')', '=', 'T', 'H', 'E', 'R', '4', 'D' | 0x80, + '(', '3', ')', '=', ' ', 'T', 'H', 'R', + 'I', 'Y', '4' | 0x80, '(', '4', ')', '=', ' ', + 'F', 'O', 'H', '4', 'R' | 0x80, ' ', '(', '5', + 'T', 'H', ')', '=', 'F', 'I', 'H', '4', + 'F', 'T', 'H' | 0x80, '(', '5', ')', '=', ' ', + 'F', 'A', 'Y', '4', 'V' | 0x80, ' ', '(', '6', + '4', ')', ' ', '=', 'S', 'I', 'H', '4', + 'K', 'S', 'T', 'I', 'Y', ' ', 'F', 'O', + 'H', 'R' | 0x80, '(', '6', ')', '=', ' ', 'S', + 'I', 'H', '4', 'K', 'S' | 0x80, '(', '7', ')', + '=', ' ', 'S', 'E', 'H', '4', 'V', 'U', + 'N' | 0x80, ' ', '(', '8', 'T', 'H', ')', '=', + 'E', 'Y', '4', 'T', 'H' | 0x80, '(', '8', ')', + '=', ' ', 'E', 'Y', '4', 'T' | 0x80, '(', '9', + ')', '=', ' ', 'N', 'A', 'Y', '4', 'N' | 0x80, + '(', ':', ')', '=', '.' | 0x80, '(', ';', ')', + '=', '.' | 0x80, '(', '<', ')', '=', ' ', 'L', + 'E', 'H', '4', 'S', ' ', 'D', 'H', 'A', + 'E', 'N' | 0x80, '(', '=', ')', '=', ' ', 'I', + 'Y', '4', 'K', 'W', 'U', 'L', 'Z' | 0x80, '(', + '>', ')', '=', ' ', 'G', 'R', 'E', 'Y', + '4', 'T', 'E', 'R', ' ', 'D', 'H', 'A', + 'E', 'N' | 0x80, '(', '?', ')', '=', '?' | 0x80, '(', + '@', ')', '=', ' ', 'A', 'E', '6', 'T' | 0x80, + '(', '^', ')', '=', ' ', 'K', 'A', 'E', + '4', 'R', 'I', 'X', 'T' | 0x80, ']', 'A' | 0x80}; + +//26 items. From 'A' to 'Z' +// positions for mem62 and mem63 for each character +const unsigned char tab37489[] = {0, 149, 247, 162, 57, 197, 6, 126, 199, 38, 55, 78, 145, + 241, 85, 161, 254, 36, 69, 45, 167, 54, 83, 46, 71, 218}; + +const unsigned char tab37515[] = {125, 126, 126, 127, 128, 129, 130, 130, 130, 132, 132, 132, 132, + 132, 133, 135, 135, 136, 136, 137, 138, 139, 139, 140, 140, 140}; + +void STM32SAM::Output8BitAry(int index, unsigned char ary[5]) { + int k; + + uint32_t bufferposOld = bufferpos; + + bufferpos += timetable[oldtimetableindex][index]; + oldtimetableindex = index; + + int sample_uS = bufferpos - bufferposOld; + + uint32_t f = 0; + + // write a little bit in advance + for(k = 0; k < 5; k++) { + // buffer[bufferpos / 50 + k] = ary[k]; + + // f = micros() + sample_uS / (_STM32SAM_SPEED + 1); + // while(micros() < f) { + // }; + f = sample_uS / (_STM32SAM_SPEED + 1); + furi_delay_us(f); + SetAUDIO(ary[k]); + // delayMicroseconds(sample_uS / 5 ); + } + + // SetAUDIO(ary[0]); +} + +void STM32SAM::Output8Bit(int index, unsigned char A) { + unsigned char ary[5] = {A, A, A, A, A}; + Output8BitAry(index, ary); +} + +//written by me because of different table positions. +// mem[47] = ... +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 +unsigned char STM32SAM::Read(unsigned char p, unsigned char Y) { + switch(p) { + case 168: + return pitches[Y]; + case 169: + return frequency1[Y]; + case 170: + return frequency2[Y]; + case 171: + return frequency3[Y]; + case 172: + return amplitude1[Y]; + case 173: + return amplitude2[Y]; + case 174: + return amplitude3[Y]; + } + // Serial1.println("Error reading to tables"); + return 0; +} + +void STM32SAM::Write(unsigned char p, unsigned char Y, unsigned char value) { + switch(p) { + case 168: + pitches[Y] = value; + return; + case 169: + frequency1[Y] = value; + return; + case 170: + frequency2[Y] = value; + return; + case 171: + frequency3[Y] = value; + return; + case 172: + amplitude1[Y] = value; + return; + case 173: + amplitude2[Y] = value; + return; + case 174: + amplitude3[Y] = value; + return; + } + //Serial1.println("Error writing to tables\n"); +} + +// ------------------------------------------------------------------------- +//Code48227 +// Render a sampled sound from the sampleTable. +// +// Phoneme Sample Start Sample End +// 32: S* 15 255 +// 33: SH 257 511 +// 34: F* 559 767 +// 35: TH 583 767 +// 36: /H 903 1023 +// 37: /X 1135 1279 +// 38: Z* 84 119 +// 39: ZH 340 375 +// 40: V* 596 639 +// 41: DH 596 631 +// +// 42: CH +// 43: ** 399 511 +// +// 44: J* +// 45: ** 257 276 +// 46: ** +// +// 66: P* +// 67: ** 743 767 +// 68: ** +// +// 69: T* +// 70: ** 231 255 +// 71: ** +// +// The SampledPhonemesTable[] holds flags indicating if a phoneme is +// voiced or not. If the upper 5 bits are zero, the sample is voiced. +// +// Samples in the sampleTable are compressed, with bits being converted to +// bytes from high bit to low, as follows: +// +// unvoiced 0 bit -> X +// unvoiced 1 bit -> 5 +// +// voiced 0 bit -> 6 +// voiced 1 bit -> 24 +// +// Where X is a value from the table: +// +// { 0x18, 0x1A, 0x17, 0x17, 0x17 }; +// +// The index into this table is determined by masking off the lower +// 3 bits from the SampledPhonemesTable: +// +// index = (SampledPhonemesTable[i] & 7) - 1; +// +// For voices samples, samples are interleaved between voiced output. + +// Code48227() +void STM32SAM::RenderSample(unsigned char* mem66) { + int tempA; + // current phoneme's index + mem49 = Y; + + // mask low three bits and subtract 1 get value to + // convert 0 bits on unvoiced samples. + A = mem39 & 7; + X = A - 1; + + // store the result + mem56 = X; + + // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } + // T, S, Z 0 0x18 + // CH, J, SH, ZH 1 0x1A + // P, F*, V, TH, DH 2 0x17 + // /H 3 0x17 + // /X 4 0x17 + + // get value from the table + mem53 = tab48426[X]; + mem47 = X; //46016+mem[56]*256 + + // voiced sample? + A = mem39 & 248; + if(A == 0) { + // voiced phoneme: Z*, ZH, V*, DH + Y = mem49; + A = pitches[mem49] >> 4; + + // jump to voiced portion + goto pos48315; + } + + Y = A ^ 255; +pos48274: + + // step through the 8 bits in the sample + mem56 = 8; + + // get the next sample from the table + // mem47*256 = offset to start of samples + A = sampleTable[mem47 * 256 + Y]; +pos48280: + + // left shift to get the high bit + tempA = A; + A = A << 1; + //48281: BCC 48290 + + // bit not set? + if((tempA & 128) == 0) { + // convert the bit to value from table + X = mem53; + //mem[54296] = X; + // output the byte + Output8Bit(1, (X & 0x0f) * 16); + // if X != 0, exit loop + if(X != 0) goto pos48296; + } + + // output a 5 for the on bit + Output8Bit(2, 5 * 16); + + //48295: NOP +pos48296: + + X = 0; + + // decrement counter + mem56--; + + // if not done, jump to top of loop + if(mem56 != 0) goto pos48280; + + // increment position + Y++; + if(Y != 0) goto pos48274; + + // restore values and return + mem44 = 1; + Y = mem49; + return; + + unsigned char phase1; + +pos48315: + // handle voiced samples here + + // number of samples? + phase1 = A ^ 255; + + Y = *mem66; + do { + //pos48321: + + // shift through all 8 bits + mem56 = 8; + //A = Read(mem47, Y); + + // fetch value from table + A = sampleTable[mem47 * 256 + Y]; + + // loop 8 times + //pos48327: + do { + //48327: ASL A + //48328: BCC 48337 + + // left shift and check high bit + tempA = A; + A = A << 1; + if((tempA & 128) != 0) { + // if bit set, output 26 + X = 26; + Output8Bit(3, (X & 0xf) * 16); + } else { + //timetable 4 + // bit is not set, output a 6 + X = 6; + Output8Bit(4, (X & 0xf) * 16); + } + + mem56--; + } while(mem56 != 0); + + // move ahead in the table + Y++; + + // continue until counter done + phase1++; + + } while(phase1 != 0); + // if (phase1 != 0) goto pos48321; + + // restore values and return + A = 1; + mem44 = 1; + *mem66 = Y; + Y = mem49; + return; +} + +// RENDER THE PHONEMES IN THE LIST +// +// The phoneme list is converted into sound through the steps: +// +// 1. Copy each phoneme number of times into the frames list, +// where each frame represents 10 milliseconds of sound. +// +// 2. Determine the transitions lengths between phonemes, and linearly +// interpolate the values across the frames. +// +// 3. Offset the pitches by the fundamental frequency. +// +// 4. Render the each frame. + +//void Code47574() +void STM32SAM::Render() { + unsigned char phase1 = 0; //mem43 + unsigned char phase2 = 0; + unsigned char phase3 = 0; + unsigned char mem66 = 0; + unsigned char mem38 = 0; + unsigned char mem40 = 0; + unsigned char speedcounter = 0; //mem45 + unsigned char mem48 = 0; + int i; + if(phonemeIndexOutput[0] == 255) return; //exit if no data + + A = 0; + X = 0; + mem44 = 0; + + // CREATE FRAMES + // + // The length parameter in the list corresponds to the number of frames + // to expand the phoneme to. Each frame represents 10 milliseconds of time. + // So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. + // + // The parameters are copied from the phoneme to the frame verbatim. + + // pos47587: + do { + // get the index + Y = mem44; + // get the phoneme at the index + A = phonemeIndexOutput[mem44]; + mem56 = A; + + // if terminal phoneme, exit the loop + if(A == 255) break; + + // period phoneme *. + if(A == 1) { + // add rising inflection + A = 1; + mem48 = 1; + //goto pos48376; + AddInflection(mem48, phase1); + } + /* + if (A == 2) goto pos48372; + */ + + // question mark phoneme? + if(A == 2) { + // create falling inflection + mem48 = 255; + AddInflection(mem48, phase1); + } + // pos47615: + + // get the stress amount (more stress = higher pitch) + phase1 = tab47492[stressOutput[Y] + 1]; + + // get number of frames to write + phase2 = phonemeLengthOutput[Y]; + Y = mem56; + + // copy from the source to the frames list + do { + frequency1[X] = freq1data[Y]; // F1 frequency + frequency2[X] = freq2data[Y]; // F2 frequency + frequency3[X] = freq3data[Y]; // F3 frequency + amplitude1[X] = ampl1data[Y]; // F1 amplitude + amplitude2[X] = ampl2data[Y]; // F2 amplitude + amplitude3[X] = ampl3data[Y]; // F3 amplitude + sampledConsonantFlag[X] = + sampledConsonantFlags[Y]; // phoneme data for sampled consonants + pitches[X] = pitch + phase1; // pitch + X++; + phase2--; + } while(phase2 != 0); + mem44++; + } while(mem44 != 0); + // ------------------- + //pos47694: + + // CREATE TRANSITIONS + // + // Linear transitions are now created to smoothly connect the + // end of one sustained portion of a phoneme to the following + // phoneme. + // + // To do this, three tables are used: + // + // Table Purpose + // ========= ================================================== + // blendRank Determines which phoneme's blend values are used. + // + // blendOut The number of frames at the end of the phoneme that + // will be used to transition to the following phoneme. + // + // blendIn The number of frames of the following phoneme that + // will be used to transition into that phoneme. + // + // In creating a transition between two phonemes, the phoneme + // with the HIGHEST rank is used. Phonemes are ranked on how much + // their identity is based on their transitions. For example, + // vowels are and diphthongs are identified by their sustained portion, + // rather than the transitions, so they are given low values. In contrast, + // stop consonants (P, B, T, K) and glides (Y, L) are almost entirely + // defined by their transitions, and are given high rank values. + // + // Here are the rankings used by SAM: + // + // Rank Type Phonemes + // 2 All vowels IY, IH, etc. + // 5 Diphthong endings YX, WX, ER + // 8 Terminal liquid consonants LX, WX, YX, N, NX + // 9 Liquid consonants L, RX, W + // 10 Glide R, OH + // 11 Glide WH + // 18 Voiceless fricatives S, SH, F, TH + // 20 Voiced fricatives Z, ZH, V, DH + // 23 Plosives, stop consonants P, T, K, KX, DX, CH + // 26 Stop consonants J, GX, B, D, G + // 27-29 Stop consonants (internal) ** + // 30 Unvoiced consonants /H, /X and Q* + // 160 Nasal M + // + // To determine how many frames to use, the two phonemes are + // compared using the blendRank[] table. The phoneme with the + // higher rank is selected. In case of a tie, a blend of each is used: + // + // if blendRank[phoneme1] == blendRank[phomneme2] + // // use lengths from each phoneme + // outBlendFrames = outBlend[phoneme1] + // inBlendFrames = outBlend[phoneme2] + // else if blendRank[phoneme1] > blendRank[phoneme2] + // // use lengths from first phoneme + // outBlendFrames = outBlendLength[phoneme1] + // inBlendFrames = inBlendLength[phoneme1] + // else + // // use lengths from the second phoneme + // // note that in and out are SWAPPED! + // outBlendFrames = inBlendLength[phoneme2] + // inBlendFrames = outBlendLength[phoneme2] + // + // Blend lengths can't be less than zero. + // + // Transitions are assumed to be symetrical, so if the transition + // values for the second phoneme are used, the inBlendLength and + // outBlendLength values are SWAPPED. + // + // For most of the parameters, SAM interpolates over the range of the last + // outBlendFrames-1 and the first inBlendFrames. + // + // The exception to this is the Pitch[] parameter, which is interpolates the + // pitch from the CENTER of the current phoneme to the CENTER of the next + // phoneme. + // + // Here are two examples. First, For example, consider the word "SUN" (S AH N) + // + // Phoneme Duration BlendWeight OutBlendFrames InBlendFrames + // S 2 18 1 3 + // AH 8 2 4 4 + // N 7 8 1 2 + // + // The formant transitions for the output frames are calculated as follows: + // + // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch + // ------------------------------------------------ + // S + // 241 0 6 0 73 0 99 61 Use S (weight 18) for transition instead of AH (weight 2) + // 241 0 6 0 73 0 99 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames + // AH + // 0 2 10 2 66 0 96 59 * <-- InBlendFrames = 3 frames + // 0 4 14 3 59 0 93 57 * + // 0 8 18 5 52 0 90 55 * + // 0 15 22 9 44 1 87 53 + // 0 15 22 9 44 1 87 53 + // 0 15 22 9 44 1 87 53 Use N (weight 8) for transition instead of AH (weight 2). + // 0 15 22 9 44 1 87 53 Since N is second phoneme, reverse the IN and OUT values. + // 0 11 17 8 47 1 98 56 * <-- (InBlendFrames-1) = (2-1) = 1 frames + // N + // 0 8 12 6 50 1 109 58 * <-- OutBlendFrames = 1 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // + // Now, consider the reverse "NUS" (N AH S): + // + // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch + // ------------------------------------------------ + // N + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 Use N (weight 8) for transition instead of AH (weight 2) + // 0 5 6 5 54 0 121 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames + // AH + // 0 8 11 6 51 0 110 59 * <-- InBlendFrames = 2 + // 0 11 16 8 48 0 99 56 * + // 0 15 22 9 44 1 87 53 Use S (weight 18) for transition instead of AH (weight 2) + // 0 15 22 9 44 1 87 53 Since S is second phoneme, reverse the IN and OUT values. + // 0 9 18 5 51 1 90 55 * <-- (InBlendFrames-1) = (3-1) = 2 + // 0 4 14 3 58 1 93 57 * + // S + // 241 2 10 2 65 1 96 59 * <-- OutBlendFrames = 1 + // 241 0 6 0 73 0 99 61 + + A = 0; + mem44 = 0; + mem49 = 0; // mem49 starts at as 0 + X = 0; + while(1) //while No. 1 + { + // get the current and following phoneme + Y = phonemeIndexOutput[X]; + A = phonemeIndexOutput[X + 1]; + X++; + + // exit loop at end token + if(A == 255) break; //goto pos47970; + + // get the ranking of each phoneme + X = A; + mem56 = blendRank[A]; + A = blendRank[Y]; + + // compare the rank - lower rank value is stronger + if(A == mem56) { + // same rank, so use out blend lengths from each phoneme + phase1 = outBlendLength[Y]; + phase2 = outBlendLength[X]; + } else if(A < mem56) { + // first phoneme is stronger, so us it's blend lengths + phase1 = inBlendLength[X]; + phase2 = outBlendLength[X]; + } else { + // second phoneme is stronger, so use it's blend lengths + // note the out/in are swapped + phase1 = outBlendLength[Y]; + phase2 = inBlendLength[Y]; + } + + Y = mem44; + A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length + mem49 = A; // mem49 now holds length + position + A = A + phase2; //Maybe Problem because of carry flag + + //47776: ADC 42 + speedcounter = A; + mem47 = 168; + phase3 = mem49 - phase1; // what is mem49 + A = phase1 + phase2; // total transition? + mem38 = A; + + X = A; + X -= 2; + if((X & 128) == 0) + do //while No. 2 + { + //pos47810: + + // mem47 is used to index the tables: + // 168 pitches[] + // 169 frequency1 + // 170 frequency2 + // 171 frequency3 + // 172 amplitude1 + // 173 amplitude2 + // 174 amplitude3 + + mem40 = mem38; + + if(mem47 == 168) // pitch + { + // unlike the other values, the pitches[] interpolates from + // the middle of the current phoneme to the middle of the + // next phoneme + + unsigned char mem36, mem37; + // half the width of the current phoneme + mem36 = phonemeLengthOutput[mem44] >> 1; + // half the width of the next phoneme + mem37 = phonemeLengthOutput[mem44 + 1] >> 1; + // sum the values + mem40 = mem36 + mem37; // length of both halves + mem37 += mem49; // center of next phoneme + mem36 = mem49 - mem36; // center index of current phoneme + A = Read( + mem47, mem37); // value at center of next phoneme - end interpolation value + //A = mem[address]; + + Y = mem36; // start index of interpolation + mem53 = A - Read(mem47, mem36); // value to center of current phoneme + } else { + // value to interpolate to + A = Read(mem47, speedcounter); + // position to start interpolation from + Y = phase3; + // value to interpolate from + mem53 = A - Read(mem47, phase3); + } + + //Code47503(mem40); + // ML : Code47503 is division with remainder, and mem50 gets the sign + + // calculate change per frame + signed char m53 = (signed char)mem53; + mem50 = mem53 & 128; + unsigned char m53abs = abs(m53); + mem51 = m53abs % mem40; //abs((char)m53) % mem40; + mem53 = (unsigned char)((signed char)(m53) / mem40); + + // interpolation range + X = mem40; // number of frames to interpolate over + Y = phase3; // starting frame + + // linearly interpolate values + + mem56 = 0; + //47907: CLC + //pos47908: + while(1) //while No. 3 + { + A = Read(mem47, Y) + mem53; //carry alway cleared + + mem48 = A; + Y++; + X--; + if(X == 0) break; + + mem56 += mem51; + if(mem56 >= mem40) //??? + { + mem56 -= mem40; //carry? is set + //if ((mem56 & 128)==0) + if((mem50 & 128) == 0) { + //47935: BIT 50 + //47937: BMI 47943 + if(mem48 != 0) mem48++; + } else + mem48--; + } + //pos47945: + Write(mem47, Y, mem48); + } //while No. 3 + + //pos47952: + mem47++; + //if (mem47 != 175) goto pos47810; + } while(mem47 != 175); //while No. 2 + //pos47963: + mem44++; + X = mem44; + } //while No. 1 + + //goto pos47701; + //pos47970: + + // add the length of this phoneme + mem48 = mem49 + phonemeLengthOutput[mem44]; + + // ASSIGN PITCH CONTOUR + // + // This subtracts the F1 frequency from the pitch to create a + // pitch contour. Without this, the output would be at a single + // pitch level (monotone). + + // don't adjust pitch if in sing mode + if(!singmode) { + // iterate through the buffer + for(i = 0; i < 256; i++) { + // subtract half the frequency of the formant 1. + // this adds variety to the voice + pitches[i] -= (frequency1[i] >> 1); + } + } + + phase1 = 0; + phase2 = 0; + phase3 = 0; + mem49 = 0; + speedcounter = 72; //sam standard speed + + // RESCALE AMPLITUDE + // + // Rescale volume from a linear scale to decibels. + // + + //amplitude rescaling + for(i = 255; i >= 0; i--) { + amplitude1[i] = amplitudeRescale[amplitude1[i]]; + amplitude2[i] = amplitudeRescale[amplitude2[i]]; + amplitude3[i] = amplitudeRescale[amplitude3[i]]; + } + + Y = 0; + A = pitches[0]; + mem44 = A; + X = A; + mem38 = A - (A >> 2); // 3/4*A ??? + + // PROCESS THE FRAMES + // + // In traditional vocal synthesis, the glottal pulse drives filters, which + // are attenuated to the frequencies of the formants. + // + // SAM generates these formants directly with sin and rectangular waves. + // To simulate them being driven by the glottal pulse, the waveforms are + // reset at the beginning of each glottal pulse. + + //finally the loop for sound output + //pos48078: + while(1) { + // get the sampled information on the phoneme + A = sampledConsonantFlag[Y]; + mem39 = A; + + // unvoiced sampled phoneme? + A = A & 248; + if(A != 0) { + // render the sample for the phoneme + RenderSample(&mem66); + + // skip ahead two in the phoneme buffer + Y += 2; + mem48 -= 2; + } else { + // simulate the glottal pulse and formants + unsigned char ary[5]; + unsigned int p1 = + phase1 * 256; // Fixed point integers because we need to divide later on + unsigned int p2 = phase2 * 256; + unsigned int p3 = phase3 * 256; + int k; + for(k = 0; k < 5; k++) { + signed char sp1 = (signed char)sinus[0xff & (p1 >> 8)]; + signed char sp2 = (signed char)sinus[0xff & (p2 >> 8)]; + signed char rp3 = (signed char)rectangle[0xff & (p3 >> 8)]; + signed int sin1 = sp1 * ((unsigned char)amplitude1[Y] & 0x0f); + signed int sin2 = sp2 * ((unsigned char)amplitude2[Y] & 0x0f); + signed int rect = rp3 * ((unsigned char)amplitude3[Y] & 0x0f); + signed int mux = sin1 + sin2 + rect; + mux /= 32; + mux += 128; // Go from signed to unsigned amplitude + ary[k] = mux; + p1 += frequency1[Y] * 256 / 4; // Compromise, this becomes a shift and works well + p2 += frequency2[Y] * 256 / 4; + p3 += frequency3[Y] * 256 / 4; + } + // output the accumulated value + Output8BitAry(0, ary); + speedcounter--; + if(speedcounter != 0) goto pos48155; + Y++; //go to next amplitude + + // decrement the frame count + mem48--; + } + + // if the frame count is zero, exit the loop + if(mem48 == 0) return; + speedcounter = speed; + pos48155: + + // decrement the remaining length of the glottal pulse + mem44--; + + // finished with a glottal pulse? + if(mem44 == 0) { + pos48159: + // fetch the next glottal pulse length + A = pitches[Y]; + mem44 = A; + A = A - (A >> 2); + mem38 = A; + + // reset the formant wave generators to keep them in + // sync with the glottal pulse + phase1 = 0; + phase2 = 0; + phase3 = 0; + continue; + } + + // decrement the count + mem38--; + + // is the count non-zero and the sampled flag is zero? + if((mem38 != 0) || (mem39 == 0)) { + // reset the phase of the formants to match the pulse + phase1 += frequency1[Y]; + phase2 += frequency2[Y]; + phase3 += frequency3[Y]; + continue; + } + + // voiced sampled phonemes interleave the sample with the + // glottal pulse. The sample flag is non-zero, so render + // the sample for the phoneme. + RenderSample(&mem66); + goto pos48159; + } //while + + // The following code is never reached. It's left over from when + // the voiced sample code was part of this loop, instead of part + // of RenderSample(); + + //pos48315: + int tempA; + phase1 = A ^ 255; + Y = mem66; + do { + //pos48321: + + mem56 = 8; + A = Read(mem47, Y); + + //pos48327: + do { + //48327: ASL A + //48328: BCC 48337 + tempA = A; + A = A << 1; + if((tempA & 128) != 0) { + X = 26; + // mem[54296] = X; + bufferpos += 150; + // + // + // buffer[bufferpos / 50] = (X & 15) * 16; + // + // + + } else { + //mem[54296] = 6; + X = 6; + bufferpos += 150; + // + // buffer[bufferpos / 50] = (X & 15) * 16; + // + // + } + + for(X = wait2; X > 0; X--) + ; //wait + mem56--; + } while(mem56 != 0); + + Y++; + phase1++; + + } while(phase1 != 0); + // if (phase1 != 0) goto pos48321; + A = 1; + mem44 = 1; + mem66 = Y; + Y = mem49; + return; +} + +// Create a rising or falling inflection 30 frames prior to +// index X. A rising inflection is used for questions, and +// a falling inflection is used for statements. + +void STM32SAM::AddInflection(unsigned char mem48, unsigned char phase1) { + //pos48372: + // mem48 = 255; + //pos48376: + + // store the location of the punctuation + mem49 = X; + A = X; + int Atemp = A; + + // backup 30 frames + A = A - 30; + // if index is before buffer, point to start of buffer + if(Atemp <= 30) A = 0; + X = A; + + // FIXME: Explain this fix better, it's not obvious + // ML : A =, fixes a problem with invalid pitch with '.' + while((A = pitches[X]) == 127) X++; + +pos48398: + //48398: CLC + //48399: ADC 48 + + // add the inflection direction + A += mem48; + phase1 = A; + + // set the inflection + pitches[X] = A; +pos48406: + + // increment the position + X++; + + // exit if the punctuation has been reached + if(X == mem49) return; //goto pos47615; + if(pitches[X] == 255) goto pos48406; + A = phase1; + goto pos48398; +} + +/* + SAM's voice can be altered by changing the frequencies of the + mouth formant (F1) and the throat formant (F2). Only the voiced + phonemes (5-29 and 48-53) are altered. +*/ +void STM32SAM::SetMouthThroat() { + unsigned char initialFrequency; + unsigned char newFrequency = 0; + //unsigned char mouth; //mem38880 + //unsigned char throat; //mem38881 + + // mouth formants (F1) 5..29 + unsigned char mouthFormants5_29[30] = {0, 0, 0, 0, 0, 10, 14, 19, 24, 27, + 23, 21, 16, 20, 14, 18, 14, 18, 18, 16, + 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; + + // throat formants (F2) 5..29 + unsigned char throatFormants5_29[30] = {255, 255, 255, 255, 255, 84, 73, 67, 63, 40, + 44, 31, 37, 45, 73, 49, 36, 30, 51, 37, + 29, 69, 24, 50, 30, 24, 83, 46, 54, 86}; + + // there must be no zeros in this 2 tables + // formant 1 frequencies (mouth) 48..53 + unsigned char mouthFormants48_53[6] = {19, 27, 21, 27, 18, 13}; + + // formant 2 frequencies (throat) 48..53 + unsigned char throatFormants48_53[6] = {72, 39, 31, 43, 30, 34}; + + unsigned char pos = 5; //mem39216 + //pos38942: + // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) + while(pos != 30) { + // recalculate mouth frequency + initialFrequency = mouthFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate throat frequency + initialFrequency = throatFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + pos++; + } + + //pos39059: + // recalculate formant frequencies 48..53 + pos = 48; + Y = 0; + while(pos != 54) { + // recalculate F1 (mouth formant) + initialFrequency = mouthFormants48_53[Y]; + newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate F2 (throat formant) + initialFrequency = throatFormants48_53[Y]; + newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + Y++; + pos++; + } +} + +//return = (mem39212*mem39213) >> 1 +unsigned char STM32SAM::trans(unsigned char mem39212, unsigned char mem39213) { + //pos39008: + unsigned char carry; + int temp; + unsigned char mem39214, mem39215; + A = 0; + mem39215 = 0; + mem39214 = 0; + X = 8; + do { + carry = mem39212 & 1; + mem39212 = mem39212 >> 1; + if(carry != 0) { + /* + 39018: LSR 39212 + 39021: BCC 39033 + */ + carry = 0; + A = mem39215; + temp = (int)A + (int)mem39213; + A = A + mem39213; + if(temp > 255) carry = 1; + mem39215 = A; + } + temp = mem39215 & 1; + mem39215 = (mem39215 >> 1) | (carry ? 128 : 0); + carry = temp; + //39033: ROR 39215 + X--; + } while(X != 0); + temp = mem39214 & 128; + mem39214 = (mem39214 << 1) | (carry ? 1 : 0); + carry = temp; + temp = mem39215 & 128; + mem39215 = (mem39215 << 1) | (carry ? 1 : 0); + carry = temp; + + return mem39215; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//char input[]={"/HAALAOAO MAYN NAAMAEAE IHSTT SAEBAASTTIHAAN \x9b\x9b\0"}; +//unsigned char input[]={"/HAALAOAO \x9b\0"}; +//unsigned char input[]={"AA \x9b\0"}; +//unsigned char input[] = {"GUH5DEHN TAEG\x9b\0"}; + +//unsigned char input[]={"AY5 AEM EY TAO4LXKIHNX KAX4MPYUX4TAH. GOW4 AH/HEH3D PAHNK.MEYK MAY8 DEY.\x9b\0"}; +//unsigned char input[]={"/HEH3LOW2, /HAW AH YUX2 TUXDEY. AY /HOH3P YUX AH FIYLIHNX OW4 KEY.\x9b\0"}; +//unsigned char input[]={"/HEY2, DHIHS IH3Z GREY2T. /HAH /HAH /HAH.AYL BIY5 BAEK.\x9b\0"}; +//unsigned char input[]={"/HAH /HAH /HAH \x9b\0"}; +//unsigned char input[]={"/HAH /HAH /HAH.\x9b\0"}; +//unsigned char input[]={".TUW BIY5Y3,, OHR NAA3T - TUW BIY5IYIY., DHAE4T IHZ DHAH KWEH4SCHAHN.\x9b\0"}; +//unsigned char input[]={"/HEY2, DHIHS \x9b\0"}; + +//unsigned char input[]={" IYIHEHAEAAAHAOOHUHUXERAXIX \x9b\0"}; +//unsigned char input[]={" RLWWYMNNXBDGJZZHVDH \x9b\0"}; +//unsigned char input[]={" SSHFTHPTKCH/H \x9b\0"}; + +//unsigned char input[]={" EYAYOYAWOWUW ULUMUNQ YXWXRXLX/XDX\x9b\0"}; + +void STM32SAM::SetInput(char* _input) { + int i, l; + l = strlen(_input); + if(l > 254) l = 254; + for(i = 0; i < l; i++) { + input[i] = _input[i]; + } + input[l] = 0; +} + +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 + +void STM32SAM::Init() { + bufferpos = 0; + int i; + SetMouthThroat(); + + bufferpos = 0; + // TODO, check for free the memory, 10 seconds of output should be more than enough + //buffer = malloc(22050*10); + + // buffer = (char*) calloc(1, sizeof(char)); + + /* + freq2data = &mem[45136]; + freq1data = &mem[45056]; + freq3data = &mem[45216]; + */ + //pitches = &mem[43008]; + /* + frequency1 = &mem[43264]; + frequency2 = &mem[43520]; + frequency3 = &mem[43776]; + */ + /* + amplitude1 = &mem[44032]; + amplitude2 = &mem[44288]; + amplitude3 = &mem[44544]; + */ + //phoneme = &mem[39904]; + /* + ampl1data = &mem[45296]; + ampl2data = &mem[45376]; + ampl3data = &mem[45456]; + */ + + for(i = 0; i < 256; i++) { + stress[i] = 0; + phonemeLength[i] = 0; + } + + for(i = 0; i < 60; i++) { + phonemeIndexOutput[i] = 0; + stressOutput[i] = 0; + phonemeLengthOutput[i] = 0; + } + phonemeindex[255] = + 255; //to prevent buffer overflow // ML : changed from 32 to 255 to stop freezing with long inputs +} + +//int Code39771() +int STM32SAM::SAMMain() { + Init(); + phonemeindex[255] = 32; //to prevent buffer overflow + + if(!Parser1()) { + return 0; + } + + Parser2(); + CopyStress(); + SetPhonemeLength(); + AdjustLengths(); + Code41240(); + do { + A = phonemeindex[X]; + if(A > 80) { + phonemeindex[X] = 255; + break; // error: delete all behind it + } + X++; + } while(X != 0); + + //pos39848: + InsertBreath(); + + //mem[40158] = 255; + + PrepareOutput(); + + return 1; +} + +//void Code48547() +void STM32SAM::PrepareOutput() { + A = 0; + X = 0; + Y = 0; + + //pos48551: + while(1) { + A = phonemeindex[X]; + if(A == 255) { + A = 255; + phonemeIndexOutput[Y] = 255; + Render(); + return; + } + if(A == 254) { + X++; + int temp = X; + //mem[48546] = X; + phonemeIndexOutput[Y] = 255; + Render(); + //X = mem[48546]; + X = temp; + Y = 0; + continue; + } + + if(A == 0) { + X++; + continue; + } + + phonemeIndexOutput[Y] = A; + phonemeLengthOutput[Y] = phonemeLength[X]; + stressOutput[Y] = stress[X]; + X++; + Y++; + } +} + +//void Code41014() +void STM32SAM::Insert( + unsigned char position /*var57*/, + unsigned char mem60, + unsigned char mem59, + unsigned char mem58) { + int i; + for(i = 253; i >= position; i--) // ML : always keep last safe-guarding 255 + { + phonemeindex[i + 1] = phonemeindex[i]; + phonemeLength[i + 1] = phonemeLength[i]; + stress[i + 1] = stress[i]; + } + + phonemeindex[position] = mem60; + phonemeLength[position] = mem59; + stress[position] = mem58; + return; +} + +//void Code48431() +void STM32SAM::InsertBreath() { + unsigned char mem54; + unsigned char mem55; + unsigned char index; //variable Y + mem54 = 255; + X++; + mem55 = 0; + unsigned char mem66 = 0; + while(1) { + //pos48440: + X = mem66; + index = phonemeindex[X]; + if(index == 255) return; + mem55 += phonemeLength[X]; + + if(mem55 < 232) { + if(index != 254) // ML : Prevents an index out of bounds problem + { + A = flags2[index] & 1; + if(A != 0) { + X++; + mem55 = 0; + Insert(X, 254, mem59, 0); + mem66++; + mem66++; + continue; + } + } + if(index == 0) mem54 = X; + mem66++; + continue; + } + X = mem54; + phonemeindex[X] = 31; // 'Q*' glottal stop + phonemeLength[X] = 4; + stress[X] = 0; + X++; + mem55 = 0; + Insert(X, 254, mem59, 0); + X++; + mem66 = X; + } +} + +// Iterates through the phoneme buffer, copying the stress value from +// the following phoneme under the following circumstance: + +// 1. The current phoneme is voiced, excluding plosives and fricatives +// 2. The following phoneme is voiced, excluding plosives and fricatives, and +// 3. The following phoneme is stressed +// +// In those cases, the stress value+1 from the following phoneme is copied. +// +// For example, the word LOITER is represented as LOY5TER, with as stress +// of 5 on the diphtong OY. This routine will copy the stress value of 6 (5+1) +// to the L that precedes it. + +//void Code41883() +void STM32SAM::CopyStress() { + // loop thought all the phonemes to be output + unsigned char pos = 0; //mem66 + while(1) { + // get the phomene + Y = phonemeindex[pos]; + + // exit at end of buffer + if(Y == 255) return; + + // if CONSONANT_FLAG set, skip - only vowels get stress + if((flags[Y] & 64) == 0) { + pos++; + continue; + } + // get the next phoneme + Y = phonemeindex[pos + 1]; + if(Y == 255) //prevent buffer overflow + { + pos++; + continue; + } else + // if the following phoneme is a vowel, skip + if((flags[Y] & 128) == 0) { + pos++; + continue; + } + + // get the stress value at the next position + Y = stress[pos + 1]; + + // if next phoneme is not stressed, skip + if(Y == 0) { + pos++; + continue; + } + + // if next phoneme is not a VOWEL OR ER, skip + if((Y & 128) != 0) { + pos++; + continue; + } + + // copy stress from prior phoneme to this one + stress[pos] = Y + 1; + + // advance pointer + pos++; + } +} + +// The input[] buffer contains a string of phonemes and stress markers along +// the lines of: +// +// DHAX KAET IHZ AH5GLIY. <0x9B> +// +// The byte 0x9B marks the end of the buffer. Some phonemes are 2 bytes +// long, such as "DH" and "AX". Others are 1 byte long, such as "T" and "Z". +// There are also stress markers, such as "5" and ".". +// +// The first character of the phonemes are stored in the table signInputTable1[]. +// The second character of the phonemes are stored in the table signInputTable2[]. +// The stress characters are arranged in low to high stress order in stressInputTable[]. +// +// The following process is used to parse the input[] buffer: +// +// Repeat until the <0x9B> character is reached: +// +// First, a search is made for a 2 character match for phonemes that do not +// end with the '*' (wildcard) character. On a match, the index of the phoneme +// is added to phonemeIndex[] and the buffer position is advanced 2 bytes. +// +// If this fails, a search is made for a 1 character match against all +// phoneme names ending with a '*' (wildcard). If this succeeds, the +// phoneme is added to phonemeIndex[] and the buffer position is advanced +// 1 byte. +// +// If this fails, search for a 1 character match in the stressInputTable[]. +// If this succeeds, the stress value is placed in the last stress[] table +// at the same index of the last added phoneme, and the buffer position is +// advanced by 1 byte. +// +// If this fails, return a 0. +// +// On success: +// +// 1. phonemeIndex[] will contain the index of all the phonemes. +// 2. The last index in phonemeIndex[] will be 255. +// 3. stress[] will contain the stress value for each phoneme + +// input[] holds the string of phonemes, each two bytes wide +// signInputTable1[] holds the first character of each phoneme +// signInputTable2[] holds te second character of each phoneme +// phonemeIndex[] holds the indexes of the phonemes after parsing input[] +// +// The parser scans through the input[], finding the names of the phonemes +// by searching signInputTable1[] and signInputTable2[]. On a match, it +// copies the index of the phoneme into the phonemeIndexTable[]. +// +// The character <0x9B> marks the end of text in input[]. When it is reached, +// the index 255 is placed at the end of the phonemeIndexTable[], and the +// function returns with a 1 indicating success. +int STM32SAM::Parser1() { + int i; + unsigned char sign1; + unsigned char sign2; + unsigned char position = 0; + X = 0; + A = 0; + Y = 0; + + // CLEAR THE STRESS TABLE + for(i = 0; i < 256; i++) stress[i] = 0; + + // THIS CODE MATCHES THE PHONEME LETTERS TO THE TABLE + // pos41078: + while(1) { + // GET THE FIRST CHARACTER FROM THE PHONEME BUFFER + sign1 = input[X]; + // TEST FOR 155 (οΏ½) END OF LINE MARKER + if(sign1 == 155) { + // MARK ENDPOINT AND RETURN + phonemeindex[position] = 255; //mark endpoint + // REACHED END OF PHONEMES, SO EXIT + return 1; //all ok + } + + // GET THE NEXT CHARACTER FROM THE BUFFER + X++; + sign2 = input[X]; + + // NOW sign1 = FIRST CHARACTER OF PHONEME, AND sign2 = SECOND CHARACTER OF PHONEME + + // TRY TO MATCH PHONEMES ON TWO TWO-CHARACTER NAME + // IGNORE PHONEMES IN TABLE ENDING WITH WILDCARDS + + // SET INDEX TO 0 + Y = 0; + pos41095: + + // GET FIRST CHARACTER AT POSITION Y IN signInputTable + // --> should change name to PhonemeNameTable1 + A = signInputTable1[Y]; + + // FIRST CHARACTER MATCHES? + if(A == sign1) { + // GET THE CHARACTER FROM THE PhonemeSecondLetterTable + A = signInputTable2[Y]; + // NOT A SPECIAL AND MATCHES SECOND CHARACTER? + if((A != '*') && (A == sign2)) { + // STORE THE INDEX OF THE PHONEME INTO THE phomeneIndexTable + phonemeindex[position] = Y; + + // ADVANCE THE POINTER TO THE phonemeIndexTable + position++; + // ADVANCE THE POINTER TO THE phonemeInputBuffer + X++; + + // CONTINUE PARSING + continue; + } + } + + // NO MATCH, TRY TO MATCH ON FIRST CHARACTER TO WILDCARD NAMES (ENDING WITH '*') + + // ADVANCE TO THE NEXT POSITION + Y++; + // IF NOT END OF TABLE, CONTINUE + if(Y != 81) goto pos41095; + + // REACHED END OF TABLE WITHOUT AN EXACT (2 CHARACTER) MATCH. + // THIS TIME, SEARCH FOR A 1 CHARACTER MATCH AGAINST THE WILDCARDS + + // RESET THE INDEX TO POINT TO THE START OF THE PHONEME NAME TABLE + Y = 0; + pos41134: + // DOES THE PHONEME IN THE TABLE END WITH '*'? + if(signInputTable2[Y] == '*') { + // DOES THE FIRST CHARACTER MATCH THE FIRST LETTER OF THE PHONEME + if(signInputTable1[Y] == sign1) { + // SAVE THE POSITION AND MOVE AHEAD + phonemeindex[position] = Y; + + // ADVANCE THE POINTER + position++; + + // CONTINUE THROUGH THE LOOP + continue; + } + } + Y++; + if(Y != 81) goto pos41134; //81 is size of PHONEME NAME table + + // FAILED TO MATCH WITH A WILDCARD. ASSUME THIS IS A STRESS + // CHARACTER. SEARCH THROUGH THE STRESS TABLE + + // SET INDEX TO POSITION 8 (END OF STRESS TABLE) + Y = 8; + + // WALK BACK THROUGH TABLE LOOKING FOR A MATCH + while((sign1 != stressInputTable[Y]) && (Y > 0)) { + // DECREMENT INDEX + Y--; + } + + // REACHED THE END OF THE SEARCH WITHOUT BREAKING OUT OF LOOP? + if(Y == 0) { + //mem[39444] = X; + //41181: JSR 42043 //Error + // FAILED TO MATCH ANYTHING, RETURN 0 ON FAILURE + return 0; + } + // SET THE STRESS FOR THE PRIOR PHONEME + stress[position - 1] = Y; + } //while +} + +//change phonemelength depedendent on stress +//void Code41203() +void STM32SAM::SetPhonemeLength() { + unsigned char A; + int position = 0; + while(phonemeindex[position] != 255) { + A = stress[position]; + //41218: BMI 41229 + if((A == 0) || ((A & 128) != 0)) { + phonemeLength[position] = phonemeLengthTable[phonemeindex[position]]; + } else { + phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]]; + } + position++; + } +} + +void STM32SAM::Code41240() { + unsigned char pos = 0; + + while(phonemeindex[pos] != 255) { + unsigned char index; //register AC + X = pos; + index = phonemeindex[pos]; + if((flags[index] & 2) == 0) { + pos++; + continue; + } else if((flags[index] & 1) == 0) { + Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); + Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); + pos += 3; + continue; + } + + do { + X++; + A = phonemeindex[X]; + } while(A == 0); + + if(A != 255) { + if((flags[A] & 8) != 0) { + pos++; + continue; + } + if((A == 36) || (A == 37)) { + pos++; // '/H' '/X' + continue; + } + } + + Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); + Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); + pos += 3; + }; +} + +// Rewrites the phonemes using the following rules: +// +// -> WX +// -> YX +// UL -> AX L +// UM -> AX M +// -> Q +// T R -> CH R +// D R -> J R +// R -> RX +// L -> LX +// G S -> G Z +// K -> KX +// G -> GX +// S P -> S B +// S T -> S D +// S K -> S G +// S KX -> S GX +// UW -> UX +// CH -> CH CH' (CH requires two phonemes to represent it) +// J -> J J' (J requires two phonemes to represent it) +// T -> DX +// D -> DX + +//void Code41397() +void STM32SAM::Parser2() { + unsigned char pos = 0; //mem66; + unsigned char mem58 = 0; + + // Loop through phonemes + while(1) { + // SET X TO THE CURRENT POSITION + X = pos; + // GET THE PHONEME AT THE CURRENT POSITION + A = phonemeindex[pos]; + + // Is phoneme pause? + if(A == 0) { + // Move ahead to the + pos++; + continue; + } + + // If end of phonemes flag reached, exit routine + if(A == 255) return; + + // Copy the current phoneme index to Y + Y = A; + + // RULE: + // -> WX + // -> YX + // Example: OIL, COW + + // Check for DIPHTONG + if((flags[A] & 16) == 0) goto pos41457; + + // Not a diphthong. Get the stress + mem58 = stress[pos]; + + // End in IY sound? + A = flags[Y] & 32; + + // If ends with IY, use YX, else use WX + if(A == 0) + A = 20; + else + A = 21; // 'WX' = 20 'YX' = 21 + //pos41443: + // Insert at WX or YX following, copying the stress + + Insert(pos + 1, A, mem59, mem58); + X = pos; + // Jump to ??? + goto pos41749; + + pos41457: + + // RULE: + // UL -> AX L + // Example: MEDDLE + + // Get phoneme + A = phonemeindex[X]; + // Skip this rule if phoneme is not UL + if(A != 78) goto pos41487; // 'UL' + A = 24; // 'L' //change 'UL' to 'AX L' + + pos41466: + // Get current phoneme stress + mem58 = stress[X]; + + // Change UL to AX + phonemeindex[X] = 13; // 'AX' + // Perform insert. Note code below may jump up here with different values + Insert(X + 1, A, mem59, mem58); + pos++; + // Move to next phoneme + continue; + + pos41487: + + // RULE: + // UM -> AX M + // Example: ASTRONOMY + + // Skip rule if phoneme != UM + if(A != 79) goto pos41495; // 'UM' + // Jump up to branch - replaces current phoneme with AX and continues + A = 27; // 'M' //change 'UM' to 'AX M' + + goto pos41466; + pos41495: + + // RULE: + // UN -> AX N + // Example: FUNCTION + + // Skip rule if phoneme != UN + if(A != 80) goto pos41503; // 'UN' + + // Jump up to branch - replaces current phoneme with AX and continues + A = 28; // 'N' //change UN to 'AX N' + + goto pos41466; + pos41503: + + // RULE: + // -> Q + // EXAMPLE: AWAY EIGHT + + Y = A; + // VOWEL set? + A = flags[A] & 128; + + // Skip if not a vowel + if(A != 0) { + // Get the stress + A = stress[X]; + + // If stressed... + if(A != 0) { + // Get the following phoneme + X++; + A = phonemeindex[X]; + // If following phoneme is a pause + + if(A == 0) { + // Get the phoneme following pause + X++; + Y = phonemeindex[X]; + + // Check for end of buffer flag + if(Y == 255) //buffer overflow + // ??? Not sure about these flags + A = 65 & 128; + else + // And VOWEL flag to current phoneme's flags + A = flags[Y] & 128; + + // If following phonemes is not a pause + if(A != 0) { + // If the following phoneme is not stressed + A = stress[X]; + if(A != 0) { + // 31 = 'Q' + Insert(X, 31, mem59, 0); + pos++; + continue; + } + } + } + } + } + + // RULES FOR PHONEMES BEFORE R + // T R -> CH R + // Example: TRACK + + // Get current position and phoneme + X = pos; + A = phonemeindex[pos]; + if(A != 23) goto pos41611; // 'R' + + // Look at prior phoneme + X--; + A = phonemeindex[pos - 1]; + //pos41567: + if(A == 69) // 'T' + { + phonemeindex[pos - 1] = 42; + goto pos41779; + } + + // RULES FOR PHONEMES BEFORE R + // D R -> J R + // Example: DRY + + // Prior phonemes D? + if(A == 57) // 'D' + { + // Change D to J + phonemeindex[pos - 1] = 44; + + goto pos41788; + } + + // RULES FOR PHONEMES BEFORE R + // R -> RX + // Example: ART + + // If vowel flag is set change R to RX + A = flags[A] & 128; + + if(A != 0) phonemeindex[pos] = 18; // 'RX' + + // continue to next phoneme + pos++; + continue; + + pos41611: + + // RULE: + // L -> LX + // Example: ALL + + // Is phoneme L? + if(A == 24) // 'L' + { + // If prior phoneme does not have VOWEL flag set, move to next phoneme + if((flags[phonemeindex[pos - 1]] & 128) == 0) { + pos++; + continue; + } + // Prior phoneme has VOWEL flag set, so change L to LX and move to next phoneme + + phonemeindex[X] = 19; // 'LX' + pos++; + continue; + } + + // RULE: + // G S -> G Z + // + // Can't get to fire - + // 1. The G -> GX rule intervenes + // 2. Reciter already replaces GS -> GZ + + // Is current phoneme S? + if(A == 32) // 'S' + { + // If prior phoneme is not G, move to next phoneme + if(phonemeindex[pos - 1] != 60) { + pos++; + continue; + } + // Replace S with Z and move on + + phonemeindex[pos] = 38; // 'Z' + pos++; + continue; + } + + // RULE: + // K -> KX + // Example: COW + + // Is current phoneme K? + if(A == 72) // 'K' + { + // Get next phoneme + Y = phonemeindex[pos + 1]; + // If at end, replace current phoneme with KX + if(Y == 255) + phonemeindex[pos] = 75; // ML : prevents an index out of bounds problem + else { + // VOWELS AND DIPHTONGS ENDING WITH IY SOUND flag set? + A = flags[Y] & 32; + + // Replace with KX + if(A == 0) phonemeindex[pos] = 75; // 'KX' + } + } else + + // RULE: + // G -> GX + // Example: GO + + // Is character a G? + if(A == 60) // 'G' + { + // Get the following character + unsigned char index = phonemeindex[pos + 1]; + + // At end of buffer? + if(index == 255) //prevent buffer overflow + { + pos++; + continue; + } else + // If diphtong ending with YX, move continue processing next phoneme + if((flags[index] & 32) != 0) { + pos++; + continue; + } + // replace G with GX and continue processing next phoneme + + phonemeindex[pos] = 63; // 'GX' + pos++; + continue; + } + + // RULE: + // S P -> S B + // S T -> S D + // S K -> S G + // S KX -> S GX + // Examples: SPY, STY, SKY, SCOWL + + Y = phonemeindex[pos]; + //pos41719: + // Replace with softer version? + A = flags[Y] & 1; + if(A == 0) goto pos41749; + A = phonemeindex[pos - 1]; + if(A != 32) // 'S' + { + A = Y; + goto pos41812; + } + // Replace with softer version + + phonemeindex[pos] = Y - 12; + pos++; + continue; + + pos41749: + + // RULE: + // UW -> UX + // + // Example: NEW, DEW, SUE, ZOO, THOO, TOO + + // UW -> UX + + A = phonemeindex[X]; + if(A == 53) // 'UW' + { + // ALVEOLAR flag set? + Y = phonemeindex[X - 1]; + A = flags2[Y] & 4; + // If not set, continue processing next phoneme + if(A == 0) { + pos++; + continue; + } + + phonemeindex[X] = 16; + pos++; + continue; + } + pos41779: + + // RULE: + // CH -> CH CH' (CH requires two phonemes to represent it) + // Example: CHEW + + if(A == 42) // 'CH' + { + // pos41783: + + Insert(X + 1, A + 1, mem59, stress[X]); + pos++; + continue; + } + + pos41788: + + // RULE: + // J -> J J' (J requires two phonemes to represent it) + // Example: JAY + + if(A == 44) // 'J' + { + Insert(X + 1, A + 1, mem59, stress[X]); + pos++; + continue; + } + + // Jump here to continue + pos41812: + + // RULE: Soften T following vowel + // NOTE: This rule fails for cases such as "ODD" + // T -> DX + // D -> DX + // Example: PARTY, TARDY + + // Past this point, only process if phoneme is T or D + + if(A != 69) // 'T' + if(A != 57) { + pos++; // 'D' + continue; + } + //pos41825: + + // If prior phoneme is not a vowel, continue processing phonemes + if((flags[phonemeindex[X - 1]] & 128) == 0) { + pos++; + continue; + } + + // Get next phoneme + X++; + A = phonemeindex[X]; + //pos41841 + // Is the next phoneme a pause? + if(A != 0) { + // If next phoneme is not a pause, continue processing phonemes + if((flags[A] & 128) == 0) { + pos++; + continue; + } + // If next phoneme is stressed, continue processing phonemes + // FIXME: How does a pause get stressed? + if(stress[X] != 0) { + pos++; + continue; + } + //pos41856: + // Set phonemes to DX + + phonemeindex[pos] = 30; // 'DX' + } else { + A = phonemeindex[X + 1]; + if(A == 255) //prevent buffer overflow + A = 65 & 128; + else + // Is next phoneme a vowel or ER? + A = flags[A] & 128; + + if(A != 0) phonemeindex[pos] = 30; // 'DX' + } + + pos++; + + } // while +} // parser 2 + +// Applies various rules that adjust the lengths of phonemes +// +// Lengthen or between and by 1.5 +// - decrease length by 1 +// - decrease vowel by 1/8th +// - increase vowel by 1/2 + 1 +// - set nasal = 5, consonant = 6 +// {optional silence} - shorten both to 1/2 + 1 +// - decrease by 2 + +//void Code48619() +void STM32SAM::AdjustLengths() { + // LENGTHEN VOWELS PRECEDING PUNCTUATION + // + // Search for punctuation. If found, back up to the first vowel, then + // process all phonemes between there and up to (but not including) the punctuation. + // If any phoneme is found that is a either a fricative or voiced, the duration is + // increased by (length * 1.5) + 1 + + // loop index + X = 0; + unsigned char index; + + // iterate through the phoneme list + unsigned char loopIndex = 0; + while(1) { + // get a phoneme + index = phonemeindex[X]; + + // exit loop if end on buffer token + if(index == 255) break; + + // not punctuation? + if((flags2[index] & 1) == 0) { + // skip + X++; + continue; + } + + // hold index + loopIndex = X; + + // Loop backwards from this point + pos48644: + + // back up one phoneme + X--; + + // stop once the beginning is reached + if(X == 0) break; + + // get the preceding phoneme + index = phonemeindex[X]; + + if(index != 255) //inserted to prevent access overrun + if((flags[index] & 128) == 0) goto pos48644; // if not a vowel, continue looping + + //pos48657: + do { + // test for vowel + index = phonemeindex[X]; + + if(index != 255) //inserted to prevent access overrun + // test for fricative/unvoiced or not voiced + if(((flags2[index] & 32) == 0) || ((flags[index] & 4) != 0)) //nochmal οΏ½berprοΏ½fen + { + //A = flags[Y] & 4; + //if(A == 0) goto pos48688; + + // get the phoneme length + A = phonemeLength[X]; + + // change phoneme length to (length * 1.5) + 1 + A = (A >> 1) + A + 1; + + phonemeLength[X] = A; + } + // keep moving forward + X++; + } while(X != loopIndex); + // if (X != loopIndex) goto pos48657; + X++; + } // while + + // Similar to the above routine, but shorten vowels under some circumstances + + // Loop throught all phonemes + loopIndex = 0; + //pos48697 + + while(1) { + // get a phoneme + X = loopIndex; + index = phonemeindex[X]; + + // exit routine at end token + if(index == 255) return; + + // vowel? + A = flags[index] & 128; + if(A != 0) { + // get next phoneme + X++; + index = phonemeindex[X]; + + // get flags + if(index == 255) + mem56 = 65; // use if end marker + else + mem56 = flags[index]; + + // not a consonant + if((flags[index] & 64) == 0) { + // RX or LX? + if((index == 18) || (index == 19)) // 'RX' & 'LX' + { + // get the next phoneme + X++; + index = phonemeindex[X]; + + // next phoneme a consonant? + if((flags[index] & 64) != 0) { + // RULE: RX | LX + + // decrease length of vowel by 1 frame + phonemeLength[loopIndex]--; + } + // move ahead + loopIndex++; + continue; + } + // move ahead + loopIndex++; + continue; + } + + // Got here if not + + // not voiced + if((mem56 & 4) == 0) { + // Unvoiced + // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX + + // not an unvoiced plosive? + if((mem56 & 1) == 0) { + // move ahead + loopIndex++; + continue; + } + + // P*, T*, K*, KX + + // RULE: + // + + // move back + X--; + + // decrease length by 1/8th + mem56 = phonemeLength[X] >> 3; + phonemeLength[X] -= mem56; + + // move ahead + loopIndex++; + continue; + } + + // RULE: + // + + // decrease length + A = phonemeLength[X - 1]; + phonemeLength[X - 1] = (A >> 2) + A + 1; // 5/4*A + 1 + + // move ahead + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, M*, N*, NX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX + + //pos48821: + + // RULE: + // Set punctuation length to 6 + // Set stop consonant length to 5 + + // nasal? + if((flags2[index] & 8) != 0) { + // M*, N*, NX, + + // get the next phoneme + X++; + index = phonemeindex[X]; + + // end of buffer? + if(index == 255) + A = 65 & 2; //prevent buffer overflow + else + A = flags[index] & 2; // check for stop consonant + + // is next phoneme a stop consonant? + if(A != 0) + + // B*, D*, G*, GX, P*, T*, K*, KX + + { + // set stop consonant length to 6 + phonemeLength[X] = 6; + + // set nasal length to 5 + phonemeLength[X - 1] = 5; + } + // move to next phoneme + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX + + // RULE: {optional silence} + // Shorten both to (length/2 + 1) + + // (voiced) stop consonant? + if((flags[index] & 2) != 0) { + // B*, D*, G*, GX + + // move past silence + do { + // move ahead + X++; + index = phonemeindex[X]; + } while(index == 0); + + // check for end of buffer + if(index == 255) //buffer overflow + { + // ignore, overflow code + if((65 & 2) == 0) { + loopIndex++; + continue; + } + } else if((flags[index] & 2) == 0) { + // if another stop consonant, move ahead + loopIndex++; + continue; + } + + // RULE: {optional silence} + + // X gets overwritten, so hold prior X value for debug statement + // int debugX = X; + // shorten the prior phoneme length to (length/2 + 1) + phonemeLength[X] = (phonemeLength[X] >> 1) + 1; + X = loopIndex; + + // also shorten this phoneme length to (length/2 +1) + phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1; + + // move ahead + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, **, + + // RULE: + // Decrease by 2 + + // liquic consonant? + if((flags2[index] & 16) != 0) { + // R*, L*, W*, Y* + + // get the prior phoneme + index = phonemeindex[X - 1]; + + // prior phoneme a stop consonant> + if((flags[index] & 2) != 0) { + // Rule: + + // decrease the phoneme length by 2 frames (20 ms) + phonemeLength[X] -= 2; + } + } + + // move to next phoneme + loopIndex++; + continue; + } + // goto pos48701; +} + +// ------------------------------------------------------------------------- +// ML : Code47503 is division with remainder, and mem50 gets the sign +void STM32SAM::Code47503(unsigned char mem52) { + Y = 0; + if((mem53 & 128) != 0) { + mem53 = -mem53; + Y = 128; + } + mem50 = Y; + A = 0; + for(X = 8; X > 0; X--) { + int temp = mem53; + mem53 = mem53 << 1; + A = A << 1; + if(temp >= 128) A++; + if(A >= mem52) { + A = A - mem52; + mem53++; + } + } + + mem51 = A; + if((mem50 & 128) != 0) mem53 = -mem53; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Reciter +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::Code37055(unsigned char mem59) { + X = mem59; + X--; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; + return; +} + +void STM32SAM::Code37066(unsigned char mem58) { + X = mem58; + X++; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; +} + +unsigned char STM32SAM::GetRuleByte(unsigned short mem62, unsigned char Y) { + unsigned int address = mem62; + + if(mem62 >= 37541) { + address -= 37541; + return rules2[address + Y]; + } + address -= 32000; + return rules[address + Y]; +} + +int STM32SAM::TextToPhonemes(unsigned char* input) // Code36484 +{ + //unsigned char *tab39445 = &mem[39445]; //input and output + //unsigned char mem29; + unsigned char mem56; //output position for phonemes + unsigned char mem57; + unsigned char mem58; + unsigned char mem59; + unsigned char mem60; + unsigned char mem61; + unsigned short mem62; // memory position of current rule + + unsigned char mem64; // position of '=' or current character + unsigned char mem65; // position of ')' + unsigned char mem66; // position of '(' + unsigned char mem36653; + + inputtemp[0] = 32; + + // secure copy of input + // because input will be overwritten by phonemes + X = 1; + Y = 0; + do { + //pos36499: + A = input[Y] & 127; + if(A >= 112) + A = A & 95; + else if(A >= 96) + A = A & 79; + + inputtemp[X] = A; + X++; + Y++; + } while(Y != 255); + + X = 255; + inputtemp[X] = 27; + mem61 = 255; + +pos36550: + A = 255; + mem56 = 255; + +pos36554: + while(1) { + mem61++; + X = mem61; + A = inputtemp[X]; + mem64 = A; + if(A == '[') { + mem56++; + X = mem56; + A = 155; + input[X] = 155; + //goto pos36542; + // Code39771(); //Code39777(); + return 1; + } + + //pos36579: + if(A != '.') break; + X++; + Y = inputtemp[X]; + A = tab36376[Y] & 1; + if(A != 0) break; + mem56++; + X = mem56; + A = '.'; + input[X] = '.'; + } //while + + //pos36607: + A = mem64; + Y = A; + A = tab36376[A]; + mem57 = A; + if((A & 2) != 0) { + mem62 = 37541; + goto pos36700; + } + + //pos36630: + A = mem57; + if(A != 0) goto pos36677; + A = 32; + inputtemp[X] = ' '; + mem56++; + X = mem56; + if(X > 120) goto pos36654; + input[X] = A; + goto pos36554; + + // ----- + + //36653 is unknown. Contains position + +pos36654: + input[X] = 155; + A = mem61; + mem36653 = A; + // mem29 = A; // not used + // Code36538(); das ist eigentlich + return 1; + //Code39771(); + //go on if there is more input ??? + mem61 = mem36653; + goto pos36550; + +pos36677: + A = mem57 & 128; + if(A == 0) { + //36683: BRK + return 0; + } + + // go to the right rules for this character. + X = mem64 - 'A'; + mem62 = tab37489[X] | (tab37515[X] << 8); + + // ------------------------------------- + // go to next rule + // ------------------------------------- + +pos36700: + + // find next rule + Y = 0; + do { + mem62 += 1; + A = GetRuleByte(mem62, Y); + } while((A & 128) == 0); + Y++; + + //pos36720: + // find '(' + while(1) { + A = GetRuleByte(mem62, Y); + if(A == '(') break; + Y++; + } + mem66 = Y; + + //pos36732: + // find ')' + do { + Y++; + A = GetRuleByte(mem62, Y); + } while(A != ')'); + mem65 = Y; + + //pos36741: + // find '=' + do { + Y++; + A = GetRuleByte(mem62, Y); + A = A & 127; + } while(A != '='); + mem64 = Y; + + X = mem61; + mem60 = X; + + // compare the string within the bracket + Y = mem66; + Y++; + //pos36759: + while(1) { + mem57 = inputtemp[X]; + A = GetRuleByte(mem62, Y); + if(A != mem57) goto pos36700; + Y++; + if(Y == mem65) break; + X++; + mem60 = X; + } + + // the string in the bracket is correct + + //pos36787: + A = mem61; + mem59 = mem61; + +pos36791: + while(1) { + mem66--; + Y = mem66; + A = GetRuleByte(mem62, Y); + mem57 = A; + //36800: BPL 36805 + if((A & 128) != 0) goto pos37180; + X = A & 127; + A = tab36376[X] & 128; + if(A == 0) break; + X = mem59 - 1; + A = inputtemp[X]; + if(A != mem57) goto pos36700; + mem59 = X; + } + + //pos36833: + A = mem57; + if(A == ' ') goto pos36895; + if(A == '#') goto pos36910; + if(A == '.') goto pos36920; + if(A == '&') goto pos36935; + if(A == '@') goto pos36967; + if(A == '^') goto pos37004; + if(A == '+') goto pos37019; + if(A == ':') goto pos37040; + // Code42041(); //Error + //36894: BRK + return 0; + + // -------------- + +pos36895: + Code37055(mem59); + A = A & 128; + if(A != 0) goto pos36700; +pos36905: + mem59 = X; + goto pos36791; + + // -------------- + +pos36910: + Code37055(mem59); + A = A & 64; + if(A != 0) goto pos36905; + goto pos36700; + + // -------------- + +pos36920: + Code37055(mem59); + A = A & 8; + if(A == 0) goto pos36700; +pos36930: + mem59 = X; + goto pos36791; + + // -------------- + +pos36935: + Code37055(mem59); + A = A & 16; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if(A != 72) goto pos36700; + X--; + A = inputtemp[X]; + if((A == 67) || (A == 83)) goto pos36930; + goto pos36700; + + // -------------- + +pos36967: + Code37055(mem59); + A = A & 4; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if(A != 72) goto pos36700; + if((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem59 = X; + goto pos36791; + + // -------------- + +pos37004: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36700; + +pos37014: + mem59 = X; + goto pos36791; + + // -------------- + +pos37019: + X = mem59; + X--; + A = inputtemp[X]; + if((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014; + goto pos36700; + // -------------- + +pos37040: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36791; + mem59 = X; + goto pos37040; + + //--------------------------------------- + +pos37077: + X = mem58 + 1; + A = inputtemp[X]; + if(A != 'E') goto pos37157; + X++; + Y = inputtemp[X]; + X--; + A = tab36376[Y] & 128; + if(A == 0) goto pos37108; + X++; + A = inputtemp[X]; + if(A != 'R') goto pos37113; +pos37108: + mem58 = X; + goto pos37184; +pos37113: + if((A == 83) || (A == 68)) goto pos37108; // 'S' 'D' + if(A != 76) goto pos37135; // 'L' + X++; + A = inputtemp[X]; + if(A != 89) goto pos36700; + goto pos37108; + +pos37135: + if(A != 70) goto pos36700; + X++; + A = inputtemp[X]; + if(A != 85) goto pos36700; + X++; + A = inputtemp[X]; + if(A == 76) goto pos37108; + goto pos36700; + +pos37157: + if(A != 73) goto pos36700; + X++; + A = inputtemp[X]; + if(A != 78) goto pos36700; + X++; + A = inputtemp[X]; + if(A == 71) goto pos37108; + //pos37177: + goto pos36700; + + // ----------------------------------------- + +pos37180: + + A = mem60; + mem58 = A; + +pos37184: + Y = mem65 + 1; + + //37187: CPY 64 + // if(? != 0) goto pos37194; + if(Y == mem64) goto pos37455; + mem65 = Y; + //37196: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + X = A; + A = tab36376[X] & 128; + if(A == 0) goto pos37226; + X = mem58 + 1; + A = inputtemp[X]; + if(A != mem57) goto pos36700; + mem58 = X; + goto pos37184; +pos37226: + A = mem57; + if(A == 32) goto pos37295; // ' ' + if(A == 35) goto pos37310; // '#' + if(A == 46) goto pos37320; // '.' + if(A == 38) goto pos37335; // '&' + if(A == 64) goto pos37367; // '' + if(A == 94) goto pos37404; // '' + if(A == 43) goto pos37419; // '+' + if(A == 58) goto pos37440; // ':' + if(A == 37) goto pos37077; // '%' + //pos37291: + // Code42041(); //Error + //37294: BRK + return 0; + + // -------------- +pos37295: + Code37066(mem58); + A = A & 128; + if(A != 0) goto pos36700; +pos37305: + mem58 = X; + goto pos37184; + + // -------------- + +pos37310: + Code37066(mem58); + A = A & 64; + if(A != 0) goto pos37305; + goto pos36700; + + // -------------- + +pos37320: + Code37066(mem58); + A = A & 8; + if(A == 0) goto pos36700; + +pos37330: + mem58 = X; + goto pos37184; + + // -------------- + +pos37335: + Code37066(mem58); + A = A & 16; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if(A != 72) goto pos36700; + X++; + A = inputtemp[X]; + if((A == 67) || (A == 83)) goto pos37330; + goto pos36700; + + // -------------- + +pos37367: + Code37066(mem58); + A = A & 4; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if(A != 72) goto pos36700; + if((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem58 = X; + goto pos37184; + + // -------------- + +pos37404: + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos36700; +pos37414: + mem58 = X; + goto pos37184; + + // -------------- + +pos37419: + X = mem58; + X++; + A = inputtemp[X]; + if((A == 69) || (A == 73) || (A == 89)) goto pos37414; + goto pos36700; + + // ---------------------- + +pos37440: + + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos37184; + mem58 = X; + goto pos37440; +pos37455: + Y = mem64; + mem61 = mem60; + +pos37461: + //37461: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + A = A & 127; + if(A != '=') { + mem56++; + X = mem56; + input[X] = A; + } + + //37478: BIT 57 + //37480: BPL 37485 //not negative flag + if((mem57 & 128) == 0) goto pos37485; //??? + goto pos36554; +pos37485: + Y++; + goto pos37461; +} + +// Constructor + +STM32SAM::STM32SAM(uint32_t STM32SAM_SPEED /* = 5 */) { + STM32SAM_SPEED = STM32SAM_SPEED & 0x1f; // limit it from 0 to 31 + + _STM32SAM_SPEED = STM32SAM_SPEED; + + // set default voice + + speed = 72; + pitch = 64; + mouth = 128; + throat = 128; + + phonetic = 0; + singmode = 0; + + wait1 = 7; + wait2 = 6; + + mem59 = 0; + + oldtimetableindex = 0; +} + +STM32SAM::STM32SAM() { + _STM32SAM_SPEED = 7; + + // set default voice + + speed = 72; + pitch = 64; + mouth = 128; + throat = 128; + + phonetic = 0; + singmode = 0; + + wait1 = 7; + wait2 = 6; + + mem59 = 0; + + oldtimetableindex = 0; +} + +/* + STM32SAM::~STM32SAM() { + { + // TODO: end(); + } +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (variable string, phonetic, sing, pitch, speed, mouth, throat) +// STM32SAM say (sing off, phonetic off) (const string) +// STM32SAM say (sing off, phonetic off) (variable string) +// STM32SAM sing (sing on, phonetic off) (const string) +// STM32SAM sing (sing on, phonetic off) (variable string) +// STM32SAM sayPhonetic (sing off, phonetic on) (const string) +// STM32SAM sayPhonetic (sing off, phonetic on) (variable string) +// STM32SAM singPhonetic (sing on, phonetic on) (const string) +// STM32SAM singPhonetic (sing on, phonetic on) (variable string) +// STM32SAM voice (pitch, speed, mouth, throat) +// STM32SAM setPitch (pitch) +// STM32SAM setSpeed (speed) +// STM32SAM setMouth (mouth) +// STM32SAM setThroat (throat) +// +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (const string, phonetic, sing, pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +char to_upper_case(char c) { + if(c >= 'a' && c <= 'z') { + return c - 'a' + 'A'; + } + return c; +} + +void STM32SAM::sam( + const char* argv, + unsigned char _phonetic, + unsigned char _singmode, + unsigned char _pitch, + unsigned char _speed, + unsigned char _mouth, + unsigned char _throat) { + phonetic = _phonetic; + singmode = _singmode; + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; + + int i; + + for(i = 0; i < 256; i++) { + input[i] = argv[i]; + } + + for(i = 0; input[i] != 0; i++) { + if(i != 0) { + input[i] = to_upper_case((int)argv[i]); + } + } + + if(!phonetic) { + strncat(input, "[", 256); + if(!TextToPhonemes((unsigned char*)input)) { + // PrintUsage(); + return; + } + + } else { + strncat(input, "\x9b", 256); + } + + SetInput(input); + + if(!SAMMain()) { + return; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (variable string, phonetic, sing, pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sam( + char* argv, + unsigned char _phonetic, + unsigned char _singmode, + unsigned char _pitch, + unsigned char _speed, + unsigned char _mouth, + unsigned char _throat) { + phonetic = _phonetic; + singmode = _singmode; + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; + + int i; + + for(i = 0; i < 256; i++) { + input[i] = argv[i]; + } + + for(i = 0; input[i] != 0; i++) { + if(i != 0) { + input[i] = to_upper_case((int)argv[i]); + } + } + + if(!phonetic) { + strncat(input, "[", 256); + if(!TextToPhonemes((unsigned char*)input)) { + // PrintUsage(); + return; + } + + } else { + strncat(input, "\x9b", 256); + } + + SetInput(input); + + if(!SAMMain()) { + return; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM say(sing off, phonetic off) (const string) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::say(const char* argv) { + int i; + + phonetic = 0; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::say(char* argv) { + int i; + + phonetic = 0; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sing (sing on, phonetic off) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sing(const char* argv) { + int i; + + phonetic = 0; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::sing(char* argv) { + int i; + + phonetic = 0; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sayPhonetic (sing off, phonetic on) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sayPhonetic(const char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::sayPhonetic(char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM singPhonetic (sing on, phonetic on) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::singPhonetic(const char* argv) { + int i; + + phonetic = 1; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::singPhonetic(char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM voice (pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setVoice( + unsigned char _pitch /* = 64 */, + unsigned char _speed /* = 72 */, + unsigned char _mouth /* = 128 */, + unsigned char _throat /* = 128 */) { + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setPitch (pitch) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setPitch(unsigned char _pitch /* = 64 */) { + pitch = _pitch; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setSpeed (speed) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setSpeed(unsigned char _speed /* = 72 */) { + speed = _speed; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setMouth (mouth) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setMouth(unsigned char _mouth /* = 128 */) { + mouth = _mouth; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setThroat (throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setThroat(unsigned char _throat /* = 128 */) { + throat = _throat; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Hardware +// +//////////////////////////////////////////////////////////////////////////////////////////// +// Hardware specifics, for easier porting to other microcontrollers + +// +// Set PA8 pin as PWM, at 256 timer ticks overflow (8bit resolution) + +#include +#include + +#define FURI_HAL_SPEAKER_TIMER TIM16 +#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 + +void STM32SAM::begin(void) { +#ifdef USE_ROGER_CORE + + pinMode(PA8, PWM); // audio output pin + + Timer1.setPeriod( + 4); // Can't set at 256 ticks, only in uS. First nearest uS is 4 (Roger core is only for bluepill, that means 72*4=288 ticks, or 128*4=512 ticks when overclocked. It's ok, just overall volume will be lower, because maximum volume will be 256/288 or 256/512) + +#endif + +#ifdef USE_STM32duino_CORE + pinMode(PA8, OUTPUT); + + PWM->pause(); + PWM->setMode(1, TIMER_OUTPUT_COMPARE_PWM1, PA8); // TIM1 CH1 (PA8) + PWM->setPrescaleFactor(1); + PWM->setOverflow(256, TICK_FORMAT); // 256 ticks overflow, no matter the CPU (timer) speed + PWM->resume(); + +#endif + + LL_TIM_InitTypeDef TIM_InitStruct; + memset(&TIM_InitStruct, 0, sizeof(LL_TIM_InitTypeDef)); + TIM_InitStruct.Prescaler = 4; + TIM_InitStruct.Autoreload = 255; + LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct; + memset(&TIM_OC_InitStruct, 0, sizeof(LL_TIM_OC_InitTypeDef)); + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 127; + LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); +} // begin + +inline void STM32SAM::SetAUDIO(unsigned char main_volume) { +#ifdef USE_ROGER_CORE + Timer1.setCompare(TIMER_CH1, main_volume); +#endif + +#ifdef USE_STM32duino_CORE + PWM->setCaptureCompare(1, main_volume, TICK_COMPARE_FORMAT); +#endif + + // if(main_volume > 64) { + // LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, 127); + // } else { + // LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, main_volume); + // } + + float data = main_volume; + data /= 255.0f; + data -= 0.5f; + data *= 4.0f; + data = tanhf(data); + + data += 0.5f; + data *= 255.0f; + + if(data < 0) { + data = 0; + } else if(data > 255) { + data = 255; + } + + LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, data); +} \ No newline at end of file diff --git a/applications/plugins/sam/stm32_sam.h b/applications/plugins/sam/stm32_sam.h new file mode 100644 index 000000000..910227ac3 --- /dev/null +++ b/applications/plugins/sam/stm32_sam.h @@ -0,0 +1,96 @@ +#include + +#ifndef __STM32SAM__ +#define __STM32SAM__ + +// SAM Text-To-Speech (TTS), ported from https://github.com/s-macke/SAM + +class STM32SAM { +public: + STM32SAM(uint32_t STM32SAM_SPEED); + STM32SAM(); + + void begin(void); + + void + sam(const char* argv, + unsigned char phonetic, + unsigned char singmode, + unsigned char pitch, + unsigned char speed, + unsigned char mouth, + unsigned char throat); + void + sam(char* argv, + unsigned char phonetic, + unsigned char singmode, + unsigned char pitch, + unsigned char speed, + unsigned char mouth, + unsigned char throat); + + void say(const char* argv); + void say(char* argv); + void sing(const char* argv); + void sing(char* argv); + void sayPhonetic(const char* argv); + void sayPhonetic(char* argv); + void singPhonetic(const char* argv); + void singPhonetic(char* argv); + void setVoice( + unsigned char _pitch = 64, + unsigned char _speed = 72, + unsigned char _mouth = 128, + unsigned char _throat = 128); + void setPitch(unsigned char _pitch = 64); + void setSpeed(unsigned char _speed = 72); + void setMouth(unsigned char _mouth = 128); + void setThroat(unsigned char _throat = 128); + +private: + void SetAUDIO(unsigned char main_volume); + + void Output8BitAry(int index, unsigned char ary[5]); + void Output8Bit(int index, unsigned char A); + unsigned char Read(unsigned char p, unsigned char Y); + void Write(unsigned char p, unsigned char Y, unsigned char value); + void RenderSample(unsigned char* mem66); + void Render(); + void AddInflection(unsigned char mem48, unsigned char phase1); + void SetMouthThroat(); + unsigned char trans(unsigned char mem39212, unsigned char mem39213); + void SetInput(char* _input); + void Init(); + int SAMMain(); + void PrepareOutput(); + void Insert( + unsigned char position /*var57*/, + unsigned char mem60, + unsigned char mem59, + unsigned char mem58); + void InsertBreath(); + void CopyStress(); + int Parser1(); + void SetPhonemeLength(); + void Code41240(); + void Parser2(); + void AdjustLengths(); + void Code47503(unsigned char mem52); + void Code37055(unsigned char mem59); + void Code37066(unsigned char mem58); + unsigned char GetRuleByte(unsigned short mem62, unsigned char Y); + int TextToPhonemes(unsigned char* input); // Code36484 + + uint32_t _STM32SAM_SPEED; + + unsigned char speed; + unsigned char pitch; + unsigned char mouth; + unsigned char throat; + + unsigned char phonetic; + unsigned char singmode; + +}; // STM32SAM class + +#endif \ No newline at end of file diff --git a/applications/plugins/scorched_tanks/LICENSE b/applications/plugins/scorched_tanks/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/plugins/scorched_tanks/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/plugins/scorched_tanks/README.md b/applications/plugins/scorched_tanks/README.md new file mode 100644 index 000000000..1c1970c19 --- /dev/null +++ b/applications/plugins/scorched_tanks/README.md @@ -0,0 +1,35 @@ +# Scorched tanks - flipper zero game +A flipper zero game inspired by scorched earth. + +Current state is shown below: + +![input](scorched_tanks_v1.gif) + +## How to do: +Do not hesitate to create PRs. If you start working on sth, please start branch name with TODO id (e.g. `feature/2-change-tank-icon`) + +If you see an improvement, modify this readme and add suggestions via PR. Bugs can be reported via Issues + +## What to do next (it's not in the priority order): +5. flatten surface beneath tanks +7. explosion animation +9. sub-ghz multi-player +11. add other types of weapons +12. code AI +13. add terain destruction +14. add terain gravity (fall down after hitting the middle of the mountain) +18. Add menu with settings (vibartion on/off, difficulty) +20. add more ground modifiers (currently there is only one hill in the middle, maybe 2 hills, skew map, etc) + +## What have been done: +1. ~~remove movement~~ +2. ~~change tank icon~~ +3. ~~power as variable not constant~~ +4. ~~better map generation: high, low regions~~ (continuation in point 20.) +6. ~~collision with the enemy~~ +8. ~~local multi-player~~ +10. ~~improve projectile trace draw on angles > 80~~ +15. ~~FIX: firing stops when bullet fly above the screen~~ +16. ~~Slightly randomize player and enemy spawn locations~~ +17. ~~Shooting vibration~~ +19. ~~Add wind~~ diff --git a/applications/plugins/scorched_tanks/application.fam b/applications/plugins/scorched_tanks/application.fam new file mode 100644 index 000000000..fa0e6dd2b --- /dev/null +++ b/applications/plugins/scorched_tanks/application.fam @@ -0,0 +1,12 @@ +App( + appid="Scorched_Tanks", + name="Scorched Tanks", + apptype=FlipperAppType.EXTERNAL, + entry_point="scorched_tanks_game_app", + cdefines=["APP_SCORCHED_TANKS_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=100, + fap_icon="scorchedTanks_10px.png", + fap_category="Games", +) diff --git a/applications/plugins/scorched_tanks/scorchedTanks_10px.png b/applications/plugins/scorched_tanks/scorchedTanks_10px.png new file mode 100644 index 000000000..6e1ae4c04 Binary files /dev/null and b/applications/plugins/scorched_tanks/scorchedTanks_10px.png differ diff --git a/applications/plugins/scorched_tanks/scorched_tanks_game_app.c b/applications/plugins/scorched_tanks/scorched_tanks_game_app.c new file mode 100644 index 000000000..fd4c73ee7 --- /dev/null +++ b/applications/plugins/scorched_tanks/scorched_tanks_game_app.c @@ -0,0 +1,540 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define PLAYER_INIT_LOCATION_X 20 +#define PLAYER_INIT_AIM 45 +#define PLAYER_INIT_POWER 50 +#define ENEMY_INIT_LOCATION_X 108 +#define TANK_BARREL_LENGTH 8 +#define GRAVITY_FORCE (double)0.5 +#define MIN_GROUND_HEIGHT 35 +#define MAX_GROUND_HEIGHT 55 +#define MAX_FIRE_POWER 100 +#define MIN_FIRE_POWER 0 +#define TANK_COLLIDER_SIZE 3 +#define MAX_WIND 10 +#define MAX_PLAYER_DIFF_X 20 +#define MAX_ENEMY_DIFF_X 20 + +// That's a filthy workaround but sin(player.aimAngle) breaks it all... If you're able to fix it, please do create a PR! +double scorched_tanks_sin[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.122, -0.139, -0.156, -0.174, -0.191, + -0.208, -0.225, -0.242, -0.259, -0.276, -0.292, -0.309, -0.326, -0.342, -0.358, -0.375, -0.391, + -0.407, -0.423, -0.438, -0.454, -0.469, -0.485, -0.500, -0.515, -0.530, -0.545, -0.559, -0.574, + -0.588, -0.602, -0.616, -0.629, -0.643, -0.656, -0.669, -0.682, -0.695, -0.707, -0.719, -0.731, + -0.743, -0.755, -0.766, -0.777, -0.788, -0.799, -0.809, -0.819, -0.829, -0.839, -0.848, -0.857, + -0.866, -0.875, -0.883, -0.891, -0.899, -0.906, -0.914, -0.921, -0.927, -0.934, -0.940, -0.946, + -0.951, -0.956, -0.961, -0.966, -0.970, -0.974, -0.978, -0.982, -0.985, -0.988, -0.990, -0.993, + -0.995, -0.996, -0.998, -0.999, -0.999, -1.000, -1.000}; +double scorched_tanks_cos[91] = { + 1.000, 1.000, 0.999, 0.999, 0.998, 0.996, 0.995, 0.993, 0.990, 0.988, 0.985, 0.982, 0.978, + 0.974, 0.970, 0.966, 0.961, 0.956, 0.951, 0.946, 0.940, 0.934, 0.927, 0.921, 0.914, 0.906, + 0.899, 0.891, 0.883, 0.875, 0.866, 0.857, 0.848, 0.839, 0.829, 0.819, 0.809, 0.799, 0.788, + 0.777, 0.766, 0.755, 0.743, 0.731, 0.719, 0.707, 0.695, 0.682, 0.669, 0.656, 0.643, 0.629, + 0.616, 0.602, 0.588, 0.574, 0.559, 0.545, 0.530, 0.515, 0.500, 0.485, 0.469, 0.454, 0.438, + 0.423, 0.407, 0.391, 0.375, 0.358, 0.342, 0.326, 0.309, 0.292, 0.276, 0.259, 0.242, 0.225, + 0.208, 0.191, 0.174, 0.156, 0.139, 0.122, 0.105, 0.087, 0.070, 0.052, 0.035, 0.017, 0.000}; +double scorched_tanks_tan[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.123, -0.141, -0.158, -0.176, + -0.194, -0.213, -0.231, -0.249, -0.268, -0.287, -0.306, -0.325, -0.344, -0.364, -0.384, + -0.404, -0.424, -0.445, -0.466, -0.488, -0.510, -0.532, -0.554, -0.577, -0.601, -0.625, + -0.649, -0.674, -0.700, -0.727, -0.754, -0.781, -0.810, -0.839, -0.869, -0.900, -0.932, + -0.966, -1.000, -1.036, -1.072, -1.111, -1.150, -1.192, -1.235, -1.280, -1.327, -1.376, + -1.428, -1.483, -1.540, -1.600, -1.664, -1.732, -1.804, -1.881, -1.963, -2.050, -2.144, + -2.246, -2.356, -2.475, -2.605, -2.747, -2.904, -3.078, -3.271, -3.487, -3.732, -4.011, + -4.331, -4.704, -5.144, -5.671, -6.313, -7.115, -8.144, -9.513, -11.429, -14.298, -19.077, + -28.627, -57.254, -90747.269}; +unsigned char scorched_tanks_ground_modifiers[SCREEN_WIDTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 26, 24, 22, 20, + 18, 16, 14, 12, 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} Point; + +typedef struct { + // +-----x + // | + // | + // y + double x; + double y; +} PointDetailed; + +typedef struct { + unsigned char locationX; + unsigned char hp; + int aimAngle; + unsigned char firePower; +} Tank; + +typedef struct { + Point ground[SCREEN_WIDTH]; + Tank player; + Tank enemy; + bool isPlayerTurn; + bool isShooting; + int windSpeed; + Point trajectory[SCREEN_WIDTH]; + unsigned char trajectoryAnimationStep; + PointDetailed bulletPosition; + PointDetailed bulletVector; +} Game; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} ScorchedTanksEvent; + +int scorched_tanks_random(int min, int max) { + return min + rand() % ((max + 1) - min); +} + +void scorched_tanks_generate_ground(Game* game_state) { + int lastHeight = 45; + + for(unsigned char a = 0; a < SCREEN_WIDTH; a++) { + int diffHeight = scorched_tanks_random(-2, 3); + int changeLength = scorched_tanks_random(1, 6); + + if(diffHeight == 0) { + changeLength = 1; + } + + for(int b = 0; b < changeLength; b++) { + if(a + b < SCREEN_WIDTH) { + int index = a + b; + int newPoint = lastHeight + diffHeight; + newPoint = newPoint < MIN_GROUND_HEIGHT ? MIN_GROUND_HEIGHT : newPoint; + newPoint = newPoint > MAX_GROUND_HEIGHT ? MAX_GROUND_HEIGHT : newPoint; + game_state->ground[index].x = index; + game_state->ground[index].y = newPoint - scorched_tanks_ground_modifiers[a]; + lastHeight = newPoint; + } else { + a += b; + break; + } + } + + a += changeLength - 1; + } +} + +void scorched_tanks_init_game(Game* game_state) { + game_state->player.locationX = PLAYER_INIT_LOCATION_X + + scorched_tanks_random(0, MAX_PLAYER_DIFF_X) - + MAX_PLAYER_DIFF_X / 2; + game_state->player.aimAngle = PLAYER_INIT_AIM; + game_state->player.firePower = PLAYER_INIT_POWER; + game_state->enemy.aimAngle = PLAYER_INIT_AIM; + game_state->enemy.firePower = PLAYER_INIT_POWER; + game_state->enemy.locationX = + ENEMY_INIT_LOCATION_X + scorched_tanks_random(0, MAX_ENEMY_DIFF_X) - MAX_ENEMY_DIFF_X / 2; + game_state->isPlayerTurn = true; + + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + scorched_tanks_generate_ground(game_state); +} + +void scorched_tanks_calculate_trajectory(Game* game_state) { + if(game_state->isShooting) { + game_state->bulletVector.x += ((double)game_state->windSpeed - MAX_WIND / 2) / 40; + game_state->bulletVector.y += GRAVITY_FORCE; + + game_state->bulletPosition.x += game_state->bulletVector.x; + game_state->bulletPosition.y += game_state->bulletVector.y; + + int totalDistanceToEnemy = 100; + + if(game_state->isPlayerTurn) { + double distanceToEnemyX = game_state->enemy.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->enemy.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } else { + double distanceToEnemyX = game_state->player.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->player.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } + + if(totalDistanceToEnemy <= TANK_COLLIDER_SIZE) { + game_state->isShooting = false; + scorched_tanks_init_game(game_state); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.x > SCREEN_WIDTH || + game_state->bulletPosition.y > + game_state->ground[(int)round(game_state->bulletPosition.x)].y) { + game_state->isShooting = false; + game_state->bulletPosition.x = 0; + game_state->bulletPosition.y = 0; + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.y > 0) { + game_state->trajectory[game_state->trajectoryAnimationStep].x = + round(game_state->bulletPosition.x); + game_state->trajectory[game_state->trajectoryAnimationStep].y = + round(game_state->bulletPosition.y); + game_state->trajectoryAnimationStep++; + } + } +} + +static void scorched_tanks_draw_tank( + Canvas* const canvas, + unsigned char x, + unsigned char y, + bool isPlayer) { + unsigned char lineIndex = 0; + + if(isPlayer) { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex++); + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex++); + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex++); + + // draw turret + canvas_draw_line(canvas, x - 2, y - lineIndex, x + 1, y - lineIndex++); + canvas_draw_line(canvas, x - 2, y - lineIndex, x, y - lineIndex++); + } else { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex++); + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex++); + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex++); + + // draw turret + canvas_draw_line(canvas, x - 1, y - lineIndex, x + 2, y - lineIndex++); + canvas_draw_line(canvas, x, y - lineIndex, x + 2, y - lineIndex++); + } +} + +static void scorched_tanks_render_callback(Canvas* const canvas, void* ctx) { + const Game* game_state = acquire_mutex((ValueMutex*)ctx, 25); + + if(game_state == NULL) { + return; + } + + canvas_draw_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + canvas_set_color(canvas, ColorBlack); + + if(game_state->isShooting) { + canvas_draw_dot(canvas, game_state->bulletPosition.x, game_state->bulletPosition.y); + } + + for(int a = 1; a < SCREEN_WIDTH; a++) { + canvas_draw_line( + canvas, + game_state->ground[a - 1].x, + game_state->ground[a - 1].y, + game_state->ground[a].x, + game_state->ground[a].y); + + if(game_state->trajectory[a].y != 0) { + canvas_draw_dot(canvas, game_state->trajectory[a].x, game_state->trajectory[a].y); + } + } + + scorched_tanks_draw_tank( + canvas, + game_state->enemy.locationX, + game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE, + true); + + scorched_tanks_draw_tank( + canvas, + game_state->player.locationX, + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE, + false); + + int aimX1 = 0; + int aimY1 = 0; + int aimX2 = 0; + int aimY2 = 0; + + if(game_state->isPlayerTurn) { + aimX1 = game_state->player.locationX; + aimY1 = game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX1 += 1; + aimX2 += 1; + } else { + aimX1 = game_state->enemy.locationX; + aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX2 = aimX1 - (aimX2 - aimX1); + + aimX1 -= 1; + aimX2 -= 1; + } + + canvas_draw_line(canvas, aimX1, aimY1 - 3, aimX2, aimY2 - 3); + + canvas_set_font(canvas, FontSecondary); + + char buffer2[12]; + snprintf(buffer2, sizeof(buffer2), "wind: %i", game_state->windSpeed - MAX_WIND / 2); + canvas_draw_str(canvas, 55, 10, buffer2); + + if(game_state->isPlayerTurn) { + canvas_draw_str(canvas, 93, 10, "player1"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->player.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->player.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } else { + canvas_draw_str(canvas, 93, 10, "player2"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->enemy.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->enemy.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } + + release_mutex((ValueMutex*)ctx, game_state); +} + +static void scorched_tanks_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void scorched_tanks_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void scorched_tanks_increase_power(Game* game_state) { + if(game_state->player.firePower < MAX_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower < MAX_FIRE_POWER) { + game_state->player.firePower++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower < MAX_FIRE_POWER) { + game_state->enemy.firePower++; + } + } +} + +static void scorched_tanks_decrease_power(Game* game_state) { + if(game_state->player.firePower > MIN_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower > MIN_FIRE_POWER) { + game_state->player.firePower--; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower > MIN_FIRE_POWER) { + game_state->enemy.firePower--; + } + } +} + +static void scorched_tanks_aim_up(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.aimAngle < 90) { + game_state->player.aimAngle++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.aimAngle < 90) { + game_state->enemy.aimAngle++; + } + } +} + +static void scorched_tanks_aim_down(Game* game_state) { + if(game_state->player.aimAngle > 0 && !game_state->isShooting) { + if(game_state->isPlayerTurn) { + game_state->player.aimAngle--; + } else { + game_state->enemy.aimAngle--; + } + } +} + +const NotificationSequence sequence_long_vibro = { + &message_vibro_on, + &message_delay_500, + &message_vibro_off, + NULL, +}; + +static void scorched_tanks_fire(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn) { + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + unsigned char aimX1 = game_state->player.locationX; + unsigned char aimY1 = + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = scorched_tanks_cos[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + } else { + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + unsigned char aimX1 = game_state->enemy.locationX; + unsigned char aimY1 = + game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + aimX2 = aimX1 - (aimX2 - aimX1); + + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = -scorched_tanks_cos[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + } + + game_state->trajectoryAnimationStep = 0; + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + game_state->isShooting = true; + + NotificationApp* notification = furi_record_open("notification"); + notification_message(notification, &sequence_long_vibro); + notification_message(notification, &sequence_blink_white_100); + furi_record_close("notification"); + } +} + +int32_t scorched_tanks_game_app(void* p) { + UNUSED(p); + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(ScorchedTanksEvent)); + + Game* game_state = malloc(sizeof(Game)); + scorched_tanks_init_game(game_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, game_state, sizeof(ScorchedTanksEvent))) { + FURI_LOG_E("ScorchedTanks", "cannot create mutex\r\n"); + free(game_state); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, scorched_tanks_render_callback, &state_mutex); + view_port_input_callback_set(view_port, scorched_tanks_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(scorched_tanks_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, 2000); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + ScorchedTanksEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); + + if(event.type == EventTypeKey) { // && game->isPlayerTurn + if(event.input.type == InputTypeRepeat || event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + scorched_tanks_aim_up(game_state); + break; + case InputKeyDown: + scorched_tanks_aim_down(game_state); + break; + case InputKeyRight: + scorched_tanks_increase_power(game_state); + break; + case InputKeyLeft: + scorched_tanks_decrease_power(game_state); + break; + case InputKeyOk: + scorched_tanks_fire(game_state); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + scorched_tanks_calculate_trajectory(game_state); + } + + view_port_update(view_port); + release_mutex(&state_mutex, game_state); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + free(game_state); + + return 0; +} diff --git a/applications/plugins/scorched_tanks/scorched_tanks_v1.gif b/applications/plugins/scorched_tanks/scorched_tanks_v1.gif new file mode 100644 index 000000000..45b2ce117 Binary files /dev/null and b/applications/plugins/scorched_tanks/scorched_tanks_v1.gif differ diff --git a/applications/plugins/sentry_safe/LICENSE b/applications/plugins/sentry_safe/LICENSE new file mode 100644 index 000000000..c0cd6b598 --- /dev/null +++ b/applications/plugins/sentry_safe/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Etienne Sellan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/sentry_safe/README.md b/applications/plugins/sentry_safe/README.md new file mode 100644 index 000000000..c4b00fefe --- /dev/null +++ b/applications/plugins/sentry_safe/README.md @@ -0,0 +1,26 @@ +# flipperzero-sentry-safe-plugin + +Flipper zero exploiting vulnerability to open any Sentry Safe and Master Lock electronic safe without any pin code. + +[Vulnerability described here](https://github.com/H4ckd4ddy/bypass-sentry-safe) + +### Installation + +- Download [last release fap file](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin/releases/latest) +- Copy fap file to the apps folder of your flipper SD card + +### Usage + +- Start "Sentry Safe" plugin +- Place wires as described on the plugin screen +- Press enter +- Open safe + +### Build + +- Recursively clone your base firmware (official or not) +- Clone this repository in `applications_user` +- Build with `./fbt fap_dist APPSRC=applications_user/flipperzero-sentry-safe-plugin` +- Retreive builed fap in dist subfolders + +(More info about build tool [here](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md)) diff --git a/applications/plugins/sentry_safe/sentry_safe.c b/applications/plugins/sentry_safe/sentry_safe.c index d36e4a63a..4d9c12fc7 100644 --- a/applications/plugins/sentry_safe/sentry_safe.c +++ b/applications/plugins/sentry_safe/sentry_safe.c @@ -143,17 +143,20 @@ int32_t sentry_safe_app(void* p) { case InputKeyBack: processing = false; break; + default: + break; } } } - } else { - // event timeout } view_port_update(view_port); release_mutex(&state_mutex, sentry_state); } + // Reset GPIO pins to default state + furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); diff --git a/applications/plugins/signal_generator/application.fam b/applications/plugins/signal_generator/application.fam index c2bb097a4..7536a4c11 100644 --- a/applications/plugins/signal_generator/application.fam +++ b/applications/plugins/signal_generator/application.fam @@ -8,5 +8,6 @@ App( stack_size=1 * 1024, order=50, fap_icon="signal_gen_10px.png", - fap_category="Tools", + fap_category="GPIO", + 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 000000000..1912e5d24 Binary files /dev/null and b/applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png differ 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 000000000..9c6242078 Binary files /dev/null and b/applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png differ diff --git a/applications/plugins/signal_generator/views/signal_gen_pwm.c b/applications/plugins/signal_generator/views/signal_gen_pwm.c index 6d1a3c1ba..a6f3de26d 100644 --- a/applications/plugins/signal_generator/views/signal_gen_pwm.c +++ b/applications/plugins/signal_generator/views/signal_gen_pwm.c @@ -1,6 +1,7 @@ #include "../signal_gen_app_i.h" #include "furi_hal.h" #include +#include typedef enum { LineIndexChannel, diff --git a/applications/plugins/snake_game/application.fam b/applications/plugins/snake_game/application.fam index 573f5c0b3..6b2dee927 100644 --- a/applications/plugins/snake_game/application.fam +++ b/applications/plugins/snake_game/application.fam @@ -4,8 +4,12 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="snake_game_app", cdefines=["APP_SNAKE_GAME"], - requires=["gui"], - stack_size=1 * 1024, + requires=[ + "gui", + "notification", + "storage", + ], + stack_size=2 * 1024, order=210, fap_icon="snake_10px.png", fap_category="Games", diff --git a/applications/plugins/snake_game/helpers/snake_file_handler.c b/applications/plugins/snake_game/helpers/snake_file_handler.c new file mode 100644 index 000000000..569bd8738 --- /dev/null +++ b/applications/plugins/snake_game/helpers/snake_file_handler.c @@ -0,0 +1,181 @@ +#include "snake_file_handler.h" + +#include +#include + +static void snake_game_close_file(FlipperFormat* file) { + if(file == NULL) { + furi_record_close(RECORD_STORAGE); + return; + } + flipper_format_file_close(file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} + +static FlipperFormat* snake_game_open_file() { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + if(storage_common_stat(storage, SNAKE_GAME_FILE_PATH, NULL) == FSE_OK) { + if(!flipper_format_file_open_existing(file, SNAKE_GAME_FILE_PATH)) { + snake_game_close_file(file); + return NULL; + } + } else { + if(storage_common_stat(storage, APPS_DATA, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, APPS_DATA)) { + return NULL; + } + } + if(storage_common_stat(storage, SNAKE_GAME_FILE_DIR_PATH, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SNAKE_GAME_FILE_DIR_PATH)) { + return NULL; + } + } + + if(!flipper_format_file_open_new(file, SNAKE_GAME_FILE_PATH)) { + snake_game_close_file(file); + return NULL; + } + + flipper_format_write_header_cstr( + file, SNAKE_GAME_FILE_HEADER, SNAKE_GAME_FILE_ACTUAL_VERSION); + flipper_format_rewind(file); + } + return file; +} + +void snake_game_save_score_to_file(int16_t highscore) { + FlipperFormat* file = snake_game_open_file(); + if(file != NULL) { + uint32_t temp = highscore; + if(!flipper_format_insert_or_update_uint32(file, SNAKE_GAME_CONFIG_HIGHSCORE, &temp, 1)) { + snake_game_close_file(file); + return; + } + snake_game_close_file(file); + } +} + +void snake_game_save_game_to_file(SnakeState* const snake_state) { + FlipperFormat* file = snake_game_open_file(); + + if(file != NULL) { + uint32_t temp = snake_state->len; + if(!flipper_format_insert_or_update_uint32(file, SNAKE_GAME_CONFIG_KEY_LEN, &temp, 1)) { + snake_game_close_file(file); + return; + } + + uint16_t array_size = snake_state->len * 2; + uint32_t temp_array[array_size]; + for(int16_t i = 0, a = 0; a < array_size && i < snake_state->len; i++) { + temp_array[a++] = snake_state->points[i].x; + temp_array[a++] = snake_state->points[i].y; + } + if(!flipper_format_insert_or_update_uint32( + file, SNAKE_GAME_CONFIG_KEY_POINTS, temp_array, array_size)) { + snake_game_close_file(file); + return; + } + + temp = snake_state->currentMovement; + if(!flipper_format_insert_or_update_uint32( + file, SNAKE_GAME_CONFIG_KEY_CURRENT_MOVEMENT, &temp, 1)) { + snake_game_close_file(file); + return; + } + + temp = snake_state->nextMovement; + if(!flipper_format_insert_or_update_uint32( + file, SNAKE_GAME_CONFIG_KEY_NEXT_MOVEMENT, &temp, 1)) { + snake_game_close_file(file); + return; + } + + array_size = 2; + uint32_t temp_point_array[array_size]; + temp_point_array[0] = snake_state->fruit.x; + temp_point_array[1] = snake_state->fruit.y; + if(!flipper_format_insert_or_update_uint32( + file, SNAKE_GAME_CONFIG_KEY_FRUIT_POINTS, temp_point_array, array_size)) { + snake_game_close_file(file); + return; + } + + snake_game_close_file(file); + } +} + +bool snake_game_init_game_from_file(SnakeState* const snake_state) { + FlipperFormat* file = snake_game_open_file(); + + if(file != NULL) { + FuriString* file_type = furi_string_alloc(); + uint32_t version = 1; + if(!flipper_format_read_header(file, file_type, &version)) { + furi_string_free(file_type); + snake_game_close_file(file); + return false; + } + furi_string_free(file_type); + + uint32_t temp; + snake_state->highscore = + (flipper_format_read_uint32(file, SNAKE_GAME_CONFIG_HIGHSCORE, &temp, 1)) ? temp : 0; + flipper_format_rewind(file); + + if(!flipper_format_read_uint32(file, SNAKE_GAME_CONFIG_KEY_LEN, &temp, 1)) { + snake_game_close_file(file); + return false; + } + snake_state->len = temp; + flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_LEN); + + uint16_t array_size = snake_state->len * 2; + uint32_t temp_array[array_size]; + if(!flipper_format_read_uint32( + file, SNAKE_GAME_CONFIG_KEY_POINTS, temp_array, array_size)) { + snake_game_close_file(file); + return false; + } + + for(int16_t i = 0, a = 0; a < array_size && i < snake_state->len; i++) { + snake_state->points[i].x = temp_array[a++]; + snake_state->points[i].y = temp_array[a++]; + } + flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_POINTS); + + if(!flipper_format_read_uint32(file, SNAKE_GAME_CONFIG_KEY_CURRENT_MOVEMENT, &temp, 1)) { + snake_game_close_file(file); + return false; + } + snake_state->currentMovement = temp; + flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_CURRENT_MOVEMENT); + + if(!flipper_format_read_uint32(file, SNAKE_GAME_CONFIG_KEY_NEXT_MOVEMENT, &temp, 1)) { + snake_game_close_file(file); + return false; + } + snake_state->nextMovement = temp; + flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_NEXT_MOVEMENT); + + array_size = 2; + uint32_t temp_point_array[array_size]; + if(!flipper_format_read_uint32( + file, SNAKE_GAME_CONFIG_KEY_FRUIT_POINTS, temp_point_array, array_size)) { + snake_game_close_file(file); + return false; + } + snake_state->fruit.x = temp_point_array[0]; + snake_state->fruit.y = temp_point_array[1]; + flipper_format_delete_key(file, SNAKE_GAME_CONFIG_KEY_FRUIT_POINTS); + + snake_game_close_file(file); + + return true; + } + + return false; +} diff --git a/applications/plugins/snake_game/helpers/snake_file_handler.h b/applications/plugins/snake_game/helpers/snake_file_handler.h new file mode 100644 index 000000000..178d5d8e0 --- /dev/null +++ b/applications/plugins/snake_game/helpers/snake_file_handler.h @@ -0,0 +1,25 @@ +#pragma once + +#include "snake_types.h" +#include +#include + +#define APPS_DATA EXT_PATH("apps_data") +#define SNAKE_GAME_FILE_DIR_PATH APPS_DATA "/snake_game" +#define SNAKE_GAME_FILE_PATH SNAKE_GAME_FILE_DIR_PATH "/.snake" + +#define SNAKE_GAME_FILE_HEADER "Flipper Snake plugin run file" +#define SNAKE_GAME_FILE_ACTUAL_VERSION 1 + +#define SNAKE_GAME_CONFIG_KEY_POINTS "SnakePoints" +#define SNAKE_GAME_CONFIG_KEY_LEN "SnakeLen" +#define SNAKE_GAME_CONFIG_KEY_CURRENT_MOVEMENT "CurrentMovement" +#define SNAKE_GAME_CONFIG_KEY_NEXT_MOVEMENT "NextMovement" +#define SNAKE_GAME_CONFIG_KEY_FRUIT_POINTS "FruitPoints" +#define SNAKE_GAME_CONFIG_HIGHSCORE "Highscore" + +void snake_game_save_score_to_file(int16_t highscore); + +void snake_game_save_game_to_file(SnakeState* const snake_state); + +bool snake_game_init_game_from_file(SnakeState* const snake_state); diff --git a/applications/plugins/snake_game/helpers/snake_types.h b/applications/plugins/snake_game/helpers/snake_types.h new file mode 100644 index 000000000..08682f585 --- /dev/null +++ b/applications/plugins/snake_game/helpers/snake_types.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} Point; + +typedef enum { + GameStateLife, + + // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto + // Armanto: While testing the early versions of the game, I noticed it was hard + // to control the snake upon getting close to and edge but not crashing β€” especially + // in the highest speed levels. I wanted the highest level to be as fast as I could + // possibly make the device "run," but on the other hand, I wanted to be friendly + // and help the player manage that level. Otherwise it might not be fun to play. So + // I implemented a little delay. A few milliseconds of extra time right before + // the player crashes, during which she can still change the directions. And if + // she does, the game continues. + GameStateLastChance, + + GameStateGameOver, +} GameState; + +// Note: do not change without purpose. Current values are used in smart +// orthogonality calculation in `snake_game_get_turn_snake`. +typedef enum { + DirectionUp, + DirectionRight, + DirectionDown, + DirectionLeft, +} Direction; + +#define MAX_SNAKE_LEN 253 + +typedef struct { + Point points[MAX_SNAKE_LEN]; + uint16_t len; + bool isNewHighscore; + int16_t highscore; + Direction currentMovement; + Direction nextMovement; // if backward of currentMovement, ignore + Point fruit; + GameState state; +} SnakeState; diff --git a/applications/plugins/snake_game/snake_game.c b/applications/plugins/snake_game/snake_game.c index bb29d410a..d4db04936 100644 --- a/applications/plugins/snake_game/snake_game.c +++ b/applications/plugins/snake_game/snake_game.c @@ -1,3 +1,6 @@ +#include "helpers/snake_file_handler.h" +#include "helpers/snake_types.h" + #include #include #include @@ -7,52 +10,6 @@ #include #include -typedef struct { - // +-----x - // | - // | - // y - uint8_t x; - uint8_t y; -} Point; - -typedef enum { - GameStateLife, - - // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto - // Armanto: While testing the early versions of the game, I noticed it was hard - // to control the snake upon getting close to and edge but not crashing β€” especially - // in the highest speed levels. I wanted the highest level to be as fast as I could - // possibly make the device "run," but on the other hand, I wanted to be friendly - // and help the player manage that level. Otherwise it might not be fun to play. So - // I implemented a little delay. A few milliseconds of extra time right before - // the player crashes, during which she can still change the directions. And if - // she does, the game continues. - GameStateLastChance, - - GameStateGameOver, -} GameState; - -// Note: do not change without purpose. Current values are used in smart -// orthogonality calculation in `snake_game_get_turn_snake`. -typedef enum { - DirectionUp, - DirectionRight, - DirectionDown, - DirectionLeft, -} Direction; - -#define MAX_SNAKE_LEN 253 - -typedef struct { - Point points[MAX_SNAKE_LEN]; - uint16_t len; - Direction currentMovement; - Direction nextMovement; // if backward of currentMovement, ignore - Point fruit; - GameState state; -} SnakeState; - typedef enum { EventTypeTick, EventTypeKey, @@ -117,26 +74,35 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { canvas_draw_box(canvas, p.x, p.y, 4, 4); } + // Show score on the game field + if(snake_state->state != GameStateGameOver) { + char buffer2[6]; + canvas_set_font(canvas, FontBatteryPercent); + snprintf(buffer2, sizeof(buffer2), "%u", snake_state->len); + canvas_draw_str_aligned(canvas, 124, 10, AlignRight, AlignBottom, buffer2); + } // Game Over banner if(snake_state->state == GameStateGameOver) { // Screen is 128x64 px canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 34, 20, 62, 24); + canvas_draw_box(canvas, 32, 20, 64, 34); canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, 34, 20, 62, 24); + canvas_draw_frame(canvas, 32, 20, 64, 34); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 37, 31, "Game Over"); - if((snake_state->len - 7) % 20 == 0 && (snake_state->len - 7) != 0) { - DOLPHIN_DEED(getRandomDeed()); - } - + char buffer[18]; canvas_set_font(canvas, FontSecondary); - char buffer[12]; - snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7); + snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len); canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); + + snprintf(buffer, sizeof(buffer), "Highscore: %d", snake_state->highscore); + canvas_draw_str_aligned(canvas, 64, 51, AlignCenter, AlignBottom, buffer); + } + if((snake_state->len - 7) % 20 == 0 && (snake_state->len - 7) != 0) { + DOLPHIN_DEED(getRandomDeed()); } release_mutex((ValueMutex*)ctx, snake_state); @@ -267,16 +233,24 @@ static void snake_game_move_snake(SnakeState* const snake_state, Point const nex snake_state->points[0] = next_step; } +static void snake_game_game_over(SnakeState* const snake_state, NotificationApp* notification) { + snake_state->state = GameStateGameOver; + snake_state->len = snake_state->len - 7; + if(snake_state->len > snake_state->highscore) { + snake_state->isNewHighscore = true; + snake_state->highscore = snake_state->len; + } + + notification_message_block(notification, &sequence_fail); +} + static void snake_game_process_game_step(SnakeState* const snake_state, NotificationApp* notification) { if(snake_state->state == GameStateGameOver) { return; } - bool can_turn = (snake_state->points[0].x % 2 == 0) && (snake_state->points[0].y % 2 == 0); - if(can_turn) { - snake_state->currentMovement = snake_game_get_turn_snake(snake_state); - } + snake_state->currentMovement = snake_game_get_turn_snake(snake_state); Point next_step = snake_game_get_next_step(snake_state); @@ -286,8 +260,7 @@ static void snake_state->state = GameStateLastChance; return; } else if(snake_state->state == GameStateLastChance) { - snake_state->state = GameStateGameOver; - notification_message_block(notification, &sequence_fail); + snake_game_game_over(snake_state, notification); return; } } else { @@ -298,8 +271,7 @@ static void crush = snake_game_collision_with_tail(snake_state, next_step); if(crush) { - snake_state->state = GameStateGameOver; - notification_message_block(notification, &sequence_fail); + snake_game_game_over(snake_state, notification); return; } @@ -307,8 +279,7 @@ static void if(eatFruit) { snake_state->len++; if(snake_state->len >= MAX_SNAKE_LEN) { - snake_state->state = GameStateGameOver; - notification_message_block(notification, &sequence_fail); + snake_game_game_over(snake_state, notification); return; } } @@ -323,12 +294,15 @@ static void int32_t snake_game_app(void* p) { UNUSED(p); - srand(DWT->CYCCNT); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); SnakeState* snake_state = malloc(sizeof(SnakeState)); - snake_game_init_game(snake_state); + snake_state->isNewHighscore = false; + snake_state->highscore = 0; + if(!snake_game_init_game_from_file(snake_state)) { + snake_game_init_game(snake_state); + } ValueMutex state_mutex; if(!init_mutex(&state_mutex, snake_state, sizeof(SnakeState))) { @@ -384,8 +358,13 @@ int32_t snake_game_app(void* p) { } break; case InputKeyBack: + if(snake_state->state == GameStateLife) { + snake_game_save_game_to_file(snake_state); + } processing = false; break; + default: + break; } } } else if(event.type == EventTypeTick) { @@ -399,6 +378,9 @@ int32_t snake_game_app(void* p) { release_mutex(&state_mutex, snake_state); } + if(snake_state->isNewHighscore) { + snake_game_save_score_to_file(snake_state->highscore); + } // Wait for all notifications to be played and return backlight to normal state notification_message_block(notification, &sequence_display_backlight_enforce_auto); @@ -414,26 +396,3 @@ int32_t snake_game_app(void* p) { return 0; } - -// Screen is 128x64 px -// (4 + 4) * 16 - 4 + 2 + 2border == 128 -// (4 + 4) * 8 - 4 + 2 + 2border == 64 -// Game field from point{x: 0, y: 0} to point{x: 30, y: 14}. -// The snake turns only in even cells - intersections. -// β”Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ” -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β•Ž β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β–ͺ β•Ž -// β””β•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ•Œβ”˜ diff --git a/applications/plugins/solitaire/application.fam b/applications/plugins/solitaire/application.fam new file mode 100644 index 000000000..66e4567ec --- /dev/null +++ b/applications/plugins/solitaire/application.fam @@ -0,0 +1,13 @@ +App( + appid="Solitaire", + name="Solitaire", + apptype=FlipperAppType.EXTERNAL, + entry_point="solitaire_app", + cdefines=["APP_SOLITAIRE"], + requires=["gui","storage","canvas"], + stack_size=2 * 1024, + order=30, + fap_icon="solitaire_10px.png", + fap_category="Games", + fap_icon_assets="assets" +) \ No newline at end of file diff --git a/applications/plugins/solitaire/assets/card_graphics.png b/applications/plugins/solitaire/assets/card_graphics.png new file mode 100644 index 000000000..8b00e351f Binary files /dev/null and b/applications/plugins/solitaire/assets/card_graphics.png differ diff --git a/applications/plugins/solitaire/assets/solitaire_main.png b/applications/plugins/solitaire/assets/solitaire_main.png new file mode 100644 index 000000000..996556d57 Binary files /dev/null and b/applications/plugins/solitaire/assets/solitaire_main.png differ diff --git a/applications/plugins/solitaire/common/card.c b/applications/plugins/solitaire/common/card.c new file mode 100644 index 000000000..199135bb5 --- /dev/null +++ b/applications/plugins/solitaire/common/card.c @@ -0,0 +1,353 @@ +#include "card.h" +#include "dml.h" +#include "ui.h" + +#define CARD_DRAW_X_START 108 +#define CARD_DRAW_Y_START 38 +#define CARD_DRAW_X_SPACE 10 +#define CARD_DRAW_Y_SPACE 8 +#define CARD_DRAW_X_OFFSET 4 +#define CARD_DRAW_FIRST_ROW_LENGTH 7 + +uint8_t pips[4][3] = { + {21, 10, 7}, //spades + {7, 10, 7}, //hearts + {0, 10, 7}, //diamonds + {14, 10, 7}, //clubs +}; +uint8_t letters[13][3] = { + {0, 0, 5}, + {5, 0, 5}, + {10, 0, 5}, + {15, 0, 5}, + {20, 0, 5}, + {25, 0, 5}, + {30, 0, 5}, + {0, 5, 5}, + {5, 5, 5}, + {10, 5, 5}, + {15, 5, 5}, + {20, 5, 5}, + {25, 5, 5}, +}; + +//region Player card positions +uint8_t playerCardPositions[22][4] = { + //first row + {108, 38}, + {98, 38}, + {88, 38}, + {78, 38}, + {68, 38}, + {58, 38}, + {48, 38}, + {38, 38}, + //second row + {104, 26}, + {94, 26}, + {84, 26}, + {74, 26}, + {64, 26}, + {54, 26}, + {44, 26}, + //third row + {99, 14}, + {89, 14}, + {79, 14}, + {69, 14}, + {59, 14}, + {49, 14}, +}; +//endregion +Icon* card_graphics = NULL; + +void set_card_graphics(const Icon* graphics) { + card_graphics = (Icon*)graphics; +} + +void draw_card_at_colored( + int8_t pos_x, + int8_t pos_y, + uint8_t pip, + uint8_t character, + bool inverted, + Canvas* const canvas) { + DrawMode primary = inverted ? Black : White; + DrawMode secondary = inverted ? White : Black; + draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, primary); + draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); + + uint8_t* drawInfo = pips[pip]; + uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; + + uint8_t left = pos_x + 2; + uint8_t right = (pos_x + CARD_WIDTH - s - 2); + uint8_t top = pos_y + 2; + uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2); + + draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s, secondary); + draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s, secondary); + + drawInfo = letters[character]; + px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; + left = pos_x + 2; + right = (pos_x + CARD_WIDTH - s - 2); + top = pos_y + 2; + bottom = (pos_y + CARD_HEIGHT - s - 2); + + draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s, secondary); + draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s, secondary); +} + +void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas) { + draw_card_at_colored(pos_x, pos_y, pip, character, false, canvas); +} + +void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas) { + for(int i = count - 1; i >= 0; i--) { + draw_card_at( + playerCardPositions[i][0], + playerCardPositions[i][1], + cards[i].pip, + cards[i].character, + canvas); + } +} + +Vector card_pos_at_index(uint8_t index) { + return (Vector){playerCardPositions[index][0], playerCardPositions[index][1]}; +} + +void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas) { + draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White); + draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); + + draw_icon_clip(canvas, card_graphics, pos_x + 1, pos_y + 1, 35, 0, 15, 21, Black); +} + +void generate_deck(Deck* deck_ptr, uint8_t deck_count) { + uint16_t counter = 0; + if(deck_ptr->cards != NULL) { + free(deck_ptr->cards); + } + + deck_ptr->deck_count = deck_count; + deck_ptr->card_count = deck_count * 52; + deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count); + + for(uint8_t deck = 0; deck < deck_count; deck++) { + for(uint8_t pip = 0; pip < 4; pip++) { + for(uint8_t label = 0; label < 13; label++) { + deck_ptr->cards[counter] = (Card){pip, label, false, false}; + counter++; + } + } + } +} + +void shuffle_deck(Deck* deck_ptr) { + srand(DWT->CYCCNT); + deck_ptr->index = 0; + int max = deck_ptr->deck_count * 52; + for(int i = 0; i < max; i++) { + int r = i + (rand() % (max - i)); + Card tmp = deck_ptr->cards[i]; + deck_ptr->cards[i] = deck_ptr->cards[r]; + deck_ptr->cards[r] = tmp; + } +} + +uint8_t hand_count(const Card* cards, uint8_t count) { + uint8_t aceCount = 0; + uint8_t score = 0; + + for(uint8_t i = 0; i < count; i++) { + if(cards[i].character == 12) + aceCount++; + else { + if(cards[i].character > 8) + score += 10; + else + score += cards[i].character + 2; + } + } + + for(uint8_t i = 0; i < aceCount; i++) { + if((score + 11) <= 21) + score += 11; + else + score++; + } + + return score; +} + +void draw_card_animation( + Card animatingCard, + Vector from, + Vector control, + Vector to, + float t, + bool extra_margin, + Canvas* const canvas) { + float time = t; + if(extra_margin) { + time += 0.2; + } + + Vector currentPos = quadratic_2d(from, control, to, time); + if(t > 1) { + draw_card_at( + currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas); + } else { + if(t < 0.5) + draw_card_back_at(currentPos.x, currentPos.y, canvas); + else + draw_card_at( + currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas); + } +} + +void init_hand(Hand* hand_ptr, uint8_t count) { + hand_ptr->cards = malloc(sizeof(Card) * count); + hand_ptr->index = 0; + hand_ptr->max = count; +} + +void free_hand(Hand* hand_ptr) { + FURI_LOG_D("CARD", "Freeing hand"); + free(hand_ptr->cards); +} + +void add_to_hand(Hand* hand_ptr, Card card) { + FURI_LOG_D("CARD", "Adding to hand"); + if(hand_ptr->index < hand_ptr->max) { + hand_ptr->cards[hand_ptr->index] = card; + hand_ptr->index++; + } +} + +void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas) { + if(highlighted) { + draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); + draw_rounded_box_frame( + canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White); + } else { + draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); + draw_rounded_box_frame( + canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White); + } +} + +int first_non_flipped_card(Hand hand) { + for(int i = 0; i < hand.index; i++) { + if(!hand.cards[i].flipped) { + return i; + } + } + return hand.index; +} + +void draw_hand_column( + Hand hand, + int16_t pos_x, + int16_t pos_y, + int8_t highlight, + Canvas* const canvas) { + if(hand.index == 0) { + draw_card_space(pos_x, pos_y, highlight > 0, canvas); + if(highlight == 0) + draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Inverse); + return; + } + + int loopEnd = hand.index; + int hStart = max(loopEnd - 4, 0); + int pos = 0; + int first = first_non_flipped_card(hand); + bool wastop = false; + if(first >= 0 && first <= hStart && highlight != first) { + if(first > 0) { + draw_card_back_at(pos_x, pos_y + pos, canvas); + pos += 4; + hStart++; + wastop = true; + } + draw_card_at_colored( + pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character, false, canvas); + pos += 8; + hStart++; + } + if(hStart > highlight && highlight >= 0) { + if(!wastop && first > 0) { + draw_card_back_at(pos_x, pos_y + pos, canvas); + pos += 4; + hStart++; + } + draw_card_at_colored( + pos_x, + pos_y + pos, + hand.cards[highlight].pip, + hand.cards[highlight].character, + true, + canvas); + pos += 8; + hStart++; + } + for(int i = hStart; i < loopEnd; i++, pos += 4) { + if(hand.cards[i].flipped) { + draw_card_back_at(pos_x, pos_y + pos, canvas); + if(i == highlight) + draw_rounded_box( + canvas, pos_x + 1, pos_y + pos + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2, Inverse); + } else { + draw_card_at_colored( + pos_x, + pos_y + pos, + hand.cards[i].pip, + hand.cards[i].character, + (i == highlight), + canvas); + if(i == highlight || i == first) pos += 4; + } + } +} + +Card remove_from_deck(uint16_t index, Deck* deck) { + FURI_LOG_D("CARD", "Removing from deck"); + Card result = {0, 0, true, false}; + if(deck->card_count > 0) { + deck->card_count--; + for(int i = 0, curr_index = 0; i <= deck->card_count; i++) { + if(i != index) { + deck->cards[curr_index] = deck->cards[i]; + curr_index++; + } else { + result = deck->cards[i]; + } + } + if(deck->index >= 0) { + deck->index--; + } + } + return result; +} + +void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index) { + FURI_LOG_D("CARD", "Extracting hand region"); + if(start_index >= hand->index) return; + + for(uint8_t i = start_index; i < hand->index; i++) { + add_to_hand(to, hand->cards[i]); + } + hand->index = start_index; +} + +void add_hand_region(Hand* to, Hand* from) { + FURI_LOG_D("CARD", "Adding hand region"); + if((to->index + from->index) <= to->max) { + for(int i = 0; i < from->index; i++) { + add_to_hand(to, from->cards[i]); + } + } +} \ No newline at end of file diff --git a/applications/plugins/solitaire/common/card.h b/applications/plugins/solitaire/common/card.h new file mode 100644 index 000000000..8e5e23bbf --- /dev/null +++ b/applications/plugins/solitaire/common/card.h @@ -0,0 +1,192 @@ +#pragma once + +#include +#include +#include +#include "dml.h" + +#define CARD_HEIGHT 23 +#define CARD_HALF_HEIGHT 11 +#define CARD_WIDTH 17 +#define CARD_HALF_WIDTH 8 + +//region types +typedef struct { + uint8_t pip; //Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs + uint8_t character; //Card letter [0-12], 0 means 2, 12 is Ace + bool disabled; + bool flipped; +} Card; + +typedef struct { + uint8_t deck_count; //Number of decks used + Card* cards; //Cards in the deck + int card_count; + int index; //Card index (to know where we at in the deck) +} Deck; + +typedef struct { + Card* cards; //Cards in the deck + uint8_t index; //Current index + uint8_t max; //How many cards we want to store +} Hand; +//endregion + +void set_card_graphics(const Icon* graphics); + +/** + * Gets card coordinates at the index (range: 0-20). + * + * @param index Index to check 0-20 + * @return Position of the card + */ +Vector card_pos_at_index(uint8_t index); + +/** + * Draws card at a given coordinate (top-left corner) + * + * @param pos_x X position + * @param pos_y Y position + * @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs + * @param character Letter [0-12] 0 is 2, 12 is A + * @param canvas Pointer to Flipper's canvas object + */ +void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas); + +/** + * Draws card at a given coordinate (top-left corner) + * + * @param pos_x X position + * @param pos_y Y position + * @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs + * @param character Letter [0-12] 0 is 2, 12 is A + * @param inverted Invert colors + * @param canvas Pointer to Flipper's canvas object + */ +void draw_card_at_colored( + int8_t pos_x, + int8_t pos_y, + uint8_t pip, + uint8_t character, + bool inverted, + Canvas* const canvas); + +/** + * Draws 'count' cards at the bottom right corner + * + * @param cards List of cards + * @param count Count of cards + * @param canvas Pointer to Flipper's canvas object + */ +void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas); + +/** + * Draws card back at a given coordinate (top-left corner) + * + * @param pos_x X coordinate + * @param pos_y Y coordinate + * @param canvas Pointer to Flipper's canvas object + */ +void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas); + +/** + * Generates the deck + * + * @param deck_ptr Pointer to the deck + * @param deck_count Number of decks + */ +void generate_deck(Deck* deck_ptr, uint8_t deck_count); + +/** + * Shuffles the deck + * + * @param deck_ptr Pointer to the deck + */ +void shuffle_deck(Deck* deck_ptr); + +/** + * Calculates the hand count for blackjack + * + * @param cards List of cards + * @param count Count of cards + * @return Hand value + */ +uint8_t hand_count(const Card* cards, uint8_t count); + +/** + * Draws card animation + * + * @param animatingCard Card to animate + * @param from Starting position + * @param control Quadratic lerp control point + * @param to End point + * @param t Current time (0-1) + * @param extra_margin Use extra margin at the end (arrives 0.2 unit before the end so it can stay there a bit) + * @param canvas Pointer to Flipper's canvas object + */ +void draw_card_animation( + Card animatingCard, + Vector from, + Vector control, + Vector to, + float t, + bool extra_margin, + Canvas* const canvas); + +/** + * Init hand pointer + * @param hand_ptr Pointer to hand + * @param count Number of cards we want to store + */ +void init_hand(Hand* hand_ptr, uint8_t count); + +/** + * Free hand resources + * @param hand_ptr Pointer to hand + */ +void free_hand(Hand* hand_ptr); + +/** + * Add card to hand + * @param hand_ptr Pointer to hand + * @param card Card to add + */ +void add_to_hand(Hand* hand_ptr, Card card); + +/** + * Draw card placement position at coordinate + * @param pos_x X coordinate + * @param pos_y Y coordinate + * @param highlighted Apply highlight effect + * @param canvas Canvas object + */ +void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas); + +/** + * Draws a column of card, displaying the last [max_cards] cards on the list + * @param hand Hand object + * @param pos_x X coordinate to draw + * @param pos_y Y coordinate to draw + * @param highlight Index to highlight, negative means no highlight + * @param canvas Canvas object + */ +void draw_hand_column( + Hand hand, + int16_t pos_x, + int16_t pos_y, + int8_t highlight, + Canvas* const canvas); + +/** + * Removes a card from the deck (Be aware, if you remove the first item, the deck index will be at -1 so you have to handle that) + * @param index Index to remove + * @param deck Deck reference + * @return The removed card + */ +Card remove_from_deck(uint16_t index, Deck* deck); + +int first_non_flipped_card(Hand hand); + +void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index); + +void add_hand_region(Hand* to, Hand* from); \ No newline at end of file diff --git a/applications/plugins/solitaire/common/dml.c b/applications/plugins/solitaire/common/dml.c new file mode 100644 index 000000000..b9a0e395f --- /dev/null +++ b/applications/plugins/solitaire/common/dml.c @@ -0,0 +1,53 @@ +#include "dml.h" +#include + +float lerp(float v0, float v1, float t) { + if(t > 1) return v1; + return (1 - t) * v0 + t * v1; +} + +Vector lerp_2d(Vector start, Vector end, float t) { + return (Vector){ + lerp(start.x, end.x, t), + lerp(start.y, end.y, t), + }; +} + +Vector quadratic_2d(Vector start, Vector control, Vector end, float t) { + return lerp_2d(lerp_2d(start, control, t), lerp_2d(control, end, t), t); +} + +Vector vector_add(Vector a, Vector b) { + return (Vector){a.x + b.x, a.y + b.y}; +} + +Vector vector_sub(Vector a, Vector b) { + return (Vector){a.x - b.x, a.y - b.y}; +} + +Vector vector_mul_components(Vector a, Vector b) { + return (Vector){a.x * b.x, a.y * b.y}; +} + +Vector vector_div_components(Vector a, Vector b) { + return (Vector){a.x / b.x, a.y / b.y}; +} + +Vector vector_normalized(Vector a) { + float length = vector_magnitude(a); + return (Vector){a.x / length, a.y / length}; +} + +float vector_magnitude(Vector a) { + return sqrt(a.x * a.x + a.y * a.y); +} + +float vector_distance(Vector a, Vector b) { + return vector_magnitude(vector_sub(a, b)); +} + +float vector_dot(Vector a, Vector b) { + Vector _a = vector_normalized(a); + Vector _b = vector_normalized(b); + return _a.x * _b.x + _a.y * _b.y; +} \ No newline at end of file diff --git a/applications/plugins/solitaire/common/dml.h b/applications/plugins/solitaire/common/dml.h new file mode 100644 index 000000000..0e1a23e23 --- /dev/null +++ b/applications/plugins/solitaire/common/dml.h @@ -0,0 +1,116 @@ +// +// Doofy's Math library +// + +#pragma once + +typedef struct { + float x; + float y; +} Vector; + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define abs(x) ((x) > 0 ? (x) : -(x)) + +/** + * Lerp function + * + * @param v0 Start value + * @param v1 End value + * @param t Time (0-1 range) + * @return Point between v0-v1 at a given time + */ +float lerp(float v0, float v1, float t); + +/** + * 2D lerp function + * + * @param start Start vector + * @param end End vector + * @param t Time (0-1 range) + * @return 2d Vector between start and end at time + */ +Vector lerp_2d(Vector start, Vector end, float t); + +/** + * Quadratic lerp function + * + * @param start Start vector + * @param control Control point + * @param end End vector + * @param t Time (0-1 range) + * @return 2d Vector at time + */ +Vector quadratic_2d(Vector start, Vector control, Vector end, float t); + +/** + * Add vector components together + * + * @param a First vector + * @param b Second vector + * @return Resulting vector + */ +Vector vector_add(Vector a, Vector b); + +/** + * Subtract vector components together + * + * @param a First vector + * @param b Second vector + * @return Resulting vector + */ +Vector vector_sub(Vector a, Vector b); + +/** + * Multiplying vector components together + * + * @param a First vector + * @param b Second vector + * @return Resulting vector + */ +Vector vector_mul_components(Vector a, Vector b); + +/** + * Dividing vector components + * + * @param a First vector + * @param b Second vector + * @return Resulting vector + */ +Vector vector_div_components(Vector a, Vector b); + +/** + * Calculating Vector length + * + * @param a Direction vector + * @return Length of the vector + */ +float vector_magnitude(Vector a); + +/** + * Get a normalized vector (length of 1) + * + * @param a Direction vector + * @return Normalized vector + */ +Vector vector_normalized(Vector a); + +/** + * Calculate two vector's distance + * + * @param a First vector + * @param b Second vector + * @return Distance between vectors + */ +float vector_distance(Vector a, Vector b); + +/** + * Calculate the dot product of the vectors. + * No need to normalize, it will do it + * + * @param a First vector + * @param b Second vector + * @return value from -1 to 1 + */ +float vector_dot(Vector a, Vector b); diff --git a/applications/plugins/solitaire/common/menu.c b/applications/plugins/solitaire/common/menu.c new file mode 100644 index 000000000..ffc3921b7 --- /dev/null +++ b/applications/plugins/solitaire/common/menu.c @@ -0,0 +1,103 @@ +#include "menu.h" + +void add_menu(Menu* menu, const char* name, void (*callback)(void*)) { + MenuItem* items = menu->items; + + menu->items = malloc(sizeof(MenuItem) * (menu->menu_count + 1)); + for(uint8_t i = 0; i < menu->menu_count; i++) { + menu->items[i] = items[i]; + } + free(items); + + menu->items[menu->menu_count] = (MenuItem){name, true, callback}; + menu->menu_count++; +} + +void free_menu(Menu* menu) { + free(menu->items); + free(menu); +} + +void set_menu_state(Menu* menu, uint8_t index, bool state) { + if(menu->menu_count > index) { + menu->items[index].enabled = state; + } + if(!state && menu->current_menu == index) move_menu(menu, 1); +} + +void move_menu(Menu* menu, int8_t direction) { + if(!menu->enabled) return; + int max = menu->menu_count; + for(int8_t i = 0; i < max; i++) { + FURI_LOG_D( + "MENU", + "Iteration %i, current %i, direction %i, state %i", + i, + menu->current_menu, + direction, + menu->items[menu->current_menu].enabled ? 1 : 0); + if(direction < 0 && menu->current_menu == 0) { + menu->current_menu = menu->menu_count - 1; + } else { + menu->current_menu = (menu->current_menu + direction) % menu->menu_count; + } + FURI_LOG_D( + "MENU", + "After process current %i, direction %i, state %i", + menu->current_menu, + direction, + menu->items[menu->current_menu].enabled ? 1 : 0); + if(menu->items[menu->current_menu].enabled) { + FURI_LOG_D("MENU", "Next menu %i", menu->current_menu); + return; + } + } + FURI_LOG_D("MENU", "Not found, setting false"); + menu->enabled = false; +} + +void activate_menu(Menu* menu, void* state) { + if(!menu->enabled) return; + menu->items[menu->current_menu].callback(state); +} + +void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y) { + if(!menu->enabled) return; + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2); + + uint8_t w = pos_x + menu->menu_width; + uint8_t h = pos_y + 10; + uint8_t p1x = pos_x + 2; + uint8_t p2x = pos_x + menu->menu_width - 2; + uint8_t p1y = pos_y + 2; + uint8_t p2y = pos_y + 8; + + canvas_set_color(canvas, ColorBlack); + canvas_draw_line(canvas, p1x, pos_y, p2x, pos_y); + canvas_draw_line(canvas, p1x, h, p2x, h); + canvas_draw_line(canvas, pos_x, p1y, pos_x, p2y); + canvas_draw_line(canvas, w, p1y, w, p2y); + canvas_draw_dot(canvas, pos_x + 1, pos_y + 1); + canvas_draw_dot(canvas, w - 1, pos_y + 1); + canvas_draw_dot(canvas, w - 1, h - 1); + canvas_draw_dot(canvas, pos_x + 1, h - 1); + + // canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + pos_x + menu->menu_width / 2, + pos_y + 6, + AlignCenter, + AlignCenter, + menu->items[menu->current_menu].name); + //9*5 + int center = pos_x + menu->menu_width / 2; + for(uint8_t i = 0; i < 4; i++) { + for(int8_t j = -i; j <= i; j++) { + canvas_draw_dot(canvas, center + j, pos_y - 4 + i); + canvas_draw_dot(canvas, center + j, pos_y + 14 - i); + } + } +} \ No newline at end of file diff --git a/applications/plugins/solitaire/common/menu.h b/applications/plugins/solitaire/common/menu.h new file mode 100644 index 000000000..9f2852522 --- /dev/null +++ b/applications/plugins/solitaire/common/menu.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +typedef struct { + const char* name; //Name of the menu + bool enabled; //Is the menu item enabled (it will not render, you cannot select it) + + void (*callback)( + void* state); //Callback for when the activate_menu is called while this menu is selected +} MenuItem; + +typedef struct { + MenuItem* items; //list of menu items + uint8_t menu_count; //count of menu items (do not change) + uint8_t current_menu; //currently selected menu item + uint8_t menu_width; //width of the menu + bool enabled; //is the menu enabled (it will not render and accept events when disabled) +} Menu; + +/** + * Cleans up the pointers used by the menu + * + * @param menu Pointer of the menu to clean up + */ +void free_menu(Menu* menu); + +/** + * Add a new menu item + * + * @param menu Pointer of the menu + * @param name Name of the menu item + * @param callback Callback called on activation + */ +void add_menu(Menu* menu, const char* name, void (*callback)(void*)); + +/** + * Setting menu item to be enabled/disabled + * + * @param menu Pointer of the menu + * @param index Menu index to set + * @param state Enabled (true), Disabled(false) + */ +void set_menu_state(Menu* menu, uint8_t index, bool state); + +/** + * Moves selection up or down + * + * @param menu Pointer of the menu + * @param direction Direction to move -1 down, 1 up + */ +void move_menu(Menu* menu, int8_t direction); + +/** + * Triggers the current menu callback + * + * @param menu Pointer of the menu + * @param state Usually your application state + */ +void activate_menu(Menu* menu, void* state); + +/** + * Renders the menu at a coordinate (call it in your render function). + * + * Keep in mind that Flipper has a 128x64 pixel screen resolution and the coordinate + * you give is the menu's rectangle top-left corner (arrows not included). + * The rectangle height is 10 px, the arrows have a 4 pixel height. Space needed is 18px. + * The width of the menu can be configured in the menu object. + * + * + * @param menu Pointer of the menu + * @param canvas Flippers Canvas pointer + * @param pos_x X position to draw + * @param pos_y Y position to draw + */ +void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y); \ No newline at end of file diff --git a/applications/plugins/solitaire/common/queue.c b/applications/plugins/solitaire/common/queue.c new file mode 100644 index 000000000..a80373460 --- /dev/null +++ b/applications/plugins/solitaire/common/queue.c @@ -0,0 +1,69 @@ +#include "queue.h" + +void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas) { + if(queue_state->current != NULL && queue_state->current->render != NULL) + ((QueueItem*)queue_state->current)->render(app_state, canvas); +} + +bool run_queue(QueueState* queue_state, void* app_state) { + if(queue_state->current != NULL) { + queue_state->running = true; + if((furi_get_tick() - queue_state->start) >= queue_state->current->duration) + dequeue(queue_state, app_state); + + return true; + } + return false; +} + +void dequeue(QueueState* queue_state, void* app_state) { + ((QueueItem*)queue_state->current)->callback(app_state); + QueueItem* f = queue_state->current; + queue_state->current = f->next; + free(f); + if(queue_state->current != NULL) { + if(queue_state->current->start != NULL) queue_state->current->start(app_state); + queue_state->start = furi_get_tick(); + } else { + queue_state->running = false; + } +} + +void queue_clear(QueueState* queue_state) { + queue_state->running = false; + QueueItem* curr = queue_state->current; + while(curr != NULL) { + QueueItem* f = curr; + curr = curr->next; + free(f); + } +} + +void enqueue( + QueueState* queue_state, + void* app_state, + void (*done)(void* state), + void (*start)(void* state), + void (*render)(const void* state, Canvas* const canvas), + uint32_t duration) { + QueueItem* next; + if(queue_state->current == NULL) { + queue_state->start = furi_get_tick(); + queue_state->current = malloc(sizeof(QueueItem)); + next = queue_state->current; + if(next->start != NULL) next->start(app_state); + + } else { + next = queue_state->current; + while(next->next != NULL) { + next = (QueueItem*)(next->next); + } + next->next = malloc(sizeof(QueueItem)); + next = next->next; + } + next->callback = done; + next->render = render; + next->start = start; + next->duration = duration; + next->next = NULL; +} \ No newline at end of file diff --git a/applications/plugins/solitaire/common/queue.h b/applications/plugins/solitaire/common/queue.h new file mode 100644 index 000000000..dcfe0c091 --- /dev/null +++ b/applications/plugins/solitaire/common/queue.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +typedef struct { + void (*callback)(void* state); //Callback for when the item is dequeued + void (*render)( + const void* state, + Canvas* const canvas); //Callback for the rendering loop while this item is running + void (*start)(void* state); //Callback when this item is started running + void* next; //Pointer to the next item + uint32_t duration; //duration of the item +} QueueItem; + +typedef struct { + unsigned int start; //current queue item start time + QueueItem* current; //current queue item + bool running; //is the queue running +} QueueState; + +/** + * Enqueue a new item. + * + * @param queue_state The queue state pointer + * @param app_state Your app state + * @param done Callback for dequeue event + * @param start Callback for when the item is activated + * @param render Callback to render loop if needed + * @param duration Length of the item + */ +void enqueue( + QueueState* queue_state, + void* app_state, + void (*done)(void* state), + void (*start)(void* state), + void (*render)(const void* state, Canvas* const canvas), + uint32_t duration); +/** + * Clears all queue items + * + * @param queue_state The queue state pointer + */ +void queue_clear(QueueState* queue_state); + +/** + * Dequeues the active queue item. Usually you don't need to call it directly. + * + * @param queue_state The queue state pointer + * @param app_state Your application state + */ +void dequeue(QueueState* queue_state, void* app_state); + +/** + * Runs the queue logic (place it in your tick function) + * + * @param queue_state The queue state pointer + * @param app_state Your application state + * @return FALSE when there is nothing to run, TRUE otherwise + */ +bool run_queue(QueueState* queue_state, void* app_state); + +/** + * Calls the currently active queue items render callback (if there is any) + * + * @param queue_state The queue state pointer + * @param app_state Your application state + * @param canvas Pointer to Flipper's canvas object + */ +void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas); \ No newline at end of file diff --git a/applications/plugins/solitaire/common/ui.c b/applications/plugins/solitaire/common/ui.c new file mode 100644 index 000000000..032877a9e --- /dev/null +++ b/applications/plugins/solitaire/common/ui.c @@ -0,0 +1,257 @@ +#include "ui.h" +#include +#include +#include +#include +#include +#include + +TileMap* tileMap; +uint8_t tileMapCount = 0; + +void ui_cleanup() { + if(tileMap != NULL) { + for(uint8_t i = 0; i < tileMapCount; i++) { + if(tileMap[i].data != NULL) free(tileMap[i].data); + } + free(tileMap); + } +} + +void add_new_tilemap(uint8_t* data, unsigned long iconId) { + TileMap* old = tileMap; + tileMapCount++; + tileMap = malloc(sizeof(TileMap) * tileMapCount); + if(tileMapCount > 1) { + for(uint8_t i = 0; i < tileMapCount; i++) tileMap[i] = old[i]; + } + tileMap[tileMapCount - 1] = (TileMap){data, iconId}; +} + +uint8_t* get_tilemap(unsigned long icon_id) { + for(uint8_t i = 0; i < tileMapCount; i++) { + if(tileMap[i].iconId == icon_id) return tileMap[i].data; + } + + return NULL; +} + +uint32_t pixel_index(uint8_t x, uint8_t y) { + return y * SCREEN_WIDTH + x; +} + +bool in_screen(int16_t x, int16_t y) { + return x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT; +} + +unsigned flipBit(uint8_t x, uint8_t bit) { + return x ^ (1 << bit); +} + +unsigned setBit(uint8_t x, uint8_t bit) { + return x | (1 << bit); +} + +unsigned unsetBit(uint8_t x, uint8_t bit) { + return x & ~(1 << bit); +} + +bool test_pixel(uint8_t* data, uint8_t x, uint8_t y, uint8_t w) { + uint8_t current_bit = (y % 8); + uint8_t current_row = ((y - current_bit) / 8); + uint8_t current_value = data[current_row * w + x]; + return current_value & (1 << current_bit); +} + +uint8_t* get_buffer(Canvas* const canvas) { + return canvas->fb.tile_buf_ptr; + // return canvas_get_buffer(canvas); +} +uint8_t* make_buffer() { + return malloc(sizeof(uint8_t) * 8 * 128); +} +void clone_buffer(uint8_t* canvas, uint8_t* data) { + for(int i = 0; i < 1024; i++) { + data[i] = canvas[i]; + } +} + +bool read_pixel(Canvas* const canvas, int16_t x, int16_t y) { + if(in_screen(x, y)) { + return test_pixel(get_buffer(canvas), x, y, SCREEN_WIDTH); + } + return false; +} + +void set_pixel(Canvas* const canvas, int16_t x, int16_t y, DrawMode draw_mode) { + if(in_screen(x, y)) { + uint8_t current_bit = (y % 8); + uint8_t current_row = ((y - current_bit) / 8); + uint32_t i = pixel_index(x, current_row); + uint8_t* buffer = get_buffer(canvas); + + uint8_t current_value = buffer[i]; + if(draw_mode == Inverse) { + buffer[i] = flipBit(current_value, current_bit); + } else { + if(draw_mode == White) { + buffer[i] = unsetBit(current_value, current_bit); + } else { + buffer[i] = setBit(current_value, current_bit); + } + } + } +} + +void draw_line( + Canvas* const canvas, + int16_t x1, + int16_t y1, + int16_t x2, + int16_t y2, + DrawMode draw_mode) { + for(int16_t x = x2; x >= x1; x--) { + for(int16_t y = y2; y >= y1; y--) { + set_pixel(canvas, x, y, draw_mode); + } + } +} + +void draw_rounded_box_frame( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode draw_mode) { + int16_t xMinCorner = x + 1; + int16_t xMax = x + w - 1; + int16_t xMaxCorner = x + w - 2; + int16_t yMinCorner = y + 1; + int16_t yMax = y + h - 1; + int16_t yMaxCorner = y + h - 2; + draw_line(canvas, xMinCorner, y, xMaxCorner, y, draw_mode); + draw_line(canvas, xMinCorner, yMax, xMaxCorner, yMax, draw_mode); + draw_line(canvas, x, yMinCorner, x, yMaxCorner, draw_mode); + draw_line(canvas, xMax, yMinCorner, xMax, yMaxCorner, draw_mode); +} + +void draw_rounded_box( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode draw_mode) { + for(int16_t o = w - 2; o >= 1; o--) { + for(int16_t p = h - 2; p >= 1; p--) { + set_pixel(canvas, x + o, y + p, draw_mode); + } + } + draw_rounded_box_frame(canvas, x, y, w, h, draw_mode); +} + +void invert_shape(Canvas* const canvas, uint8_t* data, int16_t x, int16_t y, uint8_t w, uint8_t h) { + draw_pixels(canvas, data, x, y, w, h, Inverse); +} + +void draw_pixels( + Canvas* const canvas, + uint8_t* data, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode) { + for(int8_t o = 0; o < w; o++) { + for(int8_t p = 0; p < h; p++) { + if(in_screen(o + x, p + y) && data[p * w + o] == 1) + set_pixel(canvas, o + x, p + y, drawMode); + } + } +} + +void draw_rectangle( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode) { + for(int8_t o = 0; o < w; o++) { + for(int8_t p = 0; p < h; p++) { + if(in_screen(o + x, p + y)) { + set_pixel(canvas, o + x, p + y, drawMode); + } + } + } +} + +void invert_rectangle(Canvas* const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h) { + draw_rectangle(canvas, x, y, w, h, Inverse); +} + +uint8_t* image_data(Canvas* const canvas, const Icon* icon) { + uint8_t* data = malloc(sizeof(uint8_t) * 8 * 128); + uint8_t* screen = canvas->fb.tile_buf_ptr; + canvas->fb.tile_buf_ptr = data; + canvas_draw_icon(canvas, 0, 0, icon); + canvas->fb.tile_buf_ptr = screen; + return data; +} + +uint8_t* getOrAddIconData(Canvas* const canvas, const Icon* icon) { + uint8_t* icon_data = get_tilemap((unsigned long)icon); + if(icon_data == NULL) { + icon_data = image_data(canvas, icon); + add_new_tilemap(icon_data, (unsigned long)icon); + } + return icon_data; +} + +void draw_icon_clip( + Canvas* const canvas, + const Icon* icon, + int16_t x, + int16_t y, + uint8_t left, + uint8_t top, + uint8_t w, + uint8_t h, + DrawMode drawMode) { + uint8_t* icon_data = getOrAddIconData(canvas, icon); + + for(int i = 0; i < w; i++) { + for(int j = 0; j < h; j++) { + bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH); + if(drawMode == Filled) { + set_pixel(canvas, x + i, y + j, on ? Black : White); + } else if(on) + set_pixel(canvas, x + i, y + j, drawMode); + } + } +} + +void draw_icon_clip_flipped( + Canvas* const canvas, + const Icon* icon, + int16_t x, + int16_t y, + uint8_t left, + uint8_t top, + uint8_t w, + uint8_t h, + DrawMode drawMode) { + uint8_t* icon_data = getOrAddIconData(canvas, icon); + + for(int i = 0; i < w; i++) { + for(int j = 0; j < h; j++) { + bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH); + + if(drawMode == Filled) { + set_pixel(canvas, x + w - i - 1, y + h - j - 1, on ? Black : White); + } else if(on) + set_pixel(canvas, x + w - i - 1, y + h - j - 1, drawMode); + } + } +} \ No newline at end of file diff --git a/applications/plugins/solitaire/common/ui.h b/applications/plugins/solitaire/common/ui.h new file mode 100644 index 000000000..13d8da257 --- /dev/null +++ b/applications/plugins/solitaire/common/ui.h @@ -0,0 +1,105 @@ +#pragma once + +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 + +typedef enum { + Black, + White, + Inverse, + Filled //Currently only for Icon clip drawing +} DrawMode; + +// size is the screen size + +typedef struct { + uint8_t* data; + unsigned long iconId; +} TileMap; + +bool test_pixel(uint8_t* data, uint8_t x, uint8_t y, uint8_t w); + +uint8_t* image_data(Canvas* const canvas, const Icon* icon); + +uint32_t pixel_index(uint8_t x, uint8_t y); + +void draw_icon_clip( + Canvas* const canvas, + const Icon* icon, + int16_t x, + int16_t y, + uint8_t left, + uint8_t top, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void draw_icon_clip_flipped( + Canvas* const canvas, + const Icon* icon, + int16_t x, + int16_t y, + uint8_t left, + uint8_t top, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void draw_rounded_box( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void draw_rounded_box_frame( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void draw_rectangle( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +void invert_rectangle(Canvas* const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h); + +void invert_shape(Canvas* const canvas, uint8_t* data, int16_t x, int16_t y, uint8_t w, uint8_t h); + +void draw_pixels( + Canvas* const canvas, + uint8_t* data, + int16_t x, + int16_t y, + uint8_t w, + uint8_t h, + DrawMode drawMode); + +bool read_pixel(Canvas* const canvas, int16_t x, int16_t y); + +void set_pixel(Canvas* const canvas, int16_t x, int16_t y, DrawMode draw_mode); + +void draw_line( + Canvas* const canvas, + int16_t x1, + int16_t y1, + int16_t x2, + int16_t y2, + DrawMode draw_mode); + +bool in_screen(int16_t x, int16_t y); + +void ui_cleanup(); +uint8_t* get_buffer(Canvas* const canvas); +uint8_t* make_buffer(); +void clone_buffer(uint8_t* canvas, uint8_t* data); \ No newline at end of file diff --git a/applications/plugins/solitaire/defines.h b/applications/plugins/solitaire/defines.h new file mode 100644 index 000000000..1456f1964 --- /dev/null +++ b/applications/plugins/solitaire/defines.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include +#include +#include +#include +#include "common/card.h" +#include "common/queue.h" + +#define APP_NAME "Solitaire" + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} AppEvent; + +typedef enum { GameStateGameOver, GameStateStart, GameStatePlay, GameStateAnimate } PlayState; + +typedef struct { + uint8_t* buffer; + Card card; + int8_t deck; + int indexes[4]; + float x; + float y; + float vx; + float vy; + bool started; +} CardAnimation; + +typedef struct { + Deck deck; + Hand bottom_columns[7]; + Card top_cards[4]; + bool dragging_deck; + uint8_t dragging_column; + Hand dragging_hand; + + InputKey input; + + bool started; + bool processing; + bool longPress; + PlayState state; + unsigned int last_tick; + uint8_t selectRow; + uint8_t selectColumn; + int8_t selected_card; + CardAnimation animation; + uint8_t* buffer; +} GameState; \ No newline at end of file diff --git a/applications/plugins/solitaire/solitaire.c b/applications/plugins/solitaire/solitaire.c new file mode 100644 index 000000000..e1fffbc8a --- /dev/null +++ b/applications/plugins/solitaire/solitaire.c @@ -0,0 +1,568 @@ +#include +#include +#include +#include +#include "defines.h" +#include "common/ui.h" +#include "Solitaire_icons.h" +#include +#include +void init(GameState* game_state); +const NotificationSequence sequence_fail = { + &message_vibro_on, + &message_note_c4, + &message_delay_10, + &message_vibro_off, + &message_sound_off, + &message_delay_10, + + &message_vibro_on, + &message_note_a3, + &message_delay_10, + &message_vibro_off, + &message_sound_off, + NULL, +}; +int8_t columns[7][3] = { + {1, 1, 25}, + {19, 1, 25}, + {37, 1, 25}, + {55, 1, 25}, + {73, 1, 25}, + {91, 1, 25}, + {109, 1, 25}, +}; + +bool can_place_card(Card where, Card what) { + FURI_LOG_D( + APP_NAME, + "TESTING pip %i, letter %i with pip %i, letter %i", + where.pip, + where.character, + what.pip, + what.character); + bool a_black = where.pip == 0 || where.pip == 3; + bool b_black = what.pip == 0 || what.pip == 3; + if(a_black == b_black) return false; + + int8_t a_letter = (int8_t)where.character; + int8_t b_letter = (int8_t)what.character; + if(a_letter == 12) a_letter = -1; + if(b_letter == 12) b_letter = -1; + + return (a_letter - 1) == b_letter; +} + +static void draw_scene(Canvas* const canvas, const GameState* game_state) { + int deckIndex = game_state->deck.index; + if(game_state->dragging_deck) deckIndex--; + + if((game_state->deck.index < (game_state->deck.card_count - 1) || + game_state->deck.index == -1) && + game_state->deck.card_count > 0) { + draw_card_back_at(columns[0][0], columns[0][1], canvas); + if(game_state->selectRow == 0 && game_state->selectColumn == 0) { + draw_rounded_box( + canvas, + columns[0][0] + 1, + columns[0][1] + 1, + CARD_WIDTH - 2, + CARD_HEIGHT - 2, + Inverse); + } + } else + draw_card_space( + columns[0][0], + columns[0][1], + game_state->selectRow == 0 && game_state->selectColumn == 0, + canvas); + //deck side + if(deckIndex >= 0) { + Card c = game_state->deck.cards[deckIndex]; + draw_card_at_colored( + columns[1][0], + columns[1][1], + c.pip, + c.character, + game_state->selectRow == 0 && game_state->selectColumn == 1, + canvas); + } else + draw_card_space( + columns[1][0], + columns[1][1], + game_state->selectRow == 0 && game_state->selectColumn == 1, + canvas); + + for(uint8_t i = 0; i < 4; i++) { + Card current = game_state->top_cards[i]; + bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3); + if(current.disabled) { + draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas); + } else { + draw_card_at( + columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas); + if(selected) { + draw_rounded_box( + canvas, columns[i + 3][0], columns[i + 3][1], CARD_WIDTH, CARD_HEIGHT, Inverse); + } + } + } + + for(uint8_t i = 0; i < 7; i++) { + bool selected = game_state->selectRow == 1 && game_state->selectColumn == i; + int8_t index = (game_state->bottom_columns[i].index - 1 - game_state->selected_card); + if(index < 0) index = 0; + draw_hand_column( + game_state->bottom_columns[i], + columns[i][0], + columns[i][2], + selected ? index : -1, + canvas); + } + + int8_t pos[2] = { + columns[game_state->selectColumn][0], + columns[game_state->selectColumn][game_state->selectRow + 1]}; + + /* draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5, + Filled);*/ + + if(game_state->dragging_hand.index > 0) { + draw_hand_column( + game_state->dragging_hand, + pos[0] + CARD_HALF_WIDTH + 3, + pos[1] + CARD_HALF_HEIGHT + 3, + -1, + canvas); + } +} + +static void draw_animation(Canvas* const canvas, const GameState* game_state) { + if(!game_state->animation.started) { + draw_scene(canvas, game_state); + } else { + clone_buffer(game_state->animation.buffer, get_buffer(canvas)); + + draw_card_at( + (int8_t)game_state->animation.x, + (int8_t)game_state->animation.y, + game_state->animation.card.pip, + game_state->animation.card.character, + canvas); + } + + clone_buffer(get_buffer(canvas), game_state->animation.buffer); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); + if(game_state == NULL) { + return; + } + + switch(game_state->state) { + case GameStateAnimate: + draw_animation(canvas, game_state); + break; + case GameStateStart: + canvas_draw_icon(canvas, 0, 0, &I_solitaire_main); + break; + case GameStatePlay: + draw_scene(canvas, game_state); + break; + default: + break; + } + + release_mutex((ValueMutex*)ctx, game_state); +} + +void remove_drag(GameState* game_state) { + if(game_state->dragging_deck) { + remove_from_deck(game_state->deck.index, &(game_state->deck)); + game_state->dragging_deck = false; + } else if(game_state->dragging_column < 7) { + game_state->dragging_column = 8; + } + game_state->dragging_hand.index = 0; +} + +bool handleInput(GameState* game_state) { + Hand currentHand = game_state->bottom_columns[game_state->selectColumn]; + switch(game_state->input) { + case InputKeyUp: + if(game_state->selectRow > 0) { + int first = first_non_flipped_card(currentHand); + first = currentHand.index - first; + if((first - 1) > game_state->selected_card && game_state->dragging_hand.index == 0 && + !game_state->longPress) { + game_state->selected_card++; + } else { + game_state->selectRow--; + game_state->selected_card = 0; + } + } + break; + case InputKeyDown: + if(game_state->selectRow < 1) { + game_state->selectRow++; + game_state->selected_card = 0; + } else { + if(game_state->selected_card > 0) { + if(game_state->longPress) + game_state->selected_card = 0; + else + game_state->selected_card--; + } + } + break; + case InputKeyRight: + if(game_state->selectColumn < 6) { + game_state->selectColumn++; + game_state->selected_card = 0; + } + break; + case InputKeyLeft: + if(game_state->selectColumn > 0) { + game_state->selectColumn--; + game_state->selected_card = 0; + } + break; + case InputKeyOk: + return true; + break; + default: + break; + } + if(game_state->selectRow == 0 && game_state->selectColumn == 2) { + if(game_state->input == InputKeyRight) + game_state->selectColumn++; + else + game_state->selectColumn--; + } + if(game_state->dragging_hand.index > 0) game_state->selected_card = 0; + return false; +} + +bool place_on_top(Card* where, Card what) { + if(where->disabled && what.character == 12) { + where->disabled = what.disabled; + where->pip = what.pip; + where->character = what.character; + return true; + } else if(where->pip == what.pip) { + int8_t a_letter = (int8_t)where->character; + int8_t b_letter = (int8_t)what.character; + if(a_letter == 12) a_letter = -1; + if(b_letter == 12) b_letter = -1; + if((a_letter + 1) == b_letter) { + where->disabled = what.disabled; + where->pip = what.pip; + where->character = what.character; + return true; + } + } + return false; +} + +void tick(GameState* game_state, NotificationApp* notification) { + game_state->last_tick = furi_get_tick(); + uint8_t row = game_state->selectRow; + uint8_t column = game_state->selectColumn; + if(game_state->state != GameStatePlay && game_state->state != GameStateAnimate) return; + bool wasAction = false; + if(game_state->state == GameStatePlay) { + if(game_state->top_cards[0].character == 11 && game_state->top_cards[1].character == 11 && + game_state->top_cards[2].character == 11 && game_state->top_cards[3].character == 11) { + game_state->state = GameStateAnimate; + return; + } + } + if(handleInput(game_state)) { + if(game_state->state == GameStatePlay) { + if(game_state->longPress && game_state->dragging_hand.index == 1) { + for(uint8_t i = 0; i < 4; i++) { + if(place_on_top( + &(game_state->top_cards[i]), game_state->dragging_hand.cards[0])) { + remove_drag(game_state); + wasAction = true; + break; + } + } + } else { + if(row == 0 && column == 0 && game_state->dragging_hand.index == 0) { + FURI_LOG_D(APP_NAME, "Drawing card"); + game_state->deck.index++; + wasAction = true; + if(game_state->deck.index >= (game_state->deck.card_count)) + game_state->deck.index = -1; + } + //pick/place from deck + else if(row == 0 && column == 1) { + //place + if(game_state->dragging_deck) { + wasAction = true; + game_state->dragging_deck = false; + game_state->dragging_hand.index = 0; + } + //pick + else { + if(game_state->dragging_hand.index == 0 && game_state->deck.index >= 0) { + wasAction = true; + game_state->dragging_deck = true; + add_to_hand( + &(game_state->dragging_hand), + game_state->deck.cards[game_state->deck.index]); + } + } + } + //place on top row + else if(row == 0 && game_state->dragging_hand.index == 1) { + column -= 3; + Card currCard = game_state->dragging_hand.cards[0]; + wasAction = place_on_top(&(game_state->top_cards[column]), currCard); + if(wasAction) remove_drag(game_state); + } + //pick/place from bottom + else if(row == 1) { + Hand* curr_hand = &(game_state->bottom_columns[column]); + //pick up + if(game_state->dragging_hand.index == 0) { + Card curr_card = curr_hand->cards[curr_hand->index - 1]; + if(curr_card.flipped) { + curr_hand->cards[curr_hand->index - 1].flipped = false; + wasAction = true; + } else { + if(curr_hand->index > 0) { + extract_hand_region( + curr_hand, + &(game_state->dragging_hand), + curr_hand->index - game_state->selected_card - 1); + game_state->selected_card = 0; + game_state->dragging_column = column; + wasAction = true; + } + } + } + //place + else { + Card first = game_state->dragging_hand.cards[0]; + if(game_state->dragging_column == column || + (curr_hand->index == 0 && first.character == 11) || + can_place_card(curr_hand->cards[curr_hand->index - 1], first)) { + add_hand_region(curr_hand, &(game_state->dragging_hand)); + remove_drag(game_state); + wasAction = true; + } + } + } + } + + if(!wasAction) { + notification_message(notification, &sequence_fail); + } + } + } + if(game_state->state == GameStateAnimate) { + if(game_state->animation.started && !game_state->longPress && + game_state->input == InputKeyOk) { + init(game_state); + game_state->state = GameStateStart; + } + + game_state->animation.started = true; + if(game_state->animation.x < -20 || game_state->animation.x > 128) { + game_state->animation.deck++; + if(game_state->animation.deck > 3) game_state->animation.deck = 0; + int8_t cardIndex = 11 - game_state->animation.indexes[game_state->animation.deck]; + + if(game_state->animation.indexes[0] == 13 && game_state->animation.indexes[1] == 13 && + game_state->animation.indexes[2] == 13 && game_state->animation.indexes[3] == 13) { + init(game_state); + game_state->state = GameStateStart; + return; + } + + if(cardIndex == -1) cardIndex = 12; + game_state->animation.card = (Card){ + game_state->top_cards[game_state->animation.deck].pip, cardIndex, false, false}; + game_state->animation.indexes[game_state->animation.deck]++; + game_state->animation.vx = -(rand() % 3 + 1) * (rand() % 2 == 1 ? 1 : -1); + game_state->animation.vy = (rand() % 3 + 1); + game_state->animation.x = columns[game_state->animation.deck + 3][0]; + game_state->animation.y = columns[game_state->animation.deck + 3][1]; + } + game_state->animation.x += game_state->animation.vx; + game_state->animation.y -= game_state->animation.vy; + game_state->animation.vy -= 1; + if(game_state->animation.vy < -10) game_state->animation.vy = -10; + + if(game_state->animation.y > 41) { + game_state->animation.y = 41; + game_state->animation.vy = -(game_state->animation.vy * 0.7f); + } + } +} + +void init(GameState* game_state) { + game_state->selectColumn = 0; + game_state->selected_card = 0; + game_state->selectRow = 0; + generate_deck(&(game_state->deck), 1); + shuffle_deck(&(game_state->deck)); + game_state->dragging_deck = false; + game_state->animation.started = false; + game_state->animation.deck = -1; + game_state->animation.x = -21; + game_state->state = GameStatePlay; + game_state->dragging_column = 8; + + for(uint8_t i = 0; i < 7; i++) { + free_hand(&(game_state->bottom_columns[i])); + init_hand(&(game_state->bottom_columns[i]), 21); + game_state->bottom_columns[i].index = 0; + for(uint8_t j = 0; j <= i; j++) { + Card cur = remove_from_deck(0, &(game_state->deck)); + cur.flipped = i != j; + add_to_hand(&(game_state->bottom_columns[i]), cur); + } + } + + for(uint8_t i = 0; i < 4; i++) { + game_state->animation.indexes[i] = 0; + game_state->top_cards[i] = (Card){0, 0, true, false}; + } + game_state->deck.index = -1; +} + +void init_start(GameState* game_state) { + game_state->input = InputKeyMAX; + for(uint8_t i = 0; i < 7; i++) init_hand(&(game_state->bottom_columns[i]), 21); + + init_hand(&(game_state->dragging_hand), 13); + game_state->animation.buffer = make_buffer(); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + AppEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + AppEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t solitaire_app(void* p) { + UNUSED(p); + int32_t return_code = 0; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); + GameState* game_state = malloc(sizeof(GameState)); + init_start(game_state); + set_card_graphics(&I_card_graphics); + + game_state->state = GameStateStart; + + game_state->processing = true; + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { + FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + notification_message_block(notification, &sequence_display_backlight_enforce_on); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30); + + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + AppEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150); + GameState* localstate = (GameState*)acquire_mutex_block(&state_mutex); + bool hadChange = false; + if(event_status == FuriStatusOk) { + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeLong) { + game_state->longPress = true; + switch(event.input.key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyRight: + case InputKeyLeft: + case InputKeyOk: + localstate->input = event.input.key; + break; + case InputKeyBack: + processing = false; + return_code = 1; + default: + break; + } + } else if(event.input.type == InputTypePress) { + game_state->longPress = false; + switch(event.input.key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyRight: + case InputKeyLeft: + case InputKeyOk: + if(event.input.key == InputKeyOk && localstate->state == GameStateStart) { + localstate->state = GameStatePlay; + init(game_state); + } else { + hadChange = true; + localstate->input = event.input.key; + } + break; + case InputKeyBack: + init(game_state); + processing = false; + return_code = 1; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + tick(localstate, notification); + processing = localstate->processing; + localstate->input = InputKeyMAX; + } + } else { + //FURI_LOG_W(APP_NAME, "osMessageQueue: event timeout"); + // event timeout + } + if(hadChange || game_state->state == GameStateAnimate) view_port_update(view_port); + release_mutex(&state_mutex, localstate); + } + + notification_message_block(notification, &sequence_display_backlight_enforce_auto); + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + view_port_free(view_port); + delete_mutex(&state_mutex); + +free_and_exit: + free(game_state->animation.buffer); + ui_cleanup(); + for(uint8_t i = 0; i < 7; i++) free_hand(&(game_state->bottom_columns[i])); + + free(game_state->deck.cards); + free(game_state); + furi_message_queue_free(event_queue); + return return_code; +} \ No newline at end of file diff --git a/applications/plugins/solitaire/solitaire_10px.png b/applications/plugins/solitaire/solitaire_10px.png new file mode 100644 index 000000000..3c5669dd2 Binary files /dev/null and b/applications/plugins/solitaire/solitaire_10px.png differ diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c b/applications/plugins/spectrum_analyzer/spectrum_analyzer.c index d94739bc1..8d68c5a1a 100644 --- a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/plugins/spectrum_analyzer/spectrum_analyzer.c @@ -496,6 +496,8 @@ int32_t spectrum_analyzer_app(void* p) { case InputKeyBack: exit_loop = true; break; + default: + break; } furi_mutex_release(spectrum_analyzer->model_mutex); diff --git a/applications/plugins/subbrute/LICENSE b/applications/plugins/subbrute/LICENSE new file mode 100644 index 000000000..06dcf7e87 --- /dev/null +++ b/applications/plugins/subbrute/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Der Skythe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/subbrute/README.md b/applications/plugins/subbrute/README.md index 04a0c975d..662262e3c 100644 --- a/applications/plugins/subbrute/README.md +++ b/applications/plugins/subbrute/README.md @@ -1,4 +1,24 @@ -# FlipFrid +# SubGHz Bruteforcer Plugin for Flipper Zero -SubGhz Fuzzer -select your base message, the field to fuzz and let's get fuzzy ! \ No newline at end of file +SubGhz Bruteforcer from [Unleashed Firmware](https://github.com/DarkFlippers/unleashed-firmware) + +### Supported Protocols: +- CAME 12bit 303MHz +- CAME 12bit 307MHz +- CAME 12bit 433MHz +- CAME 12bit 868MHz +- NICE 12bit 433MHz +- NICE 12bit 868MHz +- Chamberlain 9bit 300MHz +- Chamberlain 9bit 315MHz +- Chamberlain 9bit 390MHz +- Chamberlain 9bit 433MHz +- Chamberlain 8bit 300MHz +- Chamberlain 8bit 315MHz +- Chamberlain 8bit 390MHz +- Chamberlain 7bit 300MHz +- Chamberlain 7bit 315MHz +- Chamberlain 7bit 390MHz +- Linear 10bit 300MHz +- Linear 10bit 310MHz +- BF Existing dump works for all other static protocols supported by Flipper Zero \ No newline at end of file diff --git a/applications/plugins/subbrute/application.fam b/applications/plugins/subbrute/application.fam index bf9e8ed10..7b7b441d7 100644 --- a/applications/plugins/subbrute/application.fam +++ b/applications/plugins/subbrute/application.fam @@ -4,9 +4,10 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="subbrute_app", cdefines=["APP_SUB_BRUTE"], - requires=["storage", "gui", "dialogs", "subghz"], + requires=["gui","dialogs"], stack_size=2 * 1024, - order=190, + order=11, fap_icon="subbrute_10px.png", fap_category="Tools", + fap_icon_assets="images", ) diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.c b/applications/plugins/subbrute/helpers/subbrute_worker.c index 596240ded..4a9ad17da 100644 --- a/applications/plugins/subbrute/helpers/subbrute_worker.c +++ b/applications/plugins/subbrute/helpers/subbrute_worker.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "SubBruteWorker" #define SUBBRUTE_TX_TIMEOUT 5 @@ -30,6 +31,8 @@ SubBruteWorker* subbrute_worker_alloc() { instance->decoder_result = NULL; instance->transmitter = NULL; instance->environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry( + instance->environment, (void*)&subghz_protocol_registry); instance->transmit_mode = false; @@ -75,7 +78,8 @@ bool subbrute_worker_init_default_attack( SubBruteWorker* instance, SubBruteAttacks attack_type, uint64_t step, - const SubBruteProtocol* protocol) { + const SubBruteProtocol* protocol, + uint8_t extra_repeats) { furi_assert(instance); if(instance->worker_running) { @@ -90,7 +94,7 @@ bool subbrute_worker_init_default_attack( instance->step = step; instance->bits = protocol->bits; instance->te = protocol->te; - instance->repeat = protocol->repeat; + instance->repeat = protocol->repeat + extra_repeats; instance->load_index = 0; instance->file_key = NULL; instance->max_value = subbrute_protocol_calc_max_value(instance->attack, instance->bits); @@ -119,7 +123,8 @@ bool subbrute_worker_init_file_attack( uint64_t step, uint8_t load_index, const char* file_key, - SubBruteProtocol* protocol) { + SubBruteProtocol* protocol, + uint8_t extra_repeats) { furi_assert(instance); if(instance->worker_running) { @@ -135,7 +140,7 @@ bool subbrute_worker_init_file_attack( instance->bits = protocol->bits; instance->te = protocol->te; instance->load_index = load_index; - instance->repeat = protocol->repeat; + instance->repeat = protocol->repeat + extra_repeats; instance->file_key = file_key; instance->max_value = subbrute_protocol_calc_max_value(instance->attack, instance->bits); @@ -185,8 +190,11 @@ bool subbrute_worker_start(SubBruteWorker* instance) { void subbrute_worker_stop(SubBruteWorker* instance) { furi_assert(instance); - instance->worker_running = false; + if(!instance->worker_running) { + return; + } + instance->worker_running = false; furi_thread_join(instance->thread); furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.h b/applications/plugins/subbrute/helpers/subbrute_worker.h index 3a514272b..31c5eab16 100644 --- a/applications/plugins/subbrute/helpers/subbrute_worker.h +++ b/applications/plugins/subbrute/helpers/subbrute_worker.h @@ -22,13 +22,15 @@ bool subbrute_worker_init_default_attack( SubBruteWorker* instance, SubBruteAttacks attack_type, uint64_t step, - const SubBruteProtocol* protocol); + const SubBruteProtocol* protocol, + uint8_t extra_repeats); bool subbrute_worker_init_file_attack( SubBruteWorker* instance, uint64_t step, uint8_t load_index, const char* file_key, - SubBruteProtocol* protocol); + SubBruteProtocol* protocol, + uint8_t extra_repeats); bool subbrute_worker_start(SubBruteWorker* instance); void subbrute_worker_stop(SubBruteWorker* instance); bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t step); diff --git a/applications/plugins/subbrute/images/ButtonDown_7x4.png b/applications/plugins/subbrute/images/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/applications/plugins/subbrute/images/ButtonDown_7x4.png differ diff --git a/applications/plugins/subbrute/images/ButtonUp_7x4.png b/applications/plugins/subbrute/images/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/plugins/subbrute/images/ButtonUp_7x4.png differ diff --git a/applications/plugins/subbrute/images/DolphinNice_96x59.png b/applications/plugins/subbrute/images/DolphinNice_96x59.png new file mode 100644 index 000000000..a299d3630 Binary files /dev/null and b/applications/plugins/subbrute/images/DolphinNice_96x59.png differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png new file mode 100644 index 000000000..52dc4ad21 Binary files /dev/null and b/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png new file mode 100644 index 000000000..2dff1c031 Binary files /dev/null and b/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png new file mode 100644 index 000000000..c1e438b01 Binary files /dev/null and b/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png new file mode 100644 index 000000000..169fb6147 Binary files /dev/null and b/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png new file mode 100644 index 000000000..79b2bc972 Binary files /dev/null and b/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png new file mode 100644 index 000000000..8fce0c44d Binary files /dev/null and b/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png differ diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_rate b/applications/plugins/subbrute/images/Sub1ghz_14/frame_rate new file mode 100644 index 000000000..e440e5c84 --- /dev/null +++ b/applications/plugins/subbrute/images/Sub1ghz_14/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/applications/plugins/subbrute/images/sub1_10px.png b/applications/plugins/subbrute/images/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/applications/plugins/subbrute/images/sub1_10px.png differ diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c b/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c index 84df3e4e5..3ddcecf90 100644 --- a/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c +++ b/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c @@ -37,14 +37,18 @@ void subbrute_scene_load_file_on_enter(void* context) { load_result = subbrute_device_load_from_file(instance->device, furi_string_get_cstr(load_path)); if(load_result == SubBruteFileResultOk) { - load_result = subbrute_device_attack_set(instance->device, SubBruteAttackLoadFile); + uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); + + load_result = subbrute_device_attack_set( + instance->device, SubBruteAttackLoadFile, extra_repeats); if(load_result == SubBruteFileResultOk) { if(!subbrute_worker_init_file_attack( instance->worker, instance->device->key_index, instance->device->load_index, instance->device->file_key, - instance->device->file_protocol_info)) { + instance->device->file_protocol_info, + extra_repeats)) { furi_crash("Invalid attack set!"); } // Ready to run! diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c b/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c index 77db3f64b..0ec9fbc91 100644 --- a/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c +++ b/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c @@ -39,12 +39,15 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubBruteCustomEventTypeIndexSelected) { instance->device->load_index = subbrute_main_view_get_index(instance->view_main); + uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); + if(!subbrute_worker_init_file_attack( instance->worker, instance->device->key_index, instance->device->load_index, instance->device->file_key, - instance->device->file_protocol_info)) { + instance->device->file_protocol_info, + extra_repeats)) { furi_crash("Invalid attack set!"); } scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c b/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c index c1207af47..1138b4585 100644 --- a/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c +++ b/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c @@ -28,9 +28,8 @@ void subbrute_scene_run_attack_on_exit(void* context) { furi_assert(context); SubBruteState* instance = (SubBruteState*)context; - subbrute_worker_stop(instance->worker); - notification_message(instance->notifications, &sequence_blink_stop); + subbrute_worker_stop(instance->worker); } void subbrute_scene_run_attack_on_enter(void* context) { diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c b/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c index 4984b4931..bdbe2eccd 100644 --- a/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c +++ b/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c @@ -27,16 +27,17 @@ void subbrute_scene_setup_attack_on_enter(void* context) { SubBruteState* instance = (SubBruteState*)context; SubBruteAttackView* view = instance->view_attack; + notification_message(instance->notifications, &sequence_reset_vibro); + #ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Enter Attack: %d", instance->device->attack); + FURI_LOG_D(TAG, "Enter Attack: %s", subbrute_protocol_name(instance->device->attack)); #endif subbrute_worker_set_callback( instance->worker, subbrute_scene_setup_attack_device_state_changed, context); - if(subbrute_worker_is_running(instance->worker)) { - instance->device->key_index = subbrute_worker_get_step(instance->worker); subbrute_worker_stop(instance->worker); + instance->device->key_index = subbrute_worker_get_step(instance->worker); } subbrute_attack_view_init_values( @@ -44,7 +45,8 @@ void subbrute_scene_setup_attack_on_enter(void* context) { instance->device->attack, instance->device->max_value, instance->device->key_index, - false); + false, + instance->device->extra_repeats); instance->current_view = SubBruteViewAttack; subbrute_attack_view_set_callback(view, subbrute_scene_setup_attack_callback, instance); @@ -59,6 +61,7 @@ void subbrute_scene_setup_attack_on_exit(void* context) { SubBruteState* instance = (SubBruteState*)context; subbrute_worker_stop(instance->worker); notification_message(instance->notifications, &sequence_blink_stop); + notification_message(instance->notifications, &sequence_reset_vibro); } bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event) { @@ -68,7 +71,6 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubBruteCustomEventTypeTransmitStarted) { - subbrute_attack_view_set_worker_type(view, false); scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack); } else if(event.event == SubBruteCustomEventTypeSaveFile) { subbrute_attack_view_init_values( @@ -76,7 +78,8 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event instance->device->attack, instance->device->max_value, instance->device->key_index, - false); + false, + instance->device->extra_repeats); scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName); } else if(event.event == SubBruteCustomEventTypeBackPressed) { subbrute_attack_view_init_values( @@ -84,7 +87,8 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event instance->device->attack, instance->device->max_value, instance->device->key_index, - false); + false, + instance->device->extra_repeats); scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); } else if(event.event == SubBruteCustomEventTypeError) { notification_message(instance->notifications, &sequence_error); diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_start.c b/applications/plugins/subbrute/scenes/subbrute_scene_start.c index 677b1ef20..9ac29d37f 100644 --- a/applications/plugins/subbrute/scenes/subbrute_scene_start.c +++ b/applications/plugins/subbrute/scenes/subbrute_scene_start.c @@ -50,19 +50,25 @@ bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) { #endif if(event.event == SubBruteCustomEventTypeMenuSelected) { SubBruteAttacks attack = subbrute_main_view_get_index(instance->view_main); + uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); - if(subbrute_device_attack_set(instance->device, attack) != SubBruteFileResultOk || - !subbrute_worker_init_default_attack( + if((subbrute_device_attack_set(instance->device, attack, extra_repeats) != + SubBruteFileResultOk) || + (!subbrute_worker_init_default_attack( instance->worker, attack, instance->device->key_index, - instance->device->protocol_info)) { + instance->device->protocol_info, + instance->device->extra_repeats))) { furi_crash("Invalid attack set!"); } scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); consumed = true; } else if(event.event == SubBruteCustomEventTypeLoadFile) { + //uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); + + //instance->device->extra_repeats = extra_repeats; scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile); consumed = true; } diff --git a/applications/plugins/subbrute/subbrute_device.c b/applications/plugins/subbrute/subbrute_device.c index bf85e3a66..8cc98b9d9 100644 --- a/applications/plugins/subbrute/subbrute_device.c +++ b/applications/plugins/subbrute/subbrute_device.c @@ -5,6 +5,7 @@ #include #include #include +#include #define TAG "SubBruteDevice" @@ -18,6 +19,8 @@ SubBruteDevice* subbrute_device_alloc() { instance->decoder_result = NULL; instance->receiver = NULL; instance->environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry( + instance->environment, (void*)&subghz_protocol_registry); #ifdef FURI_DEBUG subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit433); @@ -130,10 +133,13 @@ bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_na return result; } -SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBruteAttacks type) { +SubBruteFileResult subbrute_device_attack_set( + SubBruteDevice* instance, + SubBruteAttacks type, + uint8_t extra_repeats) { furi_assert(instance); #ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_device_attack_set: %d", type); + FURI_LOG_D(TAG, "subbrute_device_attack_set: %d, extra_repeats: %d", type, extra_repeats); #endif subbrute_device_attack_set_default_values(instance, type); @@ -142,6 +148,8 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute instance->protocol_info = subbrute_protocol(type); } + instance->extra_repeats = extra_repeats; + // For non-file types we didn't set SubGhzProtocolDecoderBase instance->receiver = subghz_receiver_alloc_init(instance->environment); subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable); @@ -172,7 +180,7 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute #ifdef FURI_DEBUG bits = instance->protocol_info->bits; te = instance->protocol_info->te; - repeat = instance->protocol_info->repeat; + repeat = instance->protocol_info->repeat + instance->extra_repeats; preset = instance->protocol_info->preset; file = instance->protocol_info->file; #endif @@ -186,7 +194,7 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute #ifdef FURI_DEBUG bits = instance->file_protocol_info->bits; te = instance->file_protocol_info->te; - repeat = instance->file_protocol_info->repeat; + repeat = instance->file_protocol_info->repeat + instance->extra_repeats; preset = instance->file_protocol_info->preset; file = instance->file_protocol_info->file; #endif @@ -283,11 +291,12 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* fil #endif } - instance->decoder_result = - subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_file); + instance->decoder_result = subghz_receiver_search_decoder_base_by_name( + instance->receiver, furi_string_get_cstr(temp_str)); - if(!instance->decoder_result || strcmp(protocol_file, "RAW") == 0) { - FURI_LOG_E(TAG, "RAW unsupported"); + if((!instance->decoder_result) || (strcmp(protocol_file, "RAW") == 0) || + (strcmp(protocol_file, "Unknown") == 0)) { + FURI_LOG_E(TAG, "Protocol unsupported"); result = SubBruteFileResultProtocolNotSupported; break; } @@ -387,6 +396,7 @@ void subbrute_device_attack_set_default_values( instance->attack = default_attack; instance->key_index = 0x00; instance->load_index = 0x00; + instance->extra_repeats = 0; memset(instance->current_key, 0, sizeof(instance->current_key)); if(default_attack != SubBruteAttackLoadFile) { @@ -421,7 +431,7 @@ const char* subbrute_device_error_get_desc(SubBruteFileResult error_id) { result = "Missing Protocol"; break; case(SubBruteFileResultProtocolNotSupported): - result = "RAW unsupported"; + result = "Protocol unsupported"; break; case(SubBruteFileResultDynamicProtocolNotValid): result = "Dynamic protocol unsupported"; diff --git a/applications/plugins/subbrute/subbrute_device.h b/applications/plugins/subbrute/subbrute_device.h index b0f136ad2..311b8a72b 100644 --- a/applications/plugins/subbrute/subbrute_device.h +++ b/applications/plugins/subbrute/subbrute_device.h @@ -48,6 +48,7 @@ typedef struct { // Attack state SubBruteAttacks attack; uint64_t max_value; + uint8_t extra_repeats; // Loaded info for attack type char current_key[SUBBRUTE_PAYLOAD_SIZE]; @@ -59,7 +60,10 @@ void subbrute_device_free(SubBruteDevice* instance); bool subbrute_device_save_file(SubBruteDevice* instance, const char* key_name); const char* subbrute_device_error_get_desc(SubBruteFileResult error_id); -SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* context, SubBruteAttacks type); +SubBruteFileResult subbrute_device_attack_set( + SubBruteDevice* context, + SubBruteAttacks type, + uint8_t extra_repeats); uint8_t subbrute_device_load_from_file(SubBruteDevice* context, const char* file_path); uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step); diff --git a/applications/plugins/subbrute/subbrute_i.h b/applications/plugins/subbrute/subbrute_i.h index edd87eb0f..7bb91dde8 100644 --- a/applications/plugins/subbrute/subbrute_i.h +++ b/applications/plugins/subbrute/subbrute_i.h @@ -16,6 +16,8 @@ #include #include +#include + #include #include diff --git a/applications/plugins/subbrute/subbrute_protocols.c b/applications/plugins/subbrute/subbrute_protocols.c index 77a0bf791..d049c7e70 100644 --- a/applications/plugins/subbrute/subbrute_protocols.c +++ b/applications/plugins/subbrute/subbrute_protocols.c @@ -69,6 +69,39 @@ const SubBruteProtocol subbrute_protocol_nice_12bit_868 = { .preset = FuriHalSubGhzPresetOok650Async, .file = NICEFileProtocol}; +/** + * Ansonic 12bit 433.075MHz + */ +const SubBruteProtocol subbrute_protocol_ansonic_12bit_433075 = { + .frequency = 433075000, + .bits = 12, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPreset2FSKDev238Async, + .file = AnsonicFileProtocol}; + +/** + * Ansonic 12bit 433.92MHz + */ +const SubBruteProtocol subbrute_protocol_ansonic_12bit_433 = { + .frequency = 433920000, + .bits = 12, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPreset2FSKDev238Async, + .file = AnsonicFileProtocol}; + +/** + * Ansonic 12bit 434.075MHz + */ +const SubBruteProtocol subbrute_protocol_ansonic_12bit_434 = { + .frequency = 434075000, + .bits = 12, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPreset2FSKDev238Async, + .file = AnsonicFileProtocol}; + /** * Chamberlain 9bit 300MHz */ @@ -102,6 +135,83 @@ const SubBruteProtocol subbrute_protocol_chamberlain_9bit_390 = { .preset = FuriHalSubGhzPresetOok650Async, .file = ChamberlainFileProtocol}; +/** + * Chamberlain 9bit 433MHz + */ +const SubBruteProtocol subbrute_protocol_chamberlain_9bit_433 = { + .frequency = 433920000, + .bits = 9, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPresetOok650Async, + .file = ChamberlainFileProtocol}; + +/** + * Chamberlain 8bit 300MHz + */ +const SubBruteProtocol subbrute_protocol_chamberlain_8bit_300 = { + .frequency = 300000000, + .bits = 8, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPresetOok650Async, + .file = ChamberlainFileProtocol}; + +/** + * Chamberlain 8bit 315MHz + */ +const SubBruteProtocol subbrute_protocol_chamberlain_8bit_315 = { + .frequency = 315000000, + .bits = 8, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPresetOok650Async, + .file = ChamberlainFileProtocol}; + +/** + * Chamberlain 8bit 390MHz + */ +const SubBruteProtocol subbrute_protocol_chamberlain_8bit_390 = { + .frequency = 390000000, + .bits = 8, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPresetOok650Async, + .file = ChamberlainFileProtocol}; + +/** + * Chamberlain 7bit 300MHz + */ +const SubBruteProtocol subbrute_protocol_chamberlain_7bit_300 = { + .frequency = 300000000, + .bits = 7, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPresetOok650Async, + .file = ChamberlainFileProtocol}; + +/** + * Chamberlain 7bit 315MHz + */ +const SubBruteProtocol subbrute_protocol_chamberlain_7bit_315 = { + .frequency = 315000000, + .bits = 7, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPresetOok650Async, + .file = ChamberlainFileProtocol}; + +/** + * Chamberlain 7bit 390MHz + */ +const SubBruteProtocol subbrute_protocol_chamberlain_7bit_390 = { + .frequency = 390000000, + .bits = 7, + .te = 0, + .repeat = 3, + .preset = FuriHalSubGhzPresetOok650Async, + .file = ChamberlainFileProtocol}; + /** * Linear 10bit 300MHz */ @@ -128,7 +238,7 @@ const SubBruteProtocol subbrute_protocol_linear_10bit_310 = { * BF existing dump */ const SubBruteProtocol subbrute_protocol_load_file = - {0, 0, 0, 3, FuriHalSubGhzPresetOok650Async, RAWFileProtocol}; + {0, 0, 0, 3, FuriHalSubGhzPresetOok650Async, UnknownFileProtocol}; static const char* subbrute_protocol_names[] = { [SubBruteAttackCAME12bit303] = "CAME 12bit 303MHz", @@ -137,9 +247,19 @@ static const char* subbrute_protocol_names[] = { [SubBruteAttackCAME12bit868] = "CAME 12bit 868MHz", [SubBruteAttackNICE12bit433] = "NICE 12bit 433MHz", [SubBruteAttackNICE12bit868] = "NICE 12bit 868MHz", + [SubBruteAttackAnsonic12bit433075] = "Ansonic 12bit 433.07MHz", + [SubBruteAttackAnsonic12bit433] = "Ansonic 12bit 433.92MHz", + [SubBruteAttackAnsonic12bit434] = "Ansonic 12bit 434.07MHz", [SubBruteAttackChamberlain9bit300] = "Chamberlain 9bit 300MHz", [SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315MHz", [SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390MHz", + [SubBruteAttackChamberlain9bit433] = "Chamberlain 9bit 433MHz", + [SubBruteAttackChamberlain8bit300] = "Chamberlain 8bit 300MHz", + [SubBruteAttackChamberlain8bit315] = "Chamberlain 8bit 315MHz", + [SubBruteAttackChamberlain8bit390] = "Chamberlain 8bit 390MHz", + [SubBruteAttackChamberlain7bit300] = "Chamberlain 7bit 300MHz", + [SubBruteAttackChamberlain7bit315] = "Chamberlain 7bit 315MHz", + [SubBruteAttackChamberlain7bit390] = "Chamberlain 7bit 390MHz", [SubBruteAttackLinear10bit300] = "Linear 10bit 300MHz", [SubBruteAttackLinear10bit310] = "Linear 10bit 310MHz", [SubBruteAttackLoadFile] = "BF existing dump", @@ -163,9 +283,19 @@ const SubBruteProtocol* subbrute_protocol_registry[] = { [SubBruteAttackCAME12bit868] = &subbrute_protocol_came_12bit_868, [SubBruteAttackNICE12bit433] = &subbrute_protocol_nice_12bit_433, [SubBruteAttackNICE12bit868] = &subbrute_protocol_nice_12bit_868, + [SubBruteAttackAnsonic12bit433075] = &subbrute_protocol_ansonic_12bit_433075, + [SubBruteAttackAnsonic12bit433] = &subbrute_protocol_ansonic_12bit_433, + [SubBruteAttackAnsonic12bit434] = &subbrute_protocol_ansonic_12bit_434, [SubBruteAttackChamberlain9bit300] = &subbrute_protocol_chamberlain_9bit_300, [SubBruteAttackChamberlain9bit315] = &subbrute_protocol_chamberlain_9bit_315, [SubBruteAttackChamberlain9bit390] = &subbrute_protocol_chamberlain_9bit_390, + [SubBruteAttackChamberlain9bit433] = &subbrute_protocol_chamberlain_9bit_433, + [SubBruteAttackChamberlain8bit300] = &subbrute_protocol_chamberlain_8bit_300, + [SubBruteAttackChamberlain8bit315] = &subbrute_protocol_chamberlain_8bit_315, + [SubBruteAttackChamberlain8bit390] = &subbrute_protocol_chamberlain_8bit_390, + [SubBruteAttackChamberlain7bit300] = &subbrute_protocol_chamberlain_7bit_300, + [SubBruteAttackChamberlain7bit315] = &subbrute_protocol_chamberlain_7bit_315, + [SubBruteAttackChamberlain7bit390] = &subbrute_protocol_chamberlain_7bit_390, [SubBruteAttackLinear10bit300] = &subbrute_protocol_linear_10bit_300, [SubBruteAttackLinear10bit310] = &subbrute_protocol_linear_10bit_310, [SubBruteAttackLoadFile] = &subbrute_protocol_load_file}; @@ -176,7 +306,15 @@ static const char* subbrute_protocol_file_types[] = { [ChamberlainFileProtocol] = "Cham_Code", [LinearFileProtocol] = "Linear", [PrincetonFileProtocol] = "Princeton", - [RAWFileProtocol] = "RAW"}; + [RAWFileProtocol] = "RAW", + [BETTFileProtocol] = "BETT", + [ClemsaFileProtocol] = "Clemsa", + [DoitrandFileProtocol] = "Doitrand", + [GateTXFileProtocol] = "GateTX", + [MagellanFileProtocol] = "Magellan", + [IntertechnoV3FileProtocol] = "Intertechno_V3", + [AnsonicFileProtocol] = "Ansonic", + [UnknownFileProtocol] = "Unknown"}; /** * Values to not use less memory for packet parse operations @@ -196,6 +334,10 @@ const SubBruteProtocol* subbrute_protocol(SubBruteAttacks index) { return subbrute_protocol_registry[index]; } +uint8_t subbrute_protocol_repeats_count(SubBruteAttacks index) { + return subbrute_protocol_registry[index]->repeat; +} + const char* subbrute_protocol_preset(FuriHalSubGhzPreset preset) { return subbrute_protocol_presets[preset]; } @@ -221,7 +363,7 @@ SubBruteFileProtocol subbrute_protocol_file_protocol_name(FuriString* name) { } } - return RAWFileProtocol; + return UnknownFileProtocol; } void subbrute_protocol_default_payload( @@ -247,7 +389,13 @@ void subbrute_protocol_default_payload( furi_string_free(buffer); #ifdef FURI_DEBUG - //FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step); + FURI_LOG_D( + TAG, + "candidate: %s, step: %lld, repeat: %d, te: %s", + furi_string_get_cstr(candidate), + step, + repeat, + te ? "true" : "false"); #endif stream_clean(stream); if(te) { @@ -281,7 +429,13 @@ void subbrute_protocol_file_payload( furi_string_replace_at(candidate, load_index * 3, 3, subbrute_payload_byte); #ifdef FURI_DEBUG - FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step); + FURI_LOG_D( + TAG, + "candidate: %s, step: %lld, repeat: %d, te: %s", + furi_string_get_cstr(candidate), + step, + repeat, + te ? "true" : "false"); #endif stream_clean(stream); diff --git a/applications/plugins/subbrute/subbrute_protocols.h b/applications/plugins/subbrute/subbrute_protocols.h index e0a97eef2..3d25310de 100644 --- a/applications/plugins/subbrute/subbrute_protocols.h +++ b/applications/plugins/subbrute/subbrute_protocols.h @@ -4,15 +4,6 @@ #include #include #include -//typedef enum { -// FrequencyProtocolField, -// BitsProtocolField, -// HasTeProtocolField, -// RepeatProtocolField, -// PresetProtocolField, -// FileProtocolField, -// TotalProtocolFields -//} ProtocolFields; typedef enum { CAMEFileProtocol, @@ -21,6 +12,14 @@ typedef enum { LinearFileProtocol, PrincetonFileProtocol, RAWFileProtocol, + BETTFileProtocol, + ClemsaFileProtocol, + DoitrandFileProtocol, + GateTXFileProtocol, + MagellanFileProtocol, + IntertechnoV3FileProtocol, + AnsonicFileProtocol, + UnknownFileProtocol, TotalFileProtocol, } SubBruteFileProtocol; @@ -31,9 +30,19 @@ typedef enum { SubBruteAttackCAME12bit868, SubBruteAttackNICE12bit433, SubBruteAttackNICE12bit868, + SubBruteAttackAnsonic12bit433075, + SubBruteAttackAnsonic12bit433, + SubBruteAttackAnsonic12bit434, SubBruteAttackChamberlain9bit300, SubBruteAttackChamberlain9bit315, SubBruteAttackChamberlain9bit390, + SubBruteAttackChamberlain9bit433, + SubBruteAttackChamberlain8bit300, + SubBruteAttackChamberlain8bit315, + SubBruteAttackChamberlain8bit390, + SubBruteAttackChamberlain7bit300, + SubBruteAttackChamberlain7bit315, + SubBruteAttackChamberlain7bit390, SubBruteAttackLinear10bit300, SubBruteAttackLinear10bit310, SubBruteAttackLoadFile, @@ -54,6 +63,7 @@ const char* subbrute_protocol_preset(FuriHalSubGhzPreset preset); const char* subbrute_protocol_file(SubBruteFileProtocol protocol); FuriHalSubGhzPreset subbrute_protocol_convert_preset(FuriString* preset_name); SubBruteFileProtocol subbrute_protocol_file_protocol_name(FuriString* name); +uint8_t subbrute_protocol_repeats_count(SubBruteAttacks index); const char* subbrute_protocol_name(SubBruteAttacks index); void subbrute_protocol_default_payload( diff --git a/applications/plugins/subbrute/views/subbrute_attack_view.c b/applications/plugins/subbrute/views/subbrute_attack_view.c index c3412de71..00a9e1b2a 100644 --- a/applications/plugins/subbrute/views/subbrute_attack_view.c +++ b/applications/plugins/subbrute/views/subbrute_attack_view.c @@ -14,14 +14,19 @@ struct SubBruteAttackView { View* view; SubBruteAttackViewCallback callback; void* context; -}; - -typedef struct { - SubBruteAttacks index; + SubBruteAttacks attack_type; uint64_t max_value; uint64_t current_step; bool is_attacking; - bool is_continuous_worker; + uint8_t extra_repeats; +}; + +typedef struct { + SubBruteAttacks attack_type; + uint64_t max_value; + uint64_t current_step; + uint8_t extra_repeats; + bool is_attacking; IconAnimation* icon; } SubBruteAttackViewModel; @@ -39,136 +44,87 @@ void subbrute_attack_view_set_callback( bool subbrute_attack_view_input(InputEvent* event, void* context) { furi_assert(event); furi_assert(context); + SubBruteAttackView* instance = context; #ifdef FURI_DEBUG FURI_LOG_D(TAG, "InputKey: %d", event->key); #endif - SubBruteAttackView* instance = context; if(event->key == InputKeyBack && event->type == InputTypeShort) { - instance->callback(SubBruteCustomEventTypeBackPressed, instance->context); + instance->is_attacking = false; with_view_model( instance->view, SubBruteAttackViewModel * model, - { - model->is_attacking = false; - model->is_continuous_worker = false; - }, + { model->is_attacking = false; }, true); + + instance->callback(SubBruteCustomEventTypeBackPressed, instance->context); return true; } - bool is_attacking = false; + bool update = false; - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { is_attacking = model->is_attacking; }, - false); - - // if(!is_attacking) { - // instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context); - // } else { - // instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context); - // } - - if(!is_attacking) { + if(!instance->is_attacking) { if(event->type == InputTypeShort && event->key == InputKeyOk) { #ifdef FURI_DEBUG FURI_LOG_D(TAG, "InputKey: %d OK", event->key); #endif - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { - model->is_attacking = true; - model->is_continuous_worker = false; - icon_animation_stop(model->icon); - icon_animation_start(model->icon); - }, - true); + instance->is_attacking = true; instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context); - /*if(event->type == InputTypeRepeat && event->key == InputKeyOk) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "InputKey: %d OK. SubBruteCustomEventTypeTransmitContinuousStarted", event->key); -#endif - with_view_model( - instance->view, (SubBruteAttackViewModel * model) { - model->is_attacking = true; - model->is_continuous_worker = true; - icon_animation_stop(model->icon); - icon_animation_start(model->icon); - return true; - }); - instance->callback(SubBruteCustomEventTypeTransmitContinuousStarted, instance->context); - } else if(event->type == InputTypeShort && event->key == InputKeyOk) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "InputKey: %d OK", event->key); -#endif - with_view_model( - instance->view, (SubBruteAttackViewModel * model) { - model->is_attacking = true; - model->is_continuous_worker = false; - icon_animation_stop(model->icon); - icon_animation_start(model->icon); - return true; - }); - instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context);*/ + update = true; } else if(event->key == InputKeyUp) { instance->callback(SubBruteCustomEventTypeSaveFile, instance->context); + update = true; } else if(event->key == InputKeyDown) { instance->callback(SubBruteCustomEventTypeTransmitCustom, instance->context); + update = true; } else if(event->type == InputTypeShort) { if(event->key == InputKeyLeft) { instance->callback(SubBruteCustomEventTypeChangeStepDown, instance->context); } else if(event->key == InputKeyRight) { instance->callback(SubBruteCustomEventTypeChangeStepUp, instance->context); } - // with_view_model( - // instance->view, (SubBruteAttackViewModel * model) { - // if(event->key == InputKeyLeft) { - // model->current_step = - // ((model->current_step - 1) + model->max_value) % model->max_value; - // } else if(event->key == InputKeyRight) { - // model->current_step = (model->current_step + 1) % model->max_value; - // } - // return true; - // }); - // instance->callback(SubBruteCustomEventTypeChangeStep, instance->context); + update = true; } else if(event->type == InputTypeRepeat) { if(event->key == InputKeyLeft) { instance->callback(SubBruteCustomEventTypeChangeStepDownMore, instance->context); } else if(event->key == InputKeyRight) { instance->callback(SubBruteCustomEventTypeChangeStepUpMore, instance->context); } - /*with_view_model( - instance->view, (SubBruteAttackViewModel * model) { - if(event->key == InputKeyLeft) { - model->current_step = - ((model->current_step - 100) + model->max_value) % model->max_value; - } else if(event->key == InputKeyRight) { - model->current_step = (model->current_step + 100) % model->max_value; - } - return true; - }); - instance->callback(SubBruteCustomEventTypeChangeStep, instance->context);*/ + update = true; } } else { + // ATTACK Mode! if((event->type == InputTypeShort || event->type == InputTypeRepeat) && (event->key == InputKeyOk || event->key == InputKeyBack)) { - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { - model->is_attacking = false; - model->is_continuous_worker = false; - icon_animation_stop(model->icon); - icon_animation_start(model->icon); - }, - true); + instance->is_attacking = false; instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context); + + update = true; } } + if(update) { + with_view_model( + instance->view, + SubBruteAttackViewModel * model, + { + if(model->is_attacking != instance->is_attacking) { + if(instance->is_attacking) { + icon_animation_stop(model->icon); + icon_animation_start(model->icon); + } else { + icon_animation_stop(model->icon); + } + } + + model->attack_type = instance->attack_type; + model->max_value = instance->max_value; + model->current_step = instance->current_step; + model->is_attacking = instance->is_attacking; + }, + true); + } + return true; } @@ -186,13 +142,18 @@ SubBruteAttackView* subbrute_attack_view_alloc() { model->icon = icon_animation_alloc(&A_Sub1ghz_14); view_tie_icon_animation(instance->view, model->icon); }, - false); + true); view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_attack_view_draw); view_set_input_callback(instance->view, subbrute_attack_view_input); view_set_enter_callback(instance->view, subbrute_attack_view_enter); view_set_exit_callback(instance->view, subbrute_attack_view_exit); + instance->attack_type = SubBruteAttackTotalCount; + instance->max_value = 0x00; + instance->current_step = 0; + instance->is_attacking = false; + return instance; } @@ -231,6 +192,7 @@ void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_ #ifdef FURI_DEBUG //FURI_LOG_D(TAG, "Set step: %d", current_step); #endif + instance->current_step = current_step; with_view_model( instance->view, SubBruteAttackViewModel * model, @@ -238,15 +200,6 @@ void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_ true); } -void subbrute_attack_view_set_worker_type(SubBruteAttackView* instance, bool is_continuous_worker) { - furi_assert(instance); - with_view_model( - instance->view, - SubBruteAttackViewModel * model, - { model->is_continuous_worker = is_continuous_worker; }, - true); -} - // We need to call init every time, because not every time we calls enter // normally, call enter only once void subbrute_attack_view_init_values( @@ -254,23 +207,32 @@ void subbrute_attack_view_init_values( uint8_t index, uint64_t max_value, uint64_t current_step, - bool is_attacking) { + bool is_attacking, + uint8_t extra_repeats) { #ifdef FURI_DEBUG - FURI_LOG_D( + FURI_LOG_I( TAG, - "init, index: %d, max_value: %lld, current_step: %lld", + "INIT, attack_type: %d, max_value: %lld, current_step: %lld, extra_repeats: %d", index, max_value, - current_step); + current_step, + extra_repeats); #endif + instance->attack_type = index; + instance->max_value = max_value; + instance->current_step = current_step; + instance->is_attacking = is_attacking; + instance->extra_repeats = extra_repeats; + with_view_model( instance->view, SubBruteAttackViewModel * model, { model->max_value = max_value; - model->index = index; + model->attack_type = index; model->current_step = current_step; model->is_attacking = is_attacking; + model->extra_repeats = extra_repeats; if(is_attacking) { icon_animation_start(model->icon); } else { @@ -313,10 +275,12 @@ void elements_button_top_left(Canvas* canvas, const char* str) { const uint8_t x = 0; const uint8_t y = 0 + button_height; - canvas_draw_box(canvas, x, y - button_height, button_width, button_height); - canvas_draw_line(canvas, x + button_width + 0, y - button_height, x + button_width + 0, y - 1); - canvas_draw_line(canvas, x + button_width + 1, y - button_height, x + button_width + 1, y - 2); - canvas_draw_line(canvas, x + button_width + 2, y - button_height, x + button_width + 2, y - 3); + uint8_t line_x = x + button_width; + uint8_t line_y = y - button_height; + canvas_draw_box(canvas, x, line_y, button_width, button_height); + canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1); + canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2); + canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3); canvas_invert_color(canvas); canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon); @@ -345,10 +309,12 @@ void elements_button_top_right(Canvas* canvas, const char* str) { const uint8_t x = canvas_width(canvas); const uint8_t y = 0 + button_height; - canvas_draw_box(canvas, x - button_width, y - button_height, button_width, button_height); - canvas_draw_line(canvas, x - button_width - 1, y - button_height, x - button_width - 1, y - 1); - canvas_draw_line(canvas, x - button_width - 2, y - button_height, x - button_width - 2, y - 2); - canvas_draw_line(canvas, x - button_width - 3, y - button_height, x - button_width - 3, y - 3); + uint8_t line_x = x - button_width; + uint8_t line_y = y - button_height; + canvas_draw_box(canvas, line_x, line_y, button_width, button_height); + canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1); + canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2); + canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3); canvas_invert_color(canvas); canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str); @@ -360,36 +326,43 @@ void elements_button_top_right(Canvas* canvas, const char* str) { void subbrute_attack_view_draw(Canvas* canvas, void* context) { furi_assert(context); SubBruteAttackViewModel* model = (SubBruteAttackViewModel*)context; - char buffer[26]; + char buffer[64]; const char* attack_name = NULL; - attack_name = subbrute_protocol_name(model->index); + attack_name = subbrute_protocol_name(model->attack_type); + // Title if(model->is_attacking) { canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, attack_name); } - // Value + + // Current Step / Max value canvas_set_font(canvas, FontBigNumbers); snprintf(buffer, sizeof(buffer), "%04d/%04d", (int)model->current_step, (int)model->max_value); canvas_draw_str_aligned(canvas, 64, 17, AlignCenter, AlignTop, buffer); canvas_set_font(canvas, FontSecondary); + memset(buffer, 0, sizeof(buffer)); if(!model->is_attacking) { canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignBottom, attack_name); + snprintf( + buffer, + sizeof(buffer), + "x%d", + model->extra_repeats + subbrute_protocol_repeats_count(model->attack_type)); + canvas_draw_str_aligned(canvas, 60, 6, AlignCenter, AlignCenter, buffer); + elements_button_left(canvas, "-1"); elements_button_right(canvas, "+1"); elements_button_center(canvas, "Start"); elements_button_top_left(canvas, "Save"); elements_button_top_right(canvas, "Resend"); } else { - if(model->is_continuous_worker) { - canvas_invert_color(canvas); - } // canvas_draw_icon_animation const uint8_t icon_h_offset = 0; const uint8_t icon_width_with_offset = @@ -404,9 +377,14 @@ void subbrute_attack_view_draw(Canvas* canvas, void* context) { float progress_value = (float)model->current_step / model->max_value; elements_progress_bar(canvas, 8, 37, 110, progress_value > 1 ? 1 : progress_value); + snprintf( + buffer, + sizeof(buffer), + "x%d", + model->extra_repeats + subbrute_protocol_repeats_count(model->attack_type)); + canvas_draw_str(canvas, 4, y - 8, buffer); + canvas_draw_str(canvas, 4, y - 1, "repeats"); + elements_button_center(canvas, "Stop"); - if(model->is_continuous_worker) { - canvas_invert_color(canvas); - } } } diff --git a/applications/plugins/subbrute/views/subbrute_attack_view.h b/applications/plugins/subbrute/views/subbrute_attack_view.h index 1e25379e2..55e3a8222 100644 --- a/applications/plugins/subbrute/views/subbrute_attack_view.h +++ b/applications/plugins/subbrute/views/subbrute_attack_view.h @@ -16,10 +16,10 @@ SubBruteAttackView* subbrute_attack_view_alloc(); void subbrute_attack_view_free(SubBruteAttackView* instance); View* subbrute_attack_view_get_view(SubBruteAttackView* instance); void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step); -void subbrute_attack_view_set_worker_type(SubBruteAttackView* instance, bool is_continuous_worker); void subbrute_attack_view_init_values( SubBruteAttackView* instance, uint8_t index, uint64_t max_value, uint64_t current_step, - bool is_attacking); \ No newline at end of file + bool is_attacking, + uint8_t extra_repeats); \ No newline at end of file diff --git a/applications/plugins/subbrute/views/subbrute_main_view.c b/applications/plugins/subbrute/views/subbrute_main_view.c index 9d3486797..287a33f2b 100644 --- a/applications/plugins/subbrute/views/subbrute_main_view.c +++ b/applications/plugins/subbrute/views/subbrute_main_view.c @@ -5,19 +5,26 @@ #include #include #include -#include #define STATUS_BAR_Y_SHIFT 14 #define TAG "SubBruteMainView" +#define ITEMS_ON_SCREEN 3 + struct SubBruteMainView { View* view; SubBruteMainViewCallback callback; void* context; + uint8_t index; + bool is_select_byte; + const char* key_field; + uint8_t extra_repeats; + uint8_t window_position; }; typedef struct { uint8_t index; + uint8_t extra_repeats; uint8_t window_position; bool is_select_byte; const char* key_field; @@ -80,26 +87,27 @@ FuriString* center_displayed_key(const char* key_cstr, uint8_t index) { } void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { - SubBruteMainViewModel* m = model; - // Title canvas_set_font(canvas, FontPrimary); canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT); canvas_invert_color(canvas); - canvas_draw_str_aligned(canvas, 64, 3, AlignCenter, AlignTop, "Sub-GHz BruteForcer v3"); + canvas_draw_str_aligned(canvas, 64, 3, AlignCenter, AlignTop, "Sub-GHz BruteForcer 3.2"); canvas_invert_color(canvas); - if(m->is_select_byte) { + uint16_t screen_width = canvas_width(canvas); + uint16_t screen_height = canvas_height(canvas); + + if(model->is_select_byte) { #ifdef FURI_DEBUG - //FURI_LOG_D(TAG, "key_field: %s", m->key_field); + //FURI_LOG_D(TAG, "key_field: %s", model->key_field); #endif char msg_index[18]; - snprintf(msg_index, sizeof(msg_index), "Field index : %d", m->index); + snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index); FuriString* menu_items; - menu_items = center_displayed_key(m->key_field, m->index); + menu_items = center_displayed_key(model->key_field, model->index); canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( canvas, 64, 40, AlignCenter, AlignTop, furi_string_get_cstr(menu_items)); @@ -114,17 +122,16 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { // Menu canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - uint8_t items_on_screen = 3; const uint8_t item_height = 16; #ifdef FURI_DEBUG - //FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, m->index); + //FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, model->index); #endif for(uint8_t position = 0; position < SubBruteAttackTotalCount; ++position) { uint8_t item_position = position - model->window_position; - if(item_position < items_on_screen) { - if(m->index == position) { + if(item_position < ITEMS_ON_SCREEN) { + if(model->index == position) { canvas_draw_str_aligned( canvas, 4, @@ -132,6 +139,25 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { AlignLeft, AlignCenter, subbrute_protocol_name(position)); + + if(model->extra_repeats > 0) { + canvas_set_font(canvas, FontBatteryPercent); + char buffer[10]; + snprintf( + buffer, + sizeof(buffer), + "x%d", + model->extra_repeats + subbrute_protocol_repeats_count(model->index)); + canvas_draw_str_aligned( + canvas, + screen_width - 15, + 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, + AlignLeft, + AlignCenter, + buffer); + canvas_set_font(canvas, FontSecondary); + } + elements_frame( canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15); } else { @@ -148,10 +174,10 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { elements_scrollbar_pos( canvas, - canvas_width(canvas), + screen_width, STATUS_BAR_Y_SHIFT + 2, - canvas_height(canvas) - STATUS_BAR_Y_SHIFT, - m->index, + screen_height - STATUS_BAR_Y_SHIFT, + model->index, SubBruteAttackTotalCount); } } @@ -159,119 +185,110 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { bool subbrute_main_view_input(InputEvent* event, void* context) { furi_assert(event); furi_assert(context); -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "InputKey: %d", event->key); -#endif if(event->key == InputKeyBack && event->type == InputTypeShort) { +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "InputKey: BACK"); +#endif return false; } SubBruteMainView* instance = context; +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "InputKey: %d, extra_repeats: %d", event->key, instance->extra_repeats); +#endif const uint8_t min_value = 0; const uint8_t correct_total = SubBruteAttackTotalCount - 1; - uint8_t index = 0; - bool is_select_byte = false; - with_view_model( - instance->view, - SubBruteMainViewModel * model, - { is_select_byte = model->is_select_byte; }, - false); + uint8_t max_repeats = 9 - subbrute_protocol_repeats_count(instance->index); + bool updated = false; bool consumed = false; - if(!is_select_byte) { - if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { - bool ret = false; - with_view_model( - instance->view, - SubBruteMainViewModel * model, - { - uint8_t items_on_screen = 3; - if(event->key == InputKeyUp) { - if(model->index == min_value) { - model->index = correct_total; - } else { - model->index = CLAMP(model->index - 1, correct_total, min_value); - } - ret = true; - consumed = true; - } else if(event->key == InputKeyDown) { - if(model->index == correct_total) { - model->index = min_value; - } else { - model->index = CLAMP(model->index + 1, correct_total, min_value); - } - ret = true; - consumed = true; - } - if(ret) { - model->window_position = model->index; - if(model->window_position > 0) { - model->window_position -= 1; - } + bool is_short = (event->type == InputTypeShort) || (event->type == InputTypeRepeat); - if(SubBruteAttackTotalCount <= items_on_screen) { - model->window_position = 0; - } else { - if(model->window_position >= - (SubBruteAttackTotalCount - items_on_screen)) { - model->window_position = - (SubBruteAttackTotalCount - items_on_screen); - } - } - } - index = model->index; - }, - ret); - } + if(!instance->is_select_byte) { + if(event->key == InputKeyUp && is_short) { + if(instance->index == min_value) { + instance->index = correct_total; + } else { + instance->index = CLAMP(instance->index - 1, correct_total, min_value); + } + instance->extra_repeats = 0; + updated = true; + consumed = true; + } else if(event->key == InputKeyDown && is_short) { + if(instance->index == correct_total) { + instance->index = min_value; + } else { + instance->index = CLAMP(instance->index + 1, correct_total, min_value); + } + instance->extra_repeats = 0; + updated = true; + consumed = true; + } else if(event->key == InputKeyLeft && is_short) { + instance->extra_repeats = CLAMP(instance->extra_repeats - 1, max_repeats, 0); -#ifdef FURI_DEBUG - with_view_model( - instance->view, SubBruteMainViewModel * model, { index = model->index; }, false); - FURI_LOG_I(TAG, "Index: %d", index); -#endif + updated = true; + consumed = true; + } else if(event->key == InputKeyRight && is_short) { + instance->extra_repeats = CLAMP(instance->extra_repeats + 1, max_repeats, 0); - if(event->key == InputKeyOk && event->type == InputTypeShort) { - if(index == SubBruteAttackLoadFile) { + updated = true; + consumed = true; + } else if(event->key == InputKeyOk && is_short) { + if(instance->index == SubBruteAttackLoadFile) { instance->callback(SubBruteCustomEventTypeLoadFile, instance->context); } else { instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context); } consumed = true; + updated = true; + } + if(updated) { + instance->window_position = instance->index; + if(instance->window_position > 0) { + instance->window_position -= 1; + } + + if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) { + instance->window_position = 0; + } else { + if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) { + instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN); + } + } } } else { - if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { - with_view_model( - instance->view, - SubBruteMainViewModel * model, - { - if(event->key == InputKeyLeft) { - if(model->index > 0) { - model->index--; - } - } else if(event->key == InputKeyRight) { - if(model->index < 7) { - model->index++; - } - } - - index = model->index; - }, - true); - } - -#ifdef FURI_DEBUG - with_view_model( - instance->view, SubBruteMainViewModel * model, { index = model->index; }, false); - FURI_LOG_I(TAG, "Index: %d", index); -#endif - - if(event->key == InputKeyOk && event->type == InputTypeShort) { + if(event->key == InputKeyLeft && is_short) { + if(instance->index > 0) { + instance->index--; + } + updated = true; + } else if(event->key == InputKeyRight && is_short) { + if(instance->index < 7) { + instance->index++; + } + updated = true; + } else if(event->key == InputKeyOk && is_short) { instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context); consumed = true; + updated = true; } } + if(updated) { + with_view_model( + instance->view, + SubBruteMainViewModel * model, + { + model->index = instance->index; + model->window_position = instance->window_position; + model->key_field = instance->key_field; + model->is_select_byte = instance->is_select_byte; + model->extra_repeats = instance->extra_repeats; + }, + true); + } + return consumed; } @@ -309,9 +326,16 @@ SubBruteMainView* subbrute_main_view_alloc() { model->window_position = 0; model->key_field = NULL; model->is_select_byte = false; + model->extra_repeats = 0; }, true); + instance->index = 0; + instance->window_position = 0; + instance->key_field = NULL; + instance->is_select_byte = false; + instance->extra_repeats = 0; + return instance; } @@ -335,46 +359,46 @@ void subbrute_main_view_set_index( furi_assert(instance); furi_assert(idx < SubBruteAttackTotalCount); #ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Set index: %d", idx); + FURI_LOG_I(TAG, "Set index: %d, IS_SELECT_BYTE: %d", idx, is_select_byte); #endif + instance->is_select_byte = is_select_byte; + instance->key_field = key_field; + instance->index = idx; + instance->window_position = idx; + + if(!is_select_byte) { + if(instance->window_position > 0) { + instance->window_position -= 1; + } + + if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) { + instance->window_position = 0; + } else { + if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) { + instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN); + } + } + } + with_view_model( instance->view, SubBruteMainViewModel * model, { - model->is_select_byte = is_select_byte; - model->key_field = key_field; - model->index = idx; - model->window_position = idx; - - if(!is_select_byte) { - uint8_t items_on_screen = 3; - - if(model->window_position > 0) { - model->window_position -= 1; - } - - if(SubBruteAttackTotalCount <= items_on_screen) { - model->window_position = 0; - } else { - if(model->window_position >= (SubBruteAttackTotalCount - items_on_screen)) { - model->window_position = (SubBruteAttackTotalCount - items_on_screen); - } - } - } + model->index = instance->index; + model->window_position = instance->window_position; + model->key_field = instance->key_field; + model->is_select_byte = instance->is_select_byte; + model->extra_repeats = instance->extra_repeats; }, true); } SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) { furi_assert(instance); + return instance->index; +} - uint8_t idx = 0; - with_view_model( - instance->view, SubBruteMainViewModel * model, { idx = model->index; }, false); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Get index: %d", idx); -#endif - - return idx; +uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance) { + furi_assert(instance); + return instance->extra_repeats; } \ No newline at end of file diff --git a/applications/plugins/subbrute/views/subbrute_main_view.h b/applications/plugins/subbrute/views/subbrute_main_view.h index 361dbc22b..6aa11cec8 100644 --- a/applications/plugins/subbrute/views/subbrute_main_view.h +++ b/applications/plugins/subbrute/views/subbrute_main_view.h @@ -1,6 +1,7 @@ #pragma once #include "../subbrute_custom_event.h" +#include "../subbrute_protocols.h" #include #include #include @@ -21,7 +22,8 @@ void subbrute_main_view_set_index( uint8_t idx, bool is_select_byte, const char* key_field); -uint8_t subbrute_main_view_get_index(SubBruteMainView* instance); +SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance); +uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance); void subbrute_attack_view_enter(void* context); void subbrute_attack_view_exit(void* context); bool subbrute_attack_view_input(InputEvent* event, void* context); diff --git a/applications/plugins/tanksgame/application.fam b/applications/plugins/tanksgame/application.fam index f420da324..748988297 100644 --- a/applications/plugins/tanksgame/application.fam +++ b/applications/plugins/tanksgame/application.fam @@ -9,4 +9,5 @@ App( order=730, fap_icon="tanksIcon.png", fap_category="Games", + fap_icon_assets="images", ) diff --git a/applications/plugins/tanksgame/images/HappyFlipper_128x64.png b/applications/plugins/tanksgame/images/HappyFlipper_128x64.png new file mode 100644 index 000000000..d95412f3f Binary files /dev/null and b/applications/plugins/tanksgame/images/HappyFlipper_128x64.png differ diff --git a/applications/plugins/tanksgame/images/TanksSplashScreen_128x64.png b/applications/plugins/tanksgame/images/TanksSplashScreen_128x64.png new file mode 100644 index 000000000..a0dc26487 Binary files /dev/null and b/applications/plugins/tanksgame/images/TanksSplashScreen_128x64.png differ diff --git a/applications/plugins/tanksgame/images/enemy_down.png b/applications/plugins/tanksgame/images/enemy_down.png new file mode 100644 index 000000000..84d64ed44 Binary files /dev/null and b/applications/plugins/tanksgame/images/enemy_down.png differ diff --git a/applications/plugins/tanksgame/images/enemy_left.png b/applications/plugins/tanksgame/images/enemy_left.png new file mode 100644 index 000000000..e118d03a8 Binary files /dev/null and b/applications/plugins/tanksgame/images/enemy_left.png differ diff --git a/applications/plugins/tanksgame/images/enemy_right.png b/applications/plugins/tanksgame/images/enemy_right.png new file mode 100644 index 000000000..8c5f0603b Binary files /dev/null and b/applications/plugins/tanksgame/images/enemy_right.png differ diff --git a/applications/plugins/tanksgame/images/enemy_up.png b/applications/plugins/tanksgame/images/enemy_up.png new file mode 100644 index 000000000..8ad01f635 Binary files /dev/null and b/applications/plugins/tanksgame/images/enemy_up.png differ diff --git a/applications/plugins/tanksgame/images/projectile_down.png b/applications/plugins/tanksgame/images/projectile_down.png new file mode 100644 index 000000000..fd3f8c123 Binary files /dev/null and b/applications/plugins/tanksgame/images/projectile_down.png differ diff --git a/applications/plugins/tanksgame/images/projectile_left.png b/applications/plugins/tanksgame/images/projectile_left.png new file mode 100644 index 000000000..c8c54786d Binary files /dev/null and b/applications/plugins/tanksgame/images/projectile_left.png differ diff --git a/applications/plugins/tanksgame/images/projectile_right.png b/applications/plugins/tanksgame/images/projectile_right.png new file mode 100644 index 000000000..fbf5359ad Binary files /dev/null and b/applications/plugins/tanksgame/images/projectile_right.png differ diff --git a/applications/plugins/tanksgame/images/projectile_up.png b/applications/plugins/tanksgame/images/projectile_up.png new file mode 100644 index 000000000..532446657 Binary files /dev/null and b/applications/plugins/tanksgame/images/projectile_up.png differ diff --git a/applications/plugins/tanksgame/images/tank_base.png b/applications/plugins/tanksgame/images/tank_base.png new file mode 100644 index 000000000..11a524ffa Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_base.png differ diff --git a/applications/plugins/tanksgame/images/tank_down.png b/applications/plugins/tanksgame/images/tank_down.png new file mode 100644 index 000000000..d4e2c8786 Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_down.png differ diff --git a/applications/plugins/tanksgame/images/tank_explosion.png b/applications/plugins/tanksgame/images/tank_explosion.png new file mode 100644 index 000000000..8aab727cd Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_explosion.png differ diff --git a/applications/plugins/tanksgame/images/tank_hedgehog.png b/applications/plugins/tanksgame/images/tank_hedgehog.png new file mode 100644 index 000000000..fdd804be9 Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_hedgehog.png differ diff --git a/applications/plugins/tanksgame/images/tank_left.png b/applications/plugins/tanksgame/images/tank_left.png new file mode 100644 index 000000000..2c7fd40e0 Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_left.png differ diff --git a/applications/plugins/tanksgame/images/tank_right.png b/applications/plugins/tanksgame/images/tank_right.png new file mode 100644 index 000000000..436d90a61 Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_right.png differ diff --git a/applications/plugins/tanksgame/images/tank_stone.png b/applications/plugins/tanksgame/images/tank_stone.png new file mode 100644 index 000000000..87f89c8da Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_stone.png differ diff --git a/applications/plugins/tanksgame/images/tank_up.png b/applications/plugins/tanksgame/images/tank_up.png new file mode 100644 index 000000000..3d4e47579 Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_up.png differ diff --git a/applications/plugins/tanksgame/images/tank_wall.png b/applications/plugins/tanksgame/images/tank_wall.png new file mode 100644 index 000000000..88d537cdb Binary files /dev/null and b/applications/plugins/tanksgame/images/tank_wall.png differ diff --git a/applications/plugins/tanksgame/tanks_game.c b/applications/plugins/tanksgame/tanks_game.c index 8a1f14297..3d67dfbb0 100644 --- a/applications/plugins/tanksgame/tanks_game.c +++ b/applications/plugins/tanksgame/tanks_game.c @@ -10,375 +10,10 @@ #include #include #include +#include #include "constants.h" -#include - -const uint8_t _I_HappyFlipper_128x64_0[] = { - 0x01, 0x00, 0xc2, 0x03, 0xc9, 0x49, 0x29, 0x20, 0x2f, 0x01, 0x78, 0x0b, 0xc0, 0x59, 0xa4, 0xa5, - 0x65, 0x65, 0x55, 0x5d, 0x55, 0x53, 0x53, 0x52, 0xd2, 0x82, 0x16, 0x53, 0x51, 0xd1, 0x51, 0x31, - 0x31, 0x29, 0x2d, 0x2b, 0x29, 0x28, 0x04, 0x36, 0x2a, 0x29, 0x29, 0xa9, 0x69, 0x29, 0x19, 0x19, - 0x15, 0x14, 0x10, 0xba, 0x41, 0x54, 0x82, 0x12, 0x13, 0x21, 0xa1, 0x61, 0x21, 0x11, 0x19, 0x15, - 0x13, 0x11, 0x10, 0x04, 0x2d, 0x4a, 0x95, 0xf5, 0xff, 0xd4, 0xc9, 0xf5, 0xa4, 0xc9, 0x66, 0x24, - 0x9b, 0xf2, 0x8d, 0x52, 0x6a, 0x95, 0x6a, 0x4f, 0xa4, 0xa3, 0x57, 0xec, 0x4c, 0x95, 0x06, 0x39, - 0x3c, 0x55, 0x2a, 0x55, 0x2a, 0xd4, 0xea, 0x34, 0x9a, 0x2c, 0x96, 0x2b, 0xa9, 0xd3, 0xa5, 0x49, - 0x69, 0x3a, 0xea, 0x86, 0x4a, 0x85, 0x49, 0x85, 0x49, 0x65, 0x39, 0x4d, 0x25, 0x43, 0x7b, 0x56, - 0xc8, 0xd2, 0x3a, 0x53, 0xdc, 0xac, 0x93, 0x33, 0x92, 0xb2, 0x52, 0xb2, 0x52, 0x9a, 0x4c, 0x9a, - 0x41, 0xbd, 0x92, 0x65, 0x6e, 0x97, 0x99, 0x15, 0x26, 0x45, 0x24, 0xc4, 0xe4, 0x24, 0x94, 0x2c, - 0x95, 0x22, 0xeb, 0xfe, 0xa5, 0x48, 0xab, 0xf6, 0xaa, 0xae, 0x4a, 0xa9, 0x49, 0xa9, 0x0d, 0xc4, - 0x4c, 0x39, 0x5e, 0x56, 0xa6, 0xbf, 0xff, 0xc9, 0x7b, 0x31, 0x15, 0x4c, 0x48, 0x38, 0x50, 0x03, - 0x28, 0xaa, 0xc8, 0xbe, 0x14, 0xff, 0x94, 0x92, 0x53, 0x97, 0xd2, 0xa5, 0xc7, 0x15, 0x48, 0x94, - 0xc8, 0xb4, 0xa1, 0x71, 0x9e, 0xe7, 0xf2, 0x94, 0xae, 0x16, 0x96, 0x53, 0x95, 0xc5, 0x55, 0x25, - 0x55, 0x2d, 0x4d, 0x20, 0x2e, 0x34, 0xef, 0x9c, 0x8b, 0x23, 0x7c, 0xe4, 0xe4, 0xe9, 0x12, 0x49, - 0x0e, 0x47, 0x09, 0x44, 0x89, 0x44, 0xa9, 0x3f, 0x4e, 0x5f, 0xff, 0xef, 0x27, 0x8a, 0xc9, 0x74, - 0xac, 0x71, 0xa5, 0x4a, 0x65, 0x3a, 0x5d, 0x26, 0x52, 0xcb, 0x15, 0xa4, 0xd7, 0xa9, 0x5a, 0x0a, - 0x4c, 0x4b, 0x4b, 0x92, 0xc9, 0x04, 0x46, 0x54, 0x12, 0x1a, 0xc6, 0xa4, 0xf0, 0x3a, 0x88, 0xb6, - 0x4f, 0x49, 0x08, 0xd2, 0xc9, 0x2a, 0x49, 0x91, 0xa2, 0xd0, 0xb2, 0xb4, 0x3a, 0x55, 0x4b, 0x0b, - 0xa8, 0xd2, 0xcf, 0xea, 0xb2, 0x8a, 0x4e, 0x26, 0x55, 0x25, 0x77, 0x8e, 0xab, 0x28, 0x96, 0x19, - 0xf7, 0x17, 0xd3, 0x72, 0xd0, 0x85, 0x86, 0xa5, 0x44, 0xa4, 0xc4, 0x72, 0xbc, 0x44, 0x38, 0xd5, - 0x6f, 0xfa, 0x9d, 0x97, 0x11, 0x78, 0x39, 0x44, 0x38, 0xba, 0x46, 0x90, 0x3e, 0x1d, 0x56, 0x22, - 0x4f, 0xf2, 0xaf, 0xea, 0x95, 0x43, 0x49, 0x58, 0x9b, 0xf0, 0x44, 0x63, 0xa5, 0xaa, 0xff, 0xe5, - 0x79, 0x1a, 0x89, 0x88, 0x5f, 0x83, 0x48, 0x95, 0x08, 0x44, 0x02, 0x31, 0x2d, 0xcc, 0x17, 0x23, - 0xca, 0x0c, 0x0e, 0xaa, 0x55, 0x51, 0xd5, 0x51, 0x25, 0x3c, 0x96, 0x90, 0xdf, 0xe9, 0xda, 0x9a, - 0x57, 0xf3, 0x2b, 0x54, 0xa2, 0x51, 0x31, 0x51, 0x29, 0x4d, 0x0a, 0xa4, 0xd2, 0x1e, 0x4c, 0x42, - 0xa5, 0x24, 0xff, 0xb2, 0xc7, 0x25, 0x52, 0xa8, 0xe9, 0x01, 0xa3, 0x64, 0xc4, 0xf5, 0x13, 0x63, - 0x84, 0xe9, 0x66, 0xea, 0x81, 0xb1, 0x8a, 0xea, 0xa9, 0x58, 0x4c, 0x41, 0x50, 0x71, 0x20, 0x61, - 0x94, 0xff, 0xe5, 0xd8, 0x9d, 0x25, 0x13, 0x91, 0x04, 0xc8, 0xca, 0xaa, 0xbe, 0xa9, 0x27, 0x4a, - 0x89, 0x28, 0xf5, 0x6a, 0xe9, 0x75, 0x2a, 0x4e, 0xaa, 0x25, 0x69, 0x28, 0x8c, 0x89, 0x26, 0x3a, - 0x5d, 0x4e, 0x4b, 0xe5, 0xff, 0xca, 0xa3, 0xc6, 0x20, 0x41, 0x12, 0x88, 0xe5, 0x2d, 0x39, 0x2a, - 0x45, 0xd7, 0x4b, 0x7f, 0xc9, 0x7d, 0xf2, 0x32, 0x4d, 0x2d, 0x22, 0x49, 0xa9, 0xd2, 0x48, 0xba, - 0x52, 0x48, 0xaf, 0x0b, 0x93, 0x4f, 0xd4, 0xfb, 0x2a, 0xe7, 0x31, 0x11, 0x89, 0x4c, 0x75, 0x36, - 0x4d, 0x4f, 0x22, 0xa9, 0xef, 0x3b, 0x0e, 0xea, 0x19, 0x0a, 0x88, 0xc8, 0xb2, 0x29, 0xc1, 0x1f, - 0x0c, 0x2a, 0x4b, 0x89, 0xfe, 0x64, 0xba, 0x93, 0xfb, 0xea, 0x70, 0x6a, 0x92, 0xaa, 0x4b, 0x48, - 0x7a, 0x5f, 0x42, 0xd0, 0xe4, 0x7f, 0xf7, 0x5f, 0x4f, 0xc3, 0x2f, 0x44, 0xc4, 0xd1, 0x06, 0x23, - 0x4a, 0xb2, 0x52, 0x6f, 0xd2, 0x52, 0xd8, 0xef, 0xf4, 0x9c, 0x59, 0x74, 0xa7, 0x4b, 0x52, 0x18, - 0x8e, 0x52, 0x93, 0x92, 0xe9, 0x65, 0xf4, 0x92, 0x45, 0x98, 0xf5, 0x2b, 0x34, 0x58, 0x96, 0x24, - 0x62, 0x32, 0x1b, 0x53, 0xcc, 0x6b, 0x8f, 0x91, 0xa4, 0xf4, 0xa2, 0x52, 0x3a, 0x96, 0xa6, 0xa8, - 0x31, 0x1a, 0x51, 0x40, 0x51, 0xa2, 0xf9, 0x1c, 0xa5, 0xd3, 0x4b, 0xee, 0x8a, 0x48, 0xb1, 0x28, - 0x01, 0xa9, 0x23, 0xc5, 0xe0, 0x3a, 0xbf, 0xf4, 0x9f, 0x7f, 0x48, 0xcd, 0x68, 0xea, 0x95, 0x2a, - 0x94, 0x4b, 0x2b, 0xb2, 0x92, 0x52, 0x74, 0x9e, 0xea, 0xbf, 0xf6, 0x5f, 0x95, 0x9a, 0x53, 0x70, - 0xaa, 0x21, 0xd2, 0x48, 0x36, 0x89, 0x61, 0x89, 0x7e, 0x75, 0xbe, 0xfd, 0xdc, 0x82, 0xf3, 0x9a, - 0x94, 0x52, 0x61, 0x11, 0x5a, 0x6c, 0xe6, 0xa3, 0xa2, 0xd2, 0x7a, 0xbf, 0xf5, 0x79, 0xbd, 0x36, - 0xc5, 0x31, 0xaa, 0xe5, 0x75, 0x54, 0x8c, 0x95, 0x4b, 0x15, 0x14, 0xa4, 0xe4, 0x2f, 0x3f, 0xfa, - 0x56, 0x46, 0xe9, 0x6a, 0x90, 0xe8, 0x81, 0xa3, 0x29, 0xaa, 0x54, 0x5d, 0xc3, 0xa6, 0x93, 0x8e, - 0x47, 0x4b, 0xe2, 0xe2, 0x53, 0x65, 0x32, 0x49, 0x2d, 0x0b, 0x33, 0x14, 0xa1, 0x55, 0x29, 0x07, - 0xf1, 0xa9, 0x4b, 0xa6, 0xb2, 0x9c, 0x99, 0xec, 0x69, 0x15, 0x59, 0x96, 0x97, 0x29, 0x22, 0x8a, - 0xe5, 0x72, 0x58, 0x4d, 0xd5, 0xe3, 0x25, 0x2c, 0x94, 0xe5, 0x65, 0x59, 0x2a, 0xa6, 0x4a, 0x55, - 0x95, 0xd1, 0x2e, 0x87, 0x55, 0xe0, 0xcd, 0xc9, 0x2b, 0x34, 0xad, 0x15, 0x0a, 0x93, 0x99, 0x92, - 0x68, 0x69, 0x1f, 0x4a, 0x15, 0x22, 0x21, 0x15, 0x97, 0xd5, 0x4d, 0x03, 0x14, 0xcc, 0x87, 0x46, - 0x92, 0xdd, 0x1b, 0xf0, 0x9c, 0x51, 0x23, 0x54, 0xc1, 0x75, 0x22, 0x5a, 0x99, 0x24, 0x93, 0x58, - 0x62, 0x19, 0x2b, 0xb4, 0x68, 0x95, 0x5a, 0x4e, 0x06, 0x68, 0x63, 0x1a, 0x15, 0x48, 0xb2, 0x32, - 0x4c, 0x97, 0xa2, 0x5f, 0x29, 0x94, 0xc8, 0xa2, 0x55, 0xaa, 0x4c, 0x2b, 0x2f, 0x94, 0xc2, 0x6a, - 0xb3, 0x54, 0xa9, 0x2a, 0x74, 0x50, 0xe3, 0x49, 0xd2, 0xc8, 0xb2, 0x55, 0x03, 0x00, 0xca, 0x62, - 0x32, 0xcc, 0x8f, 0xff, 0xff, 0x84, 0x92, 0x61, 0x32, 0x8b, 0xc1, 0x92, 0xb6, 0xc6, 0xa5, 0x4a, - 0xa5, 0x52, 0x57, 0x83, 0xbf, 0x29, 0x8d, 0x56, 0x45, 0x45, 0x95, 0x52, 0xa5, 0x36, 0x9c, 0x25, - 0x4a, 0xa3, 0x9e, 0xb2, 0x7f, 0xf2, 0x75, 0x0d, 0x56, 0xa2, 0xa9, 0xaa, 0xa2, 0x64, 0x68, 0x58, - 0x22, 0xf8, 0xe2, 0xa5, 0x94, 0x99, 0x14, 0x97, 0x4b, 0x04, 0x82, 0xe2, 0x62, 0x06, 0x61, 0xaa, - 0x4d, 0x69, 0x62, 0x31, 0xaa, 0xe4, 0xb2, 0x94, 0x88, 0x55, 0xa7, 0x55, 0x52, 0xd2, 0xd4, 0xaa, - 0x92, 0x5a, 0x26, 0x57, 0x21, 0x52, -}; -const uint8_t* const _I_HappyFlipper_128x64[] = {_I_HappyFlipper_128x64_0}; - -const uint8_t _I_TanksSplashScreen_128x64_0[] = { - 0x01, 0x00, 0xbe, 0x03, 0xc9, 0x49, 0x29, 0x20, 0x2f, 0x01, 0x78, 0x0b, 0xc0, 0x59, 0xa4, 0xa5, - 0x65, 0x65, 0x55, 0x5d, 0x55, 0x53, 0x53, 0x52, 0xd2, 0x82, 0x16, 0x53, 0x51, 0xd1, 0x51, 0x31, - 0x31, 0x29, 0x2d, 0x2b, 0x29, 0x28, 0x04, 0x36, 0x2a, 0x29, 0x29, 0xa9, 0x69, 0x29, 0x19, 0x19, - 0x15, 0x14, 0x10, 0xba, 0x41, 0x54, 0x82, 0x12, 0x13, 0x21, 0xa1, 0x61, 0x21, 0x11, 0x19, 0x15, - 0x13, 0x11, 0x10, 0x04, 0x2d, 0x4a, 0xb4, 0x4f, 0xdd, 0x25, 0xeb, 0xd1, 0xff, 0xd3, 0x94, 0x6a, - 0x93, 0xcc, 0xb9, 0x64, 0xa3, 0xc5, 0x1a, 0xc9, 0x56, 0xb3, 0xb2, 0x4f, 0x4e, 0x4b, 0x15, 0x4a, - 0x95, 0x4a, 0xb5, 0x3a, 0x8d, 0x26, 0x88, 0x10, 0x35, 0x4a, 0x64, 0x99, 0x28, 0x34, 0xbd, 0x26, - 0x42, 0x85, 0x49, 0x85, 0x49, 0x65, 0x39, 0x4d, 0x25, 0x43, 0x13, 0xea, 0xa5, 0xa8, 0xc7, 0x2d, - 0x12, 0xaa, 0xd5, 0x32, 0x56, 0x4a, 0x56, 0x4a, 0x53, 0x49, 0x93, 0x54, 0xa5, 0x99, 0x19, 0x24, - 0xa6, 0xbf, 0xa9, 0x91, 0x62, 0x64, 0x98, 0x9c, 0x84, 0x92, 0x85, 0x92, 0xa4, 0x79, 0x3d, 0x66, - 0x51, 0xa9, 0x4f, 0xf2, 0x59, 0x4a, 0x95, 0x26, 0xa5, 0x25, 0xa4, 0xe5, 0x64, 0x99, 0x59, 0xcf, - 0xfe, 0x8c, 0x32, 0x1f, 0x48, 0x88, 0x64, 0xa0, 0xe1, 0x40, 0x0c, 0xa2, 0xab, 0x22, 0xd1, 0x6a, - 0x33, 0xfa, 0x4d, 0xe7, 0xea, 0xab, 0x42, 0xa4, 0x65, 0x69, 0x12, 0x99, 0x16, 0x97, 0x11, 0x54, - 0xba, 0x6e, 0xbf, 0xdf, 0xfa, 0x7e, 0x73, 0x07, 0x94, 0x95, 0x48, 0x72, 0xb5, 0x2d, 0x4d, 0x23, - 0x4b, 0x22, 0xf2, 0xcf, 0xe4, 0xf4, 0x99, 0x45, 0x5e, 0x6f, 0xa9, 0xa2, 0x52, 0xa4, 0x58, 0x4a, - 0x24, 0x4a, 0x25, 0x49, 0xc4, 0x7b, 0x0d, 0xe3, 0xf2, 0x92, 0xd2, 0xe2, 0x5a, 0x9c, 0xa5, 0x52, - 0x53, 0x29, 0xd2, 0xe9, 0x32, 0x95, 0x5c, 0x57, 0xfb, 0xdf, 0x9f, 0xa4, 0x64, 0xe9, 0x54, 0x59, - 0x4e, 0x26, 0xa4, 0xa4, 0x10, 0x90, 0xc8, 0xaa, 0xb9, 0x2b, 0xe5, 0x4b, 0x2b, 0x4a, 0x92, 0x44, - 0xa8, 0x4c, 0x31, 0xca, 0x52, 0x28, 0xb4, 0x2c, 0xa6, 0x52, 0x49, 0x41, 0x84, 0xc8, 0xb2, 0x44, - 0xb1, 0xaa, 0x49, 0x62, 0xb2, 0x19, 0x2b, 0xbc, 0x69, 0x28, 0x51, 0xcd, 0x55, 0xea, 0xb2, 0x7a, - 0x2b, 0x24, 0x69, 0x14, 0x9a, 0x55, 0x26, 0x23, 0x95, 0x89, 0x6a, 0xb2, 0x5e, 0x4d, 0x3e, 0x22, - 0x97, 0xa8, 0x92, 0x54, 0x65, 0x42, 0x41, 0x64, 0x8d, 0x23, 0x48, 0xd6, 0x1f, 0x4d, 0x36, 0xa5, - 0x53, 0x89, 0x87, 0x07, 0x22, 0x3f, 0x19, 0x20, 0xb4, 0x75, 0x35, 0x2a, 0x47, 0xd7, 0x3f, 0x94, - 0x93, 0xe9, 0x2a, 0x59, 0x11, 0x68, 0xc8, 0xb5, 0x31, 0x2c, 0x94, 0x4b, 0x4f, 0x52, 0xf9, 0x4b, - 0xef, 0xb4, 0xfa, 0x4c, 0x52, 0x54, 0xcb, 0x1a, 0xa4, 0x4b, 0xa9, 0x24, 0xd2, 0x52, 0xb1, 0x59, - 0x4e, 0x26, 0x5a, 0xb1, 0x2a, 0xd2, 0xb8, 0x05, 0xe6, 0x3a, 0x48, 0x4d, 0x26, 0x93, 0x92, 0xaa, - 0x7f, 0x77, 0x32, 0x2f, 0xeb, 0xe0, 0x70, 0x80, 0x81, 0x79, 0x91, 0x48, 0x64, 0xb2, 0x19, 0x36, - 0x4e, 0xa9, 0xa2, 0xaf, 0xe5, 0x65, 0x52, 0xac, 0x4c, 0x90, 0x4a, 0x2b, 0x51, 0xaa, 0xd2, 0xa9, - 0x72, 0x4c, 0x4e, 0x52, 0xe9, 0x22, 0xa8, 0xe8, 0x43, 0xc3, 0x91, 0x41, 0x8e, 0x27, 0x12, 0x77, - 0x1a, 0x50, 0x24, 0x7a, 0x55, 0x4c, 0x55, 0x30, 0x3c, 0x34, 0xa2, 0xf8, 0x9e, 0x86, 0x89, 0xa7, - 0xd4, 0x67, 0x34, 0x96, 0x41, 0x60, 0x9c, 0x07, 0x23, 0x92, 0xd5, 0x19, 0x44, 0xb0, 0x39, 0x68, - 0x95, 0xde, 0x13, 0x79, 0x60, 0x0c, 0xa8, 0x20, 0x32, 0x5c, 0x8d, 0x02, 0x93, 0x09, 0xc4, 0xf4, - 0xb4, 0xbc, 0xaa, 0x97, 0x7b, 0x17, 0x12, 0xa0, 0xca, 0x9b, 0xe3, 0x4a, 0xd6, 0xe4, 0xaa, 0x75, - 0x29, 0x24, 0x5f, 0xa7, 0xab, 0xd3, 0x55, 0xb4, 0x9a, 0xca, 0x24, 0x87, 0x2a, 0xf6, 0x1a, 0xa1, - 0x88, 0x69, 0x57, 0xe9, 0x77, 0xfa, 0xab, 0xbd, 0x25, 0x8e, 0xa7, 0x55, 0xa0, 0x2c, 0x0e, 0x26, - 0x93, 0x29, 0xc8, 0xfa, 0x3d, 0xb9, 0xfb, 0x27, 0xd5, 0x9c, 0x31, 0x28, 0x95, 0x37, 0x08, 0xc7, - 0x1e, 0xad, 0x3e, 0x55, 0x25, 0xdf, 0xc9, 0x37, 0x3c, 0x49, 0xfd, 0x27, 0x49, 0xa4, 0xc8, 0x4a, - 0xa9, 0x2e, 0x21, 0xf9, 0x51, 0x40, 0x85, 0x3e, 0xd4, 0xdd, 0xf2, 0xae, 0x01, 0x92, 0xd4, 0x62, - 0x54, 0x9c, 0x9e, 0x52, 0xbf, 0x92, 0xe5, 0x7f, 0xc0, 0x63, 0xd6, 0x90, 0x44, 0x62, 0xba, 0xa1, - 0x10, 0xaf, 0xc6, 0x93, 0xf4, 0x92, 0x7c, 0x76, 0x7a, 0xf9, 0x26, 0xb7, 0x57, 0xa4, 0xd4, 0x51, - 0x21, 0x55, 0x5a, 0xa3, 0x80, 0x72, 0x5f, 0xb9, 0xfd, 0x16, 0x9d, 0x49, 0xcd, 0x42, 0xe5, 0x34, - 0x59, 0x4d, 0x45, 0x7c, 0x39, 0x31, 0xe0, 0xd3, 0xaf, 0xfa, 0xa8, 0xf6, 0xaa, 0xc9, 0x6e, 0x38, - 0x8d, 0x16, 0x55, 0x52, 0xe9, 0x69, 0x29, 0x59, 0x29, 0xd6, 0x26, 0x21, 0x59, 0x88, 0xfa, 0x73, - 0x43, 0xc1, 0x18, 0x0c, 0x97, 0xc9, 0x4a, 0xca, 0x69, 0x3c, 0x92, 0xfd, 0x2f, 0xd3, 0x4b, 0xd2, - 0xb2, 0x42, 0x65, 0x32, 0x5d, 0x4d, 0x23, 0x93, 0x28, 0x90, 0x90, 0x46, 0xde, 0x23, 0x1a, 0xff, - 0x13, 0x53, 0x52, 0xa2, 0x8c, 0x47, 0x2b, 0x52, 0xc9, 0xea, 0xb2, 0x5a, 0x4c, 0xb0, 0x8c, 0x7e, - 0x9a, 0x9a, 0x2c, 0x97, 0x25, 0x90, 0xd2, 0x48, 0x2a, 0x84, 0xe1, 0xaa, 0x4a, 0x65, 0xa2, 0x31, - 0x89, 0x62, 0xe5, 0x35, 0x77, 0x90, 0xc5, 0x69, 0xbc, 0x91, 0x50, 0xc8, 0xb2, 0x5d, 0x4b, 0xfe, - 0x57, 0x49, 0xfe, 0xab, 0xd3, 0x0b, 0xe3, 0xa2, 0xc9, 0x54, 0xb2, 0x32, 0x47, 0x28, 0xe9, 0x74, - 0x92, 0x82, 0x20, 0xe3, 0x36, 0x45, 0xf1, 0xca, 0x49, 0x2c, 0x8c, 0x21, 0xa4, 0x52, 0x7d, 0xdc, - 0xbb, 0xff, 0xf6, 0x4f, 0x2f, 0xb4, 0x8d, 0x06, 0x53, 0x49, 0xe4, 0xd1, 0x09, 0xe3, 0xd2, 0x99, - 0xe4, 0x68, 0x59, 0x2d, 0x76, 0xa1, 0x50, 0x34, 0xa0, 0x30, 0xcc, 0xb5, 0x31, 0x29, 0x2f, 0xd3, - 0x8b, 0x7f, 0xd4, 0xc9, 0x3f, 0xb1, 0x7d, 0x24, 0xae, 0xa2, 0x3a, 0x1b, 0x23, 0x84, 0x69, 0x5f, - 0xdd, 0xd6, 0x92, 0x17, 0xc9, 0xc5, 0xdf, 0x22, 0xba, 0x69, 0x25, 0x4b, 0x53, 0xa9, 0x52, 0x8f, - 0x43, 0x7f, 0x14, 0xa4, 0xd5, 0xaa, 0xb5, 0x2a, 0x16, 0x86, 0x39, 0x49, 0x20, 0x89, 0x38, 0x43, - 0xc3, 0x5f, 0xf2, 0xea, 0xa4, 0x96, 0x2d, 0x5d, 0x6a, 0x95, 0x71, 0x95, 0x26, 0x86, 0xc9, 0x92, - 0xfe, 0x7f, 0xe5, 0x7b, 0xed, 0xd5, 0x3e, 0x93, 0xab, 0xed, 0x45, 0x39, 0x59, 0x20, 0xe8, 0xea, - 0x6a, 0x9f, 0xff, 0xfd, 0x0a, 0x93, 0x45, 0xa9, 0x6a, 0xb5, 0x52, 0xad, 0x4e, 0x4b, 0x4b, 0x90, - 0x2f, 0x8e, 0x17, 0xfb, 0x4f, 0xca, 0x7d, 0x3f, 0xf2, 0x5c, 0x8f, 0xfe, 0xbf, 0x22, 0x94, 0xc2, - 0xe4, 0xb2, 0xab, 0xa6, 0xca, 0x24, 0xd8, 0x1c, 0x93, 0x10, 0x47, 0x45, 0x54, 0xa1, 0x5b, 0x29, - 0x55, 0x10, -}; -const uint8_t* const _I_TanksSplashScreen_128x64[] = {_I_TanksSplashScreen_128x64_0}; - -const uint8_t _I_enemy_down_0[] = { - 0x00, - 0x21, - 0x3f, - 0x33, - 0x3f, - 0x2d, - 0x0c, -}; -const uint8_t* const _I_enemy_down[] = {_I_enemy_down_0}; - -const uint8_t _I_enemy_left_0[] = { - 0x00, - 0x3e, - 0x1c, - 0x17, - 0x17, - 0x1c, - 0x3e, -}; -const uint8_t* const _I_enemy_left[] = {_I_enemy_left_0}; - -const uint8_t _I_enemy_right_0[] = { - 0x00, - 0x1f, - 0x0e, - 0x3a, - 0x3a, - 0x0e, - 0x1f, -}; -const uint8_t* const _I_enemy_right[] = {_I_enemy_right_0}; - -const uint8_t _I_enemy_up_0[] = { - 0x00, - 0x0c, - 0x2d, - 0x3f, - 0x33, - 0x3f, - 0x21, -}; -const uint8_t* const _I_enemy_up[] = {_I_enemy_up_0}; - -const uint8_t _I_projectile_down_0[] = { - 0x00, - 0x00, - 0x12, - 0x1e, - 0x1e, - 0x0c, - 0x00, -}; -const uint8_t* const _I_projectile_down[] = {_I_projectile_down_0}; - -const uint8_t _I_projectile_left_0[] = { - 0x00, - 0x00, - 0x1c, - 0x0e, - 0x0e, - 0x1c, - 0x00, -}; -const uint8_t* const _I_projectile_left[] = {_I_projectile_left_0}; - -const uint8_t _I_projectile_right_0[] = { - 0x00, - 0x00, - 0x0e, - 0x1c, - 0x1c, - 0x0e, - 0x00, -}; -const uint8_t* const _I_projectile_right[] = {_I_projectile_right_0}; - -const uint8_t _I_projectile_up_0[] = { - 0x00, - 0x00, - 0x0c, - 0x1e, - 0x1e, - 0x12, - 0x00, -}; -const uint8_t* const _I_projectile_up[] = {_I_projectile_up_0}; - -const uint8_t _I_tank_base_0[] = { - 0x00, - 0x21, - 0x33, - 0x0c, - 0x1e, - 0x0c, - 0x3f, -}; -const uint8_t* const _I_tank_base[] = {_I_tank_base_0}; - -const uint8_t _I_tank_down_0[] = { - 0x00, - 0x21, - 0x3f, - 0x3f, - 0x3f, - 0x2d, - 0x0c, -}; -const uint8_t* const _I_tank_down[] = {_I_tank_down_0}; - -const uint8_t _I_tank_explosion_0[] = { - 0x00, - 0x1a, - 0x25, - 0x16, - 0x29, - 0x15, - 0x2a, -}; -const uint8_t* const _I_tank_explosion[] = {_I_tank_explosion_0}; - -const uint8_t _I_tank_hedgehog_0[] = { - 0x00, - 0x21, - 0x12, - 0x0c, - 0x0c, - 0x12, - 0x21, -}; -const uint8_t* const _I_tank_hedgehog[] = {_I_tank_hedgehog_0}; - -const uint8_t _I_tank_left_0[] = { - 0x00, - 0x3e, - 0x1c, - 0x1f, - 0x1f, - 0x1c, - 0x3e, -}; -const uint8_t* const _I_tank_left[] = {_I_tank_left_0}; - -const uint8_t _I_tank_right_0[] = { - 0x00, - 0x1f, - 0x0e, - 0x3e, - 0x3e, - 0x0e, - 0x1f, -}; -const uint8_t* const _I_tank_right[] = {_I_tank_right_0}; - -const uint8_t _I_tank_stone_0[] = { - 0x00, - 0x12, - 0x3f, - 0x1e, - 0x1e, - 0x3f, - 0x12, -}; -const uint8_t* const _I_tank_stone[] = {_I_tank_stone_0}; - -const uint8_t _I_tank_up_0[] = { - 0x00, - 0x0c, - 0x2d, - 0x3f, - 0x3f, - 0x3f, - 0x21, -}; -const uint8_t* const _I_tank_up[] = {_I_tank_up_0}; - -const uint8_t _I_tank_wall_0[] = { - 0x00, - 0x3f, - 0x2d, - 0x3f, - 0x3f, - 0x2d, - 0x3f, -}; -const uint8_t* const _I_tank_wall[] = {_I_tank_wall_0}; - -const Icon I_HappyFlipper_128x64 = { - .width = 128, - .height = 64, - .frame_count = 1, - .frame_rate = 0, - .frames = _I_HappyFlipper_128x64}; -const Icon I_TanksSplashScreen_128x64 = { - .width = 128, - .height = 64, - .frame_count = 1, - .frame_rate = 0, - .frames = _I_TanksSplashScreen_128x64}; -const Icon I_enemy_down = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_enemy_down}; -const Icon I_enemy_left = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_enemy_left}; -const Icon I_enemy_right = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_enemy_right}; -const Icon I_enemy_up = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_enemy_up}; -const Icon I_projectile_down = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_projectile_down}; -const Icon I_projectile_left = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_projectile_left}; -const Icon I_projectile_right = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_projectile_right}; -const Icon I_projectile_up = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_projectile_up}; -const Icon I_tank_base = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_base}; -const Icon I_tank_down = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_down}; -const Icon I_tank_explosion = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_explosion}; -const Icon I_tank_hedgehog = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_hedgehog}; -const Icon I_tank_left = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_left}; -const Icon I_tank_right = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_right}; -const Icon I_tank_stone = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_stone}; -const Icon I_tank_up = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_up}; -const Icon I_tank_wall = - {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_tank_wall}; - typedef struct { // +-----x // | @@ -550,6 +185,8 @@ unsigned char* tanks_game_serialize(const TanksState* const tanks_state) { case DirectionLeft: cell = CellEnemyLeft; break; + default: + break; } tanks_game_write_cell( @@ -577,6 +214,8 @@ unsigned char* tanks_game_serialize(const TanksState* const tanks_state) { case DirectionLeft: cell = CellProjectileLeft; break; + default: + break; } tanks_game_write_cell( @@ -603,6 +242,8 @@ unsigned char* tanks_game_serialize(const TanksState* const tanks_state) { case DirectionLeft: cell = CellTankLeft; break; + default: + break; } tanks_game_write_cell( @@ -625,6 +266,8 @@ unsigned char* tanks_game_serialize(const TanksState* const tanks_state) { case DirectionLeft: cell = CellTankLeft; break; + default: + break; } tanks_game_write_cell( @@ -1243,6 +886,8 @@ static Point tanks_game_get_next_step(Point coordinates, Direction direction) { case DirectionLeft: next_step.x--; break; + default: + break; } return next_step; } @@ -1591,7 +1236,7 @@ int32_t tanks_game_app(void* p) { tanks_state->menu_state = MenuStateCooperativeServerMode; } } else if(tanks_state->state == GameStateCooperativeClient) { - FuriString* goesUp; + FuriString* goesUp = NULL; char arr[2]; arr[0] = GoesUp; arr[1] = 0; @@ -1615,7 +1260,7 @@ int32_t tanks_game_app(void* p) { tanks_state->menu_state = MenuStateCooperativeClientMode; } } else if(tanks_state->state == GameStateCooperativeClient) { - FuriString* goesDown; + FuriString* goesDown = NULL; char arr[2]; arr[0] = GoesDown; arr[1] = 0; @@ -1632,7 +1277,7 @@ int32_t tanks_game_app(void* p) { break; case InputKeyRight: if(tanks_state->state == GameStateCooperativeClient) { - FuriString* goesRight; + FuriString* goesRight = NULL; char arr[2]; arr[0] = GoesRight; arr[1] = 0; @@ -1649,7 +1294,7 @@ int32_t tanks_game_app(void* p) { break; case InputKeyLeft: if(tanks_state->state == GameStateCooperativeClient) { - FuriString* goesLeft; + FuriString* goesLeft = NULL; char arr[2]; arr[0] = GoesLeft; arr[1] = 0; @@ -1682,7 +1327,7 @@ int32_t tanks_game_app(void* p) { } else if(tanks_state->state == GameStateGameOver) { tanks_game_init_game(tanks_state, tanks_state->state); } else if(tanks_state->state == GameStateCooperativeClient) { - FuriString* shoots; + FuriString* shoots = NULL; char arr[2]; arr[0] = Shoots; arr[1] = 0; @@ -1699,6 +1344,8 @@ int32_t tanks_game_app(void* p) { case InputKeyBack: processing = false; break; + default: + break; } } } else if(event.type == EventTypeTick) { @@ -1738,7 +1385,7 @@ int32_t tanks_game_app(void* p) { tanks_game_process_game_step(tanks_state); - FuriString* serializedData; + FuriString* serializedData = NULL; unsigned char* data = tanks_game_serialize(tanks_state); char arr[11 * 16 + 1]; diff --git a/applications/plugins/tetris_game/tetris_game.c b/applications/plugins/tetris_game/tetris_game.c index 273081126..a4a17d348 100644 --- a/applications/plugins/tetris_game/tetris_game.c +++ b/applications/plugins/tetris_game/tetris_game.c @@ -350,8 +350,6 @@ static void } int32_t tetris_game_app() { - srand(DWT->CYCCNT); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TetrisEvent)); TetrisState* tetris_state = malloc(sizeof(TetrisState)); @@ -442,6 +440,8 @@ int32_t tetris_game_app() { case InputKeyBack: processing = false; break; + default: + break; } } } else if(event.type == EventTypeTick) { diff --git a/applications/plugins/tictactoe_game/tictactoe_game.c b/applications/plugins/tictactoe_game/tictactoe_game.c index b3a0d5c84..fe57d7c62 100644 --- a/applications/plugins/tictactoe_game/tictactoe_game.c +++ b/applications/plugins/tictactoe_game/tictactoe_game.c @@ -358,12 +358,11 @@ int32_t tictactoe_game_app(void* p) { case InputKeyOk: tictactoe_state->button_state = true; break; + default: + break; } } } - } else { - // Event timeout - FURI_LOG_D(TAG, "furi_message_queue: Event timeout"); } view_port_update(view_port); diff --git a/applications/plugins/timelapse/.gitattributes b/applications/plugins/timelapse/.gitattributes new file mode 100644 index 000000000..cd63b2f7b --- /dev/null +++ b/applications/plugins/timelapse/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +#* text=auto diff --git a/applications/plugins/timelapse/.gitignore b/applications/plugins/timelapse/.gitignore new file mode 100644 index 000000000..722d5e71d --- /dev/null +++ b/applications/plugins/timelapse/.gitignore @@ -0,0 +1 @@ +.vscode diff --git a/applications/plugins/timelapse/README.md b/applications/plugins/timelapse/README.md new file mode 100644 index 000000000..cd7b670b8 --- /dev/null +++ b/applications/plugins/timelapse/README.md @@ -0,0 +1,72 @@ + +# zeitraffer + +[![Build FAP](https://github.com/theageoflove/flipperzero-zeitraffer/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/theageoflove/flipperzero-zeitraffer/actions/workflows/build.yml) + +english version [below](#eng) + +![zeitraffer for flipper zero](https://theageoflove.ru/uploads/2022/11/photo_2022-11-10_15-54-25.jpg) +Π’ΠΈΠ΄Π΅ΠΎ Ρ€Π°Π±ΠΎΡ‚Ρ‹: https://youtube.com/shorts/QKyQa7_s7IE + +Π“ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ Ρ„Π°ΠΏ ΠΏΠΎΠ΄ послСднюю Ρ€Π΅Π»ΠΈΠ·Π½ΡƒΡŽ ΠΏΡ€ΠΎΡˆΠΈΠ²ΠΊΡƒ [ΠΌΠΎΠΆΠ½ΠΎ ΡΠΊΠ°Ρ‡Π°Ρ‚ΡŒ здСсь](https://nightly.link/theageoflove/flipperzero-zeitraffer/workflows/build/main/zeitraffer.fap.zip). + +Π― нСнастоящий сварщик, Π½Π΅ ΠΎΠ±Π΅ΡΡΡƒΠ΄ΡŒΡ‚Π΅. Π”Π΅Π»Π°Π» для своСй Sony DSLR A100, ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚ для Π»ΡŽΠ±Ρ‹Ρ… ΠΊΠ°ΠΌΠ΅Ρ€, ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‰ΠΈΡ… ΠΏΡ€ΠΎΠ²ΠΎΠ΄Π½ΠΎΠΉ ΠΏΡƒΠ»ΡŒΡ‚ с трСмя ΠΊΠΎΠ½Ρ‚Π°ΠΊΡ‚Π°ΠΌΠΈ. + +Основано Π½Π° Ρ…Π΅Π»Π»ΠΎΠ²ΠΎΡ€Π»Π΄Π΅ https://github.com/zmactep/flipperzero-hello-world + +### Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅: + + - **Π²Π²Π΅Ρ€Ρ…-Π²Π½ΠΈΠ·** - врСмя. + - **Π²Π»Π΅Π²ΠΎ-Π²ΠΏΡ€Π°Π²ΠΎ** - количСство ΠΊΠ°Π΄Ρ€ΠΎΠ² + + 0 ΠΊΠ°Π΄Ρ€ΠΎΠ² - бСсконСчный Ρ€Π΅ΠΆΠΈΠΌ, -1 ΠΊΠ°Π΄Ρ€ΠΎΠ² - BULB + - **Π·Π°ΠΆΠ°Ρ‚ΠΈΠ΅ стрСлок** - Β±10 ΠΊΠ°Π΄Ρ€ΠΎΠ²/сСкунд + - **ОК** - пуск/ΠΏΠ°ΡƒΠ·Π° + - Π”Π»ΠΈΠ½Π½ΠΎΠ΅ Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ **ОК** - Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ/Π²Ρ‹ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ подсвСтку + - **Π½Π°Π·Π°Π΄** - сброс + - Π΄Π»ΠΈΠ½Π½ΠΎΠ΅ Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ **Π½Π°Π·Π°Π΄** - Π²Ρ‹Ρ…ΠΎΠ΄ + +ΠŸΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰Π΅ΠΌ Ρ‚Π°ΠΉΠΌΠ΅Ρ€Π΅ Π±Π»ΠΎΠΊΠΈΡ€ΡƒΡŽΡ‚ΡΡ всС ΠΊΠ½ΠΎΠΏΠΊΠΈ ΠΊΡ€ΠΎΠΌΠ΅ ОК. + +ΠŸΡ€ΠΈ запускС даётся Ρ‚Ρ€ΠΈ сСкунды Π½Π° ΠΎΡ‚ΡΠΊΠΎΡ‡ΠΈΡ‚ΡŒ. + +## Π§ΠΎ Π½Π°Π΄ΠΎ + - Π΄Π²Π΅ ΠΎΠΏΡ‚ΠΎΠΏΠ°Ρ€Ρ‹ Ρ‚ΠΈΠΏΠ° EL817C + - кусок Π³Ρ€Π΅Π±Ρ‘Π½ΠΊΠΈ Π½Π° Ρ‚Ρ€ΠΈ ΠΏΠΈΠ½Π° + - Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠ²ΠΎΠ΄Π° + - тСрмоусадка + - Ρ€Π°Π·ΡŠΡ‘ΠΌ ΠΏΡƒΠ»ΡŒΡ‚Π° ΠΎΡ‚ ΠΊΠ°ΠΌΠ΅Ρ€Ρ‹. Π“Π΄Π΅ Π²Π·ΡΡ‚ΡŒ ΠΈΠ»ΠΈ ΠΈΠ· Ρ‡Π΅Π³ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ - Π΄ΡƒΠΌΠ°ΠΉΡ‚Π΅ + +## Как ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ +Π‘Π΅Ρ€Ρ‘ΠΌ ΠΎΠΏΡ‚ΠΎΠΏΠ°Ρ€Ρ‹, соСдиняСм ΠΏΠΎ схСмС. +![](https://theageoflove.ru/uploads/2022/11/camera_cable.jpg) +Π“Π΄Π΅ ΠΊΠ°ΠΊΠΎΠΉ ΠΏΠΈΠ½ Ρƒ ΠΊΠ°ΠΌΠ΅Ρ€Ρ‹, ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ·Π½Π°Ρ‚ΡŒ Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Ρ‚ΡƒΡ‚: https://www.doc-diy.net/photo/remote_pinout/ + +# English +Simple timelapse app for Flipper Zero. + +[Get latest release](https://nightly.link/theageoflove/flipperzero-zeitraffer/workflows/build/main/zeitraffer.fap.zip) + +based on https://github.com/zmactep/flipperzero-hello-world + +### Control: + - Up and down - time. + - Left and right - number of frames + - Long press arrows - Β±10 frames/seconds + - OK - start/pause + - Long press OK - turn on/off the backlight + - Back - reset + - Long press back - exit + +When the timer is running, all buttons are blocked except OK. + +## What you need: + - two EL817C optocouplers + - pin header connector 1x3 2,54mm male + - some wire + - heat shrink + - camera remote connector +## How to assemble +Take optocouplers, connect according to the scheme. +![](https://theageoflove.ru/uploads/2022/11/camera_cable_en.jpg) +Camera pinout can be found here: https://www.doc-diy.net/photo/remote_pinout/ diff --git a/applications/plugins/timelapse/application.fam b/applications/plugins/timelapse/application.fam new file mode 100644 index 000000000..afc0ab675 --- /dev/null +++ b/applications/plugins/timelapse/application.fam @@ -0,0 +1,14 @@ +App( + appid="GPIO_Timelapse", + name="[GPIO] Timelapse", + apptype=FlipperAppType.EXTERNAL, + entry_point="zeitraffer_app", + cdefines=["APP_ZEITRAFFER"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + order=90, + fap_icon="zeitraffer.png", + fap_category="GPIO", +) \ No newline at end of file diff --git a/applications/plugins/timelapse/gpio_item.c b/applications/plugins/timelapse/gpio_item.c new file mode 100644 index 000000000..2d0f5f676 --- /dev/null +++ b/applications/plugins/timelapse/gpio_item.c @@ -0,0 +1,51 @@ +#include "gpio_item.h" + +#include + +typedef struct { + const char* name; + const GpioPin* pin; +} GpioItem; + +static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { + {"1.2: PA7", &gpio_ext_pa7}, + {"1.3: PA6", &gpio_ext_pa6}, + {"1.4: PA4", &gpio_ext_pa4}, + {"1.5: PB3", &gpio_ext_pb3}, + {"1.6: PB2", &gpio_ext_pb2}, + {"1.7: PC3", &gpio_ext_pc3}, + {"2.7: PC1", &gpio_ext_pc1}, + {"2.8: PC0", &gpio_ext_pc0}, +}; + +void gpio_item_configure_pin(uint8_t index, GpioMode mode) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, false); + furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); +} + +void gpio_item_configure_all_pins(GpioMode mode) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_configure_pin(i, mode); + } +} + +void gpio_item_set_pin(uint8_t index, bool level) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, level); +} + +void gpio_item_set_all_pins(bool level) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_set_pin(i, level); + } +} + +const char* gpio_item_get_pin_name(uint8_t index) { + furi_assert(index < GPIO_ITEM_COUNT + 1); + if(index == GPIO_ITEM_COUNT) { + return "ALL"; + } else { + return gpio_item[index].name; + } +} diff --git a/applications/plugins/timelapse/gpio_item.h b/applications/plugins/timelapse/gpio_item.h new file mode 100644 index 000000000..5cb2b86c1 --- /dev/null +++ b/applications/plugins/timelapse/gpio_item.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define GPIO_ITEM_COUNT 8 + +void gpio_item_configure_pin(uint8_t index, GpioMode mode); + +void gpio_item_configure_all_pins(GpioMode mode); + +void gpio_item_set_pin(uint8_t index, bool level); + +void gpio_item_set_all_pins(bool level); + +const char* gpio_item_get_pin_name(uint8_t index); diff --git a/applications/plugins/timelapse/zeitraffer.c b/applications/plugins/timelapse/zeitraffer.c new file mode 100644 index 000000000..7031ec7de --- /dev/null +++ b/applications/plugins/timelapse/zeitraffer.c @@ -0,0 +1,329 @@ +#include +#include +#include +#include +#include +//#include "gpio/gpio_item.h" +#include "gpio_item.h" + +// Π§Π°ΡΡ‚ΡŒ ΠΊΠΎΠ΄Π° ΠΏΠΎΠΊΡ€Π°Π΄Π΅Π½Π° ΠΈΠ· https://github.com/zmactep/flipperzero-hello-world + +int Time = 10; // Π’Π°ΠΉΠΌΠ΅Ρ€ +int Count = 10; // ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ ΠΊΠ°Π΄Ρ€ΠΎΠ² +int WorkTime = 0; // Π‘Ρ‡Ρ‘Ρ‚Ρ‡ΠΈΠΊ Ρ‚Π°ΠΉΠΌΠ΅Ρ€Π° +int WorkCount = 0; // Π‘Ρ‡Ρ‘Ρ‚Ρ‡ΠΈΠΊ ΠΊΠ°Π΄Ρ€ΠΎΠ² +bool InfiniteShot = false; // БСсконСчная ΡΡŠΡ‘ΠΌΠΊΠ° +bool Bulb = false; // Π Π΅ΠΆΠΈΠΌ BULB +int Backlight = 0; // ΠŸΠΎΠ΄ΡΠ²Π΅Ρ‚ΠΊΠ°: Π²ΠΊΠ»/Π²Ρ‹ΠΊΠ»/Π°Π²Ρ‚ΠΎ + +const NotificationSequence sequence_click = { + &message_note_c7, + &message_delay_50, + &message_sound_off, + NULL, +}; + +typedef enum { + EventTypeTick, + EventTypeInput, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} ZeitrafferEvent; + +static void draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + char temp_str[36]; + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + switch(Count) { + case -1: + snprintf(temp_str, sizeof(temp_str), "Set: BULB %i sec", Time); + break; + case 0: + snprintf(temp_str, sizeof(temp_str), "Set: infinite, %i sec", Time); + break; + default: + snprintf(temp_str, sizeof(temp_str), "Set: %i frames, %i sec", Count, Time); + } + canvas_draw_str(canvas, 3, 15, temp_str); + snprintf(temp_str, sizeof(temp_str), "Left: %i frames, %i sec", WorkCount, WorkTime); + canvas_draw_str(canvas, 3, 35, temp_str); + + switch(Backlight) { + case 1: + canvas_draw_str(canvas, 3, 55, "Backlight: ON"); + break; + case 2: + canvas_draw_str(canvas, 3, 55, "Backlight: OFF"); + break; + default: + canvas_draw_str(canvas, 3, 55, "Backlight: AUTO"); + } +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, Ρ‡Ρ‚ΠΎ контСкст Π½Π΅ Π½ΡƒΠ»Π΅Π²ΠΎΠΉ + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + ZeitrafferEvent event = {.type = EventTypeInput, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void timer_callback(FuriMessageQueue* event_queue) { + // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, Ρ‡Ρ‚ΠΎ контСкст Π½Π΅ Π½ΡƒΠ»Π΅Π²ΠΎΠΉ + furi_assert(event_queue); + + ZeitrafferEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t zeitraffer_app(void* p) { + UNUSED(p); + + // Π’Π΅ΠΊΡƒΡ‰Π΅Π΅ событиС Ρ‚ΠΈΠΏΠ° кастомного Ρ‚ΠΈΠΏΠ° ZeitrafferEvent + ZeitrafferEvent event; + // ΠžΡ‡Π΅Ρ€Π΅Π΄ΡŒ событий Π½Π° 8 элСмСнтов Ρ€Π°Π·ΠΌΠ΅Ρ€Π° ZeitrafferEvent + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(ZeitrafferEvent)); + + // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Π½ΠΎΠ²Ρ‹ΠΉ view port + ViewPort* view_port = view_port_alloc(); + // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ callback отрисовки, Π±Π΅Π· контСкста + view_port_draw_callback_set(view_port, draw_callback, NULL); + // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ callback Π½Π°ΠΆΠ°Ρ‚ΠΈΠΉ Π½Π° клавиши, Π² качСствС контСкста ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅ΠΌ + // Π½Π°ΡˆΡƒ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ сообщСний, Ρ‡Ρ‚ΠΎΠ± Π·Π°ΠΏΠΈΡ…ΠΈΠ²Π°Ρ‚ΡŒ Π² Π½Π΅Ρ‘ эти события + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ GUI прилоТСния + Gui* gui = furi_record_open(RECORD_GUI); + // ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ view port ΠΊ GUI Π² полноэкранном Ρ€Π΅ΠΆΠΈΠΌΠ΅ + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΠΌ ΠΏΠΈΠ½Ρ‹ + gpio_item_configure_all_pins(GpioModeOutputPushPull); + + // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ пСриодичСский Ρ‚Π°ΠΉΠΌΠ΅Ρ€ с коллбэком, ΠΊΡƒΠ΄Π° Π² качСствС + // контСкста Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Ρ‚ΡŒΡΡ наша ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ событий + FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue); + // ЗапускаСм Ρ‚Π°ΠΉΠΌΠ΅Ρ€ + //furi_timer_start(timer, 1500); + + // Π’ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌ Π½ΠΎΡ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); + + // БСсконСчный Ρ†ΠΈΠΊΠ» ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ событий + while(1) { + // Π’Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ событиС ΠΈΠ· ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ Π² ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ event (ΠΆΠ΄Π΅ΠΌ бСсконСчно Π΄ΠΎΠ»Π³ΠΎ, Ссли ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ пуста) + // ΠΈ провСряСм, Ρ‡Ρ‚ΠΎ Ρƒ нас ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ это ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + + // НашС событиС β€” это Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ ΠΊΠ½ΠΎΠΏΠΊΠΈ + if(event.type == EventTypeInput) { + if(event.input.type == InputTypeShort) { // ΠšΠΎΡ€ΠΎΡ‚ΠΊΠΈΠ΅ наТатия + + if(event.input.key == InputKeyBack) { + if(furi_timer_is_running( + timer)) { // Если Ρ‚Π°ΠΉΠΌΠ΅Ρ€ Π·Π°ΠΏΡƒΡ‰Π΅Π½ - Π½Π΅Ρ„ΠΈΠ³ ΠΌΠ°Ρ†Π°Ρ‚ΡŒ ΠΊΠ½ΠΎΠΏΠΊΠΈ! + notification_message(notifications, &sequence_error); + } else { + WorkCount = Count; + WorkTime = 3; + if(Count == 0) { + InfiniteShot = true; + WorkCount = 1; + } else + InfiniteShot = false; + + notification_message(notifications, &sequence_success); + } + } + if(event.input.key == InputKeyRight) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Count++; + notification_message(notifications, &sequence_click); + } + } + if(event.input.key == InputKeyLeft) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Count--; + notification_message(notifications, &sequence_click); + } + } + if(event.input.key == InputKeyUp) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Time++; + notification_message(notifications, &sequence_click); + } + } + if(event.input.key == InputKeyDown) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Time--; + notification_message(notifications, &sequence_click); + } + } + if(event.input.key == InputKeyOk) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_click); + furi_timer_stop(timer); + } else { + furi_timer_start(timer, 1000); + + if(WorkCount == 0) WorkCount = Count; + + if(WorkTime == 0) WorkTime = 3; + + if(Count == 0) { + InfiniteShot = true; + WorkCount = 1; + } else + InfiniteShot = false; + + if(Count == -1) { + gpio_item_set_pin(4, true); + gpio_item_set_pin(5, true); + Bulb = true; + WorkCount = 1; + WorkTime = Time; + } else + Bulb = false; + + notification_message(notifications, &sequence_success); + } + } + } + if(event.input.type == InputTypeLong) { // Π”Π»ΠΈΠ½Π½Ρ‹Π΅ наТатия + // Если Π½Π°ΠΆΠ°Ρ‚Π° ΠΊΠ½ΠΎΠΏΠΊΠ° "Π½Π°Π·Π°Π΄", Ρ‚ΠΎ Π²Ρ‹Ρ…ΠΎΠ΄ΠΈΠΌ ΠΈΠ· Ρ†ΠΈΠΊΠ»Π°, Π° ΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ ΠΈ ΠΈΠ· прилоТСния + if(event.input.key == InputKeyBack) { + if(furi_timer_is_running(timer)) { // А Ссли Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Ρ‚Π°ΠΉΠΌΠ΅Ρ€ - Π½Π΅ Π²Ρ‹Ρ…ΠΎΠ΄ΠΈΠΌ :D + notification_message(notifications, &sequence_error); + } else { + notification_message(notifications, &sequence_click); + gpio_item_set_all_pins(false); + furi_timer_stop(timer); + notification_message( + notifications, &sequence_display_backlight_enforce_auto); + break; + } + } + if(event.input.key == InputKeyOk) { + // Нам ваша подсвСтка ΠΈ Π½Π°Ρ…ΠΎΠΉ Π½Π΅ Π½ΡƒΠΆΠ½Π°! Или Π½ΡƒΠΆΠ½Π°? + Backlight++; + if(Backlight > 2) Backlight = 0; + } + } + + if(event.input.type == InputTypeRepeat) { // Π—Π°ΠΆΠ°Ρ‚Ρ‹Π΅ ΠΊΠ½ΠΎΠΏΠΊΠΈ + if(event.input.key == InputKeyRight) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Count = Count + 10; + } + } + if(event.input.key == InputKeyLeft) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Count = Count - 10; + } + } + if(event.input.key == InputKeyUp) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Time = Time + 10; + } + } + if(event.input.key == InputKeyDown) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Time = Time - 10; + } + } + } + } + + // НашС событиС β€” это ΡΡ€Π°Π±ΠΎΡ‚Π°Π²ΡˆΠΈΠΉ Ρ‚Π°ΠΉΠΌΠ΅Ρ€ + else if(event.type == EventTypeTick) { + WorkTime--; + + if(WorkTime < 1) { // Ρ„ΠΎΡ‚ΠΊΠ°Π΅ΠΌ + notification_message(notifications, &sequence_blink_white_100); + if(Bulb) { + gpio_item_set_all_pins(false); + WorkCount = 0; + } else { + WorkCount--; + view_port_update(view_port); + notification_message(notifications, &sequence_click); + // Π”Ρ€Ρ‹Π³Π°Π΅ΠΌ Π½ΠΎΠ³Π°ΠΌΠΈ + //gpio_item_set_all_pins(true); + gpio_item_set_pin(4, true); + gpio_item_set_pin(5, true); + furi_delay_ms(400); // На ΠΊΠΎΡ€ΠΎΡ‚ΠΊΠΈΠ΅ наТатия Ρ„ΠΎΡ‚ΠΈΠΊ ΠΏΠ»ΠΎΡ…ΠΎ Ρ€Π΅Π°Π³ΠΈΡ€ΡƒΠ΅Ρ‚ + gpio_item_set_pin(4, false); + gpio_item_set_pin(5, false); + //gpio_item_set_all_pins(false); + + if(InfiniteShot) WorkCount++; + + WorkTime = Time; + view_port_update(view_port); + } + } else { + // ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ Π½ΠΎΡ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡŽ мигания синим свСтодиодом + notification_message(notifications, &sequence_blink_blue_100); + } + + if(WorkCount < 1) { // Π·Π°ΠΊΠΎΠ½Ρ‡ΠΈΠ»ΠΈ + gpio_item_set_all_pins(false); + furi_timer_stop(timer); + notification_message(notifications, &sequence_audiovisual_alert); + WorkTime = 3; + WorkCount = 0; + } + + switch(Backlight) { // Ρ‡ΠΎ ΠΏΠΎ подсвСткС? + case 1: + notification_message(notifications, &sequence_display_backlight_on); + break; + case 2: + notification_message(notifications, &sequence_display_backlight_off); + break; + default: + notification_message(notifications, &sequence_display_backlight_enforce_auto); + } + } + if(Time < 1) Time = 1; // НС Π΄Π°Ρ‘ΠΌ ΠΎΡ‚ΠΊΡ€ΡƒΡ‚ΠΈΡ‚ΡŒ Ρ‚Π°ΠΉΠΌΠ΅Ρ€ мСньшС Π΅Π΄ΠΈΠ½ΠΈΡ†Ρ‹ + if(Count < -1) + Count = 0; // А Ρ‚ΡƒΡ‚ Π΄Π°Ρ‘ΠΌ, Π±ΠΎ 0 ΠΊΠ°Π΄Ρ€ΠΎΠ² это бСсконСчная ΡΡŠΡ‘ΠΌΠΊΠ°, Π° -1 ΠΊΠ°Π΄Ρ€ΠΎΠ² - BULB + } + + // ΠžΡ‡ΠΈΡ‰Π°Π΅ΠΌ Ρ‚Π°ΠΉΠΌΠ΅Ρ€ + furi_timer_free(timer); + + // Π‘ΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ очистка памяти, Π·Π°Π½ΠΈΠΌΠ°Π΅ΠΌΠΎΠΉ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒΡŽ + furi_message_queue_free(event_queue); + + // Чистим созданныС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹, связанныС с интСрфСйсом + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_record_close(RECORD_GUI); + + // ΠžΡ‡ΠΈΡ‰Π°Π΅ΠΌ Π½ΠΎΡ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ + furi_record_close(RECORD_NOTIFICATION); + + return 0; +} diff --git a/applications/plugins/timelapse/zeitraffer.png b/applications/plugins/timelapse/zeitraffer.png new file mode 100644 index 000000000..3a42e406d Binary files /dev/null and b/applications/plugins/timelapse/zeitraffer.png differ diff --git a/applications/plugins/totp/application.fam b/applications/plugins/totp/application.fam index 08f54619c..fbd21ddf7 100644 --- a/applications/plugins/totp/application.fam +++ b/applications/plugins/totp/application.fam @@ -15,5 +15,23 @@ App( stack_size=2 * 1024, order=20, fap_category="Misc", - fap_icon="totp_10px.png" + fap_icon_assets="images", + fap_icon="totp_10px.png", + fap_private_libs=[ + Lib( + name="base32", + ), + Lib( + name="list", + ), + Lib( + name="timezone_utils", + ), + Lib( + name="polyfills", + ), + Lib( + name="roll_value", + ), + ], ) diff --git a/applications/plugins/totp/cli/cli.c b/applications/plugins/totp/cli/cli.c new file mode 100644 index 000000000..e61c67206 --- /dev/null +++ b/applications/plugins/totp/cli/cli.c @@ -0,0 +1,76 @@ +// Original idea: https://github.com/br0ziliy + +#include "cli.h" +#include +#include "cli_helpers.h" +#include "commands/list/list.h" +#include "commands/add/add.h" +#include "commands/delete/delete.h" +#include "commands/timezone/timezone.h" +#include "commands/help/help.h" +#include "commands/move/move.h" +#include "commands/pin/pin.h" +#include "commands/notification/notification.h" + +static void totp_cli_print_unknown_command(const FuriString* unknown_command) { + TOTP_CLI_PRINTF( + "Command \"%s\" is unknown. Use \"" TOTP_CLI_COMMAND_HELP + "\" command to get list of available commands.", + furi_string_get_cstr(unknown_command)); +} + +static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { + PluginState* plugin_state = (PluginState*)context; + + FuriString* cmd = furi_string_alloc(); + + args_read_string_and_trim(args, cmd); + + if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_HELP) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_HELP_ALT) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_HELP_ALT2) == 0 || furi_string_empty(cmd)) { + totp_cli_command_help_handle(); + } else if( + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_ADD) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_ADD_ALT) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_ADD_ALT2) == 0) { + totp_cli_command_add_handle(plugin_state, args, cli); + } else if( + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_LIST) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_LIST_ALT) == 0) { + totp_cli_command_list_handle(plugin_state, cli); + } else if( + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_DELETE) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_DELETE_ALT) == 0) { + totp_cli_command_delete_handle(plugin_state, args, cli); + } else if( + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_TIMEZONE) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_TIMEZONE_ALT) == 0) { + totp_cli_command_timezone_handle(plugin_state, args, cli); + } else if( + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_MOVE) == 0 || + furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_MOVE_ALT) == 0) { + totp_cli_command_move_handle(plugin_state, args, cli); + } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_PIN) == 0) { + totp_cli_command_pin_handle(plugin_state, args, cli); + } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_NOTIFICATION) == 0) { + totp_cli_command_notification_handle(plugin_state, args, cli); + } else { + totp_cli_print_unknown_command(cmd); + } + + furi_string_free(cmd); +} + +void totp_cli_register_command_handler(PluginState* plugin_state) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command( + cli, TOTP_CLI_COMMAND_NAME, CliCommandFlagParallelSafe, totp_cli_handler, plugin_state); + furi_record_close(RECORD_CLI); +} + +void totp_cli_unregister_command_handler() { + Cli* cli = furi_record_open(RECORD_CLI); + cli_delete_command(cli, TOTP_CLI_COMMAND_NAME); + furi_record_close(RECORD_CLI); +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/cli.h b/applications/plugins/totp/cli/cli.h new file mode 100644 index 000000000..3eb18172c --- /dev/null +++ b/applications/plugins/totp/cli/cli.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include "../types/plugin_state.h" + +void totp_cli_register_command_handler(PluginState* plugin_state); +void totp_cli_unregister_command_handler(); \ No newline at end of file diff --git a/applications/plugins/totp/cli/cli_helpers.c b/applications/plugins/totp/cli/cli_helpers.c new file mode 100644 index 000000000..0bea6fd90 --- /dev/null +++ b/applications/plugins/totp/cli/cli_helpers.c @@ -0,0 +1,21 @@ +#include "cli_helpers.h" +#include + +bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { + if(plugin_state->current_scene == TotpSceneAuthentication) { + TOTP_CLI_PRINTF("Pleases enter PIN on your flipper device\r\n"); + + while(plugin_state->current_scene == TotpSceneAuthentication && + !cli_cmd_interrupt_received(cli)) { + furi_delay_ms(100); + } + + TOTP_CLI_DELETE_LAST_LINE(); + + if(plugin_state->current_scene == TotpSceneAuthentication) { //-V547 + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/cli_helpers.h b/applications/plugins/totp/cli/cli_helpers.h new file mode 100644 index 000000000..075822cd6 --- /dev/null +++ b/applications/plugins/totp/cli/cli_helpers.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include "../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_NAME "totp" + +#define DOCOPT_ARGUMENT(arg) "<" arg ">" +#define DOCOPT_MULTIPLE(arg) arg "..." +#define DOCOPT_OPTIONAL(param) "[" param "]" +#define DOCOPT_REQUIRED(param) "(" param ")" +#define DOCOPT_OPTION(option, value) option " " value +#define DOCOPT_SWITCH(option) option +#define DOCOPT_OPTIONS "[options]" +#define DOCOPT_DEFAULT(val) "[default: " val "]" + +#define TOTP_CLI_PRINTF(format, ...) \ + do { \ + _Pragma(STRINGIFY(GCC diagnostic push)) \ + _Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \ + printf(format, ##__VA_ARGS__); \ + _Pragma(STRINGIFY(GCC diagnostic pop)) \ + } while(false) + +#define TOTP_CLI_DELETE_LAST_LINE() \ + TOTP_CLI_PRINTF("\033[A\33[2K\r"); \ + fflush(stdout) + +#define TOTP_CLI_DELETE_CURRENT_LINE() \ + TOTP_CLI_PRINTF("\33[2K\r"); \ + fflush(stdout) + +#define TOTP_CLI_DELETE_LAST_CHAR() \ + TOTP_CLI_PRINTF("\b \b"); \ + fflush(stdout) + +#define TOTP_CLI_PRINT_INVALID_ARGUMENTS() \ + TOTP_CLI_PRINTF( \ + "Invalid command arguments. use \"help\" command to get list of available commands") + +/** + * @brief Checks whether user is authenticated and entered correct PIN. + * If user is not authenticated it prompts user to enter correct PIN to authenticate. + * @param plugin_state application state + * @param cli reference to the firmware CLI subsystem + * @return \c true if user is already authenticated or successfully authenticated; \c false otherwise + */ +bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli); diff --git a/applications/plugins/totp/cli/commands/add/add.c b/applications/plugins/totp/cli/commands/add/add.c new file mode 100644 index 000000000..90cc0f420 --- /dev/null +++ b/applications/plugins/totp/cli/commands/add/add.c @@ -0,0 +1,216 @@ +#include "add.h" +#include +#include +#include "../../../lib/list/list.h" +#include "../../../types/token_info.h" +#include "../../../services/config/config.h" +#include "../../../services/convert/convert.h" +#include "../../cli_helpers.h" +#include "../../../ui/scene_director.h" + +#define TOTP_CLI_COMMAND_ADD_ARG_NAME "name" +#define TOTP_CLI_COMMAND_ADD_ARG_ALGO "algo" +#define TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX "-a" +#define TOTP_CLI_COMMAND_ADD_ARG_DIGITS "digits" +#define TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX "-d" +#define TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX "-u" + +static bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) { + if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) { + token_info->algo = SHA1; + return true; + } + + if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) { + token_info->algo = SHA256; + return true; + } + + if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) { + token_info->algo = SHA512; + return true; + } + + return false; +} + +void totp_cli_command_add_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT + ", " TOTP_CLI_COMMAND_ADD_ALT2 " Add new token\r\n"); +} + +void totp_cli_command_add_docopt_usage() { + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NAME + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_NAME) " " DOCOPT_OPTIONAL( + DOCOPT_OPTION( + TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_ADD_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX)) "\r\n"); +} + +void totp_cli_command_add_docopt_arguments() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD_ARG_NAME " Token name\r\n"); +} + +void totp_cli_command_add_docopt_options() { + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX, + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ADD_ARG_ALGO)) " Token hashing algorithm.\r\n"); + TOTP_CLI_PRINTF( + " Could be one of: sha1, sha256, sha512 " DOCOPT_DEFAULT("sha1") "\r\n"); + cli_nl(); + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_ADD_ARG_DIGITS)) " Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n"); + TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( + TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX) " Show console user input as-is without masking\r\n"); +} + +static void furi_string_secure_free(FuriString* str) { + for(long i = furi_string_size(str) - 1; i >= 0; i--) { + furi_string_set_char(str, i, '\0'); + } + + furi_string_free(str); +} + +static bool totp_cli_read_secret(Cli* cli, FuriString* out_str, bool mask_user_input) { + uint8_t c; + while(cli_read(cli, &c, 1) == 1) { + if(c == CliSymbolAsciiEsc) { + // Some keys generating escape-sequences + // We need to ignore them as we care about alpha-numerics only + uint8_t c2; + cli_read_timeout(cli, &c2, 1, 0); + cli_read_timeout(cli, &c2, 1, 0); + } else if(c == CliSymbolAsciiETX) { + TOTP_CLI_DELETE_CURRENT_LINE(); + TOTP_CLI_PRINTF("Cancelled by user\r\n"); + return false; + } else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + if(mask_user_input) { + putc('*', stdout); + } else { + putc(c, stdout); + } + fflush(stdout); + furi_string_push_back(out_str, c); + } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { + size_t out_str_size = furi_string_size(out_str); + if(out_str_size > 0) { + TOTP_CLI_DELETE_LAST_CHAR(); + furi_string_left(out_str, out_str_size - 1); + } + } else if(c == CliSymbolAsciiCR) { + cli_nl(); + break; + } + } + + TOTP_CLI_DELETE_LAST_LINE(); + return true; +} + +void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + FuriString* temp_str = furi_string_alloc(); + TokenInfo* token_info = token_info_alloc(); + + // Reading token name + if(!args_read_probably_quoted_string_and_trim(args, temp_str)) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + furi_string_free(temp_str); + token_info_free(token_info); + return; + } + + size_t temp_cstr_len = furi_string_size(temp_str); + token_info->name = malloc(temp_cstr_len + 1); + furi_check(token_info->name != NULL); + strlcpy(token_info->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); + + // Read optional arguments + bool mask_user_input = true; + while(args_read_string_and_trim(args, temp_str)) { + bool parsed = false; + if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX) == 0) { + if(!args_read_string_and_trim(args, temp_str)) { + TOTP_CLI_PRINTF("Missed value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX + "\"\r\n"); + } else if(!token_info_set_algo_from_str(token_info, temp_str)) { + TOTP_CLI_PRINTF( + "\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_ALGO_PREFIX + "\"\r\n", + furi_string_get_cstr(temp_str)); + } else { + parsed = true; + } + } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX) == 0) { + if(!args_read_string_and_trim(args, temp_str)) { + TOTP_CLI_PRINTF( + "Missed value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX + "\"\r\n"); + } else if(!token_info_set_digits_from_int( + token_info, CONVERT_CHAR_TO_DIGIT(furi_string_get_char(temp_str, 0)))) { + TOTP_CLI_PRINTF( + "\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ADD_ARG_DIGITS_PREFIX + "\"\r\n", + furi_string_get_cstr(temp_str)); + } else { + parsed = true; + } + } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_ADD_ARG_UNSECURE_PREFIX) == 0) { + mask_user_input = false; + parsed = true; + } else { + TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); + } + + if(!parsed) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + furi_string_free(temp_str); + token_info_free(token_info); + return; + } + } + + // Reading token secret + furi_string_reset(temp_str); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + if(!totp_cli_read_secret(cli, temp_str, mask_user_input) || + !totp_cli_ensure_authenticated(plugin_state, cli)) { + furi_string_secure_free(temp_str); + token_info_free(token_info); + return; + } + + if(!token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + plugin_state->iv)) { + TOTP_CLI_PRINTF("Token secret seems to be invalid and can not be parsed\r\n"); + furi_string_secure_free(temp_str); + token_info_free(token_info); + return; + } + + furi_string_secure_free(temp_str); + + bool load_generate_token_scene = false; + if(plugin_state->current_scene == TotpSceneGenerateToken) { + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + load_generate_token_scene = true; + } + + TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check); + plugin_state->tokens_count++; + totp_config_file_save_new_token(token_info); + + if(load_generate_token_scene) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + + TOTP_CLI_PRINTF("Token \"%s\" has been successfully added\r\n", token_info->name); +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/add/add.h b/applications/plugins/totp/cli/commands/add/add.h new file mode 100644 index 000000000..001dc9e80 --- /dev/null +++ b/applications/plugins/totp/cli/commands/add/add.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_ADD "add" +#define TOTP_CLI_COMMAND_ADD_ALT "mk" +#define TOTP_CLI_COMMAND_ADD_ALT2 "new" + +void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_add_docopt_commands(); +void totp_cli_command_add_docopt_usage(); +void totp_cli_command_add_docopt_arguments(); +void totp_cli_command_add_docopt_options(); \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/delete/delete.c b/applications/plugins/totp/cli/commands/delete/delete.c new file mode 100644 index 000000000..7eddb96bd --- /dev/null +++ b/applications/plugins/totp/cli/commands/delete/delete.c @@ -0,0 +1,107 @@ +#include "delete.h" + +#include +#include +#include +#include "../../../lib/list/list.h" +#include "../../../services/config/config.h" +#include "../../cli_helpers.h" +#include "../../../ui/scene_director.h" + +#define TOTP_CLI_COMMAND_DELETE_ARG_INDEX "index" +#define TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX "-f" + +void totp_cli_command_delete_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE ", " TOTP_CLI_COMMAND_DELETE_ALT + " Delete existing token\r\n"); +} + +void totp_cli_command_delete_docopt_usage() { + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NAME + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_DELETE " | " TOTP_CLI_COMMAND_DELETE_ALT) " " DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_DELETE_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX)) "\r\n"); +} + +void totp_cli_command_delete_docopt_arguments() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE_ARG_INDEX " Token index in the list\r\n"); +} + +void totp_cli_command_delete_docopt_options() { + TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( + TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX) " Force command to do not ask user for interactive confirmation\r\n"); +} + +void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + int token_number; + if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || + token_number > plugin_state->tokens_count) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + return; + } + + FuriString* temp_str = furi_string_alloc(); + bool confirm_needed = true; + if(args_read_string_and_trim(args, temp_str)) { + if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_DELETE_ARG_FORCE_SUFFIX) == 0) { + confirm_needed = false; + } else { + TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + furi_string_free(temp_str); + return; + } + } + furi_string_free(temp_str); + + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); + + TokenInfo* token_info = list_node->data; + + bool confirmed = !confirm_needed; + if(confirm_needed) { + TOTP_CLI_PRINTF("WARNING!\r\n"); + TOTP_CLI_PRINTF( + "TOKEN \"%s\" WILL BE PERMANENTLY DELETED WITHOUT ABILITY TO RECOVER IT.\r\n", + token_info->name); + TOTP_CLI_PRINTF("Confirm? [y/n]\r\n"); + fflush(stdout); + char user_pick; + do { + user_pick = tolower(cli_getc(cli)); + } while(user_pick != 'y' && user_pick != 'n' && user_pick != CliSymbolAsciiCR && + user_pick != CliSymbolAsciiETX && user_pick != CliSymbolAsciiEsc); + + confirmed = user_pick == 'y' || user_pick == CliSymbolAsciiCR; + } + + if(confirmed) { + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + bool activate_generate_token_scene = false; + if(plugin_state->current_scene != TotpSceneAuthentication) { + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + activate_generate_token_scene = true; + } + + plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node); + plugin_state->tokens_count--; + + totp_full_save_config_file(plugin_state); + + if(activate_generate_token_scene) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + + TOTP_CLI_PRINTF("Token \"%s\" has been successfully deleted\r\n", token_info->name); + token_info_free(token_info); + } else { + TOTP_CLI_PRINTF("User not confirmed\r\n"); + } +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/delete/delete.h b/applications/plugins/totp/cli/commands/delete/delete.h new file mode 100644 index 000000000..deb7f74fe --- /dev/null +++ b/applications/plugins/totp/cli/commands/delete/delete.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_DELETE "delete" +#define TOTP_CLI_COMMAND_DELETE_ALT "rm" + +void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_delete_docopt_commands(); +void totp_cli_command_delete_docopt_usage(); +void totp_cli_command_delete_docopt_arguments(); +void totp_cli_command_delete_docopt_options(); \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/help/help.c b/applications/plugins/totp/cli/commands/help/help.c new file mode 100644 index 000000000..419964880 --- /dev/null +++ b/applications/plugins/totp/cli/commands/help/help.c @@ -0,0 +1,53 @@ +#include "help.h" +#include "../../cli_helpers.h" +#include "../add/add.h" +#include "../delete/delete.h" +#include "../list/list.h" +#include "../timezone/timezone.h" +#include "../move/move.h" +#include "../pin/pin.h" +#include "../notification/notification.h" + +void totp_cli_command_help_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT + ", " TOTP_CLI_COMMAND_HELP_ALT2 " Show command usage help\r\n"); +} + +void totp_cli_command_help_docopt_usage() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " DOCOPT_REQUIRED( + TOTP_CLI_COMMAND_HELP " | " TOTP_CLI_COMMAND_HELP_ALT + " | " TOTP_CLI_COMMAND_HELP_ALT2) "\r\n"); +} + +void totp_cli_command_help_handle() { + TOTP_CLI_PRINTF("Usage:\r\n"); + totp_cli_command_help_docopt_usage(); + totp_cli_command_list_docopt_usage(); + totp_cli_command_add_docopt_usage(); + totp_cli_command_delete_docopt_usage(); + totp_cli_command_timezone_docopt_usage(); + totp_cli_command_move_docopt_usage(); + totp_cli_command_pin_docopt_usage(); + totp_cli_command_notification_docopt_usage(); + cli_nl(); + TOTP_CLI_PRINTF("Commands:\r\n"); + totp_cli_command_help_docopt_commands(); + totp_cli_command_list_docopt_commands(); + totp_cli_command_add_docopt_commands(); + totp_cli_command_delete_docopt_commands(); + totp_cli_command_timezone_docopt_commands(); + totp_cli_command_move_docopt_commands(); + totp_cli_command_pin_docopt_commands(); + totp_cli_command_notification_docopt_commands(); + cli_nl(); + TOTP_CLI_PRINTF("Arguments:\r\n"); + totp_cli_command_add_docopt_arguments(); + totp_cli_command_delete_docopt_arguments(); + totp_cli_command_timezone_docopt_arguments(); + totp_cli_command_notification_docopt_arguments(); + cli_nl(); + TOTP_CLI_PRINTF("Options:\r\n"); + totp_cli_command_add_docopt_options(); + totp_cli_command_delete_docopt_options(); + totp_cli_command_move_docopt_options(); +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/help/help.h b/applications/plugins/totp/cli/commands/help/help.h new file mode 100644 index 000000000..4268b8bc5 --- /dev/null +++ b/applications/plugins/totp/cli/commands/help/help.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#define TOTP_CLI_COMMAND_HELP "help" +#define TOTP_CLI_COMMAND_HELP_ALT "h" +#define TOTP_CLI_COMMAND_HELP_ALT2 "?" + +void totp_cli_command_help_handle(); +void totp_cli_command_help_docopt_commands(); +void totp_cli_command_help_docopt_usage(); \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/list/list.c b/applications/plugins/totp/cli/commands/list/list.c new file mode 100644 index 000000000..739a0de40 --- /dev/null +++ b/applications/plugins/totp/cli/commands/list/list.c @@ -0,0 +1,58 @@ +#include "list.h" +#include +#include "../../../lib/list/list.h" +#include "../../../types/token_info.h" +#include "../../../services/config/constants.h" +#include "../../cli_helpers.h" + +static char* get_algo_as_cstr(TokenHashAlgo algo) { + switch(algo) { + case SHA1: + return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME; + case SHA256: + return TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME; + case SHA512: + return TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME; + default: + break; + } + + return "UNKNOWN"; +} + +void totp_cli_command_list_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_LIST ", " TOTP_CLI_COMMAND_LIST_ALT + " List all available tokens\r\n"); +} + +void totp_cli_command_list_docopt_usage() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " DOCOPT_REQUIRED( + TOTP_CLI_COMMAND_LIST " | " TOTP_CLI_COMMAND_LIST_ALT) "\r\n"); +} + +void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + if(plugin_state->tokens_list == NULL) { + TOTP_CLI_PRINTF("There are no tokens"); + return; + } + + TOTP_CLI_PRINTF("+-----+-----------------------------+--------+--------+\r\n"); + TOTP_CLI_PRINTF("| %-*s | %-*s | %-*s | %-s |\r\n", 3, "#", 27, "Name", 6, "Algo", "Digits"); + TOTP_CLI_PRINTF("+-----+-----------------------------+--------+--------+\r\n"); + uint16_t index = 1; + TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { + TokenInfo* token_info = (TokenInfo*)node->data; + TOTP_CLI_PRINTF( + "| %-3" PRIu16 " | %-27.27s | %-6s | %-6" PRIu8 " |\r\n", + index, + token_info->name, + get_algo_as_cstr(token_info->algo), + token_info->digits); + index++; + }); + TOTP_CLI_PRINTF("+-----+-----------------------------+--------+--------+\r\n"); +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/list/list.h b/applications/plugins/totp/cli/commands/list/list.h new file mode 100644 index 000000000..7d6de5874 --- /dev/null +++ b/applications/plugins/totp/cli/commands/list/list.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_LIST "list" +#define TOTP_CLI_COMMAND_LIST_ALT "ls" + +void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli); +void totp_cli_command_list_docopt_commands(); +void totp_cli_command_list_docopt_usage(); diff --git a/applications/plugins/totp/cli/commands/move/move.c b/applications/plugins/totp/cli/commands/move/move.c new file mode 100644 index 000000000..95cb8dcac --- /dev/null +++ b/applications/plugins/totp/cli/commands/move/move.c @@ -0,0 +1,164 @@ +#include "move.h" + +#include +#include +#include "../../../lib/list/list.h" +#include "../../../types/token_info.h" +#include "../../../services/config/config.h" +#include "../../cli_helpers.h" +#include "../../../ui/scene_director.h" + +#define TOTP_CLI_COMMAND_MOVE_ARG_INDEX "index" + +#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME "name" +#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX "-n" + +#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX "index" +#define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX "-i" + +void totp_cli_command_move_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE ", " TOTP_CLI_COMMAND_MOVE_ALT + " Move\\rename token\r\n"); +} + +void totp_cli_command_move_docopt_usage() { + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NAME + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_MOVE " | " TOTP_CLI_COMMAND_MOVE_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_INDEX) " " DOCOPT_OPTIONAL( + DOCOPT_OPTION( + TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX))) "\r\n"); +} + +void totp_cli_command_move_docopt_options() { + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX, + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME)) " New token name.\r\n"); + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX, + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX)) " New token index.\r\n"); +} + +void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + int token_index; + if(!args_read_int_and_trim(args, &token_index)) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + return; + } + + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + if(token_index < 1 || token_index > plugin_state->tokens_count) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + return; + } + + FuriString* temp_str = furi_string_alloc(); + + char* new_token_name = NULL; + int new_token_index = 0; + + while(args_read_string_and_trim(args, temp_str)) { + bool parsed = false; + if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX) == 0) { + if(!args_read_string_and_trim(args, temp_str)) { + TOTP_CLI_PRINTF( + "Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_NAME_PREFIX + "\"\r\n"); + } else { + if(new_token_name != NULL) { + free(new_token_name); + } + + new_token_name = malloc(furi_string_size(temp_str) + 1); + if(new_token_name == NULL) { + furi_string_free(temp_str); + return; + } + + strlcpy( + new_token_name, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str) + 1); + parsed = true; + } + } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX) == 0) { + if(!args_read_int_and_trim(args, &new_token_index)) { + TOTP_CLI_PRINTF( + "Missed value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX + "\"\r\n"); + } else if(new_token_index < 1 || new_token_index > plugin_state->tokens_count) { + TOTP_CLI_PRINTF( + "\"%" PRId16 + "\" is incorrect value for argument \"" TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX_PREFIX + "\"\r\n", + new_token_index); + } else { + parsed = true; + } + } else { + TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(temp_str)); + } + + if(!parsed) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + furi_string_free(temp_str); + if(new_token_name != NULL) { + free(new_token_name); + } + return; + } + } + + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + furi_string_free(temp_str); + if(new_token_name != NULL) { + free(new_token_name); + } + return; + } + + bool activate_generate_token_scene = false; + if(plugin_state->current_scene != TotpSceneAuthentication) { + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + activate_generate_token_scene = true; + } + + bool token_updated = false; + TokenInfo* token_info = NULL; + if(new_token_index > 0) { + plugin_state->tokens_list = + list_remove_at(plugin_state->tokens_list, token_index - 1, (void**)&token_info); + furi_check(token_info != NULL); + plugin_state->tokens_list = + list_insert_at(plugin_state->tokens_list, new_token_index - 1, token_info); + token_updated = true; + } else { + token_info = list_element_at(plugin_state->tokens_list, token_index - 1)->data; + } + + if(new_token_name != NULL) { + free(token_info->name); + token_info->name = new_token_name; + token_updated = true; + } + + if(token_updated) { + totp_full_save_config_file(plugin_state); + } + + if(activate_generate_token_scene) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + + if(token_updated) { + TOTP_CLI_PRINTF("Token \"%s\" has been successfully updated\r\n", token_info->name); + } else { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + } + + furi_string_free(temp_str); +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/move/move.h b/applications/plugins/totp/cli/commands/move/move.h new file mode 100644 index 000000000..9eaad5319 --- /dev/null +++ b/applications/plugins/totp/cli/commands/move/move.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_MOVE "move" +#define TOTP_CLI_COMMAND_MOVE_ALT "mv" + +void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_move_docopt_commands(); +void totp_cli_command_move_docopt_usage(); +void totp_cli_command_move_docopt_options(); \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/notification/notification.c b/applications/plugins/totp/cli/commands/notification/notification.c new file mode 100644 index 000000000..91dd44d4f --- /dev/null +++ b/applications/plugins/totp/cli/commands/notification/notification.c @@ -0,0 +1,106 @@ +#include "notification.h" +#include +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" +#include "../../cli_helpers.h" + +#define TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD "method" +#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE "none" +#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "sound" +#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "vibro" + +void totp_cli_command_notification_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NOTIFICATION + " Get or set notification method\r\n"); +} + +void totp_cli_command_notification_docopt_usage() { + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_NOTIFICATION " " DOCOPT_OPTIONAL( + DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD))) "\r\n"); +} + +void totp_cli_command_notification_docopt_arguments() { + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD + " Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE + ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND + ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n"); +} + +static void totp_cli_command_notification_print_method(NotificationMethod method) { + bool has_previous_method = false; + if(method & NotificationMethodSound) { + TOTP_CLI_PRINTF("\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "\""); + has_previous_method = true; + } + if(method & NotificationMethodVibro) { + if(has_previous_method) { + TOTP_CLI_PRINTF(" and "); + } + + TOTP_CLI_PRINTF("\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\""); + } + if(method == NotificationMethodNone) { + TOTP_CLI_PRINTF("\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE "\""); + } +} + +void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + FuriString* temp_str = furi_string_alloc(); + bool new_method_provided = false; + NotificationMethod new_method = NotificationMethodNone; + bool args_valid = true; + while(args_read_string_and_trim(args, temp_str)) { + if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE) == 0) { + new_method_provided = true; + new_method = NotificationMethodNone; + } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND) == 0) { + new_method_provided = true; + new_method |= NotificationMethodSound; + } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO) == 0) { + new_method_provided = true; + new_method |= NotificationMethodVibro; + } else { + args_valid = false; + break; + } + } + + do { + if(!args_valid) { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + break; + } + + if(new_method_provided) { + Scene previous_scene = TotpSceneNone; + if(plugin_state->current_scene == TotpSceneGenerateToken || + plugin_state->current_scene == TotpSceneAppSettings) { + previous_scene = plugin_state->current_scene; + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + } + + plugin_state->notification_method = new_method; + totp_config_file_update_notification_method(new_method); + + if(previous_scene != TotpSceneNone) { + totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); + } + + TOTP_CLI_PRINTF("Notification method is set to "); + totp_cli_command_notification_print_method(new_method); + cli_nl(); + } else { + TOTP_CLI_PRINTF("Current notification method is "); + totp_cli_command_notification_print_method(plugin_state->notification_method); + cli_nl(); + } + } while(false); + + furi_string_free(temp_str); +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/notification/notification.h b/applications/plugins/totp/cli/commands/notification/notification.h new file mode 100644 index 000000000..c349f9620 --- /dev/null +++ b/applications/plugins/totp/cli/commands/notification/notification.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_NOTIFICATION "notify" + +void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_notification_docopt_commands(); +void totp_cli_command_notification_docopt_usage(); +void totp_cli_command_notification_docopt_arguments(); \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/pin/pin.c b/applications/plugins/totp/cli/commands/pin/pin.c new file mode 100644 index 000000000..045976eef --- /dev/null +++ b/applications/plugins/totp/cli/commands/pin/pin.c @@ -0,0 +1,172 @@ +#include "pin.h" + +#include +#include +#include "../../../types/token_info.h" +#include "../../../types/user_pin_codes.h" +#include "../../../services/config/config.h" +#include "../../cli_helpers.h" +#include "../../../lib/polyfills/memset_s.h" +#include "../../../services/crypto/crypto.h" +#include "../../../ui/scene_director.h" + +#define TOTP_CLI_COMMAND_PIN_COMMAND_SET "set" +#define TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE "remove" + +void totp_cli_command_pin_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_PIN " Set\\change\\remove PIN\r\n"); +} + +void totp_cli_command_pin_docopt_usage() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_PIN " " DOCOPT_REQUIRED( + TOTP_CLI_COMMAND_PIN_COMMAND_SET " | " TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) "\r\n"); +} + +static inline uint8_t totp_cli_key_to_pin_code(uint8_t key) { + uint8_t code = 0; + switch(key) { + case 0x44: // left + code = PinCodeArrowLeft; + break; + case 0x41: // up + code = PinCodeArrowUp; + break; + case 0x43: // right + code = PinCodeArrowRight; + break; + case 0x42: // down + code = PinCodeArrowDown; + break; + default: + break; + } + + return code; +} + +static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) { + TOTP_CLI_PRINTF("Enter new PIN (use arrow keys on your keyboard): "); + fflush(stdout); + uint8_t c; + *pin_length = 0; + while(cli_read(cli, &c, 1) == 1) { + if(c == CliSymbolAsciiEsc) { + uint8_t c2; + uint8_t c3; + if(cli_read_timeout(cli, &c2, 1, 0) == 1 && cli_read_timeout(cli, &c3, 1, 0) == 1 && + c2 == 0x5b) { + uint8_t code = totp_cli_key_to_pin_code(c3); + if(code > 0) { + pin[*pin_length] = code; + *pin_length = *pin_length + 1; + putc('*', stdout); + fflush(stdout); + } + } + } else if(c == CliSymbolAsciiETX) { + TOTP_CLI_DELETE_CURRENT_LINE(); + TOTP_CLI_PRINTF("Cancelled by user\r\n"); + return false; + } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { + if(*pin_length > 0) { + *pin_length = *pin_length - 1; + pin[*pin_length] = 0; + TOTP_CLI_DELETE_LAST_CHAR(); + } + } else if(c == CliSymbolAsciiCR) { + cli_nl(); + break; + } + } + + TOTP_CLI_DELETE_LAST_LINE(); + return true; +} + +void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + UNUSED(plugin_state); + FuriString* temp_str = furi_string_alloc(); + + bool do_change = false; + bool do_remove = false; + UNUSED(do_remove); + if(args_read_string_and_trim(args, temp_str)) { + if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_SET) == 0) { + do_change = true; + } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) == 0) { + do_remove = true; + } else { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + } + } else { + TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + } + + if((do_change || do_remove) && totp_cli_ensure_authenticated(plugin_state, cli)) { + bool load_generate_token_scene = false; + do { + uint8_t old_iv[TOTP_IV_SIZE]; + memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); + uint8_t new_pin[TOTP_IV_SIZE]; + uint8_t new_pin_length = 0; + if(do_change) { + if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length) || + !totp_cli_ensure_authenticated(plugin_state, cli)) { + memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + break; + } + } else if(do_remove) { + new_pin_length = 0; + memset(&new_pin[0], 0, TOTP_IV_SIZE); + } + + if(plugin_state->current_scene == TotpSceneGenerateToken) { + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + load_generate_token_scene = true; + } + + TOTP_CLI_PRINTF("Encrypting, please wait...\r\n"); + + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); + if(plugin_state->crypto_verify_data != NULL) { + free(plugin_state->crypto_verify_data); + plugin_state->crypto_verify_data = NULL; + } + + totp_crypto_seed_iv( + plugin_state, new_pin_length > 0 ? &new_pin[0] : NULL, new_pin_length); + + TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { + TokenInfo* token_info = node->data; + size_t plain_token_length; + uint8_t* plain_token = totp_crypto_decrypt( + token_info->token, token_info->token_length, &old_iv[0], &plain_token_length); + free(token_info->token); + token_info->token = totp_crypto_encrypt( + plain_token, + plain_token_length, + &plugin_state->iv[0], + &token_info->token_length); + memset_s(plain_token, plain_token_length, 0, plain_token_length); + free(plain_token); + }); + + totp_full_save_config_file(plugin_state); + + TOTP_CLI_DELETE_LAST_LINE(); + + if(do_change) { + TOTP_CLI_PRINTF("PIN has been successfully changed\r\n"); + } else if(do_remove) { + TOTP_CLI_PRINTF("PIN has been successfully removed\r\n"); + } + } while(false); + + if(load_generate_token_scene) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + } + + furi_string_free(temp_str); +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/pin/pin.h b/applications/plugins/totp/cli/commands/pin/pin.h new file mode 100644 index 000000000..1308ae736 --- /dev/null +++ b/applications/plugins/totp/cli/commands/pin/pin.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_PIN "pin" + +void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_pin_docopt_commands(); +void totp_cli_command_pin_docopt_usage(); \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/timezone/timezone.c b/applications/plugins/totp/cli/commands/timezone/timezone.c new file mode 100644 index 000000000..7a17c1ae2 --- /dev/null +++ b/applications/plugins/totp/cli/commands/timezone/timezone.c @@ -0,0 +1,52 @@ +#include "timezone.h" +#include +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" +#include "../../cli_helpers.h" + +#define TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE "timezone" + +void totp_cli_command_timezone_docopt_commands() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE ", " TOTP_CLI_COMMAND_TIMEZONE_ALT + " Get or set current timezone\r\n"); +} + +void totp_cli_command_timezone_docopt_usage() { + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NAME + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_TIMEZONE " | " TOTP_CLI_COMMAND_TIMEZONE_ALT) " " DOCOPT_OPTIONAL( + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE)) "\r\n"); +} + +void totp_cli_command_timezone_docopt_arguments() { + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE + " Timezone offset in hours to be set.\r\n"); +} + +void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { + return; + } + + FuriString* temp_str = furi_string_alloc(); + if(args_read_string_and_trim(args, temp_str)) { + float tz = strtof(furi_string_get_cstr(temp_str), NULL); + if(tz >= -12.75f && tz <= 12.75f) { + plugin_state->timezone_offset = tz; + totp_config_file_update_timezone_offset(tz); + TOTP_CLI_PRINTF("Timezone is set to %f\r\n", tz); + if(plugin_state->current_scene == TotpSceneGenerateToken) { + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } else if(plugin_state->current_scene == TotpSceneAppSettings) { + totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); + } + } else { + TOTP_CLI_PRINTF("Invalid timezone offset\r\n"); + } + } else { + TOTP_CLI_PRINTF("Current timezone offset is %f\r\n", plugin_state->timezone_offset); + } + furi_string_free(temp_str); +} \ No newline at end of file diff --git a/applications/plugins/totp/cli/commands/timezone/timezone.h b/applications/plugins/totp/cli/commands/timezone/timezone.h new file mode 100644 index 000000000..9823cf0ee --- /dev/null +++ b/applications/plugins/totp/cli/commands/timezone/timezone.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" + +#define TOTP_CLI_COMMAND_TIMEZONE "timezone" +#define TOTP_CLI_COMMAND_TIMEZONE_ALT "tz" + +void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +void totp_cli_command_timezone_docopt_commands(); +void totp_cli_command_timezone_docopt_usage(); +void totp_cli_command_timezone_docopt_arguments(); \ No newline at end of file diff --git a/applications/plugins/totp/images/DolphinCommon_56x48.png b/applications/plugins/totp/images/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/applications/plugins/totp/images/DolphinCommon_56x48.png differ diff --git a/applications/plugins/totp/images/totp_arrow_bottom_10x5.png b/applications/plugins/totp/images/totp_arrow_bottom_10x5.png new file mode 100644 index 000000000..54e22f5ef Binary files /dev/null and b/applications/plugins/totp/images/totp_arrow_bottom_10x5.png differ diff --git a/applications/plugins/totp/images/totp_arrow_left_8x9.png b/applications/plugins/totp/images/totp_arrow_left_8x9.png new file mode 100644 index 000000000..3bf9121c0 Binary files /dev/null and b/applications/plugins/totp/images/totp_arrow_left_8x9.png differ diff --git a/applications/plugins/totp/images/totp_arrow_right_8x9.png b/applications/plugins/totp/images/totp_arrow_right_8x9.png new file mode 100644 index 000000000..8c6a8bfeb Binary files /dev/null and b/applications/plugins/totp/images/totp_arrow_right_8x9.png differ diff --git a/applications/plugins/totp/lib/base32/base32.c b/applications/plugins/totp/lib/base32/base32.c new file mode 100644 index 000000000..9781c831f --- /dev/null +++ b/applications/plugins/totp/lib/base32/base32.c @@ -0,0 +1,60 @@ +// Base32 implementation +// +// Copyright 2010 Google Inc. +// Author: Markus Gutschke +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "base32.h" + +int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) { + int buffer = 0; + int bitsLeft = 0; + int count = 0; + for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) { + uint8_t ch = *ptr; + if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') { + continue; + } + buffer <<= 5; + + // Deal with commonly mistyped characters + if(ch == '0') { + ch = 'O'; + } else if(ch == '1') { + ch = 'L'; + } else if(ch == '8') { + ch = 'B'; + } + + // Look up one base32 digit + if((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { + ch = (ch & 0x1F) - 1; + } else if(ch >= '2' && ch <= '7') { + ch -= '2' - 26; + } else { + return -1; + } + + buffer |= ch; + bitsLeft += 5; + if(bitsLeft >= 8) { + result[count++] = buffer >> (bitsLeft - 8); + bitsLeft -= 8; + } + } + if(count < bufSize) { + result[count] = '\000'; + } + return count; +} diff --git a/applications/plugins/totp/services/base32/base32.h b/applications/plugins/totp/lib/base32/base32.h similarity index 80% rename from applications/plugins/totp/services/base32/base32.h rename to applications/plugins/totp/lib/base32/base32.h index db235195c..dea1a1c81 100644 --- a/applications/plugins/totp/services/base32/base32.h +++ b/applications/plugins/totp/lib/base32/base32.h @@ -29,7 +29,11 @@ #include -int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) - __attribute__((visibility("hidden"))); -int base32_encode(const uint8_t* data, int length, uint8_t* result, int bufSize) - __attribute__((visibility("hidden"))); +/** + * @brief Decodes Base-32 encoded bytes into plain bytes. + * @param encoded Base-32 encoded bytes + * @param[out] result result output buffer + * @param bufSize result output buffer size + * @return Decoded result length in bytes if successfully decoded; \c -1 otherwise + */ +int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize); diff --git a/applications/plugins/totp/lib/list/list.c b/applications/plugins/totp/lib/list/list.c new file mode 100644 index 000000000..f7abb6c8e --- /dev/null +++ b/applications/plugins/totp/lib/list/list.c @@ -0,0 +1,136 @@ +#include "list.h" + +ListNode* list_init_head(void* data) { + ListNode* new = malloc(sizeof(ListNode)); + if(new == NULL) return NULL; + new->data = data; + new->next = NULL; + return new; +} + +ListNode* list_add(ListNode* head, void* data) { + ListNode* new = malloc(sizeof(ListNode)); + if(new == NULL) return NULL; + new->data = data; + new->next = NULL; + + if(head == NULL) + head = new; + else { + ListNode* it; + + for(it = head; it->next != NULL; it = it->next) + ; + + it->next = new; + } + + return head; +} + +ListNode* list_find(ListNode* head, const void* data) { + ListNode* it = NULL; + + for(it = head; it != NULL; it = it->next) + if(it->data == data) break; + + return it; +} + +ListNode* list_element_at(ListNode* head, uint16_t index) { + ListNode* it; + uint16_t i; + for(it = head, i = 0; it != NULL && i < index; it = it->next, i++) + ; + return it; +} + +ListNode* list_remove(ListNode* head, ListNode* ep) { + if(head == NULL) { + return NULL; + } + + if(head == ep) { + ListNode* new_head = head->next; + free(head); + return new_head; + } + + ListNode* it; + + for(it = head; it->next != ep; it = it->next) + ; + + it->next = ep->next; + free(ep); + + return head; +} + +ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data) { + if(head == NULL) { + return NULL; + } + + ListNode* it; + ListNode* prev = NULL; + + uint16_t i; + + for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) + ; + + if(it == NULL) return head; + + ListNode* new_head = head; + if(prev == NULL) { + new_head = it->next; + } else { + prev->next = it->next; + } + + if(removed_node_data != NULL) { + *removed_node_data = it->data; + } + + free(it); + + return new_head; +} + +ListNode* list_insert_at(ListNode* head, uint16_t index, void* data) { + if(index == 0 || head == NULL) { + ListNode* new_head = list_init_head(data); + if(new_head != NULL) { + new_head->next = head; + } + return new_head; + } + + ListNode* it; + ListNode* prev = NULL; + + uint16_t i; + + for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) + ; + + ListNode* new = malloc(sizeof(ListNode)); + if(new == NULL) return NULL; + new->data = data; + new->next = it; + prev->next = new; + + return head; +} + +void list_free(ListNode* head) { + ListNode* it = head; + ListNode* tmp; + + while(it != NULL) { + tmp = it; + it = it->next; + free(tmp); + } +} diff --git a/applications/plugins/totp/lib/list/list.h b/applications/plugins/totp/lib/list/list.h new file mode 100644 index 000000000..c52d4c25a --- /dev/null +++ b/applications/plugins/totp/lib/list/list.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include + +/** + * @brief Single linked list node + */ +typedef struct ListNode { + /** + * @brief Pointer to the data assigned to the current list node + */ + void* data; + + /** + * @brief Pointer to the next list node + */ + struct ListNode* next; +} ListNode; + +/** + * @brief Initializes a new list node head + * @param data data to be assigned to the head list node + * @return Head list node + */ +ListNode* list_init_head(void* data); + +/** + * @brief Adds new list node to the end of the list + * @param head head list node + * @param data data to be assigned to the newly added list node + * @return Head list node + */ +ListNode* list_add( + ListNode* head, + void* data); /* adds element with specified data to the end of the list and returns new head node. */ + +/** + * @brief Searches list node with the given assigned \p data in the list + * @param head head list node + * @param data data to be searched + * @return List node containing \p data if there is such a node in the list; \c NULL otherwise + */ +ListNode* list_find(ListNode* head, const void* data); + +/** + * @brief Searches list node with the given \p index in the list + * @param head head list node + * @param index desired list node index + * @return List node with the given \p index in the list if there is such a list node; \c NULL otherwise + */ +ListNode* list_element_at(ListNode* head, uint16_t index); + +/** + * @brief Removes list node from the list + * @param head head list node + * @param ep list node to be removed + * @return Head list node + */ +ListNode* list_remove(ListNode* head, ListNode* ep); + +/** + * @brief Removes list node with the given \p index in the list from the list + * @param head head list node + * @param index index of the node to be removed + * @param[out] removed_node_data data which was assigned to the removed list node + * @return Head list node + */ +ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data); + +/** + * @brief Inserts new list node at the given index + * @param head head list node + * @param index index in the list where the new list node should be inserted + * @param data data to be assgned to the new list node + * @return Head list node + */ +ListNode* list_insert_at(ListNode* head, uint16_t index, void* data); + +/** + * @brief Disposes all the list nodes in the list + * @param head head list node + */ +void list_free(ListNode* head); + +#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \ + do { \ + if(head == NULL) { \ + head = list_init_head(item); \ + assert(head != NULL); \ + } else { \ + assert(list_add(head, item) != NULL); \ + } \ + } while(false) + +#define TOTP_LIST_FOREACH(head, node, action) \ + do { \ + ListNode* node = head; \ + while(node != NULL) { \ + action node = node->next; \ + } \ + } while(false) diff --git a/applications/plugins/totp/lib/polyfills/memset_s.c b/applications/plugins/totp/lib/polyfills/memset_s.c new file mode 100644 index 000000000..81c285c0d --- /dev/null +++ b/applications/plugins/totp/lib/polyfills/memset_s.c @@ -0,0 +1,22 @@ +#include "memset_s.h" + +#define RSIZE_MAX 0x7fffffffffffffffUL + +errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n) { + if(!s || smax > RSIZE_MAX) { + return EINVAL; + } + + errno_t violation_present = 0; + if(n > smax) { + n = smax; + violation_present = EINVAL; + } + + volatile unsigned char* v = s; + for(rsize_t i = 0u; i < n; ++i) { + *v++ = (unsigned char)c; + } + + return violation_present; +} \ No newline at end of file diff --git a/applications/plugins/totp/lib/polyfills/memset_s.h b/applications/plugins/totp/lib/polyfills/memset_s.h new file mode 100644 index 000000000..54628860d --- /dev/null +++ b/applications/plugins/totp/lib/polyfills/memset_s.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#ifndef _RSIZE_T_DECLARED +typedef uint64_t rsize_t; +#define _RSIZE_T_DECLARED +#endif +#ifndef _ERRNOT_DECLARED +typedef int16_t errno_t; //-V677 +#define _ERRNOT_DECLARED +#endif + +/** + * @brief Copies the value \p c into each of the first \p n characters of the object pointed to by \p s. + * @param s pointer to the object to fill + * @param smax size of the destination object + * @param c fill byte + * @param n number of bytes to fill + * @return \c 0 on success; non-zero otherwise + */ +errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n); \ No newline at end of file diff --git a/applications/plugins/totp/lib/polyfills/strnlen.c b/applications/plugins/totp/lib/polyfills/strnlen.c new file mode 100644 index 000000000..54d183895 --- /dev/null +++ b/applications/plugins/totp/lib/polyfills/strnlen.c @@ -0,0 +1,11 @@ +#include "strnlen.h" + +size_t strnlen(const char* s, size_t maxlen) { + size_t len; + + for(len = 0; len < maxlen; len++, s++) { + if(!*s) break; + } + + return len; +} \ No newline at end of file diff --git a/applications/plugins/totp/lib/polyfills/strnlen.h b/applications/plugins/totp/lib/polyfills/strnlen.h new file mode 100644 index 000000000..7dcef3a18 --- /dev/null +++ b/applications/plugins/totp/lib/polyfills/strnlen.h @@ -0,0 +1,3 @@ +#include + +size_t strnlen(const char* s, size_t maxlen); \ No newline at end of file diff --git a/applications/plugins/totp/lib/roll_value/roll_value.c b/applications/plugins/totp/lib/roll_value/roll_value.c new file mode 100644 index 000000000..b8f30e078 --- /dev/null +++ b/applications/plugins/totp/lib/roll_value/roll_value.c @@ -0,0 +1,28 @@ +#include "roll_value.h" + +#define TOTP_ROLL_VALUE_FN(type, step_type) \ + TOTP_ROLL_VALUE_FN_HEADER(type, step_type) { \ + type v = *value; \ + if(step > 0 && v > max - step) { \ + if(overflow_behavior == RollOverflowBehaviorRoll) { \ + v = min; \ + } else if(overflow_behavior == RollOverflowBehaviorStop) { \ + v = max; \ + } \ + } else if(step < 0 && v < min - step) { \ + if(overflow_behavior == RollOverflowBehaviorRoll) { \ + v = max; \ + } else if(overflow_behavior == RollOverflowBehaviorStop) { \ + v = min; \ + } \ + } else { \ + v += step; \ + } \ + *value = v; \ + } + +TOTP_ROLL_VALUE_FN(int8_t, int8_t) + +TOTP_ROLL_VALUE_FN(uint8_t, int8_t) + +TOTP_ROLL_VALUE_FN(uint16_t, int16_t); \ No newline at end of file diff --git a/applications/plugins/totp/lib/roll_value/roll_value.h b/applications/plugins/totp/lib/roll_value/roll_value.h new file mode 100644 index 000000000..3c270be9a --- /dev/null +++ b/applications/plugins/totp/lib/roll_value/roll_value.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +typedef uint8_t TotpRollValueOverflowBehavior; + +enum TotpRollValueOverflowBehaviors { + /** + * @brief Do not change value if it reached constraint + */ + RollOverflowBehaviorStop, + + /** + * @brief Set value to opposite constraint value if it reached constraint + */ + RollOverflowBehaviorRoll +}; + +#define TOTP_ROLL_VALUE_FN_HEADER(type, step_type) \ + void totp_roll_value_##type( \ + type* value, \ + step_type step, \ + type min, \ + type max, \ + TotpRollValueOverflowBehavior overflow_behavior) + +/** + * @brief Rolls \c int8_t \p value using \p min and \p max as an value constraints with \p step step. + * When value reaches constraint value \p overflow_behavior defines what to do next. + * @param[in,out] value value to roll + * @param step step to be used to change value + * @param min minimal possible value + * @param max maximum possible value + * @param overflow_behavior defines what to do when value reaches constraint value + */ +TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t); + +/** + * @brief Rolls \c uint8_t \p value using \p min and \p max as an value constraints with \p step step. + * When value reaches constraint value \p overflow_behavior defines what to do next. + * @param[in,out] value value to roll + * @param step step to be used to change value + * @param min minimal possible value + * @param max maximum possible value + * @param overflow_behavior defines what to do when value reaches constraint value + */ +TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); + +/** + * @brief Rolls \c uint16_t \p value using \p min and \p max as an value constraints with \p step step. + * When value reaches constraint value \p overflow_behavior defines what to do next. + * @param[in,out] value value to roll + * @param step step to be used to change value + * @param min minimal possible value + * @param max maximum possible value + * @param overflow_behavior defines what to do when value reaches constraint value + */ +TOTP_ROLL_VALUE_FN_HEADER(uint16_t, int16_t); \ No newline at end of file diff --git a/applications/plugins/totp/services/timezone_utils/timezone_utils.c b/applications/plugins/totp/lib/timezone_utils/timezone_utils.c similarity index 100% rename from applications/plugins/totp/services/timezone_utils/timezone_utils.c rename to applications/plugins/totp/lib/timezone_utils/timezone_utils.c diff --git a/applications/plugins/totp/lib/timezone_utils/timezone_utils.h b/applications/plugins/totp/lib/timezone_utils/timezone_utils.h new file mode 100644 index 000000000..5bb3b8ead --- /dev/null +++ b/applications/plugins/totp/lib/timezone_utils/timezone_utils.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +/** + * @brief Calculates timezone offset in seconds given timezone offset in hours. + * @param hours timezone offset in hours + * @return Timezone offset in seconds. + */ +int32_t timezone_offset_from_hours(float hours); + +/** + * @brief Applies timezone offset to a given time. + * @param time time to apply offset to. + * @param offset timezone offset in seconds. + * @return Time with timezone offset applied. + */ +uint64_t timezone_offset_apply(uint64_t time, int32_t offset); diff --git a/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.c b/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.c deleted file mode 100644 index ebe5d1313..000000000 --- a/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.c +++ /dev/null @@ -1,297 +0,0 @@ -#include "totp_scene_add_new_token.h" -#include "../../types/common.h" -#include "../../services/ui/constants.h" -#include "../scene_director.h" -#include "totp_input_text.h" -#include "../../types/token_info.h" -#include "../../services/list/list.h" -#include "../../services/base32/base32.h" -#include "../../services/config/config.h" -#include "../../services/ui/ui_controls.h" -#include "../generate_token/totp_scene_generate_token.h" - -#define TOKEN_ALGO_LIST_LENGTH 3 -char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512"}; -#define TOKEN_DIGITS_LIST_LENGTH 2 -char* TOKEN_DIGITS_LIST[] = {"6 digits", "8 digits"}; - -typedef enum { - TokenNameTextBox, - TokenSecretTextBox, - TokenAlgoSelect, - TokenLengthSelect, - ConfirmButton, -} Control; - -typedef struct { - char* token_name; - uint8_t token_name_length; - char* token_secret; - uint8_t token_secret_length; - bool saved; - Control selected_control; - InputTextSceneContext* token_name_input_context; - InputTextSceneContext* token_secret_input_context; - InputTextSceneState* input_state; - uint32_t input_started_at; - int current_token_index; - int32_t screen_y_offset; - TokenHashAlgo algo; - TokenDigitsCount digits_count; -} SceneState; - -void totp_scene_add_new_token_init(PluginState* plugin_state) { - UNUSED(plugin_state); -} - -static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) { - SceneState* scene_state = result->callback_data; - free(scene_state->token_name); - scene_state->token_name = result->user_input; - scene_state->token_name_length = result->user_input_length; - scene_state->input_started_at = 0; - free(result); -} - -static void on_token_secret_user_comitted(InputTextSceneCallbackResult* result) { - SceneState* scene_state = result->callback_data; - free(scene_state->token_secret); - scene_state->token_secret = result->user_input; - scene_state->token_secret_length = result->user_input_length; - scene_state->input_started_at = 0; - free(result); -} - -void totp_scene_add_new_token_activate( - PluginState* plugin_state, - const TokenAddEditSceneContext* context) { - SceneState* scene_state = malloc(sizeof(SceneState)); - plugin_state->current_scene_state = scene_state; - scene_state->token_name = "Name"; - scene_state->token_name_length = strlen(scene_state->token_name); - scene_state->token_secret = "Secret"; - scene_state->token_secret_length = strlen(scene_state->token_secret); - - scene_state->token_name_input_context = malloc(sizeof(InputTextSceneContext)); - scene_state->token_name_input_context->header_text = "Enter token name"; - scene_state->token_name_input_context->callback_data = scene_state; - scene_state->token_name_input_context->callback = on_token_name_user_comitted; - - scene_state->token_secret_input_context = malloc(sizeof(InputTextSceneContext)); - scene_state->token_secret_input_context->header_text = "Enter token secret"; - scene_state->token_secret_input_context->callback_data = scene_state; - scene_state->token_secret_input_context->callback = on_token_secret_user_comitted; - - scene_state->screen_y_offset = 0; - - scene_state->input_state = NULL; - - if(context == NULL) { - scene_state->current_token_index = -1; - } else { - scene_state->current_token_index = context->current_token_index; - } -} - -void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(scene_state->input_started_at > 0) { - totp_input_text_render(canvas, scene_state->input_state); - return; - } - - ui_control_text_box_render( - canvas, - 10 - scene_state->screen_y_offset, - scene_state->token_name, - scene_state->selected_control == TokenNameTextBox); - ui_control_text_box_render( - canvas, - 27 - scene_state->screen_y_offset, - scene_state->token_secret, - scene_state->selected_control == TokenSecretTextBox); - ui_control_select_render( - canvas, - 0, - 44 - scene_state->screen_y_offset, - SCREEN_WIDTH, - TOKEN_ALGO_LIST[scene_state->algo], - scene_state->selected_control == TokenAlgoSelect); - ui_control_select_render( - canvas, - 0, - 63 - scene_state->screen_y_offset, - SCREEN_WIDTH, - TOKEN_DIGITS_LIST[scene_state->digits_count], - scene_state->selected_control == TokenLengthSelect); - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 24, - 85 - scene_state->screen_y_offset, - 48, - 13, - "Confirm", - scene_state->selected_control == ConfirmButton); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 0, SCREEN_WIDTH, 10); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Add new token"); - canvas_set_font(canvas, FontSecondary); -} - -void update_screen_y_offset(SceneState* scene_state) { - if(scene_state->selected_control > TokenAlgoSelect) { - scene_state->screen_y_offset = 35; - } else { - scene_state->screen_y_offset = 0; - } -} - -bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state) { - if(event->type == EventTypeKey) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(scene_state->input_started_at > 0 && - furi_get_tick() - scene_state->input_started_at > 300) { - return totp_input_text_handle_event(event, scene_state->input_state); - } - - if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { - return false; - } else if(event->input.type == InputTypePress) { - switch(event->input.key) { - case InputKeyUp: - if(scene_state->selected_control > TokenNameTextBox) { - scene_state->selected_control--; - update_screen_y_offset(scene_state); - } - break; - case InputKeyDown: - if(scene_state->selected_control < ConfirmButton) { - scene_state->selected_control++; - update_screen_y_offset(scene_state); - } - break; - case InputKeyRight: - if(scene_state->selected_control == TokenAlgoSelect) { - if(scene_state->algo < SHA512) { - scene_state->algo++; - } else { - scene_state->algo = SHA1; - } - } else if(scene_state->selected_control == TokenLengthSelect) { - if(scene_state->digits_count < TOTP_8_DIGITS) { - scene_state->digits_count++; - } else { - scene_state->digits_count = TOTP_6_DIGITS; - } - } - break; - case InputKeyLeft: - if(scene_state->selected_control == TokenAlgoSelect) { - if(scene_state->algo > SHA1) { - scene_state->algo--; - } else { - scene_state->algo = SHA512; - } - } else if(scene_state->selected_control == TokenLengthSelect) { - if(scene_state->digits_count > TOTP_6_DIGITS) { - scene_state->digits_count--; - } else { - scene_state->digits_count = TOTP_8_DIGITS; - } - } - break; - case InputKeyOk: - switch(scene_state->selected_control) { - case TokenNameTextBox: - if(scene_state->input_state != NULL) { - totp_input_text_free(scene_state->input_state); - } - scene_state->input_state = - totp_input_text_activate(scene_state->token_name_input_context); - scene_state->input_started_at = furi_get_tick(); - break; - case TokenSecretTextBox: - if(scene_state->input_state != NULL) { - totp_input_text_free(scene_state->input_state); - } - scene_state->input_state = - totp_input_text_activate(scene_state->token_secret_input_context); - scene_state->input_started_at = furi_get_tick(); - break; - case TokenAlgoSelect: - break; - case TokenLengthSelect: - break; - case ConfirmButton: { - TokenInfo* tokenInfo = token_info_alloc(); - tokenInfo->name = malloc(scene_state->token_name_length + 1); - strcpy(tokenInfo->name, scene_state->token_name); - - token_info_set_secret( - tokenInfo, - scene_state->token_secret, - scene_state->token_secret_length, - &plugin_state->iv[0]); - - tokenInfo->algo = scene_state->algo; - tokenInfo->digits = scene_state->digits_count; - - if(plugin_state->tokens_list == NULL) { - plugin_state->tokens_list = list_init_head(tokenInfo); - } else { - list_add(plugin_state->tokens_list, tokenInfo); - } - plugin_state->tokens_count++; - - totp_config_file_save_new_token(tokenInfo); - - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = plugin_state->tokens_count - 1}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - break; - } - } - break; - case InputKeyBack: - if(scene_state->current_token_index >= 0) { - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } - break; - } - } - } - return true; -} - -void totp_scene_add_new_token_deactivate(PluginState* plugin_state) { - if(plugin_state->current_scene_state == NULL) return; - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - free(scene_state->token_name); - free(scene_state->token_secret); - - free(scene_state->token_name_input_context->header_text); - free(scene_state->token_name_input_context); - - free(scene_state->token_secret_input_context->header_text); - free(scene_state->token_secret_input_context); - - if(scene_state->input_state != NULL) { - totp_input_text_free(scene_state->input_state); - } - - free(plugin_state->current_scene_state); - plugin_state->current_scene_state = NULL; -} - -void totp_scene_add_new_token_free(PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.h b/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.h deleted file mode 100644 index b65c567a2..000000000 --- a/applications/plugins/totp/scenes/add_new_token/totp_scene_add_new_token.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include -#include -#include "../../types/plugin_state.h" -#include "../../types/plugin_event.h" - -typedef struct { - uint8_t current_token_index; -} TokenAddEditSceneContext; - -void totp_scene_add_new_token_init(PluginState* plugin_state); -void totp_scene_add_new_token_activate( - PluginState* plugin_state, - const TokenAddEditSceneContext* context); -void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state); -void totp_scene_add_new_token_deactivate(PluginState* plugin_state); -void totp_scene_add_new_token_free(PluginState* plugin_state); diff --git a/applications/plugins/totp/scenes/app_settings/totp_app_settings.c b/applications/plugins/totp/scenes/app_settings/totp_app_settings.c deleted file mode 100644 index cdb775a8d..000000000 --- a/applications/plugins/totp/scenes/app_settings/totp_app_settings.c +++ /dev/null @@ -1,176 +0,0 @@ -#include "totp_app_settings.h" -#include "../../services/ui/ui_controls.h" -#include "../scene_director.h" -#include "../token_menu/totp_scene_token_menu.h" -#include "../../services/ui/constants.h" -#include "../../services/config/config.h" - -#define DIGIT_TO_CHAR(digit) ((digit) + '0') - -typedef enum { HoursInput, MinutesInput, ConfirmButton } Control; - -typedef struct { - int8_t tz_offset_hours; - uint8_t tz_offset_minutes; - int16_t current_token_index; - Control selected_control; -} SceneState; - -void totp_scene_app_settings_init(PluginState* plugin_state) { - UNUSED(plugin_state); -} - -void totp_scene_app_settings_activate( - PluginState* plugin_state, - const AppSettingsSceneContext* context) { - SceneState* scene_state = malloc(sizeof(SceneState)); - plugin_state->current_scene_state = scene_state; - if(context != NULL) { - scene_state->current_token_index = context->current_token_index; - } else { - scene_state->current_token_index = -1; - } - - float off_int; - float off_dec = modff(plugin_state->timezone_offset, &off_int); - scene_state->tz_offset_hours = off_int; - scene_state->tz_offset_minutes = 60.0f * off_dec; -} - -static void two_digit_to_str(int8_t num, char* str) { - uint8_t index = 0; - if(num < 0) { - str[0] = '-'; - index++; - num = -num; - } - - uint8_t d1 = (num / 10) % 10; - uint8_t d2 = num % 10; - str[index] = DIGIT_TO_CHAR(d1); - str[index + 1] = DIGIT_TO_CHAR(d2); - str[index + 2] = '\0'; -} - -void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_state) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Timezone offset"); - canvas_set_font(canvas, FontSecondary); - - char tmp_str[4]; - two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); - canvas_draw_str_aligned(canvas, 0, 16, AlignLeft, AlignTop, "Hours:"); - ui_control_select_render( - canvas, - 36, - 10, - SCREEN_WIDTH - 36, - &tmp_str[0], - scene_state->selected_control == HoursInput); - - two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); - canvas_draw_str_aligned(canvas, 0, 34, AlignLeft, AlignTop, "Minutes:"); - ui_control_select_render( - canvas, - 36, - 28, - SCREEN_WIDTH - 36, - &tmp_str[0], - scene_state->selected_control == MinutesInput); - - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 24, - 50, - 48, - 13, - "Confirm", - scene_state->selected_control == ConfirmButton); -} - -bool totp_scene_app_settings_handle_event(PluginEvent* const event, PluginState* plugin_state) { - if(event->type == EventTypeKey) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(event->input.type == InputTypePress) { - switch(event->input.key) { - case InputKeyUp: - if(scene_state->selected_control > HoursInput) { - scene_state->selected_control--; - } - break; - case InputKeyDown: - if(scene_state->selected_control < ConfirmButton) { - scene_state->selected_control++; - } - break; - case InputKeyRight: - if(scene_state->selected_control == HoursInput) { - if(scene_state->tz_offset_hours < 12) { - scene_state->tz_offset_hours++; - } - } else if(scene_state->selected_control == MinutesInput) { - if(scene_state->tz_offset_minutes < 45) { - scene_state->tz_offset_minutes += 15; - } else { - scene_state->tz_offset_minutes = 0; - } - } - break; - case InputKeyLeft: - if(scene_state->selected_control == HoursInput) { - if(scene_state->tz_offset_hours > -12) { - scene_state->tz_offset_hours--; - } - } else if(scene_state->selected_control == MinutesInput) { - if(scene_state->tz_offset_minutes >= 15) { - scene_state->tz_offset_minutes -= 15; - } else { - scene_state->tz_offset_minutes = 45; - } - } - break; - case InputKeyOk: - if(scene_state->selected_control == ConfirmButton) { - plugin_state->timezone_offset = (float)scene_state->tz_offset_hours + - (float)scene_state->tz_offset_minutes / 60.0f; - totp_config_file_update_timezone_offset(plugin_state->timezone_offset); - - if(scene_state->current_token_index >= 0) { - TokenMenuSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneTokenMenu, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } - } - break; - case InputKeyBack: { - if(scene_state->current_token_index >= 0) { - TokenMenuSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneTokenMenu, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } - break; - } - } - } - } - return true; -} - -void totp_scene_app_settings_deactivate(PluginState* plugin_state) { - if(plugin_state->current_scene_state == NULL) return; - - free(plugin_state->current_scene_state); - plugin_state->current_scene_state = NULL; -} - -void totp_scene_app_settings_free(PluginState* plugin_state) { - UNUSED(plugin_state); -} \ No newline at end of file diff --git a/applications/plugins/totp/scenes/app_settings/totp_app_settings.h b/applications/plugins/totp/scenes/app_settings/totp_app_settings.h deleted file mode 100644 index b97de3390..000000000 --- a/applications/plugins/totp/scenes/app_settings/totp_app_settings.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include -#include -#include "../../types/plugin_state.h" -#include "../../types/plugin_event.h" - -typedef struct { - uint8_t current_token_index; -} AppSettingsSceneContext; - -void totp_scene_app_settings_init(PluginState* plugin_state); -void totp_scene_app_settings_activate( - PluginState* plugin_state, - const AppSettingsSceneContext* context); -void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_app_settings_handle_event(PluginEvent* const event, PluginState* plugin_state); -void totp_scene_app_settings_deactivate(PluginState* plugin_state); -void totp_scene_app_settings_free(PluginState* plugin_state); \ No newline at end of file diff --git a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c b/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c deleted file mode 100644 index 180d7b9a1..000000000 --- a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.c +++ /dev/null @@ -1,203 +0,0 @@ -#include "totp_scene_authenticate.h" -#include -#include "../../types/common.h" -#include "../../services/ui/icons.h" -#include "../../services/ui/constants.h" -#include "../../services/config/config.h" -#include "../scene_director.h" -#include "../totp_scenes_enum.h" - -#define MAX_CODE_LENGTH TOTP_IV_SIZE -#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" -#define CRYPTO_VERIFY_KEY_LENGTH 16 - -typedef struct { - uint8_t code_input[MAX_CODE_LENGTH]; - uint8_t code_length; -} SceneState; - -void totp_scene_authenticate_init(PluginState* plugin_state) { - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); -} - -void totp_scene_authenticate_activate(PluginState* plugin_state) { - SceneState* scene_state = malloc(sizeof(SceneState)); - scene_state->code_length = 0; - memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); - plugin_state->current_scene_state = scene_state; -} - -void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - int v_shift = 0; - if(scene_state->code_length > 0) { - v_shift = -10; - } - - if(plugin_state->crypto_verify_data == NULL) { - canvas_draw_str_aligned( - canvas, - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER - 10 + v_shift, - AlignCenter, - AlignCenter, - "Use arrow keys"); - canvas_draw_str_aligned( - canvas, - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER + 5 + v_shift, - AlignCenter, - AlignCenter, - "to setup new PIN"); - } else { - canvas_draw_str_aligned( - canvas, - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER + v_shift, - AlignCenter, - AlignCenter, - "Use arrow keys to enter PIN"); - } - const uint8_t PIN_ASTERISK_RADIUS = 3; - const uint8_t PIN_ASTERISK_STEP = (PIN_ASTERISK_RADIUS << 1) + 2; - if(scene_state->code_length > 0) { - uint8_t left_start_x = (scene_state->code_length - 1) * PIN_ASTERISK_STEP >> 1; - for(uint8_t i = 0; i < scene_state->code_length; i++) { - canvas_draw_disc( - canvas, - SCREEN_WIDTH_CENTER - left_start_x + i * PIN_ASTERISK_STEP, - SCREEN_HEIGHT_CENTER + 10, - PIN_ASTERISK_RADIUS); - } - } -} - -bool totp_scene_authenticate_handle_event(PluginEvent* const event, PluginState* plugin_state) { - if(event->type == EventTypeKey) { - if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { - return false; - } else if(event->input.type == InputTypePress) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - const uint8_t ARROW_UP_CODE = 2; - const uint8_t ARROW_RIGHT_CODE = 8; - const uint8_t ARROW_DOWN_CODE = 11; - const uint8_t ARROW_LEFT_CODE = 5; - - switch(event->input.key) { - case InputKeyUp: - if(scene_state->code_length < MAX_CODE_LENGTH) { - scene_state->code_input[scene_state->code_length] = ARROW_UP_CODE; - scene_state->code_length++; - } - break; - case InputKeyDown: - if(scene_state->code_length < MAX_CODE_LENGTH) { - scene_state->code_input[scene_state->code_length] = ARROW_DOWN_CODE; - scene_state->code_length++; - } - break; - case InputKeyRight: - if(scene_state->code_length < MAX_CODE_LENGTH) { - scene_state->code_input[scene_state->code_length] = ARROW_RIGHT_CODE; - scene_state->code_length++; - } - break; - case InputKeyLeft: - if(scene_state->code_length < MAX_CODE_LENGTH) { - scene_state->code_input[scene_state->code_length] = ARROW_LEFT_CODE; - scene_state->code_length++; - } - break; - case InputKeyOk: - if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating new IV"); - furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE); - } - - memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE); - for(uint8_t i = 0; i < scene_state->code_length; i++) { - plugin_state->iv[i] = plugin_state->iv[i] ^ - (uint8_t)(scene_state->code_input[i] * (i + 1)); - } - - if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data"); - plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); - plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; - Storage* storage = totp_open_storage(); - FlipperFormat* config_file = totp_open_config_file(storage); - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, &plugin_state->iv[0]); - furi_hal_crypto_encrypt( - (uint8_t*)CRYPTO_VERIFY_KEY, - plugin_state->crypto_verify_data, - CRYPTO_VERIFY_KEY_LENGTH); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); - flipper_format_insert_or_update_hex( - config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE); - flipper_format_insert_or_update_hex( - config_file, - TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - CRYPTO_VERIFY_KEY_LENGTH); - totp_close_config_file(config_file); - totp_close_storage(); - } - - uint8_t decrypted_key[CRYPTO_VERIFY_KEY_LENGTH]; - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, &plugin_state->iv[0]); - furi_hal_crypto_decrypt( - plugin_state->crypto_verify_data, &decrypted_key[0], CRYPTO_VERIFY_KEY_LENGTH); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); - - bool key_valid = true; - for(uint8_t i = 0; i < CRYPTO_VERIFY_KEY_LENGTH && key_valid; i++) { - if(decrypted_key[i] != CRYPTO_VERIFY_KEY[i]) key_valid = false; - } - - if(key_valid) { - FURI_LOG_D(LOGGING_TAG, "PIN is valid"); - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } else { - FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); - memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - scene_state->code_length = 0; - - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "Try again", NULL, NULL); - dialog_message_set_header( - message, - "You entered\ninvalid PIN", - SCREEN_WIDTH_CENTER - 25, - SCREEN_HEIGHT_CENTER - 5, - AlignCenter, - AlignCenter); - dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); - dialog_message_show(plugin_state->dialogs, message); - dialog_message_free(message); - } - break; - case InputKeyBack: - if(scene_state->code_length > 0) { - scene_state->code_input[scene_state->code_length - 1] = 0; - scene_state->code_length--; - } - break; - } - } - } - - return true; -} - -void totp_scene_authenticate_deactivate(PluginState* plugin_state) { - if(plugin_state->current_scene_state == NULL) return; - free(plugin_state->current_scene_state); - plugin_state->current_scene_state = NULL; -} - -void totp_scene_authenticate_free(PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.h b/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.h deleted file mode 100644 index f1199a425..000000000 --- a/applications/plugins/totp/scenes/authenticate/totp_scene_authenticate.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include -#include "../../types/plugin_state.h" -#include "../../types/plugin_event.h" - -void totp_scene_authenticate_init(PluginState* plugin_state); -void totp_scene_authenticate_activate(PluginState* plugin_state); -void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_authenticate_handle_event(PluginEvent* const event, PluginState* plugin_state); -void totp_scene_authenticate_deactivate(PluginState* plugin_state); -void totp_scene_authenticate_free(PluginState* plugin_state); diff --git a/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.c b/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.c deleted file mode 100644 index a2ee63d45..000000000 --- a/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.c +++ /dev/null @@ -1,299 +0,0 @@ -#include -#include -#include -#include "totp_scene_generate_token.h" -#include "../../types/token_info.h" -#include "../../types/common.h" -#include "../../services/ui/icons.h" -#include "../../services/ui/constants.h" -#include "../../services/totp/totp.h" -#include "../../services/config/config.h" -#include "../scene_director.h" -#include "../token_menu/totp_scene_token_menu.h" - -#define TOKEN_LIFETIME 30 -#define DIGIT_TO_CHAR(digit) ((digit) + '0') - -typedef struct { - uint8_t current_token_index; - char last_code[9]; - char* last_code_name; - bool need_token_update; - uint32_t last_token_gen_time; -} SceneState; - -static const NotificationSequence sequence_short_vibro_and_sound = { - &message_display_backlight_on, - &message_green_255, - &message_vibro_on, - &message_note_c5, - &message_delay_50, - &message_vibro_off, - &message_sound_off, - NULL, -}; - -static void i_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) { - if(len == TOTP_8_DIGITS) { - str[8] = '\0'; - } else if(len == TOTP_6_DIGITS) { - str[6] = '\0'; - } - - if(i_token_code == 0) { - if(len > TOTP_6_DIGITS) { - str[7] = '-'; - str[6] = '-'; - } - - str[5] = '-'; - str[4] = '-'; - str[3] = '-'; - str[2] = '-'; - str[1] = '-'; - str[0] = '-'; - } else { - if(len == TOTP_8_DIGITS) { - str[7] = DIGIT_TO_CHAR(i_token_code % 10); - str[6] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10); - str[5] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10); - } else if(len == TOTP_6_DIGITS) { - str[5] = DIGIT_TO_CHAR(i_token_code % 10); - } - - str[4] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10); - str[3] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10); - str[2] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10); - str[1] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10); - str[0] = DIGIT_TO_CHAR((i_token_code = i_token_code / 10) % 10); - } -} - -TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) { - switch(algo) { - case SHA1: - return TOTP_ALGO_SHA1; - case SHA256: - return TOTP_ALGO_SHA256; - case SHA512: - return TOTP_ALGO_SHA512; - } - - return NULL; -} - -void update_totp_params(PluginState* const plugin_state) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - if(scene_state->current_token_index < plugin_state->tokens_count) { - TokenInfo* tokenInfo = - (TokenInfo*)(list_element_at( - plugin_state->tokens_list, scene_state->current_token_index) - ->data); - - scene_state->need_token_update = true; - scene_state->last_code_name = tokenInfo->name; - } -} - -void totp_scene_generate_token_init(PluginState* plugin_state) { - UNUSED(plugin_state); -} - -void totp_scene_generate_token_activate( - PluginState* plugin_state, - const GenerateTokenSceneContext* context) { - if(!plugin_state->token_list_loaded) { - totp_config_file_load_tokens(plugin_state); - } - SceneState* scene_state = malloc(sizeof(SceneState)); - if(context == NULL) { - scene_state->current_token_index = 0; - } else { - scene_state->current_token_index = context->current_token_index; - } - scene_state->need_token_update = true; - plugin_state->current_scene_state = scene_state; - FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset); - update_totp_params(plugin_state); -} - -void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) { - if(plugin_state->tokens_count == 0) { - canvas_draw_str_aligned( - canvas, - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER - 10, - AlignCenter, - AlignCenter, - "Token list is empty"); - canvas_draw_str_aligned( - canvas, - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER + 10, - AlignCenter, - AlignCenter, - "Press OK button to add"); - return; - } - - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); - - bool is_new_token_time = curr_ts % TOKEN_LIFETIME == 0; - if(is_new_token_time && scene_state->last_token_gen_time != curr_ts) { - scene_state->need_token_update = true; - } - - if(scene_state->need_token_update) { - scene_state->need_token_update = false; - scene_state->last_token_gen_time = curr_ts; - - TokenInfo* tokenInfo = - (TokenInfo*)(list_element_at( - plugin_state->tokens_list, scene_state->current_token_index) - ->data); - - uint8_t* key = malloc(tokenInfo->token_length); - - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, &plugin_state->iv[0]); - furi_hal_crypto_decrypt(tokenInfo->token, key, tokenInfo->token_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); - - i_token_to_str( - totp_at( - get_totp_algo_impl(tokenInfo->algo), - token_info_get_digits_count(tokenInfo), - key, - tokenInfo->token_length, - curr_ts, - plugin_state->timezone_offset, - TOKEN_LIFETIME), - scene_state->last_code, - tokenInfo->digits); - memset(key, 0, tokenInfo->token_length); - free(key); - - if(is_new_token_time) { - notification_message(plugin_state->notification, &sequence_short_vibro_and_sound); - } - } - - canvas_set_font(canvas, FontPrimary); - uint16_t token_name_width = canvas_string_width(canvas, scene_state->last_code_name); - if(SCREEN_WIDTH - token_name_width > 18) { - canvas_draw_str_aligned( - canvas, - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER - 20, - AlignCenter, - AlignCenter, - scene_state->last_code_name); - } else { - canvas_draw_str_aligned( - canvas, - 9, - SCREEN_HEIGHT_CENTER - 20, - AlignLeft, - AlignCenter, - scene_state->last_code_name); - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, SCREEN_HEIGHT_CENTER - 24, 9, 9); - canvas_draw_box(canvas, SCREEN_WIDTH - 10, SCREEN_HEIGHT_CENTER - 24, 9, 9); - canvas_set_color(canvas, ColorBlack); - } - - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned( - canvas, - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter, - scene_state->last_code); - - const uint8_t BAR_MARGIN = 3; - const uint8_t BAR_HEIGHT = 4; - float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME; - uint8_t barWidth = (uint8_t)((float)(SCREEN_WIDTH - (BAR_MARGIN << 1)) * percentDone); - uint8_t barX = ((SCREEN_WIDTH - (BAR_MARGIN << 1) - barWidth) >> 1) + BAR_MARGIN; - - canvas_draw_box(canvas, barX, SCREEN_HEIGHT - BAR_MARGIN - BAR_HEIGHT, barWidth, BAR_HEIGHT); - - if(plugin_state->tokens_count > 1) { - canvas_draw_xbm( - canvas, - 0, - SCREEN_HEIGHT_CENTER - 24, - ICON_ARROW_LEFT_8x9_WIDTH, - ICON_ARROW_LEFT_8x9_HEIGHT, - &ICON_ARROW_LEFT_8x9[0]); - canvas_draw_xbm( - canvas, - SCREEN_WIDTH - 9, - SCREEN_HEIGHT_CENTER - 24, - ICON_ARROW_RIGHT_8x9_WIDTH, - ICON_ARROW_RIGHT_8x9_HEIGHT, - &ICON_ARROW_RIGHT_8x9[0]); - } -} - -bool totp_scene_generate_token_handle_event(PluginEvent* const event, PluginState* plugin_state) { - if(event->type == EventTypeKey) { - if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { - return false; - } else if(event->input.type == InputTypePress) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - switch(event->input.key) { - case InputKeyUp: - break; - case InputKeyDown: - break; - case InputKeyRight: - if(scene_state->current_token_index < plugin_state->tokens_count - 1) { - scene_state->current_token_index++; - } else { - scene_state->current_token_index = 0; - } - update_totp_params(plugin_state); - break; - case InputKeyLeft: - if(scene_state->current_token_index > 0) { - scene_state->current_token_index--; - } else { - scene_state->current_token_index = plugin_state->tokens_count - 1; - } - update_totp_params(plugin_state); - break; - case InputKeyOk: - if(plugin_state->tokens_count == 0) { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } else { - TokenMenuSceneContext ctx = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx); - } - break; - case InputKeyBack: - break; - } - } - } - - return true; -} - -void totp_scene_generate_token_deactivate(PluginState* plugin_state) { - if(plugin_state->current_scene_state == NULL) return; - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - free(scene_state->last_code); - free(scene_state); - plugin_state->current_scene_state = NULL; -} - -void totp_scene_generate_token_free(PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.h b/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.h deleted file mode 100644 index 1284c7b41..000000000 --- a/applications/plugins/totp/scenes/generate_token/totp_scene_generate_token.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include -#include -#include "../../types/plugin_state.h" -#include "../../types/plugin_event.h" - -typedef struct { - uint8_t current_token_index; -} GenerateTokenSceneContext; - -void totp_scene_generate_token_init(PluginState* plugin_state); -void totp_scene_generate_token_activate( - PluginState* plugin_state, - const GenerateTokenSceneContext* context); -void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_generate_token_handle_event(PluginEvent* const event, PluginState* plugin_state); -void totp_scene_generate_token_deactivate(PluginState* plugin_state); -void totp_scene_generate_token_free(PluginState* plugin_state); diff --git a/applications/plugins/totp/scenes/scene_director.h b/applications/plugins/totp/scenes/scene_director.h deleted file mode 100644 index 3c25afff6..000000000 --- a/applications/plugins/totp/scenes/scene_director.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include "../types/plugin_state.h" -#include "../types/plugin_event.h" -#include "totp_scenes_enum.h" - -void totp_scene_director_activate_scene( - PluginState* const plugin_state, - Scene scene, - const void* context); -void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state); -void totp_scene_director_init_scenes(PluginState* const plugin_state); -void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state); -void totp_scene_director_dispose(PluginState* const plugin_state); -bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state); diff --git a/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.c b/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.c deleted file mode 100644 index 8e2356f86..000000000 --- a/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.c +++ /dev/null @@ -1,202 +0,0 @@ -#include "totp_scene_token_menu.h" -#include -#include -#include "../../services/ui/ui_controls.h" -#include "../../services/ui/constants.h" -#include "../scene_director.h" -#include "../../services/config/config.h" -#include "../../services/list/list.h" -#include "../../types/token_info.h" -#include "../generate_token/totp_scene_generate_token.h" -#include "../add_new_token/totp_scene_add_new_token.h" -#include "../app_settings/totp_app_settings.h" - -#define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) -#define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1) - -typedef enum { AddNewToken, DeleteToken, AppSettings } Control; - -typedef struct { - Control selected_control; - int16_t current_token_index; -} SceneState; - -void totp_scene_token_menu_init(PluginState* plugin_state) { - UNUSED(plugin_state); -} - -void totp_scene_token_menu_activate( - PluginState* plugin_state, - const TokenMenuSceneContext* context) { - SceneState* scene_state = malloc(sizeof(SceneState)); - plugin_state->current_scene_state = scene_state; - if(context != NULL) { - scene_state->current_token_index = context->current_token_index; - } else { - scene_state->current_token_index = -1; - } -} - -void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(scene_state->current_token_index < 0) { - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 36, - 5, - 72, - 21, - "Add new token", - scene_state->selected_control == AddNewToken); - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 36, - 39, - 72, - 21, - "Settings", - scene_state->selected_control == AppSettings); - } else { - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 36, - SCREEN_HEIGHT_THIRD_CENTER - 8, - 72, - 16, - "Add new token", - scene_state->selected_control == AddNewToken); - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 36, - SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD_CENTER - 8, - 72, - 16, - "Delete token", - scene_state->selected_control == DeleteToken); - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 36, - SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD_CENTER - 8, - 72, - 16, - "Settings", - scene_state->selected_control == AppSettings); - } -} - -bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* plugin_state) { - if(event->type == EventTypeKey) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(event->input.type == InputTypePress) { - switch(event->input.key) { - case InputKeyUp: - if(scene_state->selected_control > AddNewToken) { - scene_state->selected_control--; - if(scene_state->selected_control == DeleteToken && - scene_state->current_token_index < 0) { - scene_state->selected_control--; - } - } else { - scene_state->selected_control = AppSettings; - } - break; - case InputKeyDown: - if(scene_state->selected_control < AppSettings) { - scene_state->selected_control++; - if(scene_state->selected_control == DeleteToken && - scene_state->current_token_index < 0) { - scene_state->selected_control++; - } - } else { - scene_state->selected_control = AddNewToken; - } - break; - case InputKeyRight: - break; - case InputKeyLeft: - break; - case InputKeyOk: - switch(scene_state->selected_control) { - case AddNewToken: { - TokenAddEditSceneContext add_new_token_scene_context = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context); - break; - } - case DeleteToken: { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "No", NULL, "Yes"); - dialog_message_set_header(message, "Confirmation", 0, 0, AlignLeft, AlignTop); - dialog_message_set_text( - message, - "Are you sure want to delete?", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - DialogMessageButton dialog_result = - dialog_message_show(plugin_state->dialogs, message); - dialog_message_free(message); - if(dialog_result == DialogMessageButtonRight) { - uint8_t i = 0; - - ListNode* list_node = plugin_state->tokens_list; - while(i < scene_state->current_token_index && list_node->next != NULL) { - list_node = list_node->next; - i++; - } - - TokenInfo* tokenInfo = list_node->data; - token_info_free(tokenInfo); - plugin_state->tokens_list = - list_remove(plugin_state->tokens_list, list_node); - plugin_state->tokens_count--; - - totp_full_save_config_file(plugin_state); - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, NULL); - } - break; - } - case AppSettings: { - if(scene_state->current_token_index >= 0) { - AppSettingsSceneContext app_settings_context = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAppSettings, &app_settings_context); - } else { - totp_scene_director_activate_scene( - plugin_state, TotpSceneAppSettings, NULL); - } - break; - } - } - break; - case InputKeyBack: { - if(scene_state->current_token_index >= 0) { - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } - break; - } - } - } - } - return true; -} - -void totp_scene_token_menu_deactivate(PluginState* plugin_state) { - if(plugin_state->current_scene_state == NULL) return; - - free(plugin_state->current_scene_state); - plugin_state->current_scene_state = NULL; -} - -void totp_scene_token_menu_free(PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.h b/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.h deleted file mode 100644 index 0b117cb25..000000000 --- a/applications/plugins/totp/scenes/token_menu/totp_scene_token_menu.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include -#include -#include "../../types/plugin_state.h" -#include "../../types/plugin_event.h" - -typedef struct { - uint8_t current_token_index; -} TokenMenuSceneContext; - -void totp_scene_token_menu_init(PluginState* plugin_state); -void totp_scene_token_menu_activate( - PluginState* plugin_state, - const TokenMenuSceneContext* context); -void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* plugin_state); -void totp_scene_token_menu_deactivate(PluginState* plugin_state); -void totp_scene_token_menu_free(PluginState* plugin_state); diff --git a/applications/plugins/totp/scenes/totp_scenes_enum.h b/applications/plugins/totp/scenes/totp_scenes_enum.h deleted file mode 100644 index 72bf4d76e..000000000 --- a/applications/plugins/totp/scenes/totp_scenes_enum.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -typedef enum { - TotpSceneAuthentication, - TotpSceneGenerateToken, - TotpSceneAddNewToken, - TotpSceneTokenMenu, - TotpSceneAppSettings -} Scene; diff --git a/applications/plugins/totp/services/base32/base32.c b/applications/plugins/totp/services/base32/base32.c deleted file mode 100644 index ce1a16720..000000000 --- a/applications/plugins/totp/services/base32/base32.c +++ /dev/null @@ -1,94 +0,0 @@ -// Base32 implementation -// -// Copyright 2010 Google Inc. -// Author: Markus Gutschke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "base32.h" - -int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) { - int buffer = 0; - int bitsLeft = 0; - int count = 0; - for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) { - uint8_t ch = *ptr; - if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') { - continue; - } - buffer <<= 5; - - // Deal with commonly mistyped characters - if(ch == '0') { - ch = 'O'; - } else if(ch == '1') { - ch = 'L'; - } else if(ch == '8') { - ch = 'B'; - } - - // Look up one base32 digit - if((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { - ch = (ch & 0x1F) - 1; - } else if(ch >= '2' && ch <= '7') { - ch -= '2' - 26; - } else { - return -1; - } - - buffer |= ch; - bitsLeft += 5; - if(bitsLeft >= 8) { - result[count++] = buffer >> (bitsLeft - 8); - bitsLeft -= 8; - } - } - if(count < bufSize) { - result[count] = '\000'; - } - return count; -} - -int base32_encode(const uint8_t* data, int length, uint8_t* result, int bufSize) { - if(length < 0 || length > (1 << 28)) { - return -1; - } - int count = 0; - if(length > 0) { - int buffer = data[0]; - int next = 1; - int bitsLeft = 8; - while(count < bufSize && (bitsLeft > 0 || next < length)) { - if(bitsLeft < 5) { - if(next < length) { - buffer <<= 8; - buffer |= data[next++] & 0xFF; - bitsLeft += 8; - } else { - int pad = 5 - bitsLeft; - buffer <<= pad; - bitsLeft += pad; - } - } - int index = 0x1F & (buffer >> (bitsLeft - 5)); - bitsLeft -= 5; - result[count++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[index]; - } - } - if(count < bufSize) { - result[count] = '\000'; - } - return count; -} diff --git a/applications/plugins/totp/services/config/config.c b/applications/plugins/totp/services/config/config.c index 4b9583250..ff7e4eb2d 100644 --- a/applications/plugins/totp/services/config/config.c +++ b/applications/plugins/totp/services/config/config.c @@ -10,29 +10,7 @@ #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" #define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup" -uint8_t token_info_get_digits_as_int(TokenInfo* token_info) { - switch(token_info->digits) { - case TOTP_6_DIGITS: - return 6; - case TOTP_8_DIGITS: - return 8; - } - - return 6; -} - -void token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) { - switch(digits) { - case 6: - token_info->digits = TOTP_6_DIGITS; - break; - case 8: - token_info->digits = TOTP_8_DIGITS; - break; - } -} - -char* token_info_get_algo_as_cstr(TokenInfo* token_info) { +static char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { switch(token_info->algo) { case SHA1: return TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME; @@ -40,17 +18,19 @@ char* token_info_get_algo_as_cstr(TokenInfo* token_info) { return TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME; case SHA512: return TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME; + default: + break; } return NULL; } -void token_info_set_algo_from_str(TokenInfo* token_info, FuriString* str) { +static void token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) { if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME) == 0) { token_info->algo = SHA1; - } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME)) { + } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME) == 0) { token_info->algo = SHA256; - } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME)) { + } else if(furi_string_cmpi_str(str, TOTP_CONFIG_TOKEN_ALGO_SHA512_NAME) == 0) { token_info->algo = SHA512; } } @@ -100,6 +80,15 @@ FlipperFormat* totp_open_config_file(Storage* storage) { fff_data_file, "Timezone offset in hours. Important note: do not put '+' sign for positive values"); flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1); + + uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; + flipper_format_write_comment_cstr(fff_data_file, " "); + flipper_format_write_comment_cstr( + fff_data_file, + "How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)"); + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); + FuriString* temp_str = furi_string_alloc(); flipper_format_write_comment_cstr(fff_data_file, " "); @@ -152,18 +141,25 @@ FlipperFormat* totp_open_config_file(Storage* storage) { return fff_data_file; } -void totp_config_file_save_new_token_i(FlipperFormat* file, TokenInfo* token_info) { +void totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) { flipper_format_seek_to_end(file); flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name); + bool token_is_valid = token_info->token != NULL && token_info->token_length > 0; + if(!token_is_valid) { + flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!"); + } flipper_format_write_hex( file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length); + if(!token_is_valid) { + flipper_format_write_comment_cstr(file, "!!! WARNING END !!!"); + } flipper_format_write_string_cstr( file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info)); - uint32_t digits_count_as_uint32 = token_info_get_digits_as_int(token_info); - flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &digits_count_as_uint32, 1); + uint32_t tmp_uint32 = token_info->digits; + flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1); } -void totp_config_file_save_new_token(TokenInfo* token_info) { +void totp_config_file_save_new_token(const TokenInfo* token_info) { Storage* cfg_storage = totp_open_storage(); FlipperFormat* file = totp_open_config_file(cfg_storage); @@ -183,7 +179,33 @@ void totp_config_file_update_timezone_offset(float new_timezone_offset) { totp_close_storage(); } -void totp_full_save_config_file(PluginState* const plugin_state) { +void totp_config_file_update_notification_method(NotificationMethod new_notification_method) { + Storage* cfg_storage = totp_open_storage(); + FlipperFormat* file = totp_open_config_file(cfg_storage); + + uint32_t tmp_uint32 = new_notification_method; + flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); + + totp_close_config_file(file); + totp_close_storage(); +} + +void totp_config_file_update_user_settings(const PluginState* plugin_state) { + Storage* cfg_storage = totp_open_storage(); + FlipperFormat* file = totp_open_config_file(cfg_storage); + + flipper_format_insert_or_update_float( + file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1); + uint32_t tmp_uint32 = plugin_state->notification_method; + flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); + + totp_close_config_file(file); + totp_close_storage(); +} + +void totp_full_save_config_file(const PluginState* const plugin_state) { Storage* storage = totp_open_storage(); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); @@ -199,12 +221,15 @@ void totp_full_save_config_file(PluginState* const plugin_state) { plugin_state->crypto_verify_data_length); flipper_format_write_float( fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1); - ListNode* node = plugin_state->tokens_list; - while(node != NULL) { - TokenInfo* token_info = node->data; + flipper_format_write_bool(fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1); + uint32_t tmp_uint32 = plugin_state->notification_method; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); + + TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { + const TokenInfo* token_info = node->data; totp_config_file_save_new_token_i(fff_data_file, token_info); - node = node->next; - } + }); totp_close_config_file(fff_data_file); totp_close_storage(); @@ -272,8 +297,10 @@ void totp_config_file_load_base(PluginState* const plugin_state) { flipper_format_rewind(fff_data_file); uint32_t crypto_size; - if(flipper_format_get_value_count(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size)) { + if(flipper_format_get_value_count(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) && + crypto_size > 0) { plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size); + furi_check(plugin_state->crypto_verify_data != NULL); plugin_state->crypto_verify_data_length = crypto_size; if(!flipper_format_read_hex( fff_data_file, @@ -283,7 +310,11 @@ void totp_config_file_load_base(PluginState* const plugin_state) { FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token"); free(plugin_state->crypto_verify_data); plugin_state->crypto_verify_data = NULL; + plugin_state->crypto_verify_data_length = 0; } + } else { + plugin_state->crypto_verify_data = NULL; + plugin_state->crypto_verify_data_length = 0; } flipper_format_rewind(fff_data_file); @@ -294,12 +325,29 @@ void totp_config_file_load_base(PluginState* const plugin_state) { FURI_LOG_D(LOGGING_TAG, "Missing timezone offset information, defaulting to 0"); } + flipper_format_rewind(fff_data_file); + + if(!flipper_format_read_bool( + fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { + plugin_state->pin_set = true; + } + + flipper_format_rewind(fff_data_file); + + uint32_t tmp_uint32; + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { + tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; + } + + plugin_state->notification_method = tmp_uint32; + furi_string_free(temp_str); totp_close_config_file(fff_data_file); totp_close_storage(); } -void totp_config_file_load_tokens(PluginState* const plugin_state) { +TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) { Storage* storage = totp_open_storage(); FlipperFormat* fff_data_file = totp_open_config_file(storage); @@ -309,10 +357,11 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) { if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header"); furi_string_free(temp_str); - return; + return TokenLoadingResultError; } - uint8_t index = 0; + TokenLoadingResult result = TokenLoadingResultSuccess; + uint16_t index = 0; bool has_any_plain_secret = false; while(true) { @@ -322,62 +371,74 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) { TokenInfo* tokenInfo = token_info_alloc(); - const char* temp_cstr = furi_string_get_cstr(temp_str); - tokenInfo->name = (char*)malloc(strlen(temp_cstr) + 1); - strcpy(tokenInfo->name, temp_cstr); + size_t temp_cstr_len = furi_string_size(temp_str); + tokenInfo->name = malloc(temp_cstr_len + 1); + furi_check(tokenInfo->name != NULL); + strlcpy(tokenInfo->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); uint32_t secret_bytes_count; if(!flipper_format_get_value_count( fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { - token_info_free(tokenInfo); - continue; + secret_bytes_count = 0; } if(secret_bytes_count == 1) { // Plain secret key - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { - token_info_free(tokenInfo); - continue; + if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { + if(token_info_set_secret( + tokenInfo, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + &plugin_state->iv[0])) { + FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name); + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has invalid secret", tokenInfo->name); + result = TokenLoadingResultWarning; + } + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + result = TokenLoadingResultWarning; } - temp_cstr = furi_string_get_cstr(temp_str); - token_info_set_secret(tokenInfo, temp_cstr, strlen(temp_cstr), &plugin_state->iv[0]); has_any_plain_secret = true; - FURI_LOG_W(LOGGING_TAG, "Found token with plain secret"); } else { // encrypted tokenInfo->token_length = secret_bytes_count; - tokenInfo->token = malloc(tokenInfo->token_length); - if(!flipper_format_read_hex( - fff_data_file, - TOTP_CONFIG_KEY_TOKEN_SECRET, - tokenInfo->token, - tokenInfo->token_length)) { - token_info_free(tokenInfo); - continue; + if(secret_bytes_count > 0) { + tokenInfo->token = malloc(tokenInfo->token_length); + furi_check(tokenInfo->token != NULL); + if(!flipper_format_read_hex( + fff_data_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + tokenInfo->token, + tokenInfo->token_length)) { + free(tokenInfo->token); + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + result = TokenLoadingResultWarning; + } + } else { + tokenInfo->token = NULL; + result = TokenLoadingResultWarning; } } - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str)) { - token_info_free(tokenInfo); - continue; + if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str)) { + token_info_set_algo_from_str(tokenInfo, temp_str); + } else { + tokenInfo->algo = SHA1; } - token_info_set_algo_from_str(tokenInfo, temp_str); - if(!flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1)) { - token_info_free(tokenInfo); - continue; + fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) || + !token_info_set_digits_from_int(tokenInfo, temp_data32)) { + tokenInfo->digits = TOTP_6_DIGITS; } - token_info_set_digits_from_int(tokenInfo, temp_data32); - FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name); - if(plugin_state->tokens_list == NULL) { - plugin_state->tokens_list = list_init_head(tokenInfo); - } else { - list_add(plugin_state->tokens_list, tokenInfo); - } + TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); index++; } @@ -385,7 +446,7 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) { plugin_state->tokens_count = index; plugin_state->token_list_loaded = true; - FURI_LOG_D(LOGGING_TAG, "Found %" PRIu8 " tokens", index); + FURI_LOG_D(LOGGING_TAG, "Found %" PRIu16 " tokens", index); furi_string_free(temp_str); totp_close_config_file(fff_data_file); @@ -394,6 +455,8 @@ void totp_config_file_load_tokens(PluginState* const plugin_state) { if(has_any_plain_secret) { totp_full_save_config_file(plugin_state); } + + return result; } void totp_close_config_file(FlipperFormat* file) { diff --git a/applications/plugins/totp/services/config/config.h b/applications/plugins/totp/services/config/config.h index 5e7a8c19d..1eabe3365 100644 --- a/applications/plugins/totp/services/config/config.h +++ b/applications/plugins/totp/services/config/config.h @@ -6,12 +6,91 @@ #include "../../types/token_info.h" #include "constants.h" +typedef uint8_t TokenLoadingResult; + +/** + * @brief Token loading results + */ +enum TokenLoadingResults { + /** + * @brief All the tokens loaded successfully + */ + TokenLoadingResultSuccess, + + /** + * @brief All the tokens loaded, but there are some warnings + */ + TokenLoadingResultWarning, + + /** + * @brief Tokens not loaded because of error(s) + */ + TokenLoadingResultError +}; + +/** + * @brief Opens storage record + * @return Storage record + */ Storage* totp_open_storage(); + +/** + * @brief Closes storage record + */ void totp_close_storage(); + +/** + * @brief Opens or creates TOTP application standard config file + * @param storage storage record to use + * @return Config file reference + */ FlipperFormat* totp_open_config_file(Storage* storage); + +/** + * @brief Closes config file + * @param file config file reference + */ void totp_close_config_file(FlipperFormat* file); -void totp_full_save_config_file(PluginState* const plugin_state); + +/** + * @brief Saves all the settings and tokens to an application config file + * @param plugin_state application state + */ +void totp_full_save_config_file(const PluginState* const plugin_state); + +/** + * @brief Loads basic information from an application config file into application state without loading all the tokens + * @param plugin_state application state + */ void totp_config_file_load_base(PluginState* const plugin_state); -void totp_config_file_load_tokens(PluginState* const plugin_state); -void totp_config_file_save_new_token(TokenInfo* token_info); + +/** + * @brief Loads tokens from an application config file into application state + * @param plugin_state application state + * @return Results of the loading + */ +TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state); + +/** + * @brief Add new token to the end of the application config file + * @param token_info token information to be saved + */ +void totp_config_file_save_new_token(const TokenInfo* token_info); + +/** + * @brief Updates timezone offset in an application config file + * @param new_timezone_offset new timezone offset to be set + */ void totp_config_file_update_timezone_offset(float new_timezone_offset); + +/** + * @brief Updates notification method in an application config file + * @param new_notification_method new notification method to be set + */ +void totp_config_file_update_notification_method(NotificationMethod new_notification_method); + +/** + * @brief Updates application user settings + * @param plugin_state application state + */ +void totp_config_file_update_user_settings(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/plugins/totp/services/config/constants.h b/applications/plugins/totp/services/config/constants.h index 931db4e02..696ea1593 100644 --- a/applications/plugins/totp/services/config/constants.h +++ b/applications/plugins/totp/services/config/constants.h @@ -10,6 +10,8 @@ #define TOTP_CONFIG_KEY_TOKEN_DIGITS "TokenDigits" #define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto" #define TOTP_CONFIG_KEY_BASE_IV "BaseIV" +#define TOTP_CONFIG_KEY_PINSET "PinIsSet" +#define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod" #define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1" #define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256" diff --git a/applications/plugins/totp/services/convert/convert.h b/applications/plugins/totp/services/convert/convert.h new file mode 100644 index 000000000..740d47ace --- /dev/null +++ b/applications/plugins/totp/services/convert/convert.h @@ -0,0 +1,4 @@ +#pragma once + +#define CONVERT_DIGIT_TO_CHAR(digit) ((digit) + '0') +#define CONVERT_CHAR_TO_DIGIT(ch) ((ch) - '0') diff --git a/applications/plugins/totp/services/crypto/crypto.c b/applications/plugins/totp/services/crypto/crypto.c new file mode 100644 index 000000000..794d0b0be --- /dev/null +++ b/applications/plugins/totp/services/crypto/crypto.c @@ -0,0 +1,140 @@ +#include "crypto.h" +#include +#include +#include "../config/config.h" +#include "../../types/common.h" +#include "memset_s.h" + +#define CRYPTO_KEY_SLOT 2 +#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" +#define CRYPTO_VERIFY_KEY_LENGTH 16 +#define CRYPTO_ALIGNMENT_FACTOR 16 + +uint8_t* totp_crypto_encrypt( + const uint8_t* plain_data, + const size_t plain_data_length, + const uint8_t* iv, + size_t* encrypted_data_length) { + uint8_t* encrypted_data; + size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR; + if(remain) { + size_t plain_data_aligned_length = plain_data_length - remain + CRYPTO_ALIGNMENT_FACTOR; + uint8_t* plain_data_aligned = malloc(plain_data_aligned_length); + furi_check(plain_data_aligned != NULL); + memset(plain_data_aligned, 0, plain_data_aligned_length); + memcpy(plain_data_aligned, plain_data, plain_data_length); + + encrypted_data = malloc(plain_data_aligned_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_aligned_length; + + furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length); + furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + + memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length); + free(plain_data_aligned); + } else { + encrypted_data = malloc(plain_data_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_length; + + furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length); + furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + } + + return encrypted_data; +} + +uint8_t* totp_crypto_decrypt( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const uint8_t* iv, + size_t* decrypted_data_length) { + *decrypted_data_length = encrypted_data_length; + uint8_t* decrypted_data = malloc(*decrypted_data_length); + furi_check(decrypted_data != NULL); + furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length); + furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + return decrypted_data; +} + +void totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { + if(plugin_state->crypto_verify_data == NULL) { + FURI_LOG_D(LOGGING_TAG, "Generating new IV"); + furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE); + } + + memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE); + if(pin != NULL && pin_length > 0) { + uint8_t max_i; + if(pin_length > TOTP_IV_SIZE) { + max_i = TOTP_IV_SIZE; + } else { + max_i = pin_length; + } + + for(uint8_t i = 0; i < max_i; i++) { + plugin_state->iv[i] = plugin_state->iv[i] ^ (uint8_t)(pin[i] * (i + 1)); + } + } else { + uint8_t max_i; + size_t uid_size = furi_hal_version_uid_size(); + if(uid_size > TOTP_IV_SIZE) { + max_i = TOTP_IV_SIZE; + } else { + max_i = uid_size; + } + + const uint8_t* uid = furi_hal_version_uid(); + for(uint8_t i = 0; i < max_i; i++) { + plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; + } + } + + if(plugin_state->crypto_verify_data == NULL) { + FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data"); + plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); + furi_check(plugin_state->crypto_verify_data != NULL); + plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; + Storage* storage = totp_open_storage(); + FlipperFormat* config_file = totp_open_config_file(storage); + + plugin_state->crypto_verify_data = totp_crypto_encrypt( + (uint8_t*)CRYPTO_VERIFY_KEY, + CRYPTO_VERIFY_KEY_LENGTH, + &plugin_state->iv[0], + &plugin_state->crypto_verify_data_length); + + flipper_format_insert_or_update_hex( + config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE); + flipper_format_insert_or_update_hex( + config_file, + TOTP_CONFIG_KEY_CRYPTO_VERIFY, + plugin_state->crypto_verify_data, + CRYPTO_VERIFY_KEY_LENGTH); + plugin_state->pin_set = pin != NULL && pin_length > 0; + flipper_format_insert_or_update_bool( + config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1); + totp_close_config_file(config_file); + totp_close_storage(); + } +} + +bool totp_crypto_verify_key(const PluginState* plugin_state) { + size_t decrypted_key_length; + const uint8_t* decrypted_key = totp_crypto_decrypt( + plugin_state->crypto_verify_data, + plugin_state->crypto_verify_data_length, + &plugin_state->iv[0], + &decrypted_key_length); + + bool key_valid = true; + for(uint8_t i = 0; i < CRYPTO_VERIFY_KEY_LENGTH && key_valid; i++) { + if(decrypted_key[i] != CRYPTO_VERIFY_KEY[i]) key_valid = false; + } + + return key_valid; +} \ No newline at end of file diff --git a/applications/plugins/totp/services/crypto/crypto.h b/applications/plugins/totp/services/crypto/crypto.h new file mode 100644 index 000000000..d39fe013b --- /dev/null +++ b/applications/plugins/totp/services/crypto/crypto.h @@ -0,0 +1,46 @@ +#pragma once + +#include "../../types/plugin_state.h" + +/** + * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) + * @param plain_data plain data to be encrypted + * @param plain_data_length plain data length + * @param iv initialization vector (IV) to be used to encrypt plain data + * @param[out] encrypted_data_length encrypted data length + * @return Encrypted data + */ +uint8_t* totp_crypto_encrypt( + const uint8_t* plain_data, + const size_t plain_data_length, + const uint8_t* iv, + size_t* encrypted_data_length); + +/** + * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) + * @param encrypted_data encrypted data to be decrypted + * @param encrypted_data_length encrypted data length + * @param iv initialization vector (IV) to be used to encrypt plain data + * @param[out] decrypted_data_length decrypted data length + * @return Decrypted data + */ +uint8_t* totp_crypto_decrypt( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const uint8_t* iv, + size_t* decrypted_data_length); + +/** + * @brief Seed initialization vector (IV) using user's PIN + * @param plugin_state application state + * @param pin user's PIN + * @param pin_length user's PIN length + */ +void totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); + +/** + * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption + * @param plugin_state application state + * @return \c true if cryptographic information is valid; \c false otherwise + */ +bool totp_crypto_verify_key(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/plugins/totp/services/hmac/byteswap.h b/applications/plugins/totp/services/hmac/byteswap.h index 411f3d5c4..2e3f1743f 100644 --- a/applications/plugins/totp/services/hmac/byteswap.h +++ b/applications/plugins/totp/services/hmac/byteswap.h @@ -2,5 +2,16 @@ #include +/** + * @brief Swap bytes in 32-bit value + * @param val value to swap bytes in + * @return Value with bytes swapped + */ uint32_t swap_uint32(uint32_t val); + +/** + * @brief Swap bytes in 64-bit value + * @param val value to swap bytes in + * @return Value with bytes swapped + */ uint64_t swap_uint64(uint64_t val); diff --git a/applications/plugins/totp/services/hmac/hmac_sha1.c b/applications/plugins/totp/services/hmac/hmac_sha1.c index a597ecb3a..0a78d569a 100644 --- a/applications/plugins/totp/services/hmac/hmac_sha1.c +++ b/applications/plugins/totp/services/hmac/hmac_sha1.c @@ -1,4 +1,4 @@ -/* hmac_sha1.c -- hashed message authentication codes +/* hmac-sha1.c -- hashed message authentication codes Copyright (C) 2018-2022 Free Software Foundation, Inc. This file is free software: you can redistribute it and/or modify diff --git a/applications/plugins/totp/services/hmac/hmac_sha256.c b/applications/plugins/totp/services/hmac/hmac_sha256.c index dbf027101..c51f24b4d 100644 --- a/applications/plugins/totp/services/hmac/hmac_sha256.c +++ b/applications/plugins/totp/services/hmac/hmac_sha256.c @@ -1,4 +1,4 @@ -/* hmac_sha256.c -- hashed message authentication codes +/* hmac-sha256.c -- hashed message authentication codes Copyright (C) 2018-2022 Free Software Foundation, Inc. This file is free software: you can redistribute it and/or modify diff --git a/applications/plugins/totp/services/hmac/hmac_sha512.c b/applications/plugins/totp/services/hmac/hmac_sha512.c index eafe80fd2..dc9342a91 100644 --- a/applications/plugins/totp/services/hmac/hmac_sha512.c +++ b/applications/plugins/totp/services/hmac/hmac_sha512.c @@ -1,4 +1,4 @@ -/* hmac_sha512.c -- hashed message authentication codes +/* hmac-sha512.c -- hashed message authentication codes Copyright (C) 2018-2022 Free Software Foundation, Inc. This file is free software: you can redistribute it and/or modify diff --git a/applications/plugins/totp/services/hmac/sha1.c b/applications/plugins/totp/services/hmac/sha1.c index 5679e489d..243a6dde8 100644 --- a/applications/plugins/totp/services/hmac/sha1.c +++ b/applications/plugins/totp/services/hmac/sha1.c @@ -22,9 +22,6 @@ */ /* Specification. */ -#if HAVE_OPENSSL_SHA1 -#define GL_OPENSSL_INLINE _GL_EXTERN_INLINE -#endif #include "sha1.h" #include @@ -37,8 +34,6 @@ #define SWAP(n) swap_uint32(n) #endif -#if !HAVE_OPENSSL_SHA1 - /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (RFC 1321, 3.1: Step 1) */ static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; @@ -146,7 +141,7 @@ void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx) { #define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(uint32_t) != 0) if(UNALIGNED_P(buffer)) while(len > 64) { - sha1_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); + sha1_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); //-V1086 buffer = (const char*)buffer + 64; len -= 64; } @@ -232,86 +227,31 @@ void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx) { words++; } - R(a, b, c, d, e, F1, K1, x[0]); - R(e, a, b, c, d, F1, K1, x[1]); - R(d, e, a, b, c, F1, K1, x[2]); - R(c, d, e, a, b, F1, K1, x[3]); - R(b, c, d, e, a, F1, K1, x[4]); - R(a, b, c, d, e, F1, K1, x[5]); - R(e, a, b, c, d, F1, K1, x[6]); - R(d, e, a, b, c, F1, K1, x[7]); - R(c, d, e, a, b, F1, K1, x[8]); - R(b, c, d, e, a, F1, K1, x[9]); - R(a, b, c, d, e, F1, K1, x[10]); - R(e, a, b, c, d, F1, K1, x[11]); - R(d, e, a, b, c, F1, K1, x[12]); - R(c, d, e, a, b, F1, K1, x[13]); - R(b, c, d, e, a, F1, K1, x[14]); - R(a, b, c, d, e, F1, K1, x[15]); - R(e, a, b, c, d, F1, K1, M(16)); - R(d, e, a, b, c, F1, K1, M(17)); - R(c, d, e, a, b, F1, K1, M(18)); - R(b, c, d, e, a, F1, K1, M(19)); - R(a, b, c, d, e, F2, K2, M(20)); - R(e, a, b, c, d, F2, K2, M(21)); - R(d, e, a, b, c, F2, K2, M(22)); - R(c, d, e, a, b, F2, K2, M(23)); - R(b, c, d, e, a, F2, K2, M(24)); - R(a, b, c, d, e, F2, K2, M(25)); - R(e, a, b, c, d, F2, K2, M(26)); - R(d, e, a, b, c, F2, K2, M(27)); - R(c, d, e, a, b, F2, K2, M(28)); - R(b, c, d, e, a, F2, K2, M(29)); - R(a, b, c, d, e, F2, K2, M(30)); - R(e, a, b, c, d, F2, K2, M(31)); - R(d, e, a, b, c, F2, K2, M(32)); - R(c, d, e, a, b, F2, K2, M(33)); - R(b, c, d, e, a, F2, K2, M(34)); - R(a, b, c, d, e, F2, K2, M(35)); - R(e, a, b, c, d, F2, K2, M(36)); - R(d, e, a, b, c, F2, K2, M(37)); - R(c, d, e, a, b, F2, K2, M(38)); - R(b, c, d, e, a, F2, K2, M(39)); - R(a, b, c, d, e, F3, K3, M(40)); - R(e, a, b, c, d, F3, K3, M(41)); - R(d, e, a, b, c, F3, K3, M(42)); - R(c, d, e, a, b, F3, K3, M(43)); - R(b, c, d, e, a, F3, K3, M(44)); - R(a, b, c, d, e, F3, K3, M(45)); - R(e, a, b, c, d, F3, K3, M(46)); - R(d, e, a, b, c, F3, K3, M(47)); - R(c, d, e, a, b, F3, K3, M(48)); - R(b, c, d, e, a, F3, K3, M(49)); - R(a, b, c, d, e, F3, K3, M(50)); - R(e, a, b, c, d, F3, K3, M(51)); - R(d, e, a, b, c, F3, K3, M(52)); - R(c, d, e, a, b, F3, K3, M(53)); - R(b, c, d, e, a, F3, K3, M(54)); - R(a, b, c, d, e, F3, K3, M(55)); - R(e, a, b, c, d, F3, K3, M(56)); - R(d, e, a, b, c, F3, K3, M(57)); - R(c, d, e, a, b, F3, K3, M(58)); - R(b, c, d, e, a, F3, K3, M(59)); - R(a, b, c, d, e, F4, K4, M(60)); - R(e, a, b, c, d, F4, K4, M(61)); - R(d, e, a, b, c, F4, K4, M(62)); - R(c, d, e, a, b, F4, K4, M(63)); - R(b, c, d, e, a, F4, K4, M(64)); - R(a, b, c, d, e, F4, K4, M(65)); - R(e, a, b, c, d, F4, K4, M(66)); - R(d, e, a, b, c, F4, K4, M(67)); - R(c, d, e, a, b, F4, K4, M(68)); - R(b, c, d, e, a, F4, K4, M(69)); - R(a, b, c, d, e, F4, K4, M(70)); - R(e, a, b, c, d, F4, K4, M(71)); - R(d, e, a, b, c, F4, K4, M(72)); - R(c, d, e, a, b, F4, K4, M(73)); - R(b, c, d, e, a, F4, K4, M(74)); - R(a, b, c, d, e, F4, K4, M(75)); - R(e, a, b, c, d, F4, K4, M(76)); - R(d, e, a, b, c, F4, K4, M(77)); - R(c, d, e, a, b, F4, K4, M(78)); - R(b, c, d, e, a, F4, K4, M(79)); + for(int i = 0; i < 80; i++) { + uint32_t xx = i < 16 ? x[i] : M(i); + uint32_t ki = i / 20; + switch(ki) { + case 0: + R(a, b, c, d, e, F1, K1, xx); + break; + case 1: + R(a, b, c, d, e, F2, K2, xx); + break; + case 2: + R(a, b, c, d, e, F3, K3, xx); + break; + default: + R(a, b, c, d, e, F4, K4, xx); + break; + } + + uint32_t tt = a; + a = e; + e = d; + d = c; + c = b; + b = tt; + } a = ctx->A += a; b = ctx->B += b; @@ -321,8 +261,6 @@ void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx) { } } -#endif - /* * Hey Emacs! * Local Variables: diff --git a/applications/plugins/totp/services/hmac/sha1.h b/applications/plugins/totp/services/hmac/sha1.h index d602d26fd..e9eb7712a 100644 --- a/applications/plugins/totp/services/hmac/sha1.h +++ b/applications/plugins/totp/services/hmac/sha1.h @@ -21,23 +21,12 @@ #include #include -#if HAVE_OPENSSL_SHA1 -#ifndef OPENSSL_API_COMPAT -#define OPENSSL_API_COMPAT 0x10101000L /* FIXME: Use OpenSSL 1.1+ API. */ -#endif -#include -#endif - #ifdef __cplusplus extern "C" { #endif #define SHA1_DIGEST_SIZE 20 -#if HAVE_OPENSSL_SHA1 -#define GL_OPENSSL_NAME 1 -#include "gl_openssl.h" -#else /* Structure to save state of computation between the single steps. */ struct sha1_ctx { uint32_t A; @@ -83,16 +72,6 @@ extern void* sha1_read_ctx(const struct sha1_ctx* ctx, void* restrict resbuf); digest. */ extern void* sha1_buffer(const char* buffer, size_t len, void* restrict resblock); -#endif - -/* Compute SHA1 message digest for bytes read from STREAM. - STREAM is an open file stream. Regular files are handled more efficiently. - The contents of STREAM from its current position to its end will be read. - The case that the last operation on STREAM was an 'ungetc' is not supported. - The resulting message digest number will be written into the 20 bytes - beginning at RESBLOCK. */ -extern int sha1_stream(FILE* stream, void* resblock); - #ifdef __cplusplus } #endif diff --git a/applications/plugins/totp/services/hmac/sha256.c b/applications/plugins/totp/services/hmac/sha256.c index 2ea63dd37..89ca67c2b 100644 --- a/applications/plugins/totp/services/hmac/sha256.c +++ b/applications/plugins/totp/services/hmac/sha256.c @@ -1,4 +1,4 @@ -/* sha256.c - Functions to compute SHA256 and SHA224 message digest of files or +/* sha256.c - Functions to compute SHA256 message digest of files or memory blocks according to the NIST specification FIPS-180-2. Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. @@ -21,9 +21,6 @@ */ /* Specification. */ -#if HAVE_OPENSSL_SHA256 -#define GL_OPENSSL_INLINE _GL_EXTERN_INLINE -#endif #include "sha256.h" #include @@ -36,8 +33,6 @@ #define SWAP(n) swap_uint32(n) #endif -#if !HAVE_OPENSSL_SHA256 - /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. */ static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; @@ -61,20 +56,6 @@ void sha256_init_ctx(struct sha256_ctx* ctx) { ctx->buflen = 0; } -void sha224_init_ctx(struct sha256_ctx* ctx) { - ctx->state[0] = 0xc1059ed8UL; - ctx->state[1] = 0x367cd507UL; - ctx->state[2] = 0x3070dd17UL; - ctx->state[3] = 0xf70e5939UL; - ctx->state[4] = 0xffc00b31UL; - ctx->state[5] = 0x68581511UL; - ctx->state[6] = 0x64f98fa7UL; - ctx->state[7] = 0xbefa4fa4UL; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - /* Copy the value from v into the memory location pointed to by *CP, If your architecture allows unaligned access, this is equivalent to * (__typeof__ (v) *) cp = v */ @@ -93,15 +74,6 @@ void* sha256_read_ctx(const struct sha256_ctx* ctx, void* resbuf) { return resbuf; } -void* sha224_read_ctx(const struct sha256_ctx* ctx, void* resbuf) { - int i; - char* r = resbuf; - - for(i = 0; i < 7; i++) set_uint32(r + i * sizeof ctx->state[0], SWAP(ctx->state[i])); - - return resbuf; -} - /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. */ static void sha256_conclude_ctx(struct sha256_ctx* ctx) { @@ -130,11 +102,6 @@ void* sha256_finish_ctx(struct sha256_ctx* ctx, void* resbuf) { return sha256_read_ctx(ctx, resbuf); } -void* sha224_finish_ctx(struct sha256_ctx* ctx, void* resbuf) { - sha256_conclude_ctx(ctx); - return sha224_read_ctx(ctx, resbuf); -} - /* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message @@ -152,19 +119,6 @@ void* sha256_buffer(const char* buffer, size_t len, void* resblock) { return sha256_finish_ctx(&ctx, resblock); } -void* sha224_buffer(const char* buffer, size_t len, void* resblock) { - struct sha256_ctx ctx; - - /* Initialize the computation context. */ - sha224_init_ctx(&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - sha256_process_bytes(buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha224_finish_ctx(&ctx, resblock); -} - void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ @@ -194,7 +148,7 @@ void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx #define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(uint32_t) != 0) if(UNALIGNED_P(buffer)) while(len > 64) { - sha256_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); + sha256_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); //-V1086 buffer = (const char*)buffer + 64; len -= 64; } @@ -299,70 +253,19 @@ void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx words++; } - R(a, b, c, d, e, f, g, h, K(0), x[0]); - R(h, a, b, c, d, e, f, g, K(1), x[1]); - R(g, h, a, b, c, d, e, f, K(2), x[2]); - R(f, g, h, a, b, c, d, e, K(3), x[3]); - R(e, f, g, h, a, b, c, d, K(4), x[4]); - R(d, e, f, g, h, a, b, c, K(5), x[5]); - R(c, d, e, f, g, h, a, b, K(6), x[6]); - R(b, c, d, e, f, g, h, a, K(7), x[7]); - R(a, b, c, d, e, f, g, h, K(8), x[8]); - R(h, a, b, c, d, e, f, g, K(9), x[9]); - R(g, h, a, b, c, d, e, f, K(10), x[10]); - R(f, g, h, a, b, c, d, e, K(11), x[11]); - R(e, f, g, h, a, b, c, d, K(12), x[12]); - R(d, e, f, g, h, a, b, c, K(13), x[13]); - R(c, d, e, f, g, h, a, b, K(14), x[14]); - R(b, c, d, e, f, g, h, a, K(15), x[15]); - R(a, b, c, d, e, f, g, h, K(16), M(16)); - R(h, a, b, c, d, e, f, g, K(17), M(17)); - R(g, h, a, b, c, d, e, f, K(18), M(18)); - R(f, g, h, a, b, c, d, e, K(19), M(19)); - R(e, f, g, h, a, b, c, d, K(20), M(20)); - R(d, e, f, g, h, a, b, c, K(21), M(21)); - R(c, d, e, f, g, h, a, b, K(22), M(22)); - R(b, c, d, e, f, g, h, a, K(23), M(23)); - R(a, b, c, d, e, f, g, h, K(24), M(24)); - R(h, a, b, c, d, e, f, g, K(25), M(25)); - R(g, h, a, b, c, d, e, f, K(26), M(26)); - R(f, g, h, a, b, c, d, e, K(27), M(27)); - R(e, f, g, h, a, b, c, d, K(28), M(28)); - R(d, e, f, g, h, a, b, c, K(29), M(29)); - R(c, d, e, f, g, h, a, b, K(30), M(30)); - R(b, c, d, e, f, g, h, a, K(31), M(31)); - R(a, b, c, d, e, f, g, h, K(32), M(32)); - R(h, a, b, c, d, e, f, g, K(33), M(33)); - R(g, h, a, b, c, d, e, f, K(34), M(34)); - R(f, g, h, a, b, c, d, e, K(35), M(35)); - R(e, f, g, h, a, b, c, d, K(36), M(36)); - R(d, e, f, g, h, a, b, c, K(37), M(37)); - R(c, d, e, f, g, h, a, b, K(38), M(38)); - R(b, c, d, e, f, g, h, a, K(39), M(39)); - R(a, b, c, d, e, f, g, h, K(40), M(40)); - R(h, a, b, c, d, e, f, g, K(41), M(41)); - R(g, h, a, b, c, d, e, f, K(42), M(42)); - R(f, g, h, a, b, c, d, e, K(43), M(43)); - R(e, f, g, h, a, b, c, d, K(44), M(44)); - R(d, e, f, g, h, a, b, c, K(45), M(45)); - R(c, d, e, f, g, h, a, b, K(46), M(46)); - R(b, c, d, e, f, g, h, a, K(47), M(47)); - R(a, b, c, d, e, f, g, h, K(48), M(48)); - R(h, a, b, c, d, e, f, g, K(49), M(49)); - R(g, h, a, b, c, d, e, f, K(50), M(50)); - R(f, g, h, a, b, c, d, e, K(51), M(51)); - R(e, f, g, h, a, b, c, d, K(52), M(52)); - R(d, e, f, g, h, a, b, c, K(53), M(53)); - R(c, d, e, f, g, h, a, b, K(54), M(54)); - R(b, c, d, e, f, g, h, a, K(55), M(55)); - R(a, b, c, d, e, f, g, h, K(56), M(56)); - R(h, a, b, c, d, e, f, g, K(57), M(57)); - R(g, h, a, b, c, d, e, f, K(58), M(58)); - R(f, g, h, a, b, c, d, e, K(59), M(59)); - R(e, f, g, h, a, b, c, d, K(60), M(60)); - R(d, e, f, g, h, a, b, c, K(61), M(61)); - R(c, d, e, f, g, h, a, b, K(62), M(62)); - R(b, c, d, e, f, g, h, a, K(63), M(63)); + for(int i = 0; i < 64; i++) { + uint32_t xx = i < 16 ? x[i] : M(i); + R(a, b, c, d, e, f, g, h, K(i), xx); + uint32_t tt = a; + a = h; + h = g; + g = f; + f = e; + e = d; + d = c; + c = b; + b = tt; + } a = ctx->state[0] += a; b = ctx->state[1] += b; @@ -375,8 +278,6 @@ void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx } } -#endif - /* * Hey Emacs! * Local Variables: diff --git a/applications/plugins/totp/services/hmac/sha256.h b/applications/plugins/totp/services/hmac/sha256.h index 2f99d1428..964f2eb97 100644 --- a/applications/plugins/totp/services/hmac/sha256.h +++ b/applications/plugins/totp/services/hmac/sha256.h @@ -1,4 +1,4 @@ -/* Declarations of functions and data types used for SHA256 and SHA224 sum +/* Declarations of functions and data types used for SHA256 sum library functions. Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. @@ -20,26 +20,12 @@ #include #include -#if HAVE_OPENSSL_SHA256 -#ifndef OPENSSL_API_COMPAT -#define OPENSSL_API_COMPAT 0x10101000L /* FIXME: Use OpenSSL 1.1+ API. */ -#endif -#include -#endif - #ifdef __cplusplus extern "C" { #endif -enum { SHA224_DIGEST_SIZE = 224 / 8 }; enum { SHA256_DIGEST_SIZE = 256 / 8 }; -#if HAVE_OPENSSL_SHA256 -#define GL_OPENSSL_NAME 224 -#include "gl_openssl.h" -#define GL_OPENSSL_NAME 256 -#include "gl_openssl.h" -#else /* Structure to save state of computation between the single steps. */ struct sha256_ctx { uint32_t state[8]; @@ -51,7 +37,6 @@ struct sha256_ctx { /* Initialize structure containing state of computation. */ extern void sha256_init_ctx(struct sha256_ctx* ctx); -extern void sha224_init_ctx(struct sha256_ctx* ctx); /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes @@ -70,31 +55,17 @@ extern void sha256_process_bytes(const void* buffer, size_t len, struct sha256_c endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ extern void* sha256_finish_ctx(struct sha256_ctx* ctx, void* restrict resbuf); -extern void* sha224_finish_ctx(struct sha256_ctx* ctx, void* restrict resbuf); /* Put result from CTX in first 32 (28) bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ extern void* sha256_read_ctx(const struct sha256_ctx* ctx, void* restrict resbuf); -extern void* sha224_read_ctx(const struct sha256_ctx* ctx, void* restrict resbuf); -/* Compute SHA256 (SHA224) message digest for LEN bytes beginning at BUFFER. +/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ extern void* sha256_buffer(const char* buffer, size_t len, void* restrict resblock); -extern void* sha224_buffer(const char* buffer, size_t len, void* restrict resblock); - -#endif - -/* Compute SHA256 (SHA224) message digest for bytes read from STREAM. - STREAM is an open file stream. Regular files are handled more efficiently. - The contents of STREAM from its current position to its end will be read. - The case that the last operation on STREAM was an 'ungetc' is not supported. - The resulting message digest number will be written into the 32 (28) bytes - beginning at RESBLOCK. */ -extern int sha256_stream(FILE* stream, void* resblock); -extern int sha224_stream(FILE* stream, void* resblock); #ifdef __cplusplus } diff --git a/applications/plugins/totp/services/hmac/sha512.c b/applications/plugins/totp/services/hmac/sha512.c index 53d3bad2e..f04c4d593 100644 --- a/applications/plugins/totp/services/hmac/sha512.c +++ b/applications/plugins/totp/services/hmac/sha512.c @@ -1,4 +1,4 @@ -/* sha512.c - Functions to compute SHA512 and SHA384 message digest of files or +/* sha512.c - Functions to compute SHA512 message digest of files or memory blocks according to the NIST specification FIPS-180-2. Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. @@ -21,9 +21,6 @@ */ /* Specification. */ -#if HAVE_OPENSSL_SHA512 -#define GL_OPENSSL_INLINE _GL_EXTERN_INLINE -#endif #include "sha512.h" #include @@ -36,8 +33,6 @@ #define SWAP(n) swap_uint64(n) #endif -#if !HAVE_OPENSSL_SHA512 - /* This array contains the bytes used to pad the buffer to the next 128-byte boundary. */ static const unsigned char fillbuf[128] = {0x80, 0 /* , 0, 0, ... */}; @@ -61,20 +56,6 @@ void sha512_init_ctx(struct sha512_ctx* ctx) { ctx->buflen = 0; } -void sha384_init_ctx(struct sha512_ctx* ctx) { - ctx->state[0] = u64hilo(0xcbbb9d5d, 0xc1059ed8); - ctx->state[1] = u64hilo(0x629a292a, 0x367cd507); - ctx->state[2] = u64hilo(0x9159015a, 0x3070dd17); - ctx->state[3] = u64hilo(0x152fecd8, 0xf70e5939); - ctx->state[4] = u64hilo(0x67332667, 0xffc00b31); - ctx->state[5] = u64hilo(0x8eb44a87, 0x68581511); - ctx->state[6] = u64hilo(0xdb0c2e0d, 0x64f98fa7); - ctx->state[7] = u64hilo(0x47b5481d, 0xbefa4fa4); - - ctx->total[0] = ctx->total[1] = u64lo(0); - ctx->buflen = 0; -} - /* Copy the value from V into the memory location pointed to by *CP, If your architecture allows unaligned access, this is equivalent to * (__typeof__ (v) *) cp = v */ @@ -93,15 +74,6 @@ void* sha512_read_ctx(const struct sha512_ctx* ctx, void* resbuf) { return resbuf; } -void* sha384_read_ctx(const struct sha512_ctx* ctx, void* resbuf) { - int i; - char* r = resbuf; - - for(i = 0; i < 6; i++) set_uint64(r + i * sizeof ctx->state[0], SWAP(ctx->state[i])); - - return resbuf; -} - /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. */ static void sha512_conclude_ctx(struct sha512_ctx* ctx) { @@ -132,11 +104,6 @@ void* sha512_finish_ctx(struct sha512_ctx* ctx, void* resbuf) { return sha512_read_ctx(ctx, resbuf); } -void* sha384_finish_ctx(struct sha512_ctx* ctx, void* resbuf) { - sha512_conclude_ctx(ctx); - return sha384_read_ctx(ctx, resbuf); -} - /* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message @@ -154,19 +121,6 @@ void* sha512_buffer(const char* buffer, size_t len, void* resblock) { return sha512_finish_ctx(&ctx, resblock); } -void* sha384_buffer(const char* buffer, size_t len, void* resblock) { - struct sha512_ctx ctx; - - /* Initialize the computation context. */ - sha384_init_ctx(&ctx); - - /* Process whole buffer but last len % 128 bytes. */ - sha512_process_bytes(buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha384_finish_ctx(&ctx, resblock); -} - void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ @@ -196,7 +150,7 @@ void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx #define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(u64) != 0) if(UNALIGNED_P(buffer)) while(len > 128) { - sha512_process_block(memcpy(ctx->buffer, buffer, 128), 128, ctx); + sha512_process_block(memcpy(ctx->buffer, buffer, 128), 128, ctx); //-V1086 buffer = (const char*)buffer + 128; len -= 128; } @@ -328,86 +282,19 @@ void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx words++; } - R(a, b, c, d, e, f, g, h, K(0), x[0]); - R(h, a, b, c, d, e, f, g, K(1), x[1]); - R(g, h, a, b, c, d, e, f, K(2), x[2]); - R(f, g, h, a, b, c, d, e, K(3), x[3]); - R(e, f, g, h, a, b, c, d, K(4), x[4]); - R(d, e, f, g, h, a, b, c, K(5), x[5]); - R(c, d, e, f, g, h, a, b, K(6), x[6]); - R(b, c, d, e, f, g, h, a, K(7), x[7]); - R(a, b, c, d, e, f, g, h, K(8), x[8]); - R(h, a, b, c, d, e, f, g, K(9), x[9]); - R(g, h, a, b, c, d, e, f, K(10), x[10]); - R(f, g, h, a, b, c, d, e, K(11), x[11]); - R(e, f, g, h, a, b, c, d, K(12), x[12]); - R(d, e, f, g, h, a, b, c, K(13), x[13]); - R(c, d, e, f, g, h, a, b, K(14), x[14]); - R(b, c, d, e, f, g, h, a, K(15), x[15]); - R(a, b, c, d, e, f, g, h, K(16), M(16)); - R(h, a, b, c, d, e, f, g, K(17), M(17)); - R(g, h, a, b, c, d, e, f, K(18), M(18)); - R(f, g, h, a, b, c, d, e, K(19), M(19)); - R(e, f, g, h, a, b, c, d, K(20), M(20)); - R(d, e, f, g, h, a, b, c, K(21), M(21)); - R(c, d, e, f, g, h, a, b, K(22), M(22)); - R(b, c, d, e, f, g, h, a, K(23), M(23)); - R(a, b, c, d, e, f, g, h, K(24), M(24)); - R(h, a, b, c, d, e, f, g, K(25), M(25)); - R(g, h, a, b, c, d, e, f, K(26), M(26)); - R(f, g, h, a, b, c, d, e, K(27), M(27)); - R(e, f, g, h, a, b, c, d, K(28), M(28)); - R(d, e, f, g, h, a, b, c, K(29), M(29)); - R(c, d, e, f, g, h, a, b, K(30), M(30)); - R(b, c, d, e, f, g, h, a, K(31), M(31)); - R(a, b, c, d, e, f, g, h, K(32), M(32)); - R(h, a, b, c, d, e, f, g, K(33), M(33)); - R(g, h, a, b, c, d, e, f, K(34), M(34)); - R(f, g, h, a, b, c, d, e, K(35), M(35)); - R(e, f, g, h, a, b, c, d, K(36), M(36)); - R(d, e, f, g, h, a, b, c, K(37), M(37)); - R(c, d, e, f, g, h, a, b, K(38), M(38)); - R(b, c, d, e, f, g, h, a, K(39), M(39)); - R(a, b, c, d, e, f, g, h, K(40), M(40)); - R(h, a, b, c, d, e, f, g, K(41), M(41)); - R(g, h, a, b, c, d, e, f, K(42), M(42)); - R(f, g, h, a, b, c, d, e, K(43), M(43)); - R(e, f, g, h, a, b, c, d, K(44), M(44)); - R(d, e, f, g, h, a, b, c, K(45), M(45)); - R(c, d, e, f, g, h, a, b, K(46), M(46)); - R(b, c, d, e, f, g, h, a, K(47), M(47)); - R(a, b, c, d, e, f, g, h, K(48), M(48)); - R(h, a, b, c, d, e, f, g, K(49), M(49)); - R(g, h, a, b, c, d, e, f, K(50), M(50)); - R(f, g, h, a, b, c, d, e, K(51), M(51)); - R(e, f, g, h, a, b, c, d, K(52), M(52)); - R(d, e, f, g, h, a, b, c, K(53), M(53)); - R(c, d, e, f, g, h, a, b, K(54), M(54)); - R(b, c, d, e, f, g, h, a, K(55), M(55)); - R(a, b, c, d, e, f, g, h, K(56), M(56)); - R(h, a, b, c, d, e, f, g, K(57), M(57)); - R(g, h, a, b, c, d, e, f, K(58), M(58)); - R(f, g, h, a, b, c, d, e, K(59), M(59)); - R(e, f, g, h, a, b, c, d, K(60), M(60)); - R(d, e, f, g, h, a, b, c, K(61), M(61)); - R(c, d, e, f, g, h, a, b, K(62), M(62)); - R(b, c, d, e, f, g, h, a, K(63), M(63)); - R(a, b, c, d, e, f, g, h, K(64), M(64)); - R(h, a, b, c, d, e, f, g, K(65), M(65)); - R(g, h, a, b, c, d, e, f, K(66), M(66)); - R(f, g, h, a, b, c, d, e, K(67), M(67)); - R(e, f, g, h, a, b, c, d, K(68), M(68)); - R(d, e, f, g, h, a, b, c, K(69), M(69)); - R(c, d, e, f, g, h, a, b, K(70), M(70)); - R(b, c, d, e, f, g, h, a, K(71), M(71)); - R(a, b, c, d, e, f, g, h, K(72), M(72)); - R(h, a, b, c, d, e, f, g, K(73), M(73)); - R(g, h, a, b, c, d, e, f, K(74), M(74)); - R(f, g, h, a, b, c, d, e, K(75), M(75)); - R(e, f, g, h, a, b, c, d, K(76), M(76)); - R(d, e, f, g, h, a, b, c, K(77), M(77)); - R(c, d, e, f, g, h, a, b, K(78), M(78)); - R(b, c, d, e, f, g, h, a, K(79), M(79)); + for(int i = 0; i < 80; i++) { + u64 xx = i < 16 ? x[i] : M(i); + R(a, b, c, d, e, f, g, h, K(i), xx); + u64 tt = a; + a = h; + h = g; + g = f; + f = e; + e = d; + d = c; + c = b; + b = tt; + } a = ctx->state[0] = u64plus(ctx->state[0], a); b = ctx->state[1] = u64plus(ctx->state[1], b); @@ -420,8 +307,6 @@ void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx } } -#endif - /* * Hey Emacs! * Local Variables: diff --git a/applications/plugins/totp/services/hmac/sha512.h b/applications/plugins/totp/services/hmac/sha512.h index 6489fc3cb..38590829e 100644 --- a/applications/plugins/totp/services/hmac/sha512.h +++ b/applications/plugins/totp/services/hmac/sha512.h @@ -20,26 +20,12 @@ #include #include "u64.h" -#if HAVE_OPENSSL_SHA512 -#ifndef OPENSSL_API_COMPAT -#define OPENSSL_API_COMPAT 0x10101000L /* FIXME: Use OpenSSL 1.1+ API. */ -#endif -#include -#endif - #ifdef __cplusplus extern "C" { #endif -enum { SHA384_DIGEST_SIZE = 384 / 8 }; enum { SHA512_DIGEST_SIZE = 512 / 8 }; -#if HAVE_OPENSSL_SHA512 -#define GL_OPENSSL_NAME 384 -#include "gl_openssl.h" -#define GL_OPENSSL_NAME 512 -#include "gl_openssl.h" -#else /* Structure to save state of computation between the single steps. */ struct sha512_ctx { u64 state[8]; @@ -51,7 +37,6 @@ struct sha512_ctx { /* Initialize structure containing state of computation. */ extern void sha512_init_ctx(struct sha512_ctx* ctx); -extern void sha384_init_ctx(struct sha512_ctx* ctx); /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes @@ -70,7 +55,6 @@ extern void sha512_process_bytes(const void* buffer, size_t len, struct sha512_c endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ extern void* sha512_finish_ctx(struct sha512_ctx* ctx, void* restrict resbuf); -extern void* sha384_finish_ctx(struct sha512_ctx* ctx, void* restrict resbuf); /* Put result from CTX in first 64 (48) bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields @@ -79,25 +63,12 @@ extern void* sha384_finish_ctx(struct sha512_ctx* ctx, void* restrict resbuf); IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ extern void* sha512_read_ctx(const struct sha512_ctx* ctx, void* restrict resbuf); -extern void* sha384_read_ctx(const struct sha512_ctx* ctx, void* restrict resbuf); -/* Compute SHA512 (SHA384) message digest for LEN bytes beginning at BUFFER. +/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ extern void* sha512_buffer(const char* buffer, size_t len, void* restrict resblock); -extern void* sha384_buffer(const char* buffer, size_t len, void* restrict resblock); - -#endif - -/* Compute SHA512 (SHA384) message digest for bytes read from STREAM. - STREAM is an open file stream. Regular files are handled more efficiently. - The contents of STREAM from its current position to its end will be read. - The case that the last operation on STREAM was an 'ungetc' is not supported. - The resulting message digest number will be written into the 64 (48) bytes - beginning at RESBLOCK. */ -extern int sha512_stream(FILE* stream, void* resblock); -extern int sha384_stream(FILE* stream, void* resblock); #ifdef __cplusplus } diff --git a/applications/plugins/totp/services/hmac/u64.c b/applications/plugins/totp/services/hmac/u64.c deleted file mode 100644 index de6257ba0..000000000 --- a/applications/plugins/totp/services/hmac/u64.c +++ /dev/null @@ -1,20 +0,0 @@ -/* uint64_t-like operations that work even on hosts lacking uint64_t - - Copyright (C) 2012-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#define _GL_U64_INLINE _GL_EXTERN_INLINE -#include "u64.h" -typedef int dummy; diff --git a/applications/plugins/totp/services/hmac/u64.h b/applications/plugins/totp/services/hmac/u64.h index af0bb067b..91e42e6c9 100644 --- a/applications/plugins/totp/services/hmac/u64.h +++ b/applications/plugins/totp/services/hmac/u64.h @@ -26,7 +26,7 @@ #endif /* Return X rotated left by N bits, where 0 < N < 64. */ -#define u64rol(x, n) u64or(u64shl(x, n), u64shr(x, 64 - n)) +#define u64rol(x, n) u64or(u64shl(x, n), u64shr(x, 64 - (n))) /* Native implementations are trivial. See below for comments on what these operations do. */ diff --git a/applications/plugins/totp/services/list/list.c b/applications/plugins/totp/services/list/list.c deleted file mode 100644 index 77df1105a..000000000 --- a/applications/plugins/totp/services/list/list.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "list.h" - -ListNode* list_init_head(void* data) { - ListNode* new = (ListNode*)malloc(sizeof(ListNode)); - new->data = data; - new->next = NULL; - return new; -} - -ListNode* list_add(ListNode* head, void* data) { - ListNode* new = (ListNode*)malloc(sizeof(ListNode)); - new->data = data; - new->next = NULL; - - if(head == NULL) - head = new; - else { - ListNode* it; - - for(it = head; it->next != NULL; it = it->next) - ; - - it->next = new; - } - - return head; -} - -ListNode* list_find(ListNode* head, void* data) { - ListNode* it; - - for(it = head; it != NULL; it = it->next) - if(it->data == data) break; - - return it; -} - -ListNode* list_element_at(ListNode* head, uint16_t index) { - ListNode* it; - uint16_t i; - for(it = head, i = 0; it != NULL && i < index; it = it->next, i++) - ; - return it; -} - -ListNode* list_remove(ListNode* head, ListNode* ep) { - if(head == ep) { - ListNode* new_head = head->next; - free(head); - return new_head; - } - - ListNode* it; - - for(it = head; it->next != ep; it = it->next) - ; - - it->next = ep->next; - free(ep); - - return head; -} - -void list_free(ListNode* head) { - ListNode *it = head, *tmp; - - while(it != NULL) { - tmp = it; - it = it->next; - free(tmp); - } -} diff --git a/applications/plugins/totp/services/list/list.h b/applications/plugins/totp/services/list/list.h deleted file mode 100644 index 771eac565..000000000 --- a/applications/plugins/totp/services/list/list.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -typedef struct ListNode { - void* data; - struct ListNode* next; -} ListNode; - -ListNode* list_init_head(void* data); -ListNode* list_add( - ListNode* head, - void* data); /* adds element with specified data to the end of the list and returns new head node. */ -ListNode* list_find( - ListNode* head, - void* data); /* returns pointer of element with specified data in list. */ -ListNode* list_element_at( - ListNode* head, - uint16_t index); /* returns pointer of element with specified index in list. */ -ListNode* list_remove( - ListNode* head, - ListNode* ep); /* removes element from the list and returns new head node. */ -void list_free(ListNode* head); /* deletes all elements of the list. */ diff --git a/applications/plugins/totp/services/timezone_utils/timezone_utils.h b/applications/plugins/totp/services/timezone_utils/timezone_utils.h deleted file mode 100644 index 0ae829d74..000000000 --- a/applications/plugins/totp/services/timezone_utils/timezone_utils.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -int32_t timezone_offset_from_hours(float hours); -uint64_t timezone_offset_apply(uint64_t time, int32_t offset); diff --git a/applications/plugins/totp/services/totp/totp.c b/applications/plugins/totp/services/totp/totp.c index 8c51cde38..1a20d58c1 100644 --- a/applications/plugins/totp/services/totp/totp.c +++ b/applications/plugins/totp/services/totp/totp.c @@ -8,70 +8,46 @@ #include "../hmac/hmac_sha1.h" #include "../hmac/hmac_sha256.h" #include "../hmac/hmac_sha512.h" -#include "../timezone_utils/timezone_utils.h" +#include "../hmac/byteswap.h" +#include "../../lib/timezone_utils/timezone_utils.h" -#define UINT64_GET_BYTE(integer, index) ((integer >> (8 * index)) & 0xFF) +#define HMAC_MAX_SIZE 64 -/* - Generates the timeblock for a time in seconds. - - Timeblocks are the amount of intervals in a given time. For example, - if 1,000,000 seconds has passed for 30 second intervals, you would get - 33,333 timeblocks (intervals), where timeblock++ is effectively +30 seconds. - - for_time is a time in seconds to get the current timeblocks - - Returns - timeblock given for_time, using data->interval - error, 0 -*/ +/** + * @brief Generates the timeblock for a time in seconds. + * Timeblocks are the amount of intervals in a given time. For example, + * if 1,000,000 seconds has passed for 30 second intervals, you would get + * 33,333 timeblocks (intervals), where timeblock++ is effectively +30 seconds. + * @param interval in seconds + * @param for_time a time in seconds to get the current timeblocks + * @return Timeblock given \p for_time using \p interval + */ uint64_t totp_timecode(uint8_t interval, uint64_t for_time) { return for_time / interval; } -/* - Converts an integer into an 8 byte array. - - out_bytes is the null-terminated output string already allocated -*/ -void otp_num_to_bytes(uint64_t integer, uint8_t* out_bytes) { - out_bytes[7] = UINT64_GET_BYTE(integer, 0); - out_bytes[6] = UINT64_GET_BYTE(integer, 1); - out_bytes[5] = UINT64_GET_BYTE(integer, 2); - out_bytes[4] = UINT64_GET_BYTE(integer, 3); - out_bytes[3] = UINT64_GET_BYTE(integer, 4); - out_bytes[2] = UINT64_GET_BYTE(integer, 5); - out_bytes[1] = UINT64_GET_BYTE(integer, 6); - out_bytes[0] = UINT64_GET_BYTE(integer, 7); -} - -/* - Generates an OTP (One Time Password). - - input is a number used to generate the OTP - out_str is the null-terminated output string already allocated - - Returns - OTP code if otp code was successfully generated - 0 otherwise -*/ +/** + * @brief Generates an OTP (One Time Password) + * @param algo hashing algorithm to be used + * @param digits desired TOTP code length + * @param plain_secret plain token secret + * @param plain_secret_length plain token secret length + * @param input input data for OTP code generation + * @return OTP code if code was successfully generated; 0 otherwise + */ uint32_t otp_generate( TOTP_ALGO algo, uint8_t digits, const uint8_t* plain_secret, - uint8_t plain_secret_length, + size_t plain_secret_length, uint64_t input) { - uint8_t* bytes = malloc(8); - memset(bytes, 0, 8); - uint8_t* hmac = malloc(64); - memset(hmac, 0, 64); + uint8_t hmac[HMAC_MAX_SIZE] = {0}; - otp_num_to_bytes(input, bytes); + uint64_t input_swapped = swap_uint64(input); - int hmac_len = (*(algo))(plain_secret, plain_secret_length, bytes, 8, hmac); + int hmac_len = + (*algo)(plain_secret, plain_secret_length, (uint8_t*)&input_swapped, 8, &hmac[0]); if(hmac_len == 0) { - free(hmac); - free(bytes); return OTP_ERROR; } @@ -81,27 +57,14 @@ uint32_t otp_generate( (hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF)); i_code %= (uint64_t)pow(10, digits); - free(hmac); - free(bytes); return i_code; } -/* - Generates a OTP key using the totp algorithm. - - for_time is the time the generated key will be created for - offset is a timeblock adjustment for the generated key - out_str is the null-terminated output string already allocated - - Returns - TOTP code if otp code was successfully generated - 0 otherwise -*/ uint32_t totp_at( TOTP_ALGO algo, uint8_t digits, const uint8_t* plain_secret, - uint8_t plain_secret_length, + size_t plain_secret_length, uint64_t for_time, float timezone, uint8_t interval) { @@ -117,9 +80,9 @@ uint32_t totp_at( static int totp_algo_sha1( const uint8_t* key, - uint8_t key_length, + size_t key_length, const uint8_t* input, - uint8_t input_length, + size_t input_length, uint8_t* output) { hmac_sha1(key, key_length, input, input_length, output); return HMAC_SHA1_RESULT_SIZE; @@ -127,9 +90,9 @@ static int totp_algo_sha1( static int totp_algo_sha256( const uint8_t* key, - uint8_t key_length, + size_t key_length, const uint8_t* input, - uint8_t input_length, + size_t input_length, uint8_t* output) { hmac_sha256(key, key_length, input, input_length, output); return HMAC_SHA256_RESULT_SIZE; @@ -137,9 +100,9 @@ static int totp_algo_sha256( static int totp_algo_sha512( const uint8_t* key, - uint8_t key_length, + size_t key_length, const uint8_t* input, - uint8_t input_length, + size_t input_length, uint8_t* output) { hmac_sha512(key, key_length, input, input_length, output); return HMAC_SHA512_RESULT_SIZE; diff --git a/applications/plugins/totp/services/totp/totp.h b/applications/plugins/totp/services/totp/totp.h index 31e70e01b..3f45a0223 100644 --- a/applications/plugins/totp/services/totp/totp.h +++ b/applications/plugins/totp/services/totp/totp.h @@ -5,49 +5,53 @@ #define OTP_ERROR (0) -/* - Must compute HMAC using passed arguments, - output as char array through output. - - key is secret key. - input is input number. - output is an output buffer of the resulting HMAC operation. - - Must return 0 if error, or the length in bytes of the HMAC operation. -*/ +/** + * @brief Must compute HMAC using passed arguments, output as char array through output. + * \p key is secret key buffer. + * \p key_length is secret key buffer length. + * \p input is input buffer. + * \p input_length is input buffer length. + * \p output is an output buffer of the resulting HMAC operation. + * Must return 0 if error, or the length in bytes of the HMAC operation. + */ typedef int (*TOTP_ALGO)( const uint8_t* key, - uint8_t key_length, + size_t key_length, const uint8_t* input, - uint8_t input_length, + size_t input_length, uint8_t* output); -/* - Computes HMAC using SHA1 -*/ +/** + * @brief Computes HMAC using SHA1 + */ extern const TOTP_ALGO TOTP_ALGO_SHA1; -/* - Computes HMAC using SHA256 -*/ +/** + * @brief Computes HMAC using SHA256 + */ extern const TOTP_ALGO TOTP_ALGO_SHA256; -/* - Computes HMAC using SHA512 -*/ +/** + * @brief Computes HMAC using SHA512 + */ extern const TOTP_ALGO TOTP_ALGO_SHA512; -/* - Computes TOTP token - Returns: - TOTP token on success - 0 otherwise -*/ +/** + * @brief Generates a OTP key using the totp algorithm. + * @param algo hashing algorithm to be used + * @param digits desired TOTP code length + * @param plain_secret plain token secret + * @param plain_secret_length plain token secret length + * @param for_time the time the generated key will be created for + * @param timezone UTC timezone adjustment for the generated key + * @param interval token lifetime in seconds + * @return TOTP code if code was successfully generated; 0 otherwise + */ uint32_t totp_at( TOTP_ALGO algo, uint8_t digits, const uint8_t* plain_secret, - uint8_t plain_secret_length, + size_t plain_secret_length, uint64_t for_time, float timezone, uint8_t interval); diff --git a/applications/plugins/totp/services/ui/icons.h b/applications/plugins/totp/services/ui/icons.h deleted file mode 100644 index a9139403f..000000000 --- a/applications/plugins/totp/services/ui/icons.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -#define ICON_ARROW_LEFT_8x9_WIDTH 8 -#define ICON_ARROW_LEFT_8x9_HEIGHT 9 -static const uint8_t ICON_ARROW_LEFT_8x9[] = {0x80, 0xe0, 0xf8, 0xfe, 0xff, 0xfe, 0xf8, 0xe0, 0x80}; - -#define ICON_ARROW_RIGHT_8x9_WIDTH 8 -#define ICON_ARROW_RIGHT_8x9_HEIGHT 9 -static const uint8_t ICON_ARROW_RIGHT_8x9[] = - {0x01, 0x07, 0x1f, 0x7f, 0xff, 0x7f, 0x1f, 0x07, 0x01}; diff --git a/applications/plugins/totp/services/ui/ui_controls.h b/applications/plugins/totp/services/ui/ui_controls.h deleted file mode 100644 index e86b3e5d9..000000000 --- a/applications/plugins/totp/services/ui/ui_controls.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -void ui_control_text_box_render(Canvas* const canvas, int8_t y, char* text, bool is_selected); -void ui_control_button_render( - Canvas* const canvas, - int16_t x, - int16_t y, - uint8_t width, - uint8_t height, - char* text, - bool is_selected); -void ui_control_select_render( - Canvas* const canvas, - int16_t x, - int16_t y, - uint8_t width, - char* text, - bool is_selected); diff --git a/applications/plugins/totp/totp_app.c b/applications/plugins/totp/totp_app.c index 7438ac810..5a551c4f1 100644 --- a/applications/plugins/totp/totp_app.c +++ b/applications/plugins/totp/totp_app.c @@ -7,21 +7,22 @@ #include #include #include -#include "services/base32/base32.h" -#include "services/list/list.h" #include "services/config/config.h" #include "types/plugin_state.h" #include "types/token_info.h" #include "types/plugin_event.h" #include "types/event_type.h" #include "types/common.h" -#include "scenes/scene_director.h" +#include "ui/scene_director.h" +#include "ui/constants.h" +#include "services/crypto/crypto.h" +#include "cli/cli.h" #define IDLE_TIMEOUT 60000 static void render_callback(Canvas* const canvas, void* ctx) { PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state != NULL && !plugin_state->changing_scene) { + if(plugin_state != NULL) { totp_scene_director_render(canvas, plugin_state); } @@ -35,17 +36,67 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu furi_message_queue_put(event_queue, &event, FuriWaitForever); } -static void totp_state_init(PluginState* const plugin_state) { +static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->gui = furi_record_open(RECORD_GUI); - plugin_state->notification = furi_record_open(RECORD_NOTIFICATION); - plugin_state->dialogs = furi_record_open(RECORD_DIALOGS); + plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION); + plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); + totp_config_file_load_base(plugin_state); + totp_cli_register_command_handler(plugin_state); + totp_scene_director_init_scenes(plugin_state); - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + + if(plugin_state->crypto_verify_data == NULL) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "No", NULL, "Yes"); + dialog_message_set_text( + message, + "Would you like to setup PIN?", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + DialogMessageButton dialog_result = + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + if(dialog_result == DialogMessageButtonRight) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + } else { + totp_crypto_seed_iv(plugin_state, NULL, 0); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + } else if(plugin_state->pin_set) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + } else { + totp_crypto_seed_iv(plugin_state, NULL, 0); + if(totp_crypto_verify_key(plugin_state)) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } else { + FURI_LOG_E( + LOGGING_TAG, + "Digital signature verification failed. Looks like conf file was created on another flipper and can't be used on any other"); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Exit", NULL, NULL); + dialog_message_set_text( + message, + "Digital signature verification failed", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + return false; + } + } + + return true; } -static void dispose_plugin_state(PluginState* plugin_state) { +static void totp_plugin_state_free(PluginState* plugin_state) { + totp_cli_unregister_command_handler(); + totp_scene_director_deactivate_active_scene(plugin_state); totp_scene_director_dispose(plugin_state); @@ -58,7 +109,7 @@ static void dispose_plugin_state(PluginState* plugin_state) { ListNode* tmp; while(node != NULL) { tmp = node->next; - TokenInfo* tokenInfo = (TokenInfo*)node->data; + TokenInfo* tokenInfo = node->data; token_info_free(tokenInfo); free(node); node = tmp; @@ -73,13 +124,18 @@ static void dispose_plugin_state(PluginState* plugin_state) { int32_t totp_app() { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); + furi_check(plugin_state != NULL); - totp_state_init(plugin_state); + if(!totp_plugin_state_init(plugin_state)) { + FURI_LOG_E(LOGGING_TAG, "App state initialization failed\r\n"); + totp_plugin_state_free(plugin_state); + return 254; + } ValueMutex state_mutex; if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n"); - dispose_plugin_state(plugin_state); + totp_plugin_state_free(plugin_state); return 255; } @@ -95,25 +151,24 @@ int32_t totp_app() { bool processing = true; uint32_t last_user_interaction_time = furi_get_tick(); while(processing) { - if(plugin_state->changing_scene) continue; FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + PluginState* plugin_state_m = acquire_mutex_block(&state_mutex); if(event_status == FuriStatusOk) { if(event.type == EventTypeKey) { last_user_interaction_time = furi_get_tick(); } - processing = totp_scene_director_handle_event(&event, plugin_state); + processing = totp_scene_director_handle_event(&event, plugin_state_m); } else if( - plugin_state->current_scene != TotpSceneAuthentication && + plugin_state_m->pin_set && plugin_state_m->current_scene != TotpSceneAuthentication && furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state_m, TotpSceneAuthentication, NULL); } view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); + release_mutex(&state_mutex, plugin_state_m); } view_port_enabled_set(view_port, false); @@ -121,6 +176,6 @@ int32_t totp_app() { view_port_free(view_port); furi_message_queue_free(event_queue); delete_mutex(&state_mutex); - dispose_plugin_state(plugin_state); + totp_plugin_state_free(plugin_state); return 0; } diff --git a/applications/plugins/totp/types/common.h b/applications/plugins/totp/types/common.h index df8f6cc2d..2c6d6b293 100644 --- a/applications/plugins/totp/types/common.h +++ b/applications/plugins/totp/types/common.h @@ -1,4 +1,3 @@ #pragma once #define LOGGING_TAG "TOTP APP" -#define CRYPTO_KEY_SLOT 2 diff --git a/applications/plugins/totp/types/event_type.h b/applications/plugins/totp/types/event_type.h index ed890caf1..4fe916872 100644 --- a/applications/plugins/totp/types/event_type.h +++ b/applications/plugins/totp/types/event_type.h @@ -1,7 +1,9 @@ #pragma once #include -typedef enum { +typedef uint8_t EventType; + +enum EventTypes { EventTypeTick, EventTypeKey, -} EventType; +}; diff --git a/applications/plugins/totp/types/notification_method.h b/applications/plugins/totp/types/notification_method.h new file mode 100644 index 000000000..f86613352 --- /dev/null +++ b/applications/plugins/totp/types/notification_method.h @@ -0,0 +1,9 @@ +#pragma once + +typedef uint8_t NotificationMethod; + +enum NotificationMethods { + NotificationMethodNone = 0b00, + NotificationMethodSound = 0b01, + NotificationMethodVibro = 0b10, +}; diff --git a/applications/plugins/totp/types/nullable.h b/applications/plugins/totp/types/nullable.h new file mode 100644 index 000000000..4f9b7bc01 --- /dev/null +++ b/applications/plugins/totp/types/nullable.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#define TOTP_NULLABLE_STRUCT(value_type) \ + typedef struct TotpNullable_##value_type { \ + bool is_null; \ + value_type value; \ + } TotpNullable_##value_type + +#define TOTP_NULLABLE_NULL(s) s.is_null = true +#define TOTP_NULLABLE_VALUE(s, v) \ + s.is_null = false; \ + s.value = v + +TOTP_NULLABLE_STRUCT(uint16_t); diff --git a/applications/plugins/totp/types/plugin_state.h b/applications/plugins/totp/types/plugin_state.h index 3f2b30ed6..0aad2e125 100644 --- a/applications/plugins/totp/types/plugin_state.h +++ b/applications/plugins/totp/types/plugin_state.h @@ -3,26 +3,88 @@ #include #include #include -#include "../services/list/list.h" -#include "../scenes/totp_scenes_enum.h" +#include "../lib/list/list.h" +#include "../ui/totp_scenes_enum.h" +#include "notification_method.h" #define TOTP_IV_SIZE 16 +/** + * @brief Application state structure + */ typedef struct { + /** + * @brief Application current scene + */ Scene current_scene; + + /** + * @brief Application current scene state + */ void* current_scene_state; - bool changing_scene; - NotificationApp* notification; - DialogsApp* dialogs; + + /** + * @brief Reference to the firmware notification subsystem + */ + NotificationApp* notification_app; + + /** + * @brief Reference to the firmware dialogs subsystem + */ + DialogsApp* dialogs_app; + + /** + * @brief Reference to the firmware GUI subsystem + */ Gui* gui; + /** + * @brief Timezone UTC offset in hours + */ float timezone_offset; - ListNode* tokens_list; - bool token_list_loaded; - uint8_t tokens_count; + /** + * @brief Token list head node + */ + ListNode* tokens_list; + + /** + * @brief Whether token list is loaded or not + */ + bool token_list_loaded; + + /** + * @brief Tokens list length + */ + uint16_t tokens_count; + + /** + * @brief Encrypted well-known string data + */ uint8_t* crypto_verify_data; - uint8_t crypto_verify_data_length; + + /** + * @brief Encrypted well-known string data length + */ + size_t crypto_verify_data_length; + + /** + * @brief Whether PIN is set by user or not + */ + bool pin_set; + + /** + * @brief Initialization vector (IV) to be used for encryption\decryption + */ uint8_t iv[TOTP_IV_SIZE]; + + /** + * @brief Basic randomly-generated initialization vector (IV) + */ uint8_t base_iv[TOTP_IV_SIZE]; + + /** + * @brief Notification method + */ + NotificationMethod notification_method; } PluginState; diff --git a/applications/plugins/totp/types/token_info.c b/applications/plugins/totp/types/token_info.c index 6596cd510..c032c6d3f 100644 --- a/applications/plugins/totp/types/token_info.c +++ b/applications/plugins/totp/types/token_info.c @@ -3,10 +3,13 @@ #include "token_info.h" #include "stdlib.h" #include "common.h" -#include "../services/base32/base32.h" +#include "../lib/base32/base32.h" +#include "../services/crypto/crypto.h" +#include "../lib/polyfills/memset_s.h" TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); + furi_check(tokenInfo != NULL); tokenInfo->algo = SHA1; tokenInfo->digits = TOTP_6_DIGITS; return tokenInfo; @@ -19,43 +22,40 @@ void token_info_free(TokenInfo* token_info) { free(token_info); } -void token_info_set_secret( +bool token_info_set_secret( TokenInfo* token_info, const char* base32_token_secret, - uint8_t token_secret_length, - uint8_t* iv) { + size_t token_secret_length, + const uint8_t* iv) { uint8_t* plain_secret = malloc(token_secret_length); + furi_check(plain_secret != NULL); int plain_secret_length = - base32_decode((uint8_t*)base32_token_secret, plain_secret, token_secret_length); - token_info->token_length = plain_secret_length; - - size_t remain = token_info->token_length % 16; - if(remain) { - token_info->token_length = token_info->token_length - remain + 16; - uint8_t* plain_secret_aligned = malloc(token_info->token_length); - memcpy(plain_secret_aligned, plain_secret, plain_secret_length); - memset(plain_secret, 0, plain_secret_length); - free(plain_secret); - plain_secret = plain_secret_aligned; + base32_decode((const uint8_t*)base32_token_secret, plain_secret, token_secret_length); + bool result; + if(plain_secret_length >= 0) { + token_info->token = + totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); + result = true; + } else { + result = false; } - token_info->token = malloc(token_info->token_length); - - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); - furi_hal_crypto_encrypt(plain_secret, token_info->token, token_info->token_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); - - memset(plain_secret, 0, token_info->token_length); + memset_s(plain_secret, token_secret_length, 0, token_secret_length); free(plain_secret); + return result; } -uint8_t token_info_get_digits_count(TokenInfo* token_info) { - switch(token_info->digits) { - case TOTP_6_DIGITS: - return 6; - case TOTP_8_DIGITS: - return 8; +bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) { + switch(digits) { + case 6: + token_info->digits = TOTP_6_DIGITS; + return true; + case 8: + token_info->digits = TOTP_8_DIGITS; + return true; + default: + break; } - return 6; + return false; } diff --git a/applications/plugins/totp/types/token_info.h b/applications/plugins/totp/types/token_info.h index 2d531ec89..04caa68a8 100644 --- a/applications/plugins/totp/types/token_info.h +++ b/applications/plugins/totp/types/token_info.h @@ -2,23 +2,106 @@ #include -typedef enum { SHA1, SHA256, SHA512 } TokenHashAlgo; +typedef uint8_t TokenHashAlgo; +typedef uint8_t TokenDigitsCount; -typedef enum { TOTP_6_DIGITS, TOTP_8_DIGITS } TokenDigitsCount; +/** + * @brief Hashing algorithm to be used to generate token + */ +enum TokenHashAlgos { + /** + * @brief SHA1 hashing algorithm + */ + SHA1, + /** + * @brief SHA256 hashing algorithm + */ + SHA256, + + /** + * @brief SHA512 hashing algorithm + */ + SHA512 +}; + +/** + * @brief Token digits count to be generated. + */ +enum TokenDigitsCounts { + /** + * @brief 6 digits + */ + TOTP_6_DIGITS = 6, + + /** + * @brief 8 digits + */ + TOTP_8_DIGITS = 8 +}; + +#define TOTP_TOKEN_DIGITS_MAX_COUNT 8 + +/** + * @brief TOTP token information + */ typedef struct { + /** + * @brief Encrypted token secret + */ uint8_t* token; - uint8_t token_length; + + /** + * @brief Encrypted token secret length + */ + size_t token_length; + + /** + * @brief User-friendly token name + */ char* name; + + /** + * @brief Hashing algorithm + */ TokenHashAlgo algo; + + /** + * @brief Desired TOTP token length + */ TokenDigitsCount digits; } TokenInfo; +/** + * @brief Allocates a new instance of \c TokenInfo + * @return + */ TokenInfo* token_info_alloc(); + +/** + * @brief Disposes all the resources allocated by the given \c TokenInfo instance + * @param token_info instance to be disposed + */ void token_info_free(TokenInfo* token_info); -void token_info_set_secret( + +/** + * @brief Encrypts & sets plain token secret to the given instance of \c TokenInfo + * @param token_info instance where secret should be updated + * @param base32_token_secret plain token secret in Base32 format + * @param token_secret_length plain token secret length + * @param iv initialization vecor (IV) to be used for encryption + * @return \c true if token successfully set; \c false otherwise + */ +bool token_info_set_secret( TokenInfo* token_info, const char* base32_token_secret, - uint8_t token_secret_length, - uint8_t* iv); -uint8_t token_info_get_digits_count(TokenInfo* token_info); + size_t token_secret_length, + const uint8_t* iv); + +/** + * @brief Sets token digits count from \c uint8_t value + * @param token_info instance whichs token digits count length should be updated + * @param digits desired token digits count length + * @return \c true if token digits count length has been updated; \c false p + */ +bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits); diff --git a/applications/plugins/totp/types/user_pin_codes.h b/applications/plugins/totp/types/user_pin_codes.h new file mode 100644 index 000000000..b5d72d398 --- /dev/null +++ b/applications/plugins/totp/types/user_pin_codes.h @@ -0,0 +1,10 @@ +#pragma once + +typedef uint8_t TotpUserPinCode; + +enum TotpUserPinCodes { + PinCodeArrowUp = 2, + PinCodeArrowRight = 8, + PinCodeArrowDown = 11, + PinCodeArrowLeft = 5 +}; \ No newline at end of file diff --git a/applications/plugins/totp/services/ui/constants.h b/applications/plugins/totp/ui/constants.h similarity index 100% rename from applications/plugins/totp/services/ui/constants.h rename to applications/plugins/totp/ui/constants.h diff --git a/applications/plugins/totp/scenes/scene_director.c b/applications/plugins/totp/ui/scene_director.c similarity index 85% rename from applications/plugins/totp/scenes/scene_director.c rename to applications/plugins/totp/ui/scene_director.c index 9a07b49fb..fcc9f37d8 100644 --- a/applications/plugins/totp/scenes/scene_director.c +++ b/applications/plugins/totp/ui/scene_director.c @@ -1,16 +1,15 @@ #include "../types/common.h" #include "scene_director.h" -#include "authenticate/totp_scene_authenticate.h" -#include "generate_token/totp_scene_generate_token.h" -#include "add_new_token/totp_scene_add_new_token.h" -#include "token_menu/totp_scene_token_menu.h" -#include "app_settings/totp_app_settings.h" +#include "scenes/authenticate/totp_scene_authenticate.h" +#include "scenes/generate_token/totp_scene_generate_token.h" +#include "scenes/add_new_token/totp_scene_add_new_token.h" +#include "scenes/token_menu/totp_scene_token_menu.h" +#include "scenes/app_settings/totp_app_settings.h" void totp_scene_director_activate_scene( PluginState* const plugin_state, Scene scene, const void* context) { - plugin_state->changing_scene = true; totp_scene_director_deactivate_active_scene(plugin_state); switch(scene) { case TotpSceneGenerateToken: @@ -28,10 +27,13 @@ void totp_scene_director_activate_scene( case TotpSceneAppSettings: totp_scene_app_settings_activate(plugin_state, context); break; + case TotpSceneNone: + break; + default: + break; } plugin_state->current_scene = scene; - plugin_state->changing_scene = false; } void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state) { @@ -51,6 +53,10 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state case TotpSceneAppSettings: totp_scene_app_settings_deactivate(plugin_state); break; + case TotpSceneNone: + break; + default: + break; } } @@ -79,10 +85,14 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ case TotpSceneAppSettings: totp_scene_app_settings_render(canvas, plugin_state); break; + case TotpSceneNone: + break; + default: + break; } } -void totp_scene_director_dispose(PluginState* const plugin_state) { +void totp_scene_director_dispose(const PluginState* const plugin_state) { totp_scene_generate_token_free(plugin_state); totp_scene_authenticate_free(plugin_state); totp_scene_add_new_token_free(plugin_state); @@ -108,6 +118,10 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con case TotpSceneAppSettings: processing = totp_scene_app_settings_handle_event(event, plugin_state); break; + case TotpSceneNone: + break; + default: + break; } return processing; diff --git a/applications/plugins/totp/ui/scene_director.h b/applications/plugins/totp/ui/scene_director.h new file mode 100644 index 000000000..541a63f1c --- /dev/null +++ b/applications/plugins/totp/ui/scene_director.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include "../types/plugin_state.h" +#include "../types/plugin_event.h" +#include "totp_scenes_enum.h" + +/** + * @brief Activates scene + * @param plugin_state application state + * @param scene scene to be activated + * @param context scene context to be passed to the scene activation method + */ +void totp_scene_director_activate_scene( + PluginState* const plugin_state, + Scene scene, + const void* context); + +/** + * @brief Deactivate current scene + * @param plugin_state application state + */ +void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state); + +/** + * @brief Initializes all the available scenes + * @param plugin_state application state + */ +void totp_scene_director_init_scenes(PluginState* const plugin_state); + +/** + * @brief Renders current scene + * @param canvas canvas to render at + * @param plugin_state application state + */ +void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state); + +/** + * @brief Disposes all the available scenes + * @param plugin_state application state + */ +void totp_scene_director_dispose(const PluginState* const plugin_state); + +/** + * @brief Handles application event for the current scene + * @param event event to be handled + * @param plugin_state application state + * @return \c true if event handled and applilcation should continue; \c false if application should be closed + */ +bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state); diff --git a/applications/plugins/totp/scenes/add_new_token/totp_input_text.c b/applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.c similarity index 85% rename from applications/plugins/totp/scenes/add_new_token/totp_input_text.c rename to applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.c index e3a7f68a6..6956ec1ad 100644 --- a/applications/plugins/totp/scenes/add_new_token/totp_input_text.c +++ b/applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.c @@ -1,6 +1,6 @@ #include "totp_input_text.h" #include -#include "../../types/common.h" +#include "../../../lib/polyfills/strnlen.h" void view_draw(View* view, Canvas* canvas) { furi_assert(view); @@ -32,16 +32,23 @@ static void commit_text_input_callback(void* context) { InputTextSceneState* text_input_state = (InputTextSceneState*)context; if(text_input_state->callback != 0) { InputTextSceneCallbackResult* result = malloc(sizeof(InputTextSceneCallbackResult)); - result->user_input_length = strlen(text_input_state->text_input_buffer); + furi_check(result != NULL); + result->user_input_length = + strnlen(text_input_state->text_input_buffer, INPUT_BUFFER_SIZE); result->user_input = malloc(result->user_input_length + 1); + furi_check(result->user_input != NULL); result->callback_data = text_input_state->callback_data; - strcpy(result->user_input, text_input_state->text_input_buffer); + strlcpy( + result->user_input, + text_input_state->text_input_buffer, + result->user_input_length + 1); text_input_state->callback(result); } } InputTextSceneState* totp_input_text_activate(InputTextSceneContext* context) { InputTextSceneState* text_input_state = malloc(sizeof(InputTextSceneState)); + furi_check(text_input_state != NULL); text_input_state->text_input = text_input_alloc(); text_input_state->text_input_view = text_input_get_view(text_input_state->text_input); text_input_state->callback = context->callback; diff --git a/applications/plugins/totp/scenes/add_new_token/totp_input_text.h b/applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.h similarity index 86% rename from applications/plugins/totp/scenes/add_new_token/totp_input_text.h rename to applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.h index a73a227b5..145e8904d 100644 --- a/applications/plugins/totp/scenes/add_new_token/totp_input_text.h +++ b/applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.h @@ -3,14 +3,12 @@ #include #include #include -#include -#include -#include "../../types/plugin_state.h" -#include "../../types/plugin_event.h" +#include "../../../types/plugin_state.h" +#include "../../../types/plugin_event.h" typedef struct { char* user_input; - uint8_t user_input_length; + size_t user_input_length; void* callback_data; } InputTextSceneCallbackResult; diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c new file mode 100644 index 000000000..e6351010e --- /dev/null +++ b/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -0,0 +1,318 @@ +#include "totp_scene_add_new_token.h" +#include "../../../types/common.h" +#include "../../constants.h" +#include "../../scene_director.h" +#include "totp_input_text.h" +#include "../../../types/token_info.h" +#include "../../../lib/list/list.h" +#include "../../../services/config/config.h" +#include "../../ui_controls.h" +#include "../../../lib/roll_value/roll_value.h" +#include "../../../types/nullable.h" +#include "../generate_token/totp_scene_generate_token.h" + +char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512"}; +char* TOKEN_DIGITS_TEXT_LIST[] = {"6 digits", "8 digits"}; +TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_6_DIGITS, TOTP_8_DIGITS}; + +typedef enum { + TokenNameTextBox, + TokenSecretTextBox, + TokenAlgoSelect, + TokenLengthSelect, + ConfirmButton, +} Control; + +typedef struct { + char* token_name; + size_t token_name_length; + char* token_secret; + size_t token_secret_length; + bool saved; + Control selected_control; + InputTextSceneContext* token_name_input_context; + InputTextSceneContext* token_secret_input_context; + InputTextSceneState* input_state; + uint32_t input_started_at; + TotpNullable_uint16_t current_token_index; + int16_t screen_y_offset; + TokenHashAlgo algo; + uint8_t digits_count_index; +} SceneState; + +void totp_scene_add_new_token_init(const PluginState* plugin_state) { + UNUSED(plugin_state); +} + +static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) { + SceneState* scene_state = result->callback_data; + free(scene_state->token_name); + scene_state->token_name = result->user_input; + scene_state->token_name_length = result->user_input_length; + scene_state->input_started_at = 0; + free(result); +} + +static void on_token_secret_user_comitted(InputTextSceneCallbackResult* result) { + SceneState* scene_state = result->callback_data; + free(scene_state->token_secret); + scene_state->token_secret = result->user_input; + scene_state->token_secret_length = result->user_input_length; + scene_state->input_started_at = 0; + free(result); +} + +void totp_scene_add_new_token_activate( + PluginState* plugin_state, + const TokenAddEditSceneContext* context) { + SceneState* scene_state = malloc(sizeof(SceneState)); + furi_check(scene_state != NULL); + plugin_state->current_scene_state = scene_state; + scene_state->token_name = "Name"; + scene_state->token_name_length = strlen(scene_state->token_name); + scene_state->token_secret = "Secret"; + scene_state->token_secret_length = strlen(scene_state->token_secret); + + scene_state->token_name_input_context = malloc(sizeof(InputTextSceneContext)); + furi_check(scene_state->token_name_input_context != NULL); + scene_state->token_name_input_context->header_text = "Enter token name"; + scene_state->token_name_input_context->callback_data = scene_state; + scene_state->token_name_input_context->callback = on_token_name_user_comitted; + + scene_state->token_secret_input_context = malloc(sizeof(InputTextSceneContext)); + furi_check(scene_state->token_secret_input_context != NULL); + scene_state->token_secret_input_context->header_text = "Enter token secret"; + scene_state->token_secret_input_context->callback_data = scene_state; + scene_state->token_secret_input_context->callback = on_token_secret_user_comitted; + + scene_state->screen_y_offset = 0; + + scene_state->input_state = NULL; + + if(context == NULL) { + TOTP_NULLABLE_NULL(scene_state->current_token_index); + } else { + TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); + } +} + +void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) { + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + if(scene_state->input_started_at > 0) { + totp_input_text_render(canvas, scene_state->input_state); + return; + } + + ui_control_text_box_render( + canvas, + 10 - scene_state->screen_y_offset, + scene_state->token_name, + scene_state->selected_control == TokenNameTextBox); + ui_control_text_box_render( + canvas, + 27 - scene_state->screen_y_offset, + scene_state->token_secret, + scene_state->selected_control == TokenSecretTextBox); + ui_control_select_render( + canvas, + 0, + 44 - scene_state->screen_y_offset, + SCREEN_WIDTH, + TOKEN_ALGO_LIST[scene_state->algo], + scene_state->selected_control == TokenAlgoSelect); + ui_control_select_render( + canvas, + 0, + 63 - scene_state->screen_y_offset, + SCREEN_WIDTH, + TOKEN_DIGITS_TEXT_LIST[scene_state->digits_count_index], + scene_state->selected_control == TokenLengthSelect); + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 24, + 85 - scene_state->screen_y_offset, + 48, + 13, + "Confirm", + scene_state->selected_control == ConfirmButton); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 0, SCREEN_WIDTH, 10); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Add new token"); + canvas_set_font(canvas, FontSecondary); +} + +void update_screen_y_offset(SceneState* scene_state) { + if(scene_state->selected_control > TokenAlgoSelect) { + scene_state->screen_y_offset = 35; + } else { + scene_state->screen_y_offset = 0; + } +} + +bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state) { + if(event->type != EventTypeKey) { + return true; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + if(scene_state->input_started_at > 0 && + furi_get_tick() - scene_state->input_started_at > 300) { + return totp_input_text_handle_event(event, scene_state->input_state); + } + + if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { + return false; + } + + if(event->input.type != InputTypePress) { + return true; + } + + switch(event->input.key) { + case InputKeyUp: + totp_roll_value_uint8_t( + &scene_state->selected_control, + -1, + TokenNameTextBox, + ConfirmButton, + RollOverflowBehaviorStop); + update_screen_y_offset(scene_state); + break; + case InputKeyDown: + totp_roll_value_uint8_t( + &scene_state->selected_control, + 1, + TokenNameTextBox, + ConfirmButton, + RollOverflowBehaviorStop); + update_screen_y_offset(scene_state); + break; + case InputKeyRight: + if(scene_state->selected_control == TokenAlgoSelect) { + totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, SHA512, RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == TokenLengthSelect) { + totp_roll_value_uint8_t( + &scene_state->digits_count_index, 1, 0, 1, RollOverflowBehaviorRoll); + } + break; + case InputKeyLeft: + if(scene_state->selected_control == TokenAlgoSelect) { + totp_roll_value_uint8_t( + &scene_state->algo, -1, SHA1, SHA512, RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == TokenLengthSelect) { + totp_roll_value_uint8_t( + &scene_state->digits_count_index, -1, 0, 1, RollOverflowBehaviorRoll); + } + break; + case InputKeyOk: + switch(scene_state->selected_control) { + case TokenNameTextBox: + if(scene_state->input_state != NULL) { + totp_input_text_free(scene_state->input_state); + } + scene_state->input_state = + totp_input_text_activate(scene_state->token_name_input_context); + scene_state->input_started_at = furi_get_tick(); + break; + case TokenSecretTextBox: + if(scene_state->input_state != NULL) { + totp_input_text_free(scene_state->input_state); + } + scene_state->input_state = + totp_input_text_activate(scene_state->token_secret_input_context); + scene_state->input_started_at = furi_get_tick(); + break; + case TokenAlgoSelect: + break; + case TokenLengthSelect: + break; + case ConfirmButton: { + TokenInfo* tokenInfo = token_info_alloc(); + bool token_secret_set = token_info_set_secret( + tokenInfo, + scene_state->token_secret, + scene_state->token_secret_length, + &plugin_state->iv[0]); + + if(token_secret_set) { + tokenInfo->name = malloc(scene_state->token_name_length + 1); + furi_check(tokenInfo->name != NULL); + strlcpy( + tokenInfo->name, scene_state->token_name, scene_state->token_name_length + 1); + tokenInfo->algo = scene_state->algo; + tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[scene_state->digits_count_index]; + + TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); + plugin_state->tokens_count++; + + totp_config_file_save_new_token(tokenInfo); + + GenerateTokenSceneContext generate_scene_context = { + .current_token_index = plugin_state->tokens_count - 1}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneGenerateToken, &generate_scene_context); + } else { + token_info_free(tokenInfo); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_set_text( + message, + "Token secret is invalid", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + scene_state->selected_control = TokenSecretTextBox; + update_screen_y_offset(scene_state); + } + break; + } + default: + break; + } + break; + case InputKeyBack: + if(!scene_state->current_token_index.is_null) { + GenerateTokenSceneContext generate_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneGenerateToken, &generate_scene_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + break; + default: + break; + } + + return true; +} + +void totp_scene_add_new_token_deactivate(PluginState* plugin_state) { + if(plugin_state->current_scene_state == NULL) return; + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + free(scene_state->token_name); + free(scene_state->token_secret); + + free(scene_state->token_name_input_context->header_text); + free(scene_state->token_name_input_context); + + free(scene_state->token_secret_input_context->header_text); + free(scene_state->token_secret_input_context); + + if(scene_state->input_state != NULL) { + totp_input_text_free(scene_state->input_state); + } + + free(plugin_state->current_scene_state); + plugin_state->current_scene_state = NULL; +} + +void totp_scene_add_new_token_free(const PluginState* plugin_state) { + UNUSED(plugin_state); +} diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h new file mode 100644 index 000000000..c412e5f0f --- /dev/null +++ b/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include "../../../types/plugin_state.h" +#include "../../../types/plugin_event.h" + +typedef struct { + uint16_t current_token_index; +} TokenAddEditSceneContext; + +void totp_scene_add_new_token_init(const PluginState* plugin_state); +void totp_scene_add_new_token_activate( + PluginState* plugin_state, + const TokenAddEditSceneContext* context); +void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state); +bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state); +void totp_scene_add_new_token_deactivate(PluginState* plugin_state); +void totp_scene_add_new_token_free(const PluginState* plugin_state); diff --git a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c new file mode 100644 index 000000000..5f68c6772 --- /dev/null +++ b/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c @@ -0,0 +1,244 @@ +#include "totp_app_settings.h" +#include +#include +#include "../../ui_controls.h" +#include "../../scene_director.h" +#include "../token_menu/totp_scene_token_menu.h" +#include "../../constants.h" +#include "../../../services/config/config.h" +#include "../../../services/convert/convert.h" +#include "../../../lib/roll_value/roll_value.h" +#include "../../../types/nullable.h" + +char* YES_NO_LIST[] = {"NO", "YES"}; + +typedef enum { HoursInput, MinutesInput, Sound, Vibro, ConfirmButton } Control; + +typedef struct { + int8_t tz_offset_hours; + uint8_t tz_offset_minutes; + bool notification_sound; + bool notification_vibro; + uint8_t y_offset; + TotpNullable_uint16_t current_token_index; + Control selected_control; +} SceneState; + +void totp_scene_app_settings_init(const PluginState* plugin_state) { + UNUSED(plugin_state); +} + +void totp_scene_app_settings_activate( + PluginState* plugin_state, + const AppSettingsSceneContext* context) { + SceneState* scene_state = malloc(sizeof(SceneState)); + furi_check(scene_state != NULL); + plugin_state->current_scene_state = scene_state; + if(context != NULL) { + TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); + } else { + TOTP_NULLABLE_NULL(scene_state->current_token_index); + } + + float off_int; + float off_dec = modff(plugin_state->timezone_offset, &off_int); + scene_state->tz_offset_hours = off_int; + scene_state->tz_offset_minutes = 60.0f * off_dec; + scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound; + scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro; +} + +static void two_digit_to_str(int8_t num, char* str) { + uint8_t index = 0; + if(num < 0) { + str[index++] = '-'; + num = -num; + } + + uint8_t d1 = (num / 10) % 10; + uint8_t d2 = num % 10; + str[index++] = CONVERT_DIGIT_TO_CHAR(d1); + str[index++] = CONVERT_DIGIT_TO_CHAR(d2); + str[index++] = '\0'; +} + +void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state) { + const SceneState* scene_state = plugin_state->current_scene_state; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset"); + canvas_set_font(canvas, FontSecondary); + + char tmp_str[4]; + two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); + canvas_draw_str_aligned(canvas, 0, 16 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); + ui_control_select_render( + canvas, + 36, + 10 - scene_state->y_offset, + SCREEN_WIDTH - 36, + &tmp_str[0], + scene_state->selected_control == HoursInput); + + two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); + canvas_draw_str_aligned( + canvas, 0, 34 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); + ui_control_select_render( + canvas, + 36, + 28 - scene_state->y_offset, + SCREEN_WIDTH - 36, + &tmp_str[0], + scene_state->selected_control == MinutesInput); + + canvas_draw_icon( + canvas, + SCREEN_WIDTH_CENTER - 5, + SCREEN_HEIGHT - 5 - scene_state->y_offset, + &I_totp_arrow_bottom_10x5); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications"); + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned(canvas, 0, 80 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); + ui_control_select_render( + canvas, + 36, + 74 - scene_state->y_offset, + SCREEN_WIDTH - 36, + YES_NO_LIST[scene_state->notification_sound], + scene_state->selected_control == Sound); + + canvas_draw_str_aligned(canvas, 0, 98 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); + ui_control_select_render( + canvas, + 36, + 92 - scene_state->y_offset, + SCREEN_WIDTH - 36, + YES_NO_LIST[scene_state->notification_vibro], + scene_state->selected_control == Vibro); + + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 24, + 115 - scene_state->y_offset, + 48, + 13, + "Confirm", + scene_state->selected_control == ConfirmButton); +} + +bool totp_scene_app_settings_handle_event( + const PluginEvent* const event, + PluginState* plugin_state) { + if(event->type != EventTypeKey) { + return true; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) { + return true; + } + + switch(event->input.key) { + case InputKeyUp: + totp_roll_value_uint8_t( + &scene_state->selected_control, + -1, + HoursInput, + ConfirmButton, + RollOverflowBehaviorStop); + if(scene_state->selected_control > MinutesInput) { + scene_state->y_offset = 64; + } else { + scene_state->y_offset = 0; + } + break; + case InputKeyDown: + totp_roll_value_uint8_t( + &scene_state->selected_control, 1, HoursInput, ConfirmButton, RollOverflowBehaviorStop); + if(scene_state->selected_control > MinutesInput) { + scene_state->y_offset = 64; + } else { + scene_state->y_offset = 0; + } + break; + case InputKeyRight: + if(scene_state->selected_control == HoursInput) { + totp_roll_value_int8_t( + &scene_state->tz_offset_hours, 1, -12, 12, RollOverflowBehaviorStop); + } else if(scene_state->selected_control == MinutesInput) { + totp_roll_value_uint8_t( + &scene_state->tz_offset_minutes, 15, 0, 45, RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == Sound) { + scene_state->notification_sound = !scene_state->notification_sound; + } else if(scene_state->selected_control == Vibro) { + scene_state->notification_vibro = !scene_state->notification_vibro; + } + break; + case InputKeyLeft: + if(scene_state->selected_control == HoursInput) { + totp_roll_value_int8_t( + &scene_state->tz_offset_hours, -1, -12, 12, RollOverflowBehaviorStop); + } else if(scene_state->selected_control == MinutesInput) { + totp_roll_value_uint8_t( + &scene_state->tz_offset_minutes, -15, 0, 45, RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == Sound) { + scene_state->notification_sound = !scene_state->notification_sound; + } else if(scene_state->selected_control == Vibro) { + scene_state->notification_vibro = !scene_state->notification_vibro; + } + break; + case InputKeyOk: + if(scene_state->selected_control == ConfirmButton) { + plugin_state->timezone_offset = (float)scene_state->tz_offset_hours + + (float)scene_state->tz_offset_minutes / 60.0f; + + plugin_state->notification_method = + (scene_state->notification_sound ? NotificationMethodSound : + NotificationMethodNone) | + (scene_state->notification_vibro ? NotificationMethodVibro : + NotificationMethodNone); + totp_config_file_update_user_settings(plugin_state); + + if(!scene_state->current_token_index.is_null) { + TokenMenuSceneContext generate_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneTokenMenu, &generate_scene_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); + } + } + break; + case InputKeyBack: { + if(!scene_state->current_token_index.is_null) { + TokenMenuSceneContext generate_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneTokenMenu, &generate_scene_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); + } + break; + } + default: + break; + } + + return true; +} + +void totp_scene_app_settings_deactivate(PluginState* plugin_state) { + if(plugin_state->current_scene_state == NULL) return; + + free(plugin_state->current_scene_state); + plugin_state->current_scene_state = NULL; +} + +void totp_scene_app_settings_free(const PluginState* plugin_state) { + UNUSED(plugin_state); +} \ No newline at end of file diff --git a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.h b/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.h new file mode 100644 index 000000000..1721186ed --- /dev/null +++ b/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" +#include "../../../types/plugin_event.h" + +typedef struct { + uint16_t current_token_index; +} AppSettingsSceneContext; + +void totp_scene_app_settings_init(const PluginState* plugin_state); +void totp_scene_app_settings_activate( + PluginState* plugin_state, + const AppSettingsSceneContext* context); +void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state); +bool totp_scene_app_settings_handle_event( + const PluginEvent* const event, + PluginState* plugin_state); +void totp_scene_app_settings_deactivate(PluginState* plugin_state); +void totp_scene_app_settings_free(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c new file mode 100644 index 000000000..c595b5bd0 --- /dev/null +++ b/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -0,0 +1,167 @@ +#include "totp_scene_authenticate.h" +#include +#include +#include "../../../types/common.h" +#include "../../constants.h" +#include "../../../services/config/config.h" +#include "../../scene_director.h" +#include "../../totp_scenes_enum.h" +#include "../../../services/crypto/crypto.h" +#include "../../../types/user_pin_codes.h" + +#define MAX_CODE_LENGTH TOTP_IV_SIZE + +typedef struct { + TotpUserPinCode code_input[MAX_CODE_LENGTH]; + uint8_t code_length; +} SceneState; + +void totp_scene_authenticate_init(PluginState* plugin_state) { + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); +} + +void totp_scene_authenticate_activate(PluginState* plugin_state) { + SceneState* scene_state = malloc(sizeof(SceneState)); + furi_check(scene_state != NULL); + scene_state->code_length = 0; + memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); + plugin_state->current_scene_state = scene_state; + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); +} + +void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state) { + const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + + int v_shift = 0; + if(scene_state->code_length > 0) { + v_shift = -10; + } + + if(plugin_state->crypto_verify_data == NULL) { + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER - 10 + v_shift, + AlignCenter, + AlignCenter, + "Use arrow keys"); + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER + 5 + v_shift, + AlignCenter, + AlignCenter, + "to setup new PIN"); + } else { + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER + v_shift, + AlignCenter, + AlignCenter, + "Use arrow keys to enter PIN"); + } + const uint8_t PIN_ASTERISK_RADIUS = 3; + const uint8_t PIN_ASTERISK_STEP = (PIN_ASTERISK_RADIUS << 1) + 2; + if(scene_state->code_length > 0) { + uint8_t left_start_x = ((scene_state->code_length - 1) * PIN_ASTERISK_STEP) >> 1; + for(uint8_t i = 0; i < scene_state->code_length; i++) { + canvas_draw_disc( + canvas, + SCREEN_WIDTH_CENTER - left_start_x + i * PIN_ASTERISK_STEP, + SCREEN_HEIGHT_CENTER + 10, + PIN_ASTERISK_RADIUS); + } + } +} + +bool totp_scene_authenticate_handle_event( + const PluginEvent* const event, + PluginState* plugin_state) { + if(event->type != EventTypeKey) { + return true; + } + + if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { + return false; + } + + if(event->input.type != InputTypePress) { + return true; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + + switch(event->input.key) { + case InputKeyUp: + if(scene_state->code_length < MAX_CODE_LENGTH) { + scene_state->code_input[scene_state->code_length] = PinCodeArrowUp; + scene_state->code_length++; + } + break; + case InputKeyDown: + if(scene_state->code_length < MAX_CODE_LENGTH) { + scene_state->code_input[scene_state->code_length] = PinCodeArrowDown; + scene_state->code_length++; + } + break; + case InputKeyRight: + if(scene_state->code_length < MAX_CODE_LENGTH) { + scene_state->code_input[scene_state->code_length] = PinCodeArrowRight; + scene_state->code_length++; + } + break; + case InputKeyLeft: + if(scene_state->code_length < MAX_CODE_LENGTH) { + scene_state->code_input[scene_state->code_length] = PinCodeArrowLeft; + scene_state->code_length++; + } + break; + case InputKeyOk: + totp_crypto_seed_iv(plugin_state, &scene_state->code_input[0], scene_state->code_length); + + if(totp_crypto_verify_key(plugin_state)) { + FURI_LOG_D(LOGGING_TAG, "PIN is valid"); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } else { + FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); + memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + scene_state->code_length = 0; + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Try again", NULL, NULL); + dialog_message_set_header( + message, + "You entered\ninvalid PIN", + SCREEN_WIDTH_CENTER - 25, + SCREEN_HEIGHT_CENTER - 5, + AlignCenter, + AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + } + break; + case InputKeyBack: + if(scene_state->code_length > 0) { + scene_state->code_input[scene_state->code_length - 1] = 0; + scene_state->code_length--; + } + break; + default: + break; + } + + return true; +} + +void totp_scene_authenticate_deactivate(PluginState* plugin_state) { + if(plugin_state->current_scene_state == NULL) return; + free(plugin_state->current_scene_state); + plugin_state->current_scene_state = NULL; +} + +void totp_scene_authenticate_free(const PluginState* plugin_state) { + UNUSED(plugin_state); +} diff --git a/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.h b/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.h new file mode 100644 index 000000000..b8fe174ae --- /dev/null +++ b/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" +#include "../../../types/plugin_event.h" + +void totp_scene_authenticate_init(PluginState* plugin_state); +void totp_scene_authenticate_activate(PluginState* plugin_state); +void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state); +bool totp_scene_authenticate_handle_event( + const PluginEvent* const event, + PluginState* plugin_state); +void totp_scene_authenticate_deactivate(PluginState* plugin_state); +void totp_scene_authenticate_free(const PluginState* plugin_state); diff --git a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c new file mode 100644 index 000000000..c90cc6b23 --- /dev/null +++ b/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -0,0 +1,416 @@ +#include +#include +#include +#include +#include "totp_scene_generate_token.h" +#include "../../../types/token_info.h" +#include "../../../types/common.h" +#include "../../constants.h" +#include "../../../services/totp/totp.h" +#include "../../../services/config/config.h" +#include "../../../services/crypto/crypto.h" +#include "../../../services/convert/convert.h" +#include "../../../lib/polyfills/memset_s.h" +#include "../../../lib/roll_value/roll_value.h" +#include "../../scene_director.h" +#include "../token_menu/totp_scene_token_menu.h" +#include "../../../workers/type_code/type_code.h" + +#define TOKEN_LIFETIME 30 + +typedef struct { + uint16_t current_token_index; + char last_code[TOTP_TOKEN_DIGITS_MAX_COUNT + 1]; + char* last_code_name; + bool need_token_update; + uint32_t last_token_gen_time; + TotpTypeCodeWorkerContext* type_code_worker_context; + NotificationMessage const** notification_sequence_new_token; + NotificationMessage const** notification_sequence_badusb; +} SceneState; + +static const NotificationSequence* + get_notification_sequence_new_token(const PluginState* plugin_state, SceneState* scene_state) { + if(scene_state->notification_sequence_new_token == NULL) { + uint8_t i = 0; + uint8_t length = 4; + if(plugin_state->notification_method & NotificationMethodVibro) { + length += 2; + } + + if(plugin_state->notification_method & NotificationMethodSound) { + length += 2; + } + + scene_state->notification_sequence_new_token = malloc(sizeof(void*) * length); + furi_check(scene_state->notification_sequence_new_token != NULL); + scene_state->notification_sequence_new_token[i++] = &message_display_backlight_on; + scene_state->notification_sequence_new_token[i++] = &message_green_255; + if(plugin_state->notification_method & NotificationMethodVibro) { + scene_state->notification_sequence_new_token[i++] = &message_vibro_on; + } + + if(plugin_state->notification_method & NotificationMethodSound) { + scene_state->notification_sequence_new_token[i++] = &message_note_c5; + } + + scene_state->notification_sequence_new_token[i++] = &message_delay_50; + + if(plugin_state->notification_method & NotificationMethodVibro) { + scene_state->notification_sequence_new_token[i++] = &message_vibro_off; + } + + if(plugin_state->notification_method & NotificationMethodSound) { + scene_state->notification_sequence_new_token[i++] = &message_sound_off; + } + + scene_state->notification_sequence_new_token[i++] = NULL; + } + + return (NotificationSequence*)scene_state->notification_sequence_new_token; +} + +static const NotificationSequence* + get_notification_sequence_badusb(const PluginState* plugin_state, SceneState* scene_state) { + if(scene_state->notification_sequence_badusb == NULL) { + uint8_t i = 0; + uint8_t length = 3; + if(plugin_state->notification_method & NotificationMethodVibro) { + length += 2; + } + + if(plugin_state->notification_method & NotificationMethodSound) { + length += 6; + } + + scene_state->notification_sequence_badusb = malloc(sizeof(void*) * length); + furi_check(scene_state->notification_sequence_badusb != NULL); + + scene_state->notification_sequence_badusb[i++] = &message_blue_255; + if(plugin_state->notification_method & NotificationMethodVibro) { + scene_state->notification_sequence_badusb[i++] = &message_vibro_on; + } + + if(plugin_state->notification_method & NotificationMethodSound) { + scene_state->notification_sequence_badusb[i++] = &message_note_d5; //-V525 + scene_state->notification_sequence_badusb[i++] = &message_delay_50; + scene_state->notification_sequence_badusb[i++] = &message_note_e4; + scene_state->notification_sequence_badusb[i++] = &message_delay_50; + scene_state->notification_sequence_badusb[i++] = &message_note_f3; + } + + scene_state->notification_sequence_badusb[i++] = &message_delay_50; + + if(plugin_state->notification_method & NotificationMethodVibro) { + scene_state->notification_sequence_badusb[i++] = &message_vibro_off; + } + + if(plugin_state->notification_method & NotificationMethodSound) { + scene_state->notification_sequence_badusb[i++] = &message_sound_off; + } + + scene_state->notification_sequence_badusb[i++] = NULL; + } + + return (NotificationSequence*)scene_state->notification_sequence_badusb; +} + +static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) { + if(i_token_code == OTP_ERROR) { + memset(&str[0], '-', len); + } else { + for(int i = len - 1; i >= 0; i--) { + str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10); + i_token_code = i_token_code / 10; + } + } + + str[len] = '\0'; +} + +TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) { + switch(algo) { + case SHA1: + return TOTP_ALGO_SHA1; + case SHA256: + return TOTP_ALGO_SHA256; + case SHA512: + return TOTP_ALGO_SHA512; + default: + break; + } + + return NULL; +} + +void update_totp_params(PluginState* const plugin_state) { + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + + if(scene_state->current_token_index < plugin_state->tokens_count) { + TokenInfo* tokenInfo = + list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data; + + scene_state->need_token_update = true; + scene_state->last_code_name = tokenInfo->name; + } +} + +void totp_scene_generate_token_init(const PluginState* plugin_state) { + UNUSED(plugin_state); +} + +void totp_scene_generate_token_activate( + PluginState* plugin_state, + const GenerateTokenSceneContext* context) { + if(!plugin_state->token_list_loaded) { + TokenLoadingResult token_load_result = totp_config_file_load_tokens(plugin_state); + if(token_load_result != TokenLoadingResultSuccess) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, NULL, "Okay", NULL); + if(token_load_result == TokenLoadingResultWarning) { + dialog_message_set_text( + message, + "Unable to load some tokens\nPlease review conf file", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + } else if(token_load_result == TokenLoadingResultError) { + dialog_message_set_text( + message, + "Unable to load tokens\nPlease review conf file", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + } + + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + } + } + SceneState* scene_state = malloc(sizeof(SceneState)); + furi_check(scene_state != NULL); + if(context == NULL || context->current_token_index > plugin_state->tokens_count) { + scene_state->current_token_index = 0; + } else { + scene_state->current_token_index = context->current_token_index; + } + scene_state->need_token_update = true; + plugin_state->current_scene_state = scene_state; + FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset); + update_totp_params(plugin_state); + scene_state->type_code_worker_context = totp_type_code_worker_start(); + scene_state->type_code_worker_context->string = &scene_state->last_code[0]; + scene_state->type_code_worker_context->string_length = TOTP_TOKEN_DIGITS_MAX_COUNT + 1; +} + +void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) { + if(plugin_state->tokens_count == 0) { + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER - 10, + AlignCenter, + AlignCenter, + "Token list is empty"); + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER + 10, + AlignCenter, + AlignCenter, + "Press OK button to add"); + return; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + bool is_new_token_time = curr_ts % TOKEN_LIFETIME == 0; + if(is_new_token_time && scene_state->last_token_gen_time != curr_ts) { + scene_state->need_token_update = true; + } + + if(scene_state->need_token_update) { + scene_state->need_token_update = false; + scene_state->last_token_gen_time = curr_ts; + + const TokenInfo* tokenInfo = + (TokenInfo*)(list_element_at( + plugin_state->tokens_list, scene_state->current_token_index) + ->data); + + if(tokenInfo->token != NULL && tokenInfo->token_length > 0) { + furi_mutex_acquire( + scene_state->type_code_worker_context->string_sync, FuriWaitForever); + size_t key_length; + uint8_t* key = totp_crypto_decrypt( + tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length); + + int_token_to_str( + totp_at( + get_totp_algo_impl(tokenInfo->algo), + tokenInfo->digits, + key, + key_length, + curr_ts, + plugin_state->timezone_offset, + TOKEN_LIFETIME), + scene_state->last_code, + tokenInfo->digits); + memset_s(key, key_length, 0, key_length); + free(key); + } else { + furi_mutex_acquire( + scene_state->type_code_worker_context->string_sync, FuriWaitForever); + int_token_to_str(0, scene_state->last_code, tokenInfo->digits); + } + + furi_mutex_release(scene_state->type_code_worker_context->string_sync); + + if(is_new_token_time) { + notification_message( + plugin_state->notification_app, + get_notification_sequence_new_token(plugin_state, scene_state)); + } + } + + canvas_set_font(canvas, FontPrimary); + uint16_t token_name_width = canvas_string_width(canvas, scene_state->last_code_name); + if(SCREEN_WIDTH - token_name_width > 18) { + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER - 20, + AlignCenter, + AlignCenter, + scene_state->last_code_name); + } else { + canvas_draw_str_aligned( + canvas, + 9, + SCREEN_HEIGHT_CENTER - 20, + AlignLeft, + AlignCenter, + scene_state->last_code_name); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, SCREEN_HEIGHT_CENTER - 24, 9, 9); + canvas_draw_box(canvas, SCREEN_WIDTH - 10, SCREEN_HEIGHT_CENTER - 24, 9, 9); + canvas_set_color(canvas, ColorBlack); + } + + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned( + canvas, + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter, + scene_state->last_code); + + const uint8_t BAR_MARGIN = 3; + const uint8_t BAR_HEIGHT = 4; + float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME; + uint8_t barWidth = (uint8_t)((float)(SCREEN_WIDTH - (BAR_MARGIN << 1)) * percentDone); + uint8_t barX = ((SCREEN_WIDTH - (BAR_MARGIN << 1) - barWidth) >> 1) + BAR_MARGIN; + + canvas_draw_box(canvas, barX, SCREEN_HEIGHT - BAR_MARGIN - BAR_HEIGHT, barWidth, BAR_HEIGHT); + + if(plugin_state->tokens_count > 1) { + canvas_draw_icon(canvas, 0, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_left_8x9); + canvas_draw_icon( + canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); + } +} + +bool totp_scene_generate_token_handle_event( + const PluginEvent* const event, + PluginState* plugin_state) { + if(event->type != EventTypeKey) { + return true; + } + + if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { + return false; + } + + SceneState* scene_state; + if(event->input.type == InputTypeLong && event->input.key == InputKeyDown) { + scene_state = (SceneState*)plugin_state->current_scene_state; + totp_type_code_worker_notify( + scene_state->type_code_worker_context, TotpTypeCodeWorkerEventType); + notification_message( + plugin_state->notification_app, + get_notification_sequence_badusb(plugin_state, scene_state)); + return true; + } + + if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) { + return true; + } + + scene_state = (SceneState*)plugin_state->current_scene_state; + switch(event->input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + totp_roll_value_uint16_t( + &scene_state->current_token_index, + 1, + 0, + plugin_state->tokens_count - 1, + RollOverflowBehaviorRoll); + update_totp_params(plugin_state); + break; + case InputKeyLeft: + totp_roll_value_uint16_t( + &scene_state->current_token_index, + -1, + 0, + plugin_state->tokens_count - 1, + RollOverflowBehaviorRoll); + update_totp_params(plugin_state); + break; + case InputKeyOk: + if(plugin_state->tokens_count == 0) { + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); + } else { + TokenMenuSceneContext ctx = {.current_token_index = scene_state->current_token_index}; + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx); + } + break; + case InputKeyBack: + break; + default: + break; + } + + return true; +} + +void totp_scene_generate_token_deactivate(PluginState* plugin_state) { + if(plugin_state->current_scene_state == NULL) return; + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + + totp_type_code_worker_stop(scene_state->type_code_worker_context); + + if(scene_state->notification_sequence_new_token != NULL) { + free(scene_state->notification_sequence_new_token); + } + + if(scene_state->notification_sequence_badusb != NULL) { + free(scene_state->notification_sequence_badusb); + } + + free(scene_state); + plugin_state->current_scene_state = NULL; +} + +void totp_scene_generate_token_free(const PluginState* plugin_state) { + UNUSED(plugin_state); +} diff --git a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.h b/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.h new file mode 100644 index 000000000..44a3b1c0f --- /dev/null +++ b/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" +#include "../../../types/plugin_event.h" + +typedef struct { + uint16_t current_token_index; +} GenerateTokenSceneContext; + +void totp_scene_generate_token_init(const PluginState* plugin_state); +void totp_scene_generate_token_activate( + PluginState* plugin_state, + const GenerateTokenSceneContext* context); +void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state); +bool totp_scene_generate_token_handle_event( + const PluginEvent* const event, + PluginState* plugin_state); +void totp_scene_generate_token_deactivate(PluginState* plugin_state); +void totp_scene_generate_token_free(const PluginState* plugin_state); diff --git a/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.c new file mode 100644 index 000000000..dc713f0a8 --- /dev/null +++ b/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -0,0 +1,206 @@ +#include "totp_scene_token_menu.h" +#include +#include +#include "../../ui_controls.h" +#include "../../constants.h" +#include "../../scene_director.h" +#include "../../../services/config/config.h" +#include "../../../lib/list/list.h" +#include "../../../types/token_info.h" +#include "../generate_token/totp_scene_generate_token.h" +#include "../add_new_token/totp_scene_add_new_token.h" +#include "../app_settings/totp_app_settings.h" +#include "../../../types/nullable.h" +#include "../../../lib/roll_value/roll_value.h" + +#define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) +#define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1) + +typedef enum { AddNewToken, DeleteToken, AppSettings } Control; + +typedef struct { + Control selected_control; + TotpNullable_uint16_t current_token_index; +} SceneState; + +void totp_scene_token_menu_init(const PluginState* plugin_state) { + UNUSED(plugin_state); +} + +void totp_scene_token_menu_activate( + PluginState* plugin_state, + const TokenMenuSceneContext* context) { + SceneState* scene_state = malloc(sizeof(SceneState)); + furi_check(scene_state != NULL); + plugin_state->current_scene_state = scene_state; + if(context != NULL) { + TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); + } else { + TOTP_NULLABLE_NULL(scene_state->current_token_index); + } +} + +void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) { + const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + if(scene_state->current_token_index.is_null) { + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 36, + 5, + 72, + 21, + "Add new token", + scene_state->selected_control == AddNewToken); + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 36, + 39, + 72, + 21, + "Settings", + scene_state->selected_control == AppSettings); + } else { + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 36, + SCREEN_HEIGHT_THIRD_CENTER - 8, + 72, + 16, + "Add new token", + scene_state->selected_control == AddNewToken); + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 36, + SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD_CENTER - 8, + 72, + 16, + "Delete token", + scene_state->selected_control == DeleteToken); + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 36, + SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD_CENTER - 8, + 72, + 16, + "Settings", + scene_state->selected_control == AppSettings); + } +} + +bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state) { + if(event->type != EventTypeKey) { + return true; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + if(event->input.type != InputTypePress) { + return true; + } + + switch(event->input.key) { + case InputKeyUp: + totp_roll_value_uint8_t( + &scene_state->selected_control, -1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); + if(scene_state->selected_control == DeleteToken && + scene_state->current_token_index.is_null) { + scene_state->selected_control--; + } + break; + case InputKeyDown: + totp_roll_value_uint8_t( + &scene_state->selected_control, 1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); + if(scene_state->selected_control == DeleteToken && + scene_state->current_token_index.is_null) { + scene_state->selected_control++; + } + break; + case InputKeyRight: + break; + case InputKeyLeft: + break; + case InputKeyOk: + switch(scene_state->selected_control) { + case AddNewToken: { + if(scene_state->current_token_index.is_null) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, NULL); + } else { + TokenAddEditSceneContext add_new_token_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context); + } + break; + } + case DeleteToken: { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "No", NULL, "Yes"); + dialog_message_set_header(message, "Confirmation", 0, 0, AlignLeft, AlignTop); + dialog_message_set_text( + message, + "Are you sure want to delete?", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + DialogMessageButton dialog_result = + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + if(dialog_result == DialogMessageButtonRight && + !scene_state->current_token_index.is_null) { + TokenInfo* tokenInfo = NULL; + plugin_state->tokens_list = list_remove_at( + plugin_state->tokens_list, + scene_state->current_token_index.value, + (void**)&tokenInfo); + plugin_state->tokens_count--; + furi_check(tokenInfo != NULL); + token_info_free(tokenInfo); + + totp_full_save_config_file(plugin_state); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + break; + } + case AppSettings: { + if(!scene_state->current_token_index.is_null) { + AppSettingsSceneContext app_settings_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneAppSettings, &app_settings_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); + } + break; + } + default: + break; + } + break; + case InputKeyBack: { + if(!scene_state->current_token_index.is_null) { + GenerateTokenSceneContext generate_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneGenerateToken, &generate_scene_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + break; + } + default: + break; + } + + return true; +} + +void totp_scene_token_menu_deactivate(PluginState* plugin_state) { + if(plugin_state->current_scene_state == NULL) return; + + free(plugin_state->current_scene_state); + plugin_state->current_scene_state = NULL; +} + +void totp_scene_token_menu_free(const PluginState* plugin_state) { + UNUSED(plugin_state); +} diff --git a/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.h b/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.h new file mode 100644 index 000000000..059b8e571 --- /dev/null +++ b/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "../../../types/plugin_state.h" +#include "../../../types/plugin_event.h" + +typedef struct { + uint16_t current_token_index; +} TokenMenuSceneContext; + +void totp_scene_token_menu_init(const PluginState* plugin_state); +void totp_scene_token_menu_activate( + PluginState* plugin_state, + const TokenMenuSceneContext* context); +void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state); +bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state); +void totp_scene_token_menu_deactivate(PluginState* plugin_state); +void totp_scene_token_menu_free(const PluginState* plugin_state); diff --git a/applications/plugins/totp/ui/totp_scenes_enum.h b/applications/plugins/totp/ui/totp_scenes_enum.h new file mode 100644 index 000000000..0c73af772 --- /dev/null +++ b/applications/plugins/totp/ui/totp_scenes_enum.h @@ -0,0 +1,38 @@ +#pragma once + +typedef uint8_t Scene; + +/** + * @brief TOTP application scenes + */ +enum Scenes { + /** + * @brief Empty scene which does nothing + */ + TotpSceneNone, + + /** + * @brief Scene where user have to enter PIN to authenticate + */ + TotpSceneAuthentication, + + /** + * @brief Scene where actual TOTP token is getting generated and displayed to the user + */ + TotpSceneGenerateToken, + + /** + * @brief Scene where user can add new token + */ + TotpSceneAddNewToken, + + /** + * @brief Scene with a menu for given token, allowing user to do multiple actions + */ + TotpSceneTokenMenu, + + /** + * @brief Scene where user can change application settings + */ + TotpSceneAppSettings +}; diff --git a/applications/plugins/totp/services/ui/ui_controls.c b/applications/plugins/totp/ui/ui_controls.c similarity index 81% rename from applications/plugins/totp/services/ui/ui_controls.c rename to applications/plugins/totp/ui/ui_controls.c index 7f6a4dd4d..af029dd9f 100644 --- a/applications/plugins/totp/services/ui/ui_controls.c +++ b/applications/plugins/totp/ui/ui_controls.c @@ -1,11 +1,15 @@ #include "ui_controls.h" +#include #include "constants.h" -#include "icons.h" #define TEXT_BOX_HEIGHT 13 #define TEXT_BOX_MARGIN 4 -void ui_control_text_box_render(Canvas* const canvas, int8_t y, char* text, bool is_selected) { +void ui_control_text_box_render( + Canvas* const canvas, + int16_t y, + const char* text, + bool is_selected) { if(y < -TEXT_BOX_HEIGHT) { return; } @@ -44,7 +48,7 @@ void ui_control_select_render( int16_t x, int16_t y, uint8_t width, - char* text, + const char* text, bool is_selected) { if(y < -TEXT_BOX_HEIGHT) { return; @@ -77,20 +81,10 @@ void ui_control_select_render( canvas_draw_str_aligned( canvas, x + (width >> 1), TEXT_BOX_MARGIN + 3 + y, AlignCenter, AlignTop, text); - canvas_draw_xbm( - canvas, - x + TEXT_BOX_MARGIN + 2, - TEXT_BOX_MARGIN + 2 + y, - ICON_ARROW_LEFT_8x9_WIDTH, - ICON_ARROW_LEFT_8x9_HEIGHT, - &ICON_ARROW_LEFT_8x9[0]); - canvas_draw_xbm( - canvas, - x + width - TEXT_BOX_MARGIN - 10, - TEXT_BOX_MARGIN + 2 + y, - ICON_ARROW_RIGHT_8x9_WIDTH, - ICON_ARROW_RIGHT_8x9_HEIGHT, - &ICON_ARROW_RIGHT_8x9[0]); + canvas_draw_icon( + canvas, x + TEXT_BOX_MARGIN + 2, TEXT_BOX_MARGIN + 2 + y, &I_totp_arrow_left_8x9); + canvas_draw_icon( + canvas, x + width - TEXT_BOX_MARGIN - 10, TEXT_BOX_MARGIN + 2 + y, &I_totp_arrow_right_8x9); } void ui_control_button_render( @@ -99,7 +93,7 @@ void ui_control_button_render( int16_t y, uint8_t width, uint8_t height, - char* text, + const char* text, bool is_selected) { if(y < -height) { return; diff --git a/applications/plugins/totp/ui/ui_controls.h b/applications/plugins/totp/ui/ui_controls.h new file mode 100644 index 000000000..b97006a03 --- /dev/null +++ b/applications/plugins/totp/ui/ui_controls.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +/** + * @brief Renders TextBox control + * @param canvas canvas to render control at + * @param y vertical position of a control to be rendered at + * @param text text to be rendered inside control + * @param is_selected whether control should be rendered as focused or not + */ +void ui_control_text_box_render( + Canvas* const canvas, + int16_t y, + const char* text, + bool is_selected); + +/** + * @brief Renders Button control + * @param canvas canvas to render control at + * @param x horizontal position of a control to be rendered at + * @param y vertical position of a control to be rendered at + * @param width control width + * @param height control height + * @param text text to be rendered inside control + * @param is_selected whether control should be rendered as focused or not + */ +void ui_control_button_render( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t width, + uint8_t height, + const char* text, + bool is_selected); + +/** + * @brief Renders Select control + * @param canvas canvas to render control at + * @param x horizontal position of a control to be rendered at + * @param y vertical position of a control to be rendered at + * @param width control width + * @param text text to be rendered inside control + * @param is_selected whether control should be rendered as focused or not + */ +void ui_control_select_render( + Canvas* const canvas, + int16_t x, + int16_t y, + uint8_t width, + const char* text, + bool is_selected); diff --git a/applications/plugins/totp/workers/type_code/type_code.c b/applications/plugins/totp/workers/type_code/type_code.c new file mode 100644 index 000000000..3eb59047a --- /dev/null +++ b/applications/plugins/totp/workers/type_code/type_code.c @@ -0,0 +1,115 @@ +#include "type_code.h" +#include "../../services/convert/convert.h" + +static const uint8_t hid_number_keys[10] = { + HID_KEYBOARD_0, + HID_KEYBOARD_1, + HID_KEYBOARD_2, + HID_KEYBOARD_3, + HID_KEYBOARD_4, + HID_KEYBOARD_5, + HID_KEYBOARD_6, + HID_KEYBOARD_7, + HID_KEYBOARD_8, + HID_KEYBOARD_9}; + +static void totp_type_code_worker_restore_usb_mode(TotpTypeCodeWorkerContext* context) { + if(context->usb_mode_prev != NULL) { + furi_hal_usb_set_config(context->usb_mode_prev, NULL); + context->usb_mode_prev = NULL; + } +} + +static inline bool totp_type_code_worker_stop_requested() { + return furi_thread_flags_get() & TotpTypeCodeWorkerEventStop; +} + +static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context) { + context->usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); + uint8_t i = 0; + do { + furi_delay_ms(500); + i++; + } while(!furi_hal_hid_is_connected() && i < 100 && !totp_type_code_worker_stop_requested()); + + if(furi_hal_hid_is_connected() && + furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) { + furi_delay_ms(500); + i = 0; + while(i < context->string_length && context->string[i] != 0) { + uint8_t digit = CONVERT_CHAR_TO_DIGIT(context->string[i]); + if(digit > 9) break; + uint8_t hid_kb_key = hid_number_keys[digit]; + furi_hal_hid_kb_press(hid_kb_key); + furi_delay_ms(30); + furi_hal_hid_kb_release(hid_kb_key); + i++; + } + + furi_mutex_release(context->string_sync); + + furi_delay_ms(100); + } + + totp_type_code_worker_restore_usb_mode(context); +} + +static int32_t totp_type_code_worker_callback(void* context) { + ValueMutex context_mutex; + if(!init_mutex(&context_mutex, context, sizeof(TotpTypeCodeWorkerContext))) { + return 251; + } + + while(true) { + uint32_t flags = furi_thread_flags_wait( + TotpTypeCodeWorkerEventStop | TotpTypeCodeWorkerEventType, + FuriFlagWaitAny, + FuriWaitForever); + furi_check((flags & FuriFlagError) == 0); //-V562 + if(flags & TotpTypeCodeWorkerEventStop) break; + + TotpTypeCodeWorkerContext* h_context = acquire_mutex_block(&context_mutex); + if(flags & TotpTypeCodeWorkerEventType) { + totp_type_code_worker_type_code(h_context); + } + + release_mutex(&context_mutex, h_context); + } + + delete_mutex(&context_mutex); + + return 0; +} + +TotpTypeCodeWorkerContext* totp_type_code_worker_start() { + TotpTypeCodeWorkerContext* context = malloc(sizeof(TotpTypeCodeWorkerContext)); + furi_check(context != NULL); + context->string_sync = furi_mutex_alloc(FuriMutexTypeNormal); + context->thread = furi_thread_alloc(); + context->usb_mode_prev = NULL; + furi_thread_set_name(context->thread, "TOTPHidWorker"); + furi_thread_set_stack_size(context->thread, 1024); + furi_thread_set_context(context->thread, context); + furi_thread_set_callback(context->thread, totp_type_code_worker_callback); + furi_thread_start(context->thread); + return context; +} + +void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context) { + furi_assert(context != NULL); + furi_thread_flags_set(furi_thread_get_id(context->thread), TotpTypeCodeWorkerEventStop); + furi_thread_join(context->thread); + furi_thread_free(context->thread); + furi_mutex_free(context->string_sync); + totp_type_code_worker_restore_usb_mode(context); + free(context); +} + +void totp_type_code_worker_notify( + TotpTypeCodeWorkerContext* context, + TotpTypeCodeWorkerEvent event) { + furi_assert(context != NULL); + furi_thread_flags_set(furi_thread_get_id(context->thread), event); +} \ No newline at end of file diff --git a/applications/plugins/totp/workers/type_code/type_code.h b/applications/plugins/totp/workers/type_code/type_code.h new file mode 100644 index 000000000..27f2e02d4 --- /dev/null +++ b/applications/plugins/totp/workers/type_code/type_code.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +typedef uint8_t TotpTypeCodeWorkerEvent; + +typedef struct { + char* string; + uint8_t string_length; + FuriThread* thread; + FuriMutex* string_sync; + FuriHalUsbInterface* usb_mode_prev; +} TotpTypeCodeWorkerContext; + +enum TotpTypeCodeWorkerEvents { + TotpTypeCodeWorkerEventReserved = (1 << 0), + TotpTypeCodeWorkerEventStop = (1 << 1), + TotpTypeCodeWorkerEventType = (1 << 2) +}; + +TotpTypeCodeWorkerContext* totp_type_code_worker_start(); +void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context); +void totp_type_code_worker_notify( + TotpTypeCodeWorkerContext* context, + TotpTypeCodeWorkerEvent event); \ No newline at end of file diff --git a/applications/plugins/trex_runner/.editorconfig b/applications/plugins/trex_runner/.editorconfig new file mode 100644 index 000000000..8b9972089 --- /dev/null +++ b/applications/plugins/trex_runner/.editorconfig @@ -0,0 +1,30 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 4 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab + +[*.{yml,jade,js,css,lua,json}] +indent_size = 2 + +[*.go] +indent_style = tab +indent_size = 4 diff --git a/applications/plugins/trex_runner/.gitignore b/applications/plugins/trex_runner/.gitignore new file mode 100644 index 000000000..7b6e9d57b --- /dev/null +++ b/applications/plugins/trex_runner/.gitignore @@ -0,0 +1,2 @@ +/.idea/ +/cmake-build-*/ diff --git a/applications/plugins/trex_runner/LICENSE b/applications/plugins/trex_runner/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/plugins/trex_runner/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/plugins/trex_runner/Makefile b/applications/plugins/trex_runner/Makefile new file mode 100644 index 000000000..c9e3c6540 --- /dev/null +++ b/applications/plugins/trex_runner/Makefile @@ -0,0 +1,20 @@ +FLIPPER_FIRMWARE_PATH ?= /home/gelin/work/github/flipperzero-firmware/ + +.PHONY: build +build: $(FLIPPER_FIRMWARE_PATH)/applications_user/t-rex-runner + cd $(FLIPPER_FIRMWARE_PATH) && ./fbt fap_t-rex-runner + +.PHONY: launch +launch: $(FLIPPER_FIRMWARE_PATH)/applications_user/t-rex-runner + cd $(FLIPPER_FIRMWARE_PATH) && ./fbt launch_app APPSRC=applications_user/t-rex-runner + +.PHONY: assets +assets: + rm assets_icons.* + $(MAKE) assets_icons.c + +assets_icons.c: $(FLIPPER_FIRMWARE_PATH)/applications_user/t-rex-runner + cd $(FLIPPER_FIRMWARE_PATH) && ./scripts/assets.py icons applications_user/t-rex-runner/assets/ applications_user/t-rex-runner/ + +$(FLIPPER_FIRMWARE_PATH)/applications_user/t-rex-runner: + ln -s $(PWD) $(FLIPPER_FIRMWARE_PATH)/applications_user/t-rex-runner diff --git a/applications/plugins/trex_runner/README.md b/applications/plugins/trex_runner/README.md new file mode 100644 index 000000000..96ec3db00 --- /dev/null +++ b/applications/plugins/trex_runner/README.md @@ -0,0 +1,19 @@ +# t-rex-runner +Flipper Zero port of Chrome's running T-rex game + +## Compiling + +You need a full source of the [Flipper Zero firmware](https://github.com/flipperdevices/flipperzero-firmware/tree/release), +take the `release` branch to build for the stable release. + +Copy or symlink this folder as `flipperzero-firmware/applications_user/t-rex-runner`. + +Run the build from the root of the firmware folder: +``` +./fbt firmware_t-rex-runner +``` + +If you have Flipper Zero attached to USB, you can immediately compile and run the app on device: +``` +./fbt launch_app APPSRC=applications_user/t-rex-runner +``` diff --git a/applications/plugins/trex_runner/application.fam b/applications/plugins/trex_runner/application.fam new file mode 100644 index 000000000..e37bb23a1 --- /dev/null +++ b/applications/plugins/trex_runner/application.fam @@ -0,0 +1,12 @@ +App( + appid="TRex_Runner", + name="T-Rex Runner", + apptype=FlipperAppType.EXTERNAL, + entry_point="trexrunner_app", + cdefines=["APP_TREXRUNNER"], + requires=["gui"], + stack_size=8 * 1024, + fap_category="Misc", + fap_icon="trexrunner_icon.png", + order=36, +) diff --git a/applications/plugins/trex_runner/assets/Dino.png b/applications/plugins/trex_runner/assets/Dino.png new file mode 100644 index 000000000..9e33092d6 Binary files /dev/null and b/applications/plugins/trex_runner/assets/Dino.png differ diff --git a/applications/plugins/trex_runner/assets/DinoRun0.png b/applications/plugins/trex_runner/assets/DinoRun0.png new file mode 100644 index 000000000..f55faebce Binary files /dev/null and b/applications/plugins/trex_runner/assets/DinoRun0.png differ diff --git a/applications/plugins/trex_runner/assets/DinoRun1.png b/applications/plugins/trex_runner/assets/DinoRun1.png new file mode 100644 index 000000000..d1e738f5e Binary files /dev/null and b/applications/plugins/trex_runner/assets/DinoRun1.png differ diff --git a/applications/plugins/trex_runner/assets/HorizonLine0.png b/applications/plugins/trex_runner/assets/HorizonLine0.png new file mode 100644 index 000000000..b326cbb7f Binary files /dev/null and b/applications/plugins/trex_runner/assets/HorizonLine0.png differ diff --git a/applications/plugins/trex_runner/assets/HorizonLine1.png b/applications/plugins/trex_runner/assets/HorizonLine1.png new file mode 100644 index 000000000..f63a0a74e Binary files /dev/null and b/applications/plugins/trex_runner/assets/HorizonLine1.png differ diff --git a/applications/plugins/trex_runner/assets_icons.c b/applications/plugins/trex_runner/assets_icons.c new file mode 100644 index 000000000..b5c328e11 --- /dev/null +++ b/applications/plugins/trex_runner/assets_icons.c @@ -0,0 +1,68 @@ +#include "assets_icons.h" + +#include + +const uint8_t _I_Dino_0[] = { + 0x01, 0x00, 0x3d, 0x00, 0x80, 0x7e, 0x20, 0xf0, 0x0f, 0xe4, 0x3e, 0x01, 0xec, + 0x01, 0x08, 0x14, 0x80, 0x4b, 0x7c, 0x0a, 0x0f, 0xf2, 0x07, 0x01, 0x9f, 0x40, + 0x30, 0x33, 0xf8, 0x07, 0x0f, 0xff, 0x00, 0xf3, 0xef, 0xe0, 0x1f, 0xf0, 0x40, + 0x80, 0x8b, 0xfd, 0x1f, 0x0b, 0x08, 0x08, 0x7f, 0x01, 0xf1, 0xf8, 0x0c, 0x47, + 0xc1, 0x06, 0x80, 0x58, 0x20, 0x90, 0x09, 0x00, 0x08, 0x6c, 0x10, 0xc8, 0x00, +}; +const uint8_t* const _I_Dino[] = {_I_Dino_0}; + +const uint8_t _I_DinoRun0_0[] = { + 0x01, 0x00, 0x3b, 0x00, 0x80, 0x7e, 0x20, 0xf0, 0x0f, 0xe4, 0x3e, 0x01, 0xec, 0x01, 0x08, 0x14, + 0x80, 0x4b, 0x7c, 0x0a, 0x0f, 0xf2, 0x07, 0x01, 0x9f, 0x40, 0x30, 0x33, 0xf8, 0x07, 0x0f, 0xff, + 0x00, 0xf3, 0xef, 0xe0, 0x1f, 0xf0, 0x40, 0x80, 0x8b, 0xfd, 0x1f, 0x0b, 0x08, 0x08, 0x7f, 0x01, + 0xf1, 0xf8, 0x0c, 0x47, 0xc1, 0x0c, 0x80, 0x58, 0x04, 0xa3, 0x20, 0x01, 0x08, 0x14, 0x80, +}; +const uint8_t* const _I_DinoRun0[] = {_I_DinoRun0_0}; + +const uint8_t _I_DinoRun1_0[] = { + 0x01, 0x00, 0x3b, 0x00, 0x80, 0x7e, 0x20, 0xf0, 0x0f, 0xe4, 0x3e, 0x01, 0xec, 0x01, 0x08, 0x14, + 0x80, 0x4b, 0x7c, 0x0a, 0x0f, 0xf2, 0x07, 0x01, 0x9f, 0x40, 0x30, 0x33, 0xf8, 0x07, 0x0f, 0xff, + 0x00, 0xf3, 0xef, 0xe0, 0x1f, 0xf0, 0x40, 0x80, 0x8b, 0xfd, 0x1f, 0x0b, 0x08, 0x08, 0x7f, 0x01, + 0xf1, 0xf8, 0x0c, 0x46, 0xc1, 0x06, 0x80, 0x70, 0x20, 0x82, 0x61, 0x01, 0x14, 0x32, 0x00, +}; +const uint8_t* const _I_DinoRun1[] = {_I_DinoRun1_0}; + +const uint8_t _I_HorizonLine0_0[] = { + 0x01, 0x00, 0x9c, 0x00, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, + 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, + 0x03, 0xc0, 0x17, 0xff, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, 0xf0, 0x04, 0x96, 0xbc, 0x01, 0xe0, + 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, 0xf0, 0x07, 0x80, 0x3c, 0x01, 0x78, 0x08, 0x18, 0x2e, + 0xa0, 0xe0, 0x97, 0x8e, 0x09, 0x7a, 0x3c, 0x36, 0xf1, 0xa6, 0xf0, 0x08, 0x91, 0x20, 0x05, 0x88, + 0x66, 0x01, 0x27, 0x80, 0x01, 0x95, 0x00, 0x23, 0x10, 0x60, 0x21, 0xc1, 0x20, 0x1e, 0x00, 0xfb, + 0x40, 0x40, 0xeb, 0x0e, 0x02, 0xa5, 0x8a, 0x01, 0x38, 0x2d, 0x78, 0x03, 0xc9, 0x86, 0x00, 0x48, + 0x40, 0x5b, 0x21, 0x80, 0x98, 0x0b, 0x91, 0x13, 0xe0, 0x48, 0x74, 0x9e, 0x1f, 0xaa, 0xe0, 0x1a, + 0x7a, 0x00, 0xe0, 0x5c, 0xfe, 0x00, 0x66, 0x74, 0x00, 0x0e, 0x20, 0x03, 0x28, 0x7b, 0x55, 0x21, + 0xe1, 0x27, 0x61, 0x08, 0x03, 0xd4, 0x50, 0x3a, 0x98, 0x78, 0xe0, 0xc0, 0x0f, 0x30, 0x26, 0x40, +}; +const uint8_t* const _I_HorizonLine0[] = {_I_HorizonLine0_0}; + +const uint8_t _I_HorizonLine1_0[] = { + 0x01, 0x00, 0xc6, 0x00, 0x00, 0x7f, 0x02, 0x1e, 0x11, 0xf0, 0x07, 0x80, 0x17, 0x01, 0x1f, 0x01, + 0x18, 0x03, 0xc0, 0x19, 0x60, 0x8c, 0x04, 0x7c, 0x01, 0xe7, 0x26, 0x88, 0x00, 0x8c, 0x01, 0xe0, + 0x0c, 0x9c, 0x4c, 0x02, 0x3e, 0x00, 0xf0, 0x02, 0xdc, 0x2c, 0x00, 0x23, 0x00, 0x78, 0x03, 0x21, + 0x9c, 0x00, 0x8f, 0x80, 0x3c, 0x00, 0xb1, 0x8c, 0x04, 0x06, 0x18, 0x02, 0x10, 0x96, 0x7f, 0xc0, + 0x1d, 0x07, 0xc0, 0x00, 0xc6, 0x07, 0xf8, 0x0a, 0xf8, 0x03, 0xc0, 0x03, 0x0f, 0x1c, 0x8c, 0x1c, + 0x79, 0xc6, 0xdf, 0x00, 0x1c, 0x1a, 0x18, 0x11, 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x08, 0xfe, + 0x1b, 0xc4, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x02, 0x70, 0x10, 0x30, 0x5d, 0x12, 0x22, 0x38, 0x25, + 0xe8, 0xf0, 0xdb, 0xc6, 0x9b, 0xc0, 0x22, 0x44, 0x80, 0x16, 0x21, 0x98, 0x04, 0x9e, 0x00, 0x06, + 0x54, 0x07, 0xa4, 0x81, 0x0e, 0x09, 0x00, 0xf0, 0x07, 0xda, 0x02, 0x07, 0x58, 0x70, 0x15, 0x2c, + 0x50, 0x09, 0xc1, 0x6b, 0xc0, 0x1e, 0x4c, 0x30, 0x02, 0x42, 0x02, 0xd9, 0x0c, 0x04, 0xc0, 0x5c, + 0x88, 0x9f, 0x02, 0x43, 0xa4, 0xf0, 0xfd, 0x57, 0x00, 0xd3, 0xd0, 0x07, 0x02, 0xe7, 0xf0, 0x03, + 0x33, 0xa0, 0x00, 0x71, 0x00, 0x19, 0x43, 0xda, 0xa9, 0x0f, 0x09, 0x3b, 0x08, 0x40, 0x1e, 0xa2, + 0x81, 0xd4, 0xc3, 0xc7, 0x06, 0x00, 0x79, 0x81, 0x32, 0x00, +}; +const uint8_t* const _I_HorizonLine1[] = {_I_HorizonLine1_0}; + +const Icon I_Dino = + {.width = 20, .height = 22, .frame_count = 1, .frame_rate = 0, .frames = _I_Dino}; +const Icon I_DinoRun0 = + {.width = 20, .height = 22, .frame_count = 1, .frame_rate = 0, .frames = _I_DinoRun0}; +const Icon I_DinoRun1 = + {.width = 20, .height = 22, .frame_count = 1, .frame_rate = 0, .frames = _I_DinoRun1}; +//const Icon I_HorizonLine0 = {.width=600,.height=12,.frame_count=1,.frame_rate=0,.frames=_I_HorizonLine0}; +//const Icon I_HorizonLine1 = {.width=600,.height=12,.frame_count=1,.frame_rate=0,.frames=_I_HorizonLine1}; diff --git a/applications/plugins/trex_runner/assets_icons.h b/applications/plugins/trex_runner/assets_icons.h new file mode 100644 index 000000000..05fe6f332 --- /dev/null +++ b/applications/plugins/trex_runner/assets_icons.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +extern const Icon I_Dino; +extern const Icon I_DinoRun0; +extern const Icon I_DinoRun1; +//extern const Icon I_HorizonLine0; +//extern const Icon I_HorizonLine1; diff --git a/applications/plugins/trex_runner/trexrunner.c b/applications/plugins/trex_runner/trexrunner.c new file mode 100644 index 000000000..699021b6a --- /dev/null +++ b/applications/plugins/trex_runner/trexrunner.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include + +#include "assets_icons.h" + +#define DINO_START_X 0 +#define DINO_START_Y 42 + +#define FPS 60 + +#define DINO_RUNNING_MS_PER_FRAME 100 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + FuriTimer* timer; + uint32_t last_tick; + const Icon* dino_icon; + int dino_frame_ms; +} GameState; + +static void timer_callback(void* ctx) { + GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); + if(game_state == NULL) { + return; + } + + uint32_t ticks_elapsed = furi_get_tick() - game_state->last_tick; + int delta_time_ms = ticks_elapsed * 1000 / furi_kernel_get_tick_frequency(); + + // dino update + game_state->dino_frame_ms += delta_time_ms; + // TODO: switch by dino state + if(game_state->dino_frame_ms >= DINO_RUNNING_MS_PER_FRAME) { + if(game_state->dino_icon == &I_DinoRun0) { + game_state->dino_icon = &I_DinoRun1; + } else { + game_state->dino_icon = &I_DinoRun0; + } + game_state->dino_frame_ms = 0; + } + + release_mutex((ValueMutex*)ctx, game_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); + if(game_state == NULL) { + return; + } + + // canvas_draw_xbm(canvas, 0, 0, dino_width, dino_height, dino_bits); + canvas_draw_icon(canvas, DINO_START_X, DINO_START_Y, game_state->dino_icon); + + release_mutex((ValueMutex*)ctx, game_state); +} + +static void game_state_init(GameState* const game_state) { + game_state->last_tick = furi_get_tick(); + game_state->dino_frame_ms = 0; + game_state->dino_icon = &I_Dino; +} + +int32_t trexrunner_app(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + game_state_init(game_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, game_state, sizeof(game_state))) { + FURI_LOG_E("T-rex runner", "cannot create mutex\r\n"); + free(game_state); + return 255; + } + // BEGIN IMPLEMENTATION + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + game_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, &state_mutex); + + furi_timer_start(game_state->timer, (uint32_t)furi_kernel_get_tick_frequency() / FPS); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + // Minesweeper* minesweeper_state = (Minesweeper*)acquire_mutex_block(&state_mutex); + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyLeft: + break; + case InputKeyRight: + break; + case InputKeyOk: + break; + case InputKeyBack: + // Exit the app + processing = false; + break; + } + } + } + } else { + // event timeout + ; + } + view_port_update(view_port); + release_mutex(&state_mutex, game_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + furi_timer_free(game_state->timer); + free(game_state); + + return 0; +} diff --git a/applications/plugins/trex_runner/trexrunner_icon.png b/applications/plugins/trex_runner/trexrunner_icon.png new file mode 100644 index 000000000..0b8329327 Binary files /dev/null and b/applications/plugins/trex_runner/trexrunner_icon.png differ diff --git a/applications/plugins/tuning_fork/LICENSE b/applications/plugins/tuning_fork/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/plugins/tuning_fork/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/plugins/Tuning Fork/README.md b/applications/plugins/tuning_fork/README.md similarity index 100% rename from applications/plugins/Tuning Fork/README.md rename to applications/plugins/tuning_fork/README.md diff --git a/applications/plugins/Tuning Fork/application.fam b/applications/plugins/tuning_fork/application.fam similarity index 100% rename from applications/plugins/Tuning Fork/application.fam rename to applications/plugins/tuning_fork/application.fam diff --git a/applications/plugins/Tuning Fork/img/screenshot_1.png b/applications/plugins/tuning_fork/img/screenshot_1.png similarity index 100% rename from applications/plugins/Tuning Fork/img/screenshot_1.png rename to applications/plugins/tuning_fork/img/screenshot_1.png diff --git a/applications/plugins/Tuning Fork/img/screenshot_2.png b/applications/plugins/tuning_fork/img/screenshot_2.png similarity index 100% rename from applications/plugins/Tuning Fork/img/screenshot_2.png rename to applications/plugins/tuning_fork/img/screenshot_2.png diff --git a/applications/plugins/Tuning Fork/img/tuning_fork.gif b/applications/plugins/tuning_fork/img/tuning_fork.gif similarity index 100% rename from applications/plugins/Tuning Fork/img/tuning_fork.gif rename to applications/plugins/tuning_fork/img/tuning_fork.gif diff --git a/applications/plugins/Tuning Fork/notes.h b/applications/plugins/tuning_fork/notes.h similarity index 99% rename from applications/plugins/Tuning Fork/notes.h rename to applications/plugins/tuning_fork/notes.h index dac0fbc88..c00b4f8ed 100644 --- a/applications/plugins/Tuning Fork/notes.h +++ b/applications/plugins/tuning_fork/notes.h @@ -1,7 +1,6 @@ #ifndef NOTES #define NOTES - #define C0 16.35f #define Cs0 17.32f #define Db0 17.32f @@ -157,4 +156,3 @@ #define B8 7902.13f #endif //NOTES - diff --git a/applications/plugins/tuning_fork/tuning_fork.c b/applications/plugins/tuning_fork/tuning_fork.c new file mode 100644 index 000000000..ab39e1f14 --- /dev/null +++ b/applications/plugins/tuning_fork/tuning_fork.c @@ -0,0 +1,402 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "notes.h" +#include "tunings.h" + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +enum Page { Tunings, Notes }; + +typedef struct { + bool playing; + enum Page page; + int current_tuning_note_index; + int current_tuning_index; + float volume; + TUNING tuning; +} TuningForkState; + +static TUNING current_tuning(TuningForkState* tuningForkState) { + return tuningForkState->tuning; +} + +static NOTE current_tuning_note(TuningForkState* tuningForkState) { + return current_tuning(tuningForkState).notes[tuningForkState->current_tuning_note_index]; +} + +static float current_tuning_note_freq(TuningForkState* tuningForkState) { + return current_tuning_note(tuningForkState).frequency; +} + +static void current_tuning_note_label(TuningForkState* tuningForkState, char* outNoteLabel) { + for(int i = 0; i < 20; ++i) { + outNoteLabel[i] = current_tuning_note(tuningForkState).label[i]; + } +} + +static void current_tuning_label(TuningForkState* tuningForkState, char* outTuningLabel) { + for(int i = 0; i < 20; ++i) { + outTuningLabel[i] = current_tuning(tuningForkState).label[i]; + } +} + +static void updateTuning(TuningForkState* tuning_fork_state) { + tuning_fork_state->tuning = TuningList[tuning_fork_state->current_tuning_index]; + tuning_fork_state->current_tuning_note_index = 0; +} + +static void next_tuning(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->current_tuning_index == TUNINGS_COUNT - 1) { + tuning_fork_state->current_tuning_index = 0; + } else { + tuning_fork_state->current_tuning_index += 1; + } + updateTuning(tuning_fork_state); +} + +static void prev_tuning(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->current_tuning_index - 1 < 0) { + tuning_fork_state->current_tuning_index = TUNINGS_COUNT - 1; + } else { + tuning_fork_state->current_tuning_index -= 1; + } + updateTuning(tuning_fork_state); +} + +static void next_note(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->current_tuning_note_index == + current_tuning(tuning_fork_state).notes_length - 1) { + tuning_fork_state->current_tuning_note_index = 0; + } else { + tuning_fork_state->current_tuning_note_index += 1; + } +} + +static void prev_note(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->current_tuning_note_index == 0) { + tuning_fork_state->current_tuning_note_index = + current_tuning(tuning_fork_state).notes_length - 1; + } else { + tuning_fork_state->current_tuning_note_index -= 1; + } +} + +static void increase_volume(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->volume < 1.0f) { + tuning_fork_state->volume += 0.1f; + } +} + +static void decrease_volume(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->volume > 0.0f) { + tuning_fork_state->volume -= 0.1f; + } +} + +static void play(TuningForkState* tuning_fork_state) { + furi_hal_speaker_start(current_tuning_note_freq(tuning_fork_state), tuning_fork_state->volume); +} + +static void stop() { + furi_hal_speaker_stop(); +} + +static void replay(TuningForkState* tuning_fork_state) { + stop(); + play(tuning_fork_state); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + TuningForkState* tuning_fork_state = acquire_mutex((ValueMutex*)ctx, 25); + if(tuning_fork_state == NULL) { + return; + } + + string_t tempStr; + string_init(tempStr); + + canvas_draw_frame(canvas, 0, 0, 128, 64); + + canvas_set_font(canvas, FontPrimary); + + if(tuning_fork_state->page == Tunings) { + char tuningLabel[20]; + current_tuning_label(tuning_fork_state, tuningLabel); + string_printf(tempStr, "< %s >", tuningLabel); + canvas_draw_str_aligned( + canvas, 64, 28, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + } else { + char tuningLabel[20]; + current_tuning_label(tuning_fork_state, tuningLabel); + string_printf(tempStr, "%s", tuningLabel); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + + char tuningNoteLabel[20]; + current_tuning_note_label(tuning_fork_state, tuningNoteLabel); + string_printf(tempStr, "< %s >", tuningNoteLabel); + canvas_draw_str_aligned( + canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + } + + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "Prev"); + elements_button_right(canvas, "Next"); + + if(tuning_fork_state->page == Notes) { + if(tuning_fork_state->playing) { + elements_button_center(canvas, "Stop "); + } else { + elements_button_center(canvas, "Play"); + } + } else { + elements_button_center(canvas, "Select"); + } + if(tuning_fork_state->page == Notes) { + elements_progress_bar(canvas, 8, 36, 112, tuning_fork_state->volume); + } + + string_clear(tempStr); + release_mutex((ValueMutex*)ctx, tuning_fork_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void tuning_fork_state_init(TuningForkState* const tuning_fork_state) { + tuning_fork_state->playing = false; + tuning_fork_state->page = Tunings; + tuning_fork_state->volume = 1.0f; + tuning_fork_state->tuning = GuitarStandard6; + tuning_fork_state->current_tuning_index = 2; + tuning_fork_state->current_tuning_note_index = 0; +} + +int32_t tuning_fork_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + TuningForkState* tuning_fork_state = malloc(sizeof(TuningForkState)); + tuning_fork_state_init(tuning_fork_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, tuning_fork_state, sizeof(TuningForkState))) { + FURI_LOG_E("TuningFork", "cannot create mutex\r\n"); + free(tuning_fork_state); + return 255; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + TuningForkState* tuning_fork_state = (TuningForkState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeShort) { + // push events + switch(event.input.key) { + case InputKeyUp: + if(tuning_fork_state->page == Notes) { + increase_volume(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyDown: + if(tuning_fork_state->page == Notes) { + decrease_volume(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyRight: + if(tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyLeft: + if(tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyOk: + if(tuning_fork_state->page == Tunings) { + tuning_fork_state->page = Notes; + } else { + tuning_fork_state->playing = !tuning_fork_state->playing; + if(tuning_fork_state->playing) { + play(tuning_fork_state); + } else { + stop(); + } + } + break; + case InputKeyBack: + if(tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + tuning_fork_state->current_tuning_note_index = 0; + stop(); + tuning_fork_state->page = Tunings; + } + break; + default: + break; + } + } else if(event.input.type == InputTypeLong) { + // hold events + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + if(tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyLeft: + if(tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyOk: + break; + case InputKeyBack: + if(tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + stop(); + tuning_fork_state->page = Tunings; + tuning_fork_state->current_tuning_note_index = 0; + } + break; + default: + break; + } + } else if(event.input.type == InputTypeRepeat) { + // repeat events + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + if(tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyLeft: + if(tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyOk: + break; + case InputKeyBack: + if(tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + stop(); + tuning_fork_state->page = Tunings; + tuning_fork_state->current_tuning_note_index = 0; + } + break; + default: + break; + } + } + } + } else { + FURI_LOG_D("TuningFork", "FuriMessageQueue: event timeout"); + } + + view_port_update(view_port); + release_mutex(&state_mutex, tuning_fork_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + furi_record_close(RECORD_NOTIFICATION); + free(tuning_fork_state); + + return 0; +} diff --git a/applications/plugins/Tuning Fork/tuning_fork_icon.png b/applications/plugins/tuning_fork/tuning_fork_icon.png similarity index 100% rename from applications/plugins/Tuning Fork/tuning_fork_icon.png rename to applications/plugins/tuning_fork/tuning_fork_icon.png diff --git a/applications/plugins/tuning_fork/tunings.h b/applications/plugins/tuning_fork/tunings.h new file mode 100644 index 000000000..14bf469fe --- /dev/null +++ b/applications/plugins/tuning_fork/tunings.h @@ -0,0 +1,151 @@ +#include "notes.h" + +#ifndef TUNINGS +#define TUNINGS + +typedef struct { + char label[20]; + float frequency; +} NOTE; + +typedef struct { + char label[20]; + int notes_length; + NOTE notes[20]; +} TUNING; + +const TUNING TuningForks = { + "Tuning forks", + 6, + { + {"Common A4 (440)", 440.00f}, + {"Sarti's A4 (436)", 436.00f}, + {"1858 A4 (435)", 435.00f}, + {"Verdi's A4 (432)", 432.00f}, + {"1750-1820 A4 (423.5)", 423.50f}, + {"Verdi's C4 (256.00)", 256.00f}, + }}; + +const TUNING ScientificPitch = { + "Scientific pitch", + 12, + {{"C0 (16Hz)", 16.0f}, + {"C1 (32Hz)", 32.0f}, + {"C2 (64Hz)", 64.0f}, + {"C3 (128Hz)", 128.0f}, + {"C4 (256Hz)", 256.0f}, + {"C5 (512Hz)", 512.0f}, + {"C6 (1024Hz)", 1024.0f}, + {"C7 (2048Hz)", 2048.0f}, + {"C8 (4096Hz)", 4096.0f}, + {"C9 (8192Hz)", 8192.0f}, + {"C10 (16384Hz)", 16384.0f}, + {"C11 (32768Hz)", 32768.0f}}}; + +const TUNING GuitarStandard6 = { + "Guitar Standard 6", + 6, + {{"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", E2}}}; + +const TUNING GuitarDropD6 = { + "Guitar Drop D 6", + 6, + {{"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", D2}}}; + +const TUNING GuitarD6 = { + "Guitar D 6", + 6, + {{"String 1", D4}, + {"String 2", A3}, + {"String 3", F3}, + {"String 4", C3}, + {"String 5", G2}, + {"String 6", D2}}}; + +const TUNING GuitarDropC6 = { + "Guitar Drop C 6", + 6, + {{"String 1", D4}, + {"String 2", A3}, + {"String 3", F3}, + {"String 4", C3}, + {"String 5", G2}, + {"String 6", C2}}}; + +const TUNING GuitarStandard7 = { + "Guitar Standard 7", + 7, + {{"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", E2}, + {"String 7", B1}}}; + +const TUNING BassStandard4 = { + "Bass Standard 4", + 4, + {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}}}; + +const TUNING BassStandardTenor4 = { + "Bass Stand Tenor 4", + 4, + {{"String 1", C3}, {"String 2", G2}, {"String 3", D2}, {"String 4", A1}}}; + +const TUNING BassStandard5 = { + "Bass Standard 5", + 5, + {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}, {"String 5", B0}}}; + +const TUNING BassStandardTenor5 = { + "Bass Stand Tenor 5", + 5, + {{"String 1", C3}, {"String 2", G2}, {"String 3", D2}, {"String 4", A1}, {"String 5", E1}}}; + +const TUNING BassDropD4 = { + "Bass Drop D 4", + 4, + {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", D1}}}; + +const TUNING BassD4 = { + "Bass D 4", + 4, + {{"String 1", F2}, {"String 2", C2}, {"String 3", G1}, {"String 4", D1}}}; + +const TUNING BassDropA5 = { + "Bass Drop A 5", + 5, + {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}, {"String 5", A0}}}; + +#define TUNINGS_COUNT 14 + +TUNING TuningList[TUNINGS_COUNT] = { + ScientificPitch, + TuningForks, + + GuitarStandard6, + GuitarDropD6, + GuitarD6, + GuitarDropC6, + GuitarStandard7, + + BassStandard4, + BassStandardTenor4, + BassStandard5, + BassStandardTenor5, + BassDropD4, + BassD4, + BassDropA5}; + +#endif //TUNINGS diff --git a/applications/plugins/usb_hid_autofire/.gitignore b/applications/plugins/usb_hid_autofire/.gitignore new file mode 100644 index 000000000..e4e5f6c8b --- /dev/null +++ b/applications/plugins/usb_hid_autofire/.gitignore @@ -0,0 +1 @@ +*~ \ No newline at end of file diff --git a/applications/plugins/usb_hid_autofire/CHANGELOG.md b/applications/plugins/usb_hid_autofire/CHANGELOG.md new file mode 100644 index 000000000..d2da0910d --- /dev/null +++ b/applications/plugins/usb_hid_autofire/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## 0.2 +- update icon + +## 0.1 +- initial release of the USB HID Autofire application diff --git a/applications/plugins/usb_hid_autofire/LICENSE b/applications/plugins/usb_hid_autofire/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/plugins/usb_hid_autofire/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/plugins/usb_hid_autofire/README.md b/applications/plugins/usb_hid_autofire/README.md new file mode 100644 index 000000000..b2cbd7606 --- /dev/null +++ b/applications/plugins/usb_hid_autofire/README.md @@ -0,0 +1,32 @@ +# USB HID Autofire + +[GitHub](https://github.com/pbek/usb_hid_autofire) | +[Latest release](https://github.com/pbek/usb_hid_autofire/releases/latest) | +[Changelog](CHANGELOG.md) | +[License](LICENSE.md) + +[![Build](https://github.com/pbek/usb_hid_autofire/actions/workflows/build-test.yml/badge.svg)](https://github.com/pbek/usb_hid_autofire/actions/workflows/build-test.yml) + +This is a simple Flipper Zero application to send left-clicks as a USB HID device. + +![Screenshot](screenshot.png) + +## Installation + +Download the [latest release](https://github.com/pbek/usb_hid_autofire/releases/latest) +of the *fap* file and put it into the `apps` folder on your SD card of your Flipper Zero. + +## Building + +```shell +cd applications_user +git clone https://github.com/pbek/usb_hid_autofire.git + +cd .. + +# Build the application +./fbt fap_usb_hid_autofire + +# Build and launch the application +./fbt launch_app APPSRC=usb_hid_autofire +``` diff --git a/applications/plugins/usb_hid_autofire/application.fam b/applications/plugins/usb_hid_autofire/application.fam new file mode 100644 index 000000000..9e7b9378c --- /dev/null +++ b/applications/plugins/usb_hid_autofire/application.fam @@ -0,0 +1,13 @@ +App( + appid="usb_hid_autofire", + name="USB HID Autofire", + apptype=FlipperAppType.EXTERNAL, + entry_point="usb_hid_autofire_app", + cdefines=["APP_USB_HID_AUTOFIRE"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + fap_icon="usb_hid_autofire.png", + fap_category="Misc", +) diff --git a/applications/plugins/usb_hid_autofire/screenshot.png b/applications/plugins/usb_hid_autofire/screenshot.png new file mode 100644 index 000000000..d2053c13d Binary files /dev/null and b/applications/plugins/usb_hid_autofire/screenshot.png differ diff --git a/applications/plugins/usb_hid_autofire/usb_hid_autofire.c b/applications/plugins/usb_hid_autofire/usb_hid_autofire.c new file mode 100644 index 000000000..2e7a096d9 --- /dev/null +++ b/applications/plugins/usb_hid_autofire/usb_hid_autofire.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include "version.h" + +// Uncomment to be able to make a screenshot +//#define USB_HID_AUTOFIRE_SCREENSHOT + +typedef enum { + EventTypeInput, +} EventType; + +typedef struct { + union { + InputEvent input; + }; + EventType type; +} UsbMouseEvent; + +bool btn_left_autofire = false; + +static void usb_hid_autofire_render_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + canvas_clear(canvas); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "USB HID Autofire"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 90, 10, "v"); + canvas_draw_str(canvas, 96, 10, VERSION); + canvas_draw_str(canvas, 0, 22, "Press [ok] for auto left clicking"); + canvas_draw_str(canvas, 0, 34, btn_left_autofire ? "" : ""); + canvas_draw_str(canvas, 0, 63, "Press [back] to exit"); +} + +static void usb_hid_autofire_input_callback(InputEvent* input_event, void* ctx) { + FuriMessageQueue* event_queue = ctx; + + UsbMouseEvent event; + event.type = EventTypeInput; + event.input = *input_event; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +//void wait(int ticks) { +// for (int i = 0; i < ticks; i++) {} +//} + +int32_t usb_hid_autofire_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent)); + furi_check(event_queue); + ViewPort* view_port = view_port_alloc(); + + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); +#ifndef USB_HID_AUTOFIRE_SCREENSHOT + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); +#endif + + view_port_draw_callback_set(view_port, usb_hid_autofire_render_callback, NULL); + view_port_input_callback_set(view_port, usb_hid_autofire_input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + UsbMouseEvent event; + while(1) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.key == InputKeyBack) { + break; + } + + if(event.input.key == InputKeyOk && event.input.type == InputTypeRelease) { + btn_left_autofire = !btn_left_autofire; + } + } + } + + if(btn_left_autofire) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); + // wait(100); + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + // wait(100); + } + + view_port_update(view_port); + } + + furi_hal_usb_set_config(usb_mode_prev, NULL); + + // remove & free all stuff created by app + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + return 0; +} diff --git a/applications/plugins/usb_hid_autofire/usb_hid_autofire.kra b/applications/plugins/usb_hid_autofire/usb_hid_autofire.kra new file mode 100644 index 000000000..21d416548 Binary files /dev/null and b/applications/plugins/usb_hid_autofire/usb_hid_autofire.kra differ diff --git a/applications/plugins/usb_hid_autofire/usb_hid_autofire.png b/applications/plugins/usb_hid_autofire/usb_hid_autofire.png new file mode 100644 index 000000000..369bff022 Binary files /dev/null and b/applications/plugins/usb_hid_autofire/usb_hid_autofire.png differ diff --git a/applications/plugins/usb_hid_autofire/usb_hid_autofire.svg b/applications/plugins/usb_hid_autofire/usb_hid_autofire.svg new file mode 100644 index 000000000..ed66f3cfd --- /dev/null +++ b/applications/plugins/usb_hid_autofire/usb_hid_autofire.svg @@ -0,0 +1,75 @@ + + + + + + + + + A + + diff --git a/applications/plugins/usb_hid_autofire/version.h b/applications/plugins/usb_hid_autofire/version.h new file mode 100644 index 000000000..0502a24e9 --- /dev/null +++ b/applications/plugins/usb_hid_autofire/version.h @@ -0,0 +1 @@ +#define VERSION "0.2" diff --git a/applications/plugins/usb_midi/.gitattributes b/applications/plugins/usb_midi/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/applications/plugins/usb_midi/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/applications/plugins/usb_midi/.gitignore b/applications/plugins/usb_midi/.gitignore new file mode 100644 index 000000000..c6127b38c --- /dev/null +++ b/applications/plugins/usb_midi/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/applications/plugins/usb_midi/application.fam b/applications/plugins/usb_midi/application.fam new file mode 100644 index 000000000..d25ec2dfd --- /dev/null +++ b/applications/plugins/usb_midi/application.fam @@ -0,0 +1,14 @@ +App( + appid="USB_Midi", + name="USB Midi", + apptype=FlipperAppType.PLUGIN, + entry_point="usb_midi_app", + requires=[ + "gui", + ], + stack_size=4 * 1024, + order=20, + fap_icon="usb_midi.png", + fap_category="NeedsTesting", + # fap_icon_assets="icons", +) diff --git a/applications/plugins/usb_midi/midi/config.h b/applications/plugins/usb_midi/midi/config.h new file mode 100644 index 000000000..c62c1b1ef --- /dev/null +++ b/applications/plugins/usb_midi/midi/config.h @@ -0,0 +1,3 @@ +#include + +#define SYSEX_BUFFER_LEN 16 \ No newline at end of file diff --git a/applications/plugins/usb_midi/midi/message.c b/applications/plugins/usb_midi/midi/message.c new file mode 100644 index 000000000..7bee9816a --- /dev/null +++ b/applications/plugins/usb_midi/midi/message.c @@ -0,0 +1,144 @@ +#include "message.h" + +/** Returns the data within the MidiEvent as a NoteOffEvent struct */ +NoteOffEvent AsNoteOff(MidiEvent* event) { + NoteOffEvent m; + m.channel = event->channel; + m.note = event->data[0]; + m.velocity = event->data[1]; + return m; +} + +/** Returns the data within the MidiEvent as a NoteOnEvent struct */ +NoteOnEvent AsNoteOn(MidiEvent* event) { + NoteOnEvent m; + m.channel = event->channel; + m.note = event->data[0]; + m.velocity = event->data[1]; + return m; +} + +/** Returns the data within the MidiEvent as a PolyphonicKeyPressureEvent struct */ +PolyphonicKeyPressureEvent AsPolyphonicKeyPressure(MidiEvent* event) { + PolyphonicKeyPressureEvent m; + m.channel = event->channel; + m.note = event->data[0]; + m.pressure = event->data[1]; + return m; +} + +/** Returns the data within the MidiEvent as a ControlChangeEvent struct.*/ +ControlChangeEvent AsControlChange(MidiEvent* event) { + ControlChangeEvent m; + m.channel = event->channel; + m.control_number = event->data[0]; + m.value = event->data[1]; + return m; +} + +/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/ +ProgramChangeEvent AsProgramChange(MidiEvent* event) { + ProgramChangeEvent m; + m.channel = event->channel; + m.program = event->data[0]; + return m; +} + +/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/ +ChannelPressureEvent AsChannelPressure(MidiEvent* event) { + ChannelPressureEvent m; + m.channel = event->channel; + m.pressure = event->data[0]; + return m; +} + +/** Returns the data within the MidiEvent as a PitchBendEvent struct.*/ +PitchBendEvent AsPitchBend(MidiEvent* event) { + PitchBendEvent m; + m.channel = event->channel; + m.value = ((uint16_t)(event->data[1]) << 7) + (event->data[0] - 8192); + return m; +} + +SystemExclusiveEvent AsSystemExclusive(MidiEvent* event) { + SystemExclusiveEvent m; + m.length = event->sysex_message_len; + for(int i = 0; i < SYSEX_BUFFER_LEN; i++) { + m.data[i] = 0; + if(i < m.length) { + m.data[i] = event->sysex_data[i]; + } + } + return m; +} + +MTCQuarterFrameEvent AsMTCQuarterFrame(MidiEvent* event) { + MTCQuarterFrameEvent m; + m.message_type = (event->data[0] & 0x70) >> 4; + m.value = (event->data[0]) & 0x0f; + return m; +} + +SongPositionPointerEvent AsSongPositionPointer(MidiEvent* event) { + SongPositionPointerEvent m; + m.position = ((uint16_t)(event->data[1]) << 7) | (event->data[0]); + return m; +} + +SongSelectEvent AsSongSelect(MidiEvent* event) { + SongSelectEvent m; + m.song = event->data[0]; + return m; +} + +AllSoundOffEvent AsAllSoundOff(MidiEvent* event) { + AllSoundOffEvent m; + m.channel = event->channel; + return m; +} + +ResetAllControllersEvent AsResetAllControllers(MidiEvent* event) { + ResetAllControllersEvent m; + m.channel = event->channel; + m.value = event->data[1]; + return m; +} + +LocalControlEvent AsLocalControl(MidiEvent* event) { + LocalControlEvent m; + m.channel = event->channel; + m.local_control_off = (event->data[1] == 0); + m.local_control_on = (event->data[1] == 127); + return m; +} + +AllNotesOffEvent AsAllNotesOff(MidiEvent* event) { + AllNotesOffEvent m; + m.channel = event->channel; + return m; +} + +OmniModeOffEvent AsOmniModeOff(MidiEvent* event) { + OmniModeOffEvent m; + m.channel = event->channel; + return m; +} + +OmniModeOnEvent AsOmniModeOn(MidiEvent* event) { + OmniModeOnEvent m; + m.channel = event->channel; + return m; +} + +MonoModeOnEvent AsMonoModeOn(MidiEvent* event) { + MonoModeOnEvent m; + m.channel = event->channel; + m.num_channels = event->data[1]; + return m; +} + +PolyModeOnEvent AsPolyModeOn(MidiEvent* event) { + PolyModeOnEvent m; + m.channel = event->channel; + return m; +} \ No newline at end of file diff --git a/applications/plugins/usb_midi/midi/message.h b/applications/plugins/usb_midi/midi/message.h new file mode 100644 index 000000000..88402c4a4 --- /dev/null +++ b/applications/plugins/usb_midi/midi/message.h @@ -0,0 +1,251 @@ +#pragma once +#include +#include +#include "config.h" + +typedef enum { + NoteOff, /**< & */ + NoteOn, /**< & */ + PolyphonicKeyPressure, /**< & */ + ControlChange, /**< & */ + ProgramChange, /**< & */ + ChannelPressure, /**< & */ + PitchBend, /**< & */ + SystemCommon, /**< & */ + SystemRealTime, /**< & */ + ChannelMode, /**< & */ + MessageLast, /**< & */ +} MidiMessageType; + +typedef enum { + SystemExclusive, /**< & */ + MTCQuarterFrame, /**< & */ + SongPositionPointer, /**< & */ + SongSelect, /**< & */ + SCUndefined0, /**< & */ + SCUndefined1, /**< & */ + TuneRequest, /**< & */ + SysExEnd, /**< & */ + SystemCommonLast, /**< & */ +} SystemCommonType; + +typedef enum { + TimingClock, /**< & */ + SRTUndefined0, /**< & */ + Start, /**< & */ + Continue, /**< & */ + Stop, /**< & */ + SRTUndefined1, /**< & */ + ActiveSensing, /**< & */ + Reset, /**< & */ + SystemRealTimeLast, /**< & */ +} SystemRealTimeType; + +typedef enum { + AllSoundOff, /**< & */ + ResetAllControllers, /**< & */ + LocalControl, /**< & */ + AllNotesOff, /**< & */ + OmniModeOff, /**< & */ + OmniModeOn, /**< & */ + MonoModeOn, /**< & */ + PolyModeOn, /**< & */ + ChannelModeLast, /**< & */ +} ChannelModeType; + +/** Struct containing note, and velocity data for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t note; /**< & */ + uint8_t velocity; /**< & */ +} NoteOffEvent; + +/** Struct containing note, and velocity data for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t note; /**< & */ + uint8_t velocity; /**< & */ +} NoteOnEvent; + +/** Struct containing note, and pressure data for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; + uint8_t note; + uint8_t pressure; +} PolyphonicKeyPressureEvent; + +/** Struct containing control number, and value for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t control_number; /**< & */ + uint8_t value; /**< & */ +} ControlChangeEvent; + +/** Struct containing new program number, for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t program; /**< & */ +} ProgramChangeEvent; + +/** Struct containing pressure (aftertouch), for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t pressure; /**< & */ +} ChannelPressureEvent; + +/** Struct containing pitch bend value for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + int16_t value; /**< & */ +} PitchBendEvent; + +/** Struct containing sysex data. +Can be made from MidiEvent +*/ +typedef struct { + int length; + uint8_t data[SYSEX_BUFFER_LEN]; /**< & */ +} SystemExclusiveEvent; + +/** Struct containing QuarterFrame data. +Can be made from MidiEvent +*/ +typedef struct { + uint8_t message_type; /**< & */ + uint8_t value; /**< & */ +} MTCQuarterFrameEvent; + +/** Struct containing song position data. +Can be made from MidiEvent +*/ +typedef struct { + uint16_t position; /**< & */ +} SongPositionPointerEvent; + +/** Struct containing song select data. +Can be made from MidiEvent +*/ +typedef struct { + uint8_t song; /**< & */ +} SongSelectEvent; + +/** Struct containing sound off data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} AllSoundOffEvent; + +/** Struct containing ResetAllControllersEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t value; /**< & */ +} ResetAllControllersEvent; + +/** Struct containing LocalControlEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + bool local_control_off; /**< & */ + bool local_control_on; /**< & */ +} LocalControlEvent; + +/** Struct containing AllNotesOffEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} AllNotesOffEvent; + +/** Struct containing OmniModeOffEvent data. + * Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} OmniModeOffEvent; + +/** Struct containing OmniModeOnEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} OmniModeOnEvent; + +/** Struct containing MonoModeOnEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t num_channels; /**< & */ +} MonoModeOnEvent; + +/** Struct containing PolyModeOnEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} PolyModeOnEvent; + +/** Simple MidiEvent with message type, channel, and data[2] members. +*/ +typedef struct { + MidiMessageType type; + int channel; + uint8_t data[2]; + uint8_t sysex_data[SYSEX_BUFFER_LEN]; + uint8_t sysex_message_len; + SystemCommonType sc_type; + SystemRealTimeType srt_type; + ChannelModeType cm_type; +} MidiEvent; + +/** Returns the data within the MidiEvent as a NoteOffEvent struct */ +NoteOffEvent AsNoteOff(MidiEvent* event); + +/** Returns the data within the MidiEvent as a NoteOnEvent struct */ +NoteOnEvent AsNoteOn(MidiEvent* event); + +/** Returns the data within the MidiEvent as a PolyphonicKeyPressureEvent struct */ +PolyphonicKeyPressureEvent AsPolyphonicKeyPressure(MidiEvent* event); + +/** Returns the data within the MidiEvent as a ControlChangeEvent struct.*/ +ControlChangeEvent AsControlChange(MidiEvent* event); + +/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/ +ProgramChangeEvent AsProgramChange(MidiEvent* event); + +/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/ +ChannelPressureEvent AsChannelPressure(MidiEvent* event); + +/** Returns the data within the MidiEvent as a PitchBendEvent struct.*/ +PitchBendEvent AsPitchBend(MidiEvent* event); + +SystemExclusiveEvent AsSystemExclusive(MidiEvent* event); +MTCQuarterFrameEvent AsMTCQuarterFrame(MidiEvent* event); +SongPositionPointerEvent AsSongPositionPointer(MidiEvent* event); +SongSelectEvent AsSongSelect(MidiEvent* event); +AllSoundOffEvent AsAllSoundOff(MidiEvent* event); +ResetAllControllersEvent AsResetAllControllers(MidiEvent* event); +LocalControlEvent AsLocalControl(MidiEvent* event); +AllNotesOffEvent AsAllNotesOff(MidiEvent* event); +OmniModeOffEvent AsOmniModeOff(MidiEvent* event); +OmniModeOnEvent AsOmniModeOn(MidiEvent* event); +MonoModeOnEvent AsMonoModeOn(MidiEvent* event); +PolyModeOnEvent AsPolyModeOn(MidiEvent* event); \ No newline at end of file diff --git a/applications/plugins/usb_midi/midi/parser.c b/applications/plugins/usb_midi/midi/parser.c new file mode 100644 index 000000000..86120e007 --- /dev/null +++ b/applications/plugins/usb_midi/midi/parser.c @@ -0,0 +1,149 @@ +#include +#include "parser.h" + +typedef enum { + ParserEmpty, + ParserHasStatus, + ParserHasData0, + ParserSysEx, +} ParserState; + +const uint8_t kStatusByteMask = 0x80; +const uint8_t kMessageMask = 0x70; +const uint8_t kDataByteMask = 0x7F; +const uint8_t kSystemCommonMask = 0xF0; +const uint8_t kChannelMask = 0x0F; +const uint8_t kRealTimeMask = 0xF8; +const uint8_t kSystemRealTimeMask = 0x07; + +struct MidiParser { + MidiMessageType status; + ParserState state; + MidiEvent incoming_message; +}; + +MidiParser* midi_parser_alloc(void) { + MidiParser* parser = malloc(sizeof(MidiParser)); + parser->incoming_message.type = MessageLast; + parser->state = ParserEmpty; + return parser; +} + +void midi_parser_free(MidiParser* parser) { + free(parser); +} + +bool midi_parser_parse(MidiParser* parser, uint8_t byte) { + bool parsed = false; + MidiEvent* event = &parser->incoming_message; + + switch(parser->state) { + case ParserEmpty: + // check byte for valid Status Byte + if(byte & kStatusByteMask) { + // Get MessageType, and Channel + event->channel = byte & kChannelMask; + event->type = (MidiMessageType)((byte & kMessageMask) >> 4); + + // Validate, and move on. + if(event->type < MessageLast) { + parser->state = ParserHasStatus; + // Mark this status byte as running_status + parser->status = event->type; + + if(parser->status == SystemCommon) { + event->channel = 0; + //system real time = 1111 1xxx + if(byte & 0x08) { + event->type = SystemRealTime; + parser->status = SystemRealTime; + event->srt_type = (SystemRealTimeType)(byte & kSystemRealTimeMask); + + //short circuit to start + parser->state = ParserEmpty; + //queue_.push(incoming_message_); + parsed = true; + } + //system common + else { + event->sc_type = (SystemCommonType)(byte & 0x07); + //sysex + if(event->sc_type == SystemExclusive) { + parser->state = ParserSysEx; + event->sysex_message_len = 0; + } + //short circuit + else if(event->sc_type > SongSelect) { + parser->state = ParserEmpty; + //queue_.push(incoming_message_); + parsed = true; + } + } + } + } + // Else we'll keep waiting for a valid incoming status byte + } else { + // Handle as running status + event->type = parser->status; + event->data[0] = byte & kDataByteMask; + parser->state = ParserHasData0; + } + break; + case ParserHasStatus: + if((byte & kStatusByteMask) == 0) { + event->data[0] = byte & kDataByteMask; + if(parser->status == ChannelPressure || parser->status == ProgramChange || + event->sc_type == MTCQuarterFrame || event->sc_type == SongSelect) { + //these are just one data byte, so we short circuit back to start + parser->state = ParserEmpty; + //queue_.push(incoming_message_); + parsed = true; + } else { + parser->state = ParserHasData0; + } + + //ChannelModeMessages (reserved Control Changes) + if(parser->status == ControlChange && event->data[0] > 119) { + event->type = ChannelMode; + parser->status = ChannelMode; + event->cm_type = (ChannelModeType)(event->data[0] - 120); + } + } else { + // invalid message go back to start ;p + parser->state = ParserEmpty; + } + break; + case ParserHasData0: + if((byte & kStatusByteMask) == 0) { + event->data[1] = byte & kDataByteMask; + // At this point the message is valid, and we can add this MidiEvent to the queue + //queue_.push(incoming_message_); + parsed = true; + } + // Regardless, of whether the data was valid or not we go back to empty + // because either the message is queued for handling or its not. + parser->state = ParserEmpty; + break; + case ParserSysEx: + // end of sysex + if(byte == 0xf7) { + parser->state = ParserEmpty; + //queue_.push(incoming_message_); + parsed = true; + } else { + if(event->sysex_message_len < SYSEX_BUFFER_LEN) { + event->sysex_data[event->sysex_message_len] = byte; + event->sysex_message_len++; + } + } + break; + default: + break; + } + + return parsed; +} + +MidiEvent* midi_parser_get_message(MidiParser* parser) { + return &parser->incoming_message; +} \ No newline at end of file diff --git a/applications/plugins/usb_midi/midi/parser.h b/applications/plugins/usb_midi/midi/parser.h new file mode 100644 index 000000000..93630f026 --- /dev/null +++ b/applications/plugins/usb_midi/midi/parser.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include "message.h" + +typedef struct MidiParser MidiParser; + +MidiParser* midi_parser_alloc(void); + +void midi_parser_free(MidiParser* parser); + +bool midi_parser_parse(MidiParser* parser, uint8_t data); + +MidiEvent* midi_parser_get_message(MidiParser* parser); \ No newline at end of file diff --git a/applications/plugins/usb_midi/midi/usb_message.c b/applications/plugins/usb_midi/midi/usb_message.c new file mode 100644 index 000000000..b6844c5f4 --- /dev/null +++ b/applications/plugins/usb_midi/midi/usb_message.c @@ -0,0 +1,40 @@ +#include "usb_message.h" + +CodeIndex code_index_from_data(uint8_t data) { + return (CodeIndex)(data & 0b00001111); +} + +uint8_t cable_id_from_data(uint8_t data) { + return (data >> 4) & 0b00001111; +} + +uint8_t usb_message_data_size(CodeIndex code_index) { + uint8_t data_size = 0; + switch(code_index) { + case CodeIndexCommon1Byte: + /* case CodeIndexSysExEnd1Byte: */ + case CodeIndexSingleByte: + data_size = 1; + break; + case CodeIndexSysEx2Byte: + case CodeIndexSysExEnd2Byte: + case CodeIndexProgramChange: + case CodeIndexChannelPressure: + data_size = 2; + break; + case CodeIndexSysEx3Byte: + case CodeIndexSysExStart: + case CodeIndexSysExEnd3Byte: + case CodeIndexNoteOff: + case CodeIndexNoteOn: + case CodeIndexPolyKeyPress: + case CodeIndexControlChange: + case CodeIndexPitchBendChange: + data_size = 3; + break; + default: + break; + } + + return data_size; +} \ No newline at end of file diff --git a/applications/plugins/usb_midi/midi/usb_message.h b/applications/plugins/usb_midi/midi/usb_message.h new file mode 100644 index 000000000..852e9cb4f --- /dev/null +++ b/applications/plugins/usb_midi/midi/usb_message.h @@ -0,0 +1,28 @@ +#pragma once +#include + +typedef enum { + CodeIndexMisc = 0x0, /**< Reserved, MIDI Size: 1, 2, 3 */ + CodeIndexCableEvent = 0x1, /**< Reserved, MIDI Size: 1, 2, 3 */ + CodeIndexSysEx2Byte = 0x2, /**< MIDI Size: 2 */ + CodeIndexSysEx3Byte = 0x3, /**< MIDI Size: 3 */ + CodeIndexSysExStart = 0x4, /**< MIDI Size: 3 */ + CodeIndexCommon1Byte = 0x5, /**< MIDI Size: 1 */ + CodeIndexSysExEnd1Byte = 0x5, /**< MIDI Size: 1 */ + CodeIndexSysExEnd2Byte = 0x6, /**< MIDI Size: 2 */ + CodeIndexSysExEnd3Byte = 0x7, /**< MIDI Size: 3 */ + CodeIndexNoteOff = 0x8, /**< MIDI Size: 3 */ + CodeIndexNoteOn = 0x9, /**< MIDI Size: 3 */ + CodeIndexPolyKeyPress = 0xA, /**< MIDI Size: 3 */ + CodeIndexControlChange = 0xB, /**< MIDI Size: 3 */ + CodeIndexProgramChange = 0xC, /**< MIDI Size: 2 */ + CodeIndexChannelPressure = 0xD, /**< MIDI Size: 2 */ + CodeIndexPitchBendChange = 0xE, /**< MIDI Size: 3 */ + CodeIndexSingleByte = 0xF, /**< MIDI Size: 1 */ +} CodeIndex; + +CodeIndex code_index_from_data(uint8_t data); + +uint8_t cable_id_from_data(uint8_t data); + +uint8_t usb_message_data_size(CodeIndex code_index); \ No newline at end of file diff --git a/applications/plugins/usb_midi/usb/cm3_usb_audio.h b/applications/plugins/usb_midi/usb/cm3_usb_audio.h new file mode 100644 index 000000000..3c767f929 --- /dev/null +++ b/applications/plugins/usb_midi/usb/cm3_usb_audio.h @@ -0,0 +1,234 @@ +/** @defgroup usb_audio_defines USB Audio Type Definitions + +@brief Defined Constants and Types for the USB Audio Type Definitions + +@ingroup USB_defines + +@version 1.0.0 + +@author @htmlonly © @endhtmlonly 2014 +Daniel Thompson +Seb Holzapfel + +@date 19 April 2014 + +LGPL License Terms @ref lgpl_license +*/ + +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Daniel Thompson + * Copyright (C) 2018 Seb Holzapfel + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/**@{*/ + +#ifndef LIBOPENCM3_USB_AUDIO_H +#define LIBOPENCM3_USB_AUDIO_H + +#include + +/* + * Definitions from the USB_AUDIO_ or usb_audio_ namespace come from: + * "Universal Serial Bus Class Definitions for Audio Devices, Revision 1.0" + */ + +/* Table A-1: Audio Interface Class Code */ +#define USB_CLASS_AUDIO 0x01 + +/* Table A-2: Audio Interface Subclass Codes */ +#define USB_AUDIO_SUBCLASS_UNDEFINED 0x00 +#define USB_AUDIO_SUBCLASS_CONTROL 0x01 +#define USB_AUDIO_SUBCLASS_AUDIOSTREAMING 0x02 +#define USB_AUDIO_SUBCLASS_MIDISTREAMING 0x03 + +/* Table A-4: Audio Class-specific Descriptor Types */ +#define USB_AUDIO_DT_CS_UNDEFINED 0x20 +#define USB_AUDIO_DT_CS_DEVICE 0x21 +#define USB_AUDIO_DT_CS_CONFIGURATION 0x22 +#define USB_AUDIO_DT_CS_STRING 0x23 +#define USB_AUDIO_DT_CS_INTERFACE 0x24 +#define USB_AUDIO_DT_CS_ENDPOINT 0x25 + +/* Table A-5: Audio Class-Specific AC Interface Descriptor Subtypes */ +#define USB_AUDIO_TYPE_AC_DESCRIPTOR_UNDEFINED 0x00 +#define USB_AUDIO_TYPE_HEADER 0x01 +#define USB_AUDIO_TYPE_INPUT_TERMINAL 0x02 +#define USB_AUDIO_TYPE_OUTPUT_TERMINAL 0x03 +#define USB_AUDIO_TYPE_MIXER_UNIT 0x04 +#define USB_AUDIO_TYPE_SELECTOR_UNIT 0x05 +#define USB_AUDIO_TYPE_FEATURE_UNIT 0x06 +#define USB_AUDIO_TYPE_PROCESSING_UNIT 0x07 +#define USB_AUDIO_TYPE_EXTENSION_UNIT 0x08 + +/* Table 4-2: Class-Specific AC Interface Header Descriptor (head) */ +struct usb_audio_header_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint16_t bcdADC; + uint16_t wTotalLength; + uint8_t bInCollection; + /* ... */ +} __attribute__((packed)); + +/* Table 4-2: Class-Specific AC Interface Header Descriptor (body) */ +struct usb_audio_header_descriptor_body { + /* ... */ + uint8_t baInterfaceNr; +} __attribute__((packed)); + +/* Table 4-3: Input Terminal Descriptor */ +struct usb_audio_input_terminal_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t bNrChannels; + uint16_t wChannelConfig; + uint8_t iChannelNames; + uint8_t iTerminal; +} __attribute__((packed)); + +/* Table 4-3: Output Terminal Descriptor */ +struct usb_audio_output_terminal_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t bSourceID; + uint8_t iTerminal; +} __attribute__((packed)); + +/* Table 4-7: Feature Unit Descriptor (head) */ +struct usb_audio_feature_unit_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bUnitID; + uint8_t bSourceID; + uint8_t bControlSize; + uint16_t bmaControlMaster; /* device can assume 16-bit, given highest + * defined bit in spec is bit #9. + * (it is thus required bControlSize=2) */ + /* ... */ +} __attribute__((packed)); + +/* Table 4-7: Feature Unit Descriptor (body) */ +struct usb_audio_feature_unit_descriptor_body { + /* ... */ + uint16_t bmaControl; + /* ... */ +} __attribute__((packed)); + +/* Table 4-7: Feature Unit Descriptor (tail) */ +struct usb_audio_feature_unit_descriptor_tail { + /* ... */ + uint8_t iFeature; +} __attribute__((packed)); + +/* Table 4-7: Feature Unit Descriptor (2-channel) + * + * This structure is a convenience covering the (common) case where + * there are 2 channels associated with the feature unit + */ +struct usb_audio_feature_unit_descriptor_2ch { + struct usb_audio_feature_unit_descriptor_head head; + struct usb_audio_feature_unit_descriptor_body channel_control[2]; + struct usb_audio_feature_unit_descriptor_tail tail; +} __attribute__((packed)); + +/* Table 4-19: Class-Specific AS Interface Descriptor */ +struct usb_audio_stream_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bTerminalLink; + uint8_t bDelay; + uint16_t wFormatTag; +} __attribute__((packed)); + +/* Table 4-20: Standard AS Isochronous Audio Data Endpoint Descriptor */ +struct usb_audio_stream_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + uint8_t bRefresh; + uint8_t bSynchAddress; +} __attribute__((packed)); + +/* Table 4-21: Class-Specific AS Isochronous Audio Data Endpoint Descriptor */ +struct usb_audio_stream_audio_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bmAttributes; + uint8_t bLockDelayUnits; + uint16_t wLockDelay; +} __attribute__((packed)); + +/* + * Definitions from the USB_AUDIO_FORMAT_ or usb_audio_format_ namespace come from: + * "Universal Serial Bus Device Class Definition for Audio Data Formats, Revision 1.0" + */ + +/* Table 2-1: Type I Format Type Descriptor (head) */ +struct usb_audio_format_type1_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bFormatType; + uint8_t bNrChannels; + uint8_t bSubFrameSize; + uint8_t bBitResolution; + uint8_t bSamFreqType; + /* ... */ +} __attribute__((packed)); + +/* Table 2-2: Continuous Sampling Frequency */ +struct usb_audio_format_continuous_sampling_frequency { + /* ... */ + uint32_t tLowerSamFreq : 24; + uint32_t tUpperSamFreq : 24; +} __attribute__((packed)); + +/* Table 2-3: Discrete Number of Sampling Frequencies */ +struct usb_audio_format_discrete_sampling_frequency { + /* ... */ + uint32_t tSamFreq : 24; +} __attribute__((packed)); + +/* Table 2-1: Type I Format Type Descriptor (1 sampling frequency) + * + * This structure is a convenience covering the (common) case where + * only 1 discrete sampling frequency is used + */ +struct usb_audio_format_type1_descriptor_1freq { + struct usb_audio_format_type1_descriptor_head head; + struct usb_audio_format_discrete_sampling_frequency freqs[1]; +} __attribute__((packed)); + +#endif + +/**@}*/ diff --git a/applications/plugins/usb_midi/usb/cm3_usb_midi.h b/applications/plugins/usb_midi/usb/cm3_usb_midi.h new file mode 100644 index 000000000..8435c883e --- /dev/null +++ b/applications/plugins/usb_midi/usb/cm3_usb_midi.h @@ -0,0 +1,190 @@ +/** @defgroup usb_audio_defines USB MIDI Type Definitions + +@brief Defined Constants and Types for the USB MIDI Type Definitions + +@ingroup USB_defines + +@version 1.0.0 + +@author @htmlonly © @endhtmlonly 2014 +Daniel Thompson + +@date 19 April 2014 + +LGPL License Terms @ref lgpl_license +*/ + +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Daniel Thompson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/**@{*/ + +#ifndef LIBOPENCM3_USB_MIDI_H +#define LIBOPENCM3_USB_MIDI_H + +#include + +/* + * Definitions from the USB_MIDI_ or usb_midi_ namespace come from: + * "Universal Serial Bus Class Definitions for MIDI Devices, Revision 1.0" + */ + +/* Appendix A.1: MS Class-Specific Interface Descriptor Subtypes */ +#define USB_MIDI_SUBTYPE_MS_DESCRIPTOR_UNDEFINED 0x00 +#define USB_MIDI_SUBTYPE_MS_HEADER 0x01 +#define USB_MIDI_SUBTYPE_MIDI_IN_JACK 0x02 +#define USB_MIDI_SUBTYPE_MIDI_OUT_JACK 0x03 +#define USB_MIDI_SUBTYPE_MIDI_ELEMENT 0x04 + +/* Appendix A.2: MS Class-Specific Endpoint Descriptor Subtypes */ +#define USB_MIDI_SUBTYPE_DESCRIPTOR_UNDEFINED 0x00 +#define USB_MIDI_SUBTYPE_MS_GENERAL 0x01 + +/* Appendix A.3: MS MIDI IN and OUT Jack types */ +#define USB_MIDI_JACK_TYPE_UNDEFINED 0x00 +#define USB_MIDI_JACK_TYPE_EMBEDDED 0x01 +#define USB_MIDI_JACK_TYPE_EXTERNAL 0x02 + +/* Appendix A.5.1 Endpoint Control Selectors */ +#define USB_MIDI_EP_CONTROL_UNDEFINED 0x00 +#define USB_MIDI_ASSOCIATION_CONTROL 0x01 + +/* Table 6-2: Class-Specific MS Interface Header Descriptor */ +struct usb_midi_header_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint16_t bcdMSC; + uint16_t wTotalLength; +} __attribute__((packed)); + +/* Table 6-3: MIDI IN Jack Descriptor */ +struct usb_midi_in_jack_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bJackType; + uint8_t bJackID; + uint8_t iJack; +} __attribute__((packed)); + +/* Table 6-4: MIDI OUT Jack Descriptor (head) */ +struct usb_midi_out_jack_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bJackType; + uint8_t bJackID; + uint8_t bNrInputPins; + /* ... */ +} __attribute__((packed)); + +/* Table 6.4: MIDI OUT Jack Descriptor (body) */ +struct usb_midi_out_jack_descriptor_body { + /* ... */ + uint8_t baSourceID; + uint8_t baSourcePin; + /* ... */ +} __attribute__((packed)); + +/* Table 6.4: MIDI OUT Jack Descriptor (tail) */ +struct usb_midi_out_jack_descriptor_tail { + /* ... */ + uint8_t iJack; +} __attribute__((packed)); + +/* Table 6.4: MIDI OUT Jack Descriptor (single) + * + * This structure is a convenience covering the (normal) case where + * there is only one input pin. + */ +struct usb_midi_out_jack_descriptor { + struct usb_midi_out_jack_descriptor_head head; + struct usb_midi_out_jack_descriptor_body source[1]; + struct usb_midi_out_jack_descriptor_tail tail; +} __attribute__((packed)); + +/* Table 6-5: MIDI Element Descriptor (head) */ +struct usb_midi_element_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bElementID; + uint8_t bNrInputPins; + /* ... */ +} __attribute__((packed)); + +/* Table 6-5: MIDI Element Descriptor (body) */ +struct usb_midi_element_descriptor_body { + /* ... */ + uint8_t baSourceID; + uint8_t baSourcePin; + /* ... */ +} __attribute__((packed)); + +/* Table 6-5: MIDI Element Descriptor (tail) */ +struct usb_midi_element_descriptor_tail { + /* ... */ + uint8_t bNrOutputPins; + uint8_t bInTerminalLink; + uint8_t bOutTerminalLink; + uint8_t bElCapsSize; + uint16_t bmElementCaps; /* host cannot assume this is 16-bit but device + can (since highest defined bitmap value in + v1.0 is bit 11) */ + uint8_t iElement; +} __attribute__((packed)); + +/* Table 6-5: MIDI Element Descriptor (single) + * + * This structure is a convenience covering the (common) case where + * there is only one input pin. + */ +struct usb_midi_element_descriptor { + struct usb_midi_element_descriptor_head head; + struct usb_midi_element_descriptor_body source[1]; + struct usb_midi_element_descriptor_tail tail; +} __attribute__((packed)); + +/* Table 6-7: Class-specific MS Bulk Data Endpoint Descriptor (head) */ +struct usb_midi_endpoint_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bNumEmbMIDIJack; +} __attribute__((packed)); + +/* Table 6-7: Class-specific MS Bulk Data Endpoint Descriptor (body) */ +struct usb_midi_endpoint_descriptor_body { + uint8_t baAssocJackID; +} __attribute__((packed)); + +/* Table 6.7: Class-specific MS Bulk Data Endpoint Descriptor (single) + * + * This structure is a convenience covering the (normal) case where + * there is only one input pin. + */ +struct usb_midi_endpoint_descriptor { + struct usb_midi_endpoint_descriptor_head head; + struct usb_midi_endpoint_descriptor_body jack[1]; +} __attribute__((packed)); + +#endif + +/**@}*/ diff --git a/applications/plugins/usb_midi/usb/usb_midi_driver.c b/applications/plugins/usb_midi/usb/usb_midi_driver.c new file mode 100644 index 000000000..9abf77d12 --- /dev/null +++ b/applications/plugins/usb_midi/usb/usb_midi_driver.c @@ -0,0 +1,428 @@ +#include +#include +#include +#include + +#include "usb_midi_driver.h" +#include "cm3_usb_audio.h" +#include "cm3_usb_midi.h" + +// Appendix B. "Example: Simple MIDI Adapter" from "Universal Serial Bus Device Class Definition for MIDI Devices", Revision 1.0 + +#define USB_VID 0x6666 +#define USB_PID 0x5119 + +#define USB_EP0_SIZE 8 + +#define USB_MIDI_EP_SIZE 64 +#define USB_MIDI_EP_IN 0x81 +#define USB_MIDI_EP_OUT 0x01 + +#define EP_CFG_DECONFIGURE 0 +#define EP_CFG_CONFIGURE 1 + +enum { + USB_STR_ZERO, + USB_STR_MANUFACTURER, + USB_STR_PRODUCT, + USB_STR_SERIAL_NUMBER, +}; + +/* + B.1 Device Descriptor +*/ +static const struct usb_device_descriptor device_descriptor = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), // was 0x0110, 1.10 - current revision of USBspecification. + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = USB_SUBCLASS_NONE, + .bDeviceProtocol = USB_PROTO_NONE, + .bMaxPacketSize0 = USB_EP0_SIZE, + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = USB_STR_MANUFACTURER, + .iProduct = USB_STR_PRODUCT, + .iSerialNumber = USB_STR_SERIAL_NUMBER, + .bNumConfigurations = 1, +}; + +struct usb_audio_header_descriptor { + struct usb_audio_header_descriptor_head head; + struct usb_audio_header_descriptor_body body; +} __attribute__((packed)); + +struct usb_midi_jacks_descriptor { + struct usb_midi_header_descriptor header; + struct usb_midi_in_jack_descriptor in_embedded; + struct usb_midi_in_jack_descriptor in_external; + struct usb_midi_out_jack_descriptor out_embedded; + struct usb_midi_out_jack_descriptor out_external; +} __attribute__((packed)); + +struct MidiConfigDescriptor { + /* + B.2 Configuration Descriptor + */ + struct usb_config_descriptor config; + + /* + B.3 AudioControl Interface Descriptors + + The AudioControl interface describes the device structure (audio function topology) + and is used to manipulate the Audio Controls. This device has no audio function incorporated. + However, the AudioControl interface is mandatory and therefore both the standard AC interface + descriptor and the classspecific AC interface descriptor must be present. + The class-specific AC interface descriptor only contains the header descriptor. + */ + // B.3.1 Standard AC Interface Descriptor + struct usb_interface_descriptor audio_control_iface; + // B.3.2 Class-specific AC Interface Descriptor + struct usb_audio_header_descriptor audio_control_header; + + /* + B.4 MIDIStreaming Interface Descriptors + */ + // B.4.1 Standard MS Interface Descriptor + struct usb_interface_descriptor midi_streaming_iface; + // B.4.2 Class-specific MS Interface Descriptor + // B.4.3 MIDI IN Jack Descriptor + // B.4.4 MIDI OUT Jack Descriptor + struct usb_midi_jacks_descriptor midi_jacks; + + /* + B.5 Bulk OUT Endpoint Descriptors + */ + // B.5.1 Standard Bulk OUT Endpoint Descriptor + struct usb_endpoint_descriptor bulk_out; + // B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor + struct usb_midi_endpoint_descriptor midi_bulk_out; + + /* + B.6 Bulk IN Endpoint Descriptors + */ + // B.6.1 Standard Bulk IN Endpoint Descriptor + struct usb_endpoint_descriptor bulk_in; + // B.6.2 Class-specific MS Bulk IN Endpoint Descriptor + struct usb_midi_endpoint_descriptor midi_bulk_in; +} __attribute__((packed)); + +static const struct MidiConfigDescriptor config_descriptor = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct MidiConfigDescriptor), + .bNumInterfaces = 2, /* control and data */ + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CFG_ATTR_RESERVED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .audio_control_iface = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_AUDIO_SUBCLASS_CONTROL, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = 0, + }, + .audio_control_header = + { + .head = + { + .bLength = sizeof(struct usb_audio_header_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_AUDIO_TYPE_HEADER, + .bcdADC = VERSION_BCD(1, 0, 0), + .wTotalLength = sizeof(struct usb_audio_header_descriptor), + .bInCollection = 1, + }, + .body = + { + .baInterfaceNr = 1, + }, + }, + .midi_streaming_iface = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_AUDIO_SUBCLASS_MIDISTREAMING, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = 0, + }, + .midi_jacks = + { + .header = + { + .bLength = sizeof(struct usb_midi_header_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MS_HEADER, + .bcdMSC = VERSION_BCD(1, 0, 0), + .wTotalLength = sizeof(struct usb_midi_jacks_descriptor), + }, + .in_embedded = + { + .bLength = sizeof(struct usb_midi_in_jack_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_IN_JACK, + .bJackType = USB_MIDI_JACK_TYPE_EMBEDDED, + .bJackID = 0x01, + .iJack = 0x00, + }, + .in_external = + { + .bLength = sizeof(struct usb_midi_in_jack_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_IN_JACK, + .bJackType = USB_MIDI_JACK_TYPE_EXTERNAL, + .bJackID = 0x02, + .iJack = 0x00, + }, + .out_embedded = + { + .head = + { + .bLength = sizeof(struct usb_midi_out_jack_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_OUT_JACK, + .bJackType = USB_MIDI_JACK_TYPE_EMBEDDED, + .bJackID = 0x03, + .bNrInputPins = 1, + }, + .source[0] = + { + .baSourceID = 0x02, + .baSourcePin = 0x01, + }, + .tail = + { + .iJack = 0x00, + }, + }, + .out_external = + { + .head = + { + .bLength = sizeof(struct usb_midi_out_jack_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_OUT_JACK, + .bJackType = USB_MIDI_JACK_TYPE_EXTERNAL, + .bJackID = 0x04, + .bNrInputPins = 1, + }, + .source[0] = + { + .baSourceID = 0x01, + .baSourcePin = 0x01, + }, + .tail = + { + .iJack = 0x00, + }, + }, + }, + .bulk_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MIDI_EP_OUT, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MIDI_EP_SIZE, + .bInterval = 0, + }, + .midi_bulk_out = + { + .head = + { + .bLength = sizeof(struct usb_midi_endpoint_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_ENDPOINT, + .bDescriptorSubType = USB_MIDI_SUBTYPE_MS_GENERAL, + .bNumEmbMIDIJack = 1, + }, + .jack[0] = + { + .baAssocJackID = 0x01, + }, + }, + .bulk_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MIDI_EP_IN, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MIDI_EP_SIZE, + .bInterval = 0, + }, + .midi_bulk_in = + { + .head = + { + .bLength = sizeof(struct usb_midi_endpoint_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_ENDPOINT, + .bDescriptorSubType = USB_MIDI_SUBTYPE_MS_GENERAL, + .bNumEmbMIDIJack = 1, + }, + .jack[0] = + { + .baAssocJackID = 0x03, + }, + }, +}; + +static const struct usb_string_descriptor dev_manufacturer_string = + USB_STRING_DESC("Flipper Devices Inc."); + +static const struct usb_string_descriptor dev_product_string = + USB_STRING_DESC("Flipper MIDI Device"); + +static const struct usb_string_descriptor dev_serial_number_string = + USB_STRING_DESC("Serial Number"); + +static void midi_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); +static void midi_deinit(usbd_device* dev); +static void midi_on_wakeup(usbd_device* dev); +static void midi_on_suspend(usbd_device* dev); +static usbd_respond midi_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond midi_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); + +FuriHalUsbInterface midi_usb_interface = { + .init = midi_init, + .deinit = midi_deinit, + .wakeup = midi_on_wakeup, + .suspend = midi_on_suspend, + .dev_descr = (struct usb_device_descriptor*)&device_descriptor, + .cfg_descr = (void*)&config_descriptor, +}; + +typedef struct { + usbd_device* dev; + MidiRxCallback rx_callback; + void* context; + FuriSemaphore* semaphore_tx; + bool connected; +} MidiUsb; + +static MidiUsb midi_usb; + +void midi_usb_set_context(void* context) { + midi_usb.context = context; +} + +void midi_usb_set_rx_callback(MidiRxCallback callback) { + midi_usb.rx_callback = callback; +} + +size_t midi_usb_rx(uint8_t* buffer, size_t size) { + size_t len = usbd_ep_read(midi_usb.dev, USB_MIDI_EP_OUT, buffer, size); + return len; +} + +size_t midi_usb_tx(uint8_t* buffer, uint8_t size) { + if((midi_usb.semaphore_tx == NULL) || (midi_usb.connected == false)) return 0; + + furi_check(furi_semaphore_acquire(midi_usb.semaphore_tx, FuriWaitForever) == FuriStatusOk); + + if(midi_usb.connected) { + int32_t len = usbd_ep_write(midi_usb.dev, USB_MIDI_EP_IN, buffer, size); + return len; + } else { + return 0; + } +} + +static void midi_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + UNUSED(ctx); + + midi_usb_interface.str_manuf_descr = (void*)&dev_manufacturer_string; + midi_usb_interface.str_prod_descr = (void*)&dev_product_string; + midi_usb_interface.str_serial_descr = (void*)&dev_serial_number_string; + midi_usb_interface.dev_descr->idVendor = USB_VID; + midi_usb_interface.dev_descr->idProduct = USB_PID; + + midi_usb.dev = dev; + if(midi_usb.semaphore_tx == NULL) midi_usb.semaphore_tx = furi_semaphore_alloc(1, 1); + + usbd_reg_config(dev, midi_ep_config); + usbd_reg_control(dev, midi_control); + + usbd_connect(dev, true); +} + +static void midi_deinit(usbd_device* dev) { + midi_usb.connected = false; + midi_usb.dev = NULL; + furi_semaphore_free(midi_usb.semaphore_tx); + + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); +} + +static void midi_on_wakeup(usbd_device* dev) { + UNUSED(dev); + if(!midi_usb.connected) { + midi_usb.connected = true; + } +} + +static void midi_on_suspend(usbd_device* dev) { + UNUSED(dev); + if(midi_usb.connected) { + midi_usb.connected = false; + } +} + +static void midi_tx_rx(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(ep); + + switch(event) { + case usbd_evt_eptx: + furi_semaphore_release(midi_usb.semaphore_tx); + break; + case usbd_evt_eprx: + if(midi_usb.rx_callback != NULL) { + midi_usb.rx_callback(midi_usb.context); + } + break; + default: + break; + } +} + +static usbd_respond midi_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case EP_CFG_DECONFIGURE: + usbd_ep_deconfig(dev, USB_MIDI_EP_OUT); + usbd_ep_deconfig(dev, USB_MIDI_EP_IN); + usbd_reg_endpoint(dev, USB_MIDI_EP_OUT, NULL); + usbd_reg_endpoint(dev, USB_MIDI_EP_IN, NULL); + return usbd_ack; + case EP_CFG_CONFIGURE: + usbd_ep_config(dev, USB_MIDI_EP_OUT, USB_EPTYPE_BULK, USB_MIDI_EP_SIZE); + usbd_ep_config(dev, USB_MIDI_EP_IN, USB_EPTYPE_BULK, USB_MIDI_EP_SIZE); + usbd_reg_endpoint(dev, USB_MIDI_EP_OUT, midi_tx_rx); + usbd_reg_endpoint(dev, USB_MIDI_EP_IN, midi_tx_rx); + return usbd_ack; + default: + return usbd_fail; + } +} + +static usbd_respond midi_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(dev); + UNUSED(req); + UNUSED(callback); + + return usbd_fail; +} \ No newline at end of file diff --git a/applications/plugins/usb_midi/usb/usb_midi_driver.h b/applications/plugins/usb_midi/usb/usb_midi_driver.h new file mode 100644 index 000000000..d385efcb5 --- /dev/null +++ b/applications/plugins/usb_midi/usb/usb_midi_driver.h @@ -0,0 +1,14 @@ +#pragma once +#include + +extern FuriHalUsbInterface midi_usb_interface; + +typedef void (*MidiRxCallback)(void* context); + +void midi_usb_set_context(void* context); + +void midi_usb_set_rx_callback(MidiRxCallback callback); + +size_t midi_usb_rx(uint8_t* buffer, size_t size); + +size_t midi_usb_tx(uint8_t* buffer, uint8_t size); \ No newline at end of file diff --git a/applications/plugins/usb_midi/usb_midi.c b/applications/plugins/usb_midi/usb_midi.c new file mode 100644 index 000000000..b1149a176 --- /dev/null +++ b/applications/plugins/usb_midi/usb_midi.c @@ -0,0 +1,80 @@ +#include +#include +#include "usb/usb_midi_driver.h" +#include "midi/parser.h" +#include "midi/usb_message.h" +#include + +float note_to_frequency(int note) { + float a = 440; + return (a / 32) * powf(2, ((note - 9) / 12.0)); +} + +typedef enum { + MidiThreadEventStop = (1 << 0), + MidiThreadEventRx = (1 << 1), + MidiThreadEventAll = MidiThreadEventStop | MidiThreadEventRx, +} MidiThreadEvent; + +static void midi_rx_callback(void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + furi_thread_flags_set(thread_id, MidiThreadEventRx); +} + +int32_t usb_midi_app(void* p) { + UNUSED(p); + + FuriHalUsbInterface* usb_config_prev; + usb_config_prev = furi_hal_usb_get_config(); + midi_usb_set_context(furi_thread_get_id(furi_thread_get_current())); + midi_usb_set_rx_callback(midi_rx_callback); + furi_hal_usb_set_config(&midi_usb_interface, NULL); + + MidiParser* parser = midi_parser_alloc(); + uint32_t events; + uint8_t current_note = 255; + + while(1) { + events = furi_thread_flags_wait(MidiThreadEventAll, FuriFlagWaitAny, FuriWaitForever); + + if(!(events & FuriFlagError)) { + if(events & MidiThreadEventRx) { + uint8_t buffer[64]; + size_t size = midi_usb_rx(buffer, sizeof(buffer)); + // loopback + // midi_usb_tx(buffer, size); + size_t start = 0; + while(start < size) { + CodeIndex code_index = code_index_from_data(buffer[start]); + uint8_t data_size = usb_message_data_size(code_index); + if(data_size == 0) break; + + start += 1; + for(size_t j = 0; j < data_size; j++) { + if(midi_parser_parse(parser, buffer[start + j])) { + MidiEvent* event = midi_parser_get_message(parser); + if(event->type == NoteOn) { + NoteOnEvent note_on = AsNoteOn(event); + current_note = note_on.note; + furi_hal_speaker_start( + note_to_frequency(note_on.note), note_on.velocity / 127.0f); + } else if(event->type == NoteOff) { + NoteOffEvent note_off = AsNoteOff(event); + if(note_off.note == current_note) { + furi_hal_speaker_stop(); + } + } + } + } + start += data_size; + } + } + } + } + + midi_parser_free(parser); + furi_hal_usb_set_config(usb_config_prev, NULL); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/usb_midi/usb_midi.png b/applications/plugins/usb_midi/usb_midi.png new file mode 100644 index 000000000..6d0ac6fed Binary files /dev/null and b/applications/plugins/usb_midi/usb_midi.png differ diff --git a/applications/plugins/usbkeyboard/application.fam b/applications/plugins/usbkeyboard/application.fam index b5ac0f74f..1e419f3fe 100644 --- a/applications/plugins/usbkeyboard/application.fam +++ b/applications/plugins/usbkeyboard/application.fam @@ -11,4 +11,5 @@ App( order=60, fap_icon="usb_keyboard_10px.png", fap_category="Misc", + fap_icon_assets="assets", ) diff --git a/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png b/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png new file mode 100644 index 000000000..d4034efc4 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Arr_dwn_7x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png b/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png new file mode 100644 index 000000000..28b4236a2 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Arr_up_7x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png b/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/ButtonDown_7x4.png differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png b/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/ButtonLeft_4x7.png differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png b/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/ButtonRight_4x7.png differ diff --git a/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png b/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/ButtonUp_7x4.png differ diff --git a/applications/plugins/usbkeyboard/assets/Button_18x18.png b/applications/plugins/usbkeyboard/assets/Button_18x18.png new file mode 100644 index 000000000..30a5b4fab Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Button_18x18.png differ diff --git a/applications/plugins/usbkeyboard/assets/Circles_47x47.png b/applications/plugins/usbkeyboard/assets/Circles_47x47.png new file mode 100644 index 000000000..6a16ebf7b Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Circles_47x47.png differ diff --git a/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png b/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png new file mode 100644 index 000000000..c533d8572 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Left_mouse_icon_9x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Like_def_11x9.png b/applications/plugins/usbkeyboard/assets/Like_def_11x9.png new file mode 100644 index 000000000..555bea3d4 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Like_def_11x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png b/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png new file mode 100644 index 000000000..f5bf276f3 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Like_pressed_17x17.png differ diff --git a/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png b/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Ok_btn_9x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png b/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png new file mode 100644 index 000000000..6b46ba3a8 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Ok_btn_pressed_13x13.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png new file mode 100644 index 000000000..9687397af Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_arrow_down_7x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png new file mode 100644 index 000000000..fb4ded78f Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_arrow_left_9x7.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png new file mode 100644 index 000000000..97648d176 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_arrow_right_9x7.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png b/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png new file mode 100644 index 000000000..a91a6fd5e Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_arrow_up_7x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png b/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pin_back_arrow_10x8.png differ diff --git a/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png b/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png new file mode 100644 index 000000000..823926b84 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Pressed_Button_13x13.png differ diff --git a/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png b/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png new file mode 100644 index 000000000..446d7176c Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Right_mouse_icon_9x9.png differ diff --git a/applications/plugins/usbkeyboard/assets/Space_65x18.png b/applications/plugins/usbkeyboard/assets/Space_65x18.png new file mode 100644 index 000000000..b60ae5097 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Space_65x18.png differ diff --git a/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png b/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png new file mode 100644 index 000000000..d7a82a2df Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Voldwn_6x6.png differ diff --git a/applications/plugins/usbkeyboard/assets/Volup_8x6.png b/applications/plugins/usbkeyboard/assets/Volup_8x6.png new file mode 100644 index 000000000..4b7ec66d6 Binary files /dev/null and b/applications/plugins/usbkeyboard/assets/Volup_8x6.png differ diff --git a/applications/plugins/usbkeyboard/views/usb_hid_dirpad.c b/applications/plugins/usbkeyboard/views/usb_hid_dirpad.c index f17dc1a00..d6aec9ef2 100644 --- a/applications/plugins/usbkeyboard/views/usb_hid_dirpad.c +++ b/applications/plugins/usbkeyboard/views/usb_hid_dirpad.c @@ -2,6 +2,7 @@ #include #include #include +#include struct UsbHidDirpad { View* view; diff --git a/applications/plugins/usbkeyboard/views/usb_hid_keyboard.c b/applications/plugins/usbkeyboard/views/usb_hid_keyboard.c index 2e4495c46..cc047b683 100644 --- a/applications/plugins/usbkeyboard/views/usb_hid_keyboard.c +++ b/applications/plugins/usbkeyboard/views/usb_hid_keyboard.c @@ -3,6 +3,7 @@ #include #include #include +#include struct UsbHidKeyboard { View* view; @@ -42,7 +43,7 @@ typedef struct { #define KEY_WIDTH 9 #define KEY_HEIGHT 12 #define KEY_PADDING 1 -#define ROW_COUNT 6 +#define ROW_COUNT 7 #define COLUMN_COUNT 12 // 0 width items are not drawn, but there value is used @@ -135,6 +136,21 @@ const UsbHidKeyboardKey usb_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, }, + { + {.width = 1, .icon = NULL, .key = "1", .shift_key = "1", .value = HID_KEYBOARD_F1}, + {.width = 1, .icon = NULL, .key = "2", .shift_key = "2", .value = HID_KEYBOARD_F2}, + {.width = 1, .icon = NULL, .key = "3", .shift_key = "3", .value = HID_KEYBOARD_F3}, + {.width = 1, .icon = NULL, .key = "4", .shift_key = "4", .value = HID_KEYBOARD_F4}, + {.width = 1, .icon = NULL, .key = "5", .shift_key = "5", .value = HID_KEYBOARD_F5}, + {.width = 1, .icon = NULL, .key = "6", .shift_key = "6", .value = HID_KEYBOARD_F6}, + {.width = 1, .icon = NULL, .key = "7", .shift_key = "7", .value = HID_KEYBOARD_F7}, + {.width = 1, .icon = NULL, .key = "8", .shift_key = "8", .value = HID_KEYBOARD_F8}, + {.width = 1, .icon = NULL, .key = "9", .shift_key = "9", .value = HID_KEYBOARD_F9}, + {.width = 1, .icon = NULL, .key = "0", .shift_key = "0", .value = HID_KEYBOARD_F10}, + {.width = 1, .icon = NULL, .key = "1", .shift_key = "1", .value = HID_KEYBOARD_F11}, + {.width = 1, .icon = NULL, .key = "2", .shift_key = "2", .value = HID_KEYBOARD_F12}, + } + }; static void usb_hid_keyboard_to_upper(char* str) { @@ -216,6 +232,7 @@ static void usb_hid_keyboard_draw_callback(Canvas* canvas, void* context) { // Select if back is clicked and its the backspace key // Deselect when the button clicked or not hovered bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; + keySelected = y == ROW_COUNT - 1 ? !keySelected : keySelected; bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; usb_hid_keyboard_draw_key( canvas, @@ -366,4 +383,4 @@ void usb_hid_keyboard_free(UsbHidKeyboard* usb_hid_keyboard) { View* usb_hid_keyboard_get_view(UsbHidKeyboard* usb_hid_keyboard) { furi_assert(usb_hid_keyboard); return usb_hid_keyboard->view; -} \ No newline at end of file +} diff --git a/applications/plugins/usbkeyboard/views/usb_hid_media.c b/applications/plugins/usbkeyboard/views/usb_hid_media.c index 520d04717..8d2188434 100644 --- a/applications/plugins/usbkeyboard/views/usb_hid_media.c +++ b/applications/plugins/usbkeyboard/views/usb_hid_media.c @@ -2,6 +2,7 @@ #include #include #include +#include struct UsbHidMedia { View* view; diff --git a/applications/plugins/usbkeyboard/views/usb_hid_mouse.c b/applications/plugins/usbkeyboard/views/usb_hid_mouse.c index aa6e89aa0..cf545b164 100644 --- a/applications/plugins/usbkeyboard/views/usb_hid_mouse.c +++ b/applications/plugins/usbkeyboard/views/usb_hid_mouse.c @@ -2,6 +2,7 @@ #include #include #include +#include struct UsbHidMouse { View* view; diff --git a/applications/plugins/videopoker/poker.c b/applications/plugins/videopoker/poker.c index 664057b43..0205d1319 100644 --- a/applications/plugins/videopoker/poker.c +++ b/applications/plugins/videopoker/poker.c @@ -806,6 +806,8 @@ int32_t video_poker_app(void* p) { /* if game is not over, we should store the game state. */ processing = false; break; + default: + break; } } } diff --git a/applications/plugins/wav_player/application.fam b/applications/plugins/wav_player/application.fam index 31720bcbd..ec0c76291 100644 --- a/applications/plugins/wav_player/application.fam +++ b/applications/plugins/wav_player/application.fam @@ -8,4 +8,5 @@ App( order=60, fap_icon="wav_10px.png", fap_category="Music", + fap_icon_assets="images", ) diff --git a/applications/plugins/wav_player/images/music_10px.png b/applications/plugins/wav_player/images/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/applications/plugins/wav_player/images/music_10px.png differ diff --git a/applications/plugins/wav_player/wav_player.c b/applications/plugins/wav_player/wav_player.c index a0ff3eddf..f8fce4549 100644 --- a/applications/plugins/wav_player/wav_player.c +++ b/applications/plugins/wav_player/wav_player.c @@ -11,6 +11,9 @@ #include "wav_parser.h" #include "wav_player_view.h" #include +#include + +#include #define TAG "WavPlayer" diff --git a/applications/plugins/weather_station/application.fam b/applications/plugins/weather_station/application.fam new file mode 100644 index 000000000..1074fc040 --- /dev/null +++ b/applications/plugins/weather_station/application.fam @@ -0,0 +1,13 @@ +App( + appid="weather_station", + name="Weather Station", + apptype=FlipperAppType.PLUGIN, + entry_point="weather_station_app", + cdefines=["APP_WEATHER_STATION"], + requires=["gui"], + stack_size=4 * 1024, + order=50, + fap_icon="weather_station_10px.png", + fap_category="Tools", + fap_icon_assets="images", +) diff --git a/applications/plugins/weather_station/helpers/weather_station_event.h b/applications/plugins/weather_station/helpers/weather_station_event.h new file mode 100644 index 000000000..b0486183d --- /dev/null +++ b/applications/plugins/weather_station/helpers/weather_station_event.h @@ -0,0 +1,14 @@ +#pragma once + +typedef enum { + //WSCustomEvent + WSCustomEventStartId = 100, + + WSCustomEventSceneSettingLock, + + WSCustomEventViewReceiverOK, + WSCustomEventViewReceiverConfig, + WSCustomEventViewReceiverBack, + WSCustomEventViewReceiverOffDisplay, + WSCustomEventViewReceiverUnlock, +} WSCustomEvent; diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h new file mode 100644 index 000000000..a23540e3d --- /dev/null +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#define WS_VERSION_APP "0.5" +#define WS_DEVELOPED "SkorP" +#define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +#define WS_KEY_FILE_VERSION 1 +#define WS_KEY_FILE_TYPE "Flipper Weather Station Key File" + +/** WSRxKeyState state */ +typedef enum { + WSRxKeyStateIDLE, + WSRxKeyStateBack, + WSRxKeyStateStart, + WSRxKeyStateAddKey, +} WSRxKeyState; + +/** WSHopperState state */ +typedef enum { + WSHopperStateOFF, + WSHopperStateRunnig, + WSHopperStatePause, + WSHopperStateRSSITimeOut, +} WSHopperState; + +/** WSLock */ +typedef enum { + WSLockOff, + WSLockOn, +} WSLock; + +typedef enum { + WeatherStationViewVariableItemList, + WeatherStationViewSubmenu, + WeatherStationViewReceiver, + WeatherStationViewReceiverInfo, + WeatherStationViewWidget, +} WeatherStationView; + +/** WeatherStationTxRx state */ +typedef enum { + WSTxRxStateIDLE, + WSTxRxStateRx, + WSTxRxStateTx, + WSTxRxStateSleep, +} WSTxRxState; diff --git a/applications/plugins/weather_station/images/Humid_10x15.png b/applications/plugins/weather_station/images/Humid_10x15.png new file mode 100644 index 000000000..34b074e5f Binary files /dev/null and b/applications/plugins/weather_station/images/Humid_10x15.png differ 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 000000000..f7c9ca2c7 Binary files /dev/null and b/applications/plugins/weather_station/images/Lock_7x8.png differ 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 000000000..3bafabd14 Binary files /dev/null and b/applications/plugins/weather_station/images/Pin_back_arrow_10x8.png differ 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 000000000..6825247fb Binary files /dev/null and b/applications/plugins/weather_station/images/Quest_7x8.png differ diff --git a/applications/plugins/weather_station/images/Scanning_123x52.png b/applications/plugins/weather_station/images/Scanning_123x52.png new file mode 100644 index 000000000..ec785948d Binary files /dev/null and b/applications/plugins/weather_station/images/Scanning_123x52.png differ diff --git a/applications/plugins/weather_station/images/Therm_7x16.png b/applications/plugins/weather_station/images/Therm_7x16.png new file mode 100644 index 000000000..7c55500b7 Binary files /dev/null and b/applications/plugins/weather_station/images/Therm_7x16.png differ 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 000000000..9d82b4daf Binary files /dev/null and b/applications/plugins/weather_station/images/Unlock_7x8.png differ 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 000000000..d766ffbb4 Binary files /dev/null and b/applications/plugins/weather_station/images/WarningDolphin_45x42.png differ diff --git a/applications/plugins/weather_station/images/station_icon.png b/applications/plugins/weather_station/images/station_icon.png new file mode 100644 index 000000000..b839eeb7a Binary files /dev/null and b/applications/plugins/weather_station/images/station_icon.png differ 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..5384a3c91 --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_592txr.c @@ -0,0 +1,306 @@ +#include "acurite_592txr.h" + +#define TAG "WSProtocolAcurite_592TXR" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/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:%3.1f 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, + (double)instance->generic.temp, + 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/acurite_606tx.c b/applications/plugins/weather_station/protocols/acurite_606tx.c new file mode 100644 index 000000000..4cb5d18b8 --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_606tx.c @@ -0,0 +1,247 @@ +#include "acurite_606tx.h" + +#define TAG "WSProtocolAcurite_606TX" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L1644 + * + * 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 + * iiii iiii | buuu tttt | tttt tttt | cccc cccc + * - i: identification; changes on battery switch + * - c: lfsr_digest8; + * - u: unknown; + * - b: battery low; flag to indicate low battery voltage + * - t: Temperature; in Β°C + * + */ + +static const SubGhzBlockConst ws_protocol_acurite_606tx_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 32, +}; + +struct WSProtocolDecoderAcurite_606TX { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderAcurite_606TX { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Acurite_606TXDecoderStepReset = 0, + Acurite_606TXDecoderStepSaveDuration, + Acurite_606TXDecoderStepCheckDuration, +} Acurite_606TXDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_acurite_606tx_decoder = { + .alloc = ws_protocol_decoder_acurite_606tx_alloc, + .free = ws_protocol_decoder_acurite_606tx_free, + + .feed = ws_protocol_decoder_acurite_606tx_feed, + .reset = ws_protocol_decoder_acurite_606tx_reset, + + .get_hash_data = ws_protocol_decoder_acurite_606tx_get_hash_data, + .serialize = ws_protocol_decoder_acurite_606tx_serialize, + .deserialize = ws_protocol_decoder_acurite_606tx_deserialize, + .get_string = ws_protocol_decoder_acurite_606tx_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_acurite_606tx_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_acurite_606tx = { + .name = WS_PROTOCOL_ACURITE_606TX_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_acurite_606tx_decoder, + .encoder = &ws_protocol_acurite_606tx_encoder, +}; + +void* ws_protocol_decoder_acurite_606tx_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAcurite_606TX* instance = malloc(sizeof(WSProtocolDecoderAcurite_606TX)); + instance->base.protocol = &ws_protocol_acurite_606tx; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_acurite_606tx_free(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + free(instance); +} + +void ws_protocol_decoder_acurite_606tx_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; +} + +static bool ws_protocol_acurite_606tx_check(WSProtocolDecoderAcurite_606TX* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + + uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); + return (crc == (instance->decoder.decode_data & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_acurite_606tx_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 24) & 0xFF; + instance->battery_low = (instance->data >> 23) & 1; + + instance->channel = WS_NO_CHANNEL; + + if(!((instance->data >> 19) & 1)) { + instance->temp = (float)((instance->data >> 8) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 8) & 0x07FF) + 1) / -10.0f; + } + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + + switch(instance->decoder.parser_step) { + case Acurite_606TXDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short * 17) < + ws_protocol_acurite_606tx_const.te_delta * 8)) { + //Found syncPrefix + instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case Acurite_606TXDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Acurite_606TXDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + } + break; + + 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) { + 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; + } + } else { + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_606TXDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_acurite_606tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* 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_606tx_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_606tx_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAcurite_606TX* 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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.h b/applications/plugins/weather_station/protocols/acurite_606tx.h new file mode 100644 index 000000000..5bab3bcb7 --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_606tx.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_ACURITE_606TX_NAME "Acurite-606TX" + +typedef struct WSProtocolDecoderAcurite_606TX WSProtocolDecoderAcurite_606TX; +typedef struct WSProtocolEncoderAcurite_606TX WSProtocolEncoderAcurite_606TX; + +extern const SubGhzProtocolDecoder ws_protocol_acurite_606tx_decoder; +extern const SubGhzProtocolEncoder ws_protocol_acurite_606tx_encoder; +extern const SubGhzProtocol ws_protocol_acurite_606tx; + +/** + * Allocate WSProtocolDecoderAcurite_606TX. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAcurite_606TX* pointer to a WSProtocolDecoderAcurite_606TX instance + */ +void* ws_protocol_decoder_acurite_606tx_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAcurite_606TX. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + */ +void ws_protocol_decoder_acurite_606tx_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAcurite_606TX. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + */ +void ws_protocol_decoder_acurite_606tx_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAcurite_606TX. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX 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_606tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAcurite_606TX. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance + * @param output Resulting text + */ +void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.c b/applications/plugins/weather_station/protocols/acurite_609txc.c new file mode 100644 index 000000000..aeb0785eb --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_609txc.c @@ -0,0 +1,247 @@ +#include "acurite_609txc.h" + +#define TAG "WSProtocolAcurite_609TXC" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L216 + * + * 0000 1111 | 0011 0000 | 0101 1100 | 0000 0000 | 1110 0111 + * iiii iiii | buuu tttt | tttt tttt | hhhh hhhh | cccc cccc + * - i: identification; changes on battery switch + * - c: checksum (sum of previous by bytes) + * - u: unknown + * - b: battery low; flag to indicate low battery voltage + * - t: temperature; in Β°C * 10, 12 bit with complement + * - h: humidity + * + */ + +static const SubGhzBlockConst ws_protocol_acurite_609txc_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 150, + .min_count_bit_for_found = 40, +}; + +struct WSProtocolDecoderAcurite_609TXC { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderAcurite_609TXC { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Acurite_609TXCDecoderStepReset = 0, + Acurite_609TXCDecoderStepSaveDuration, + Acurite_609TXCDecoderStepCheckDuration, +} Acurite_609TXCDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder = { + .alloc = ws_protocol_decoder_acurite_609txc_alloc, + .free = ws_protocol_decoder_acurite_609txc_free, + + .feed = ws_protocol_decoder_acurite_609txc_feed, + .reset = ws_protocol_decoder_acurite_609txc_reset, + + .get_hash_data = ws_protocol_decoder_acurite_609txc_get_hash_data, + .serialize = ws_protocol_decoder_acurite_609txc_serialize, + .deserialize = ws_protocol_decoder_acurite_609txc_deserialize, + .get_string = ws_protocol_decoder_acurite_609txc_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_acurite_609txc = { + .name = WS_PROTOCOL_ACURITE_609TXC_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_acurite_609txc_decoder, + .encoder = &ws_protocol_acurite_609txc_encoder, +}; + +void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAcurite_609TXC* instance = malloc(sizeof(WSProtocolDecoderAcurite_609TXC)); + instance->base.protocol = &ws_protocol_acurite_609txc; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_acurite_609txc_free(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + free(instance); +} + +void ws_protocol_decoder_acurite_609txc_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; +} + +static bool ws_protocol_acurite_609txc_check(WSProtocolDecoderAcurite_609TXC* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t crc = (uint8_t)(instance->decoder.decode_data >> 32) + + (uint8_t)(instance->decoder.decode_data >> 24) + + (uint8_t)(instance->decoder.decode_data >> 16) + + (uint8_t)(instance->decoder.decode_data >> 8); + return (crc == (instance->decoder.decode_data & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_acurite_609txc_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 32) & 0xFF; + instance->battery_low = (instance->data >> 31) & 1; + + instance->channel = WS_NO_CHANNEL; + + // Temperature in Celsius is encoded as a 12 bit integer value + // multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2) + // negative values are recovered by sign extend from int16_t. + int16_t temp_raw = + (int16_t)(((instance->data >> 12) & 0xf000) | ((instance->data >> 16) << 4)); + instance->temp = (temp_raw >> 4) * 0.1f; + instance->humidity = (instance->data >> 8) & 0xff; + instance->btn = WS_NO_BTN; +} + +void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + + switch(instance->decoder.parser_step) { + case Acurite_609TXCDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short * 17) < + ws_protocol_acurite_609txc_const.te_delta * 8)) { + //Found syncPrefix + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case Acurite_609TXCDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Acurite_609TXCDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + break; + + case Acurite_609TXCDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_609txc_const.te_short) < + ws_protocol_acurite_609txc_const.te_delta) { + if((DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short) < + ws_protocol_acurite_609txc_const.te_delta) || + (duration > ws_protocol_acurite_609txc_const.te_long * 3)) { + //Found syncPostfix + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_acurite_609txc_const.min_count_bit_for_found) && + ws_protocol_acurite_609txc_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_acurite_609txc_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_609txc_const.te_long) < + ws_protocol_acurite_609txc_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long * 2) < + ws_protocol_acurite_609txc_const.te_delta * 4) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + } else { + instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_acurite_609txc_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* 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_609txc_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_609txc_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAcurite_609TXC* 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:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 40), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.h b/applications/plugins/weather_station/protocols/acurite_609txc.h new file mode 100644 index 000000000..f87c20e9b --- /dev/null +++ b/applications/plugins/weather_station/protocols/acurite_609txc.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_ACURITE_609TXC_NAME "Acurite-609TXC" + +typedef struct WSProtocolDecoderAcurite_609TXC WSProtocolDecoderAcurite_609TXC; +typedef struct WSProtocolEncoderAcurite_609TXC WSProtocolEncoderAcurite_609TXC; + +extern const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder; +extern const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder; +extern const SubGhzProtocol ws_protocol_acurite_609txc; + +/** + * Allocate WSProtocolDecoderAcurite_609TXC. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAcurite_609TXC* pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void ws_protocol_decoder_acurite_609txc_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + */ +void ws_protocol_decoder_acurite_609txc_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC 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_609txc_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAcurite_609TXC. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance + * @param output Resulting text + */ +void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output); 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..5ae22b790 --- /dev/null +++ b/applications/plugins/weather_station/protocols/ambient_weather.c @@ -0,0 +1,276 @@ +#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->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:%3.1f 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, + (double)instance->generic.temp, + 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_02.c b/applications/plugins/weather_station/protocols/gt_wt_02.c new file mode 100644 index 000000000..cbe119192 --- /dev/null +++ b/applications/plugins/weather_station/protocols/gt_wt_02.c @@ -0,0 +1,265 @@ +#include "gt_wt_02.h" + +#define TAG "WSProtocolGT_WT02" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/gt_wt_02.c + * + * GT-WT-02 sensor on 433.92MHz. + * Example and frame description provided by https://github.com/ludwich66 + * [01] {37} 34 00 ed 47 60 : 00110100 00000000 11101101 01000111 01100000 + * code, BatOK,not-man-send, Channel1, +23,7°C, 35% + * [01] {37} 34 8f 87 15 90 : 00110100 10001111 10000111 00010101 10010000 + * code, BatOK,not-man-send, Channel1,-12,1°C, 10% + * Humidity: + * - the working range is 20-90 % + * - if "LL" in display view it sends 10 % + * - if "HH" in display view it sends 110% + * SENSOR: GT-WT-02 (ALDI Globaltronics..) + * TYP IIIIIIII BMCCTTTT TTTTTTTT HHHHHHHX XXXXX + * TYPE Description: + * - I = Random Device Code, changes with battery reset + * - B = Battery 0=OK 1=LOW + * - M = Manual Send Button Pressed 0=not pressed 1=pressed + * - C = Channel 00=CH1, 01=CH2, 10=CH3 + * - T = Temperature, 12 Bit 2's complement, scaled by 10 + * - H = Humidity = 7 Bit bin2dez 00-99, Display LL=10%, Display HH=110% (Range 20-90%) + * - X = Checksum, sum modulo 64 + * A Lidl AURIO (from 12/2018) with PCB marking YJ-T12 V02 has two extra bits in front. + * +*/ + +static const SubGhzBlockConst ws_protocol_gt_wt_02_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 37, +}; + +struct WSProtocolDecoderGT_WT02 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderGT_WT02 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + GT_WT02DecoderStepReset = 0, + GT_WT02DecoderStepSaveDuration, + GT_WT02DecoderStepCheckDuration, +} GT_WT02DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_gt_wt_02_decoder = { + .alloc = ws_protocol_decoder_gt_wt_02_alloc, + .free = ws_protocol_decoder_gt_wt_02_free, + + .feed = ws_protocol_decoder_gt_wt_02_feed, + .reset = ws_protocol_decoder_gt_wt_02_reset, + + .get_hash_data = ws_protocol_decoder_gt_wt_02_get_hash_data, + .serialize = ws_protocol_decoder_gt_wt_02_serialize, + .deserialize = ws_protocol_decoder_gt_wt_02_deserialize, + .get_string = ws_protocol_decoder_gt_wt_02_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_gt_wt_02_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_gt_wt_02 = { + .name = WS_PROTOCOL_GT_WT_02_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_gt_wt_02_decoder, + .encoder = &ws_protocol_gt_wt_02_encoder, +}; + +void* ws_protocol_decoder_gt_wt_02_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderGT_WT02* instance = malloc(sizeof(WSProtocolDecoderGT_WT02)); + instance->base.protocol = &ws_protocol_gt_wt_02; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_gt_wt_02_free(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + free(instance); +} + +void ws_protocol_decoder_gt_wt_02_reset(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + instance->decoder.parser_step = GT_WT02DecoderStepReset; +} + +static bool ws_protocol_gt_wt_02_check(WSProtocolDecoderGT_WT02* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t sum = (instance->decoder.decode_data >> 5) & 0xe; + uint64_t temp_data = instance->decoder.decode_data >> 9; + for(uint8_t i = 0; i < 7; i++) { + sum += (temp_data >> (i * 4)) & 0xF; + } + return ((uint8_t)(instance->decoder.decode_data & 0x3F) == (sum & 0x3F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_gt_wt_02_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 29) & 0xFF; + instance->battery_low = (instance->data >> 28) & 1; + instance->btn = (instance->data >> 27) & 1; + instance->channel = ((instance->data >> 25) & 0x3) + 1; + + if(!((instance->data >> 24) & 1)) { + instance->temp = (float)((instance->data >> 13) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 13) & 0x07FF) + 1) / -10.0f; + } + + instance->humidity = (instance->data >> 6) & 0x7F; + if(instance->humidity <= 10) // actually the sensors sends 10 below working range of 20% + instance->humidity = 0; + else if(instance->humidity > 90) // actually the sensors sends 110 above working range of 90% + instance->humidity = 100; +} + +void ws_protocol_decoder_gt_wt_02_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + + switch(instance->decoder.parser_step) { + case GT_WT02DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_short * 18) < + ws_protocol_gt_wt_02_const.te_delta * 8)) { + //Found syncPrefix + instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case GT_WT02DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = GT_WT02DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = GT_WT02DecoderStepReset; + } + break; + + case GT_WT02DecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_02_const.te_short) < + ws_protocol_gt_wt_02_const.te_delta) { + if(DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_short * 18) < + ws_protocol_gt_wt_02_const.te_delta * 8) { + //Found syncPostfix + instance->decoder.parser_step = GT_WT02DecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_gt_wt_02_const.min_count_bit_for_found) && + ws_protocol_gt_wt_02_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_gt_wt_02_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } else if(instance->decoder.decode_count_bit == 1) { + instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else if( + DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_long) < + ws_protocol_gt_wt_02_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_long * 2) < + ws_protocol_gt_wt_02_const.te_delta * 4) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = GT_WT02DecoderStepReset; + } + } else { + instance->decoder.parser_step = GT_WT02DecoderStepReset; + } + } else { + instance->decoder.parser_step = GT_WT02DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_gt_wt_02_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderGT_WT02* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_gt_wt_02_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_gt_wt_02_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderGT_WT02* 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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.h b/applications/plugins/weather_station/protocols/gt_wt_02.h new file mode 100644 index 000000000..f17d5baa0 --- /dev/null +++ b/applications/plugins/weather_station/protocols/gt_wt_02.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_GT_WT_02_NAME "GT-WT02" + +typedef struct WSProtocolDecoderGT_WT02 WSProtocolDecoderGT_WT02; +typedef struct WSProtocolEncoderGT_WT02 WSProtocolEncoderGT_WT02; + +extern const SubGhzProtocolDecoder ws_protocol_gt_wt_02_decoder; +extern const SubGhzProtocolEncoder ws_protocol_gt_wt_02_encoder; +extern const SubGhzProtocol ws_protocol_gt_wt_02; + +/** + * Allocate WSProtocolDecoderGT_WT02. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderGT_WT02* pointer to a WSProtocolDecoderGT_WT02 instance + */ +void* ws_protocol_decoder_gt_wt_02_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderGT_WT02. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + */ +void ws_protocol_decoder_gt_wt_02_free(void* context); + +/** + * Reset decoder WSProtocolDecoderGT_WT02. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + */ +void ws_protocol_decoder_gt_wt_02_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_gt_wt_02_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderGT_WT02. + * @param context Pointer to a WSProtocolDecoderGT_WT02 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_gt_wt_02_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderGT_WT02. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderGT_WT02 instance + * @param output Resulting text + */ +void ws_protocol_decoder_gt_wt_02_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 new file mode 100644 index 000000000..7831cf069 --- /dev/null +++ b/applications/plugins/weather_station/protocols/gt_wt_03.c @@ -0,0 +1,340 @@ +#include "gt_wt_03.h" + +#define TAG "WSProtocolGT_WT03" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/gt_wt_03.c + * + * + * Globaltronics GT-WT-03 sensor on 433.92MHz. + * The 01-set sensor has 60 ms packet gap with 10 repeats. + * The 02-set sensor has no packet gap with 23 repeats. + * Example: + * {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes ] + * {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes Batt-Changed ] + * {41} 17 cf fe fa ea 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-No Batt-Changed ] + * {41} 01 cf 6f 11 b2 80 [ S2 C2 23,8 C 74.8 F 48% Bat-LOW Manual-No ] + * {41} 01 c8 d0 2b 76 80 [ S2 C3 -4,4 C 24.1 F 55% Bat-Good Manual-No Batt-Changed ] + * Format string: + * ID:8h HUM:8d B:b M:b C:2d TEMP:12d CHK:8h 1x + * Data layout: + * TYP IIIIIIII HHHHHHHH BMCCTTTT TTTTTTTT XXXXXXXX + * - I: Random Device Code: changes with battery reset + * - H: Humidity: 8 Bit 00-99, Display LL=10%, Display HH=110% (Range 20-95%) + * - B: Battery: 0=OK 1=LOW + * - M: Manual Send Button Pressed: 0=not pressed, 1=pressed + * - C: Channel: 00=CH1, 01=CH2, 10=CH3 + * - T: Temperature: 12 Bit 2's complement, scaled by 10, range-50.0 C (-50.1 shown as Lo) to +70.0 C (+70.1 C is shown as Hi) + * - X: Checksum, xor shifting key per byte + * Humidity: + * - the working range is 20-95 % + * - if "LL" in display view it sends 10 % + * - if "HH" in display view it sends 110% + * Checksum: + * Per byte xor the key for each 1-bit, shift per bit. Key list per bit, starting at MSB: + * - 0x00 [07] + * - 0x80 [06] + * - 0x40 [05] + * - 0x20 [04] + * - 0x10 [03] + * - 0x88 [02] + * - 0xc4 [01] + * - 0x62 [00] + * Note: this can also be seen as lower byte of a Galois/Fibonacci LFSR-16, gen 0x00, init 0x3100 (or 0x62 if reversed) resetting at every byte. + * Battery voltages: + * - U=<2,65V +- ~5% Battery indicator + * - U=>2.10C +- 5% plausible readings + * - U=2,00V +- ~5% Temperature offset -5Β°C Humidity offset unknown + * - U=<1,95V +- ~5% does not initialize anymore + * - U=1,90V +- 5% temperature offset -15Β°C + * - U=1,80V +- 5% Display is showing refresh pattern + * - U=1.75V +- ~5% TX causes cut out + * + */ + +static const SubGhzBlockConst ws_protocol_gt_wt_03_const = { + .te_short = 285, + .te_long = 570, + .te_delta = 120, + .min_count_bit_for_found = 41, +}; + +struct WSProtocolDecoderGT_WT03 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderGT_WT03 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + GT_WT03DecoderStepReset = 0, + GT_WT03DecoderStepCheckPreambule, + GT_WT03DecoderStepSaveDuration, + GT_WT03DecoderStepCheckDuration, +} GT_WT03DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder = { + .alloc = ws_protocol_decoder_gt_wt_03_alloc, + .free = ws_protocol_decoder_gt_wt_03_free, + + .feed = ws_protocol_decoder_gt_wt_03_feed, + .reset = ws_protocol_decoder_gt_wt_03_reset, + + .get_hash_data = ws_protocol_decoder_gt_wt_03_get_hash_data, + .serialize = ws_protocol_decoder_gt_wt_03_serialize, + .deserialize = ws_protocol_decoder_gt_wt_03_deserialize, + .get_string = ws_protocol_decoder_gt_wt_03_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_gt_wt_03 = { + .name = WS_PROTOCOL_GT_WT_03_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_gt_wt_03_decoder, + .encoder = &ws_protocol_gt_wt_03_encoder, +}; + +void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderGT_WT03* instance = malloc(sizeof(WSProtocolDecoderGT_WT03)); + instance->base.protocol = &ws_protocol_gt_wt_03; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_gt_wt_03_free(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + free(instance); +} + +void ws_protocol_decoder_gt_wt_03_reset(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + instance->decoder.parser_step = GT_WT03DecoderStepReset; +} + +static bool ws_protocol_gt_wt_03_check_crc(WSProtocolDecoderGT_WT03* instance) { + uint8_t msg[] = { + instance->decoder.decode_data >> 33, + instance->decoder.decode_data >> 25, + instance->decoder.decode_data >> 17, + instance->decoder.decode_data >> 9}; + + uint8_t sum = 0; + for(unsigned k = 0; k < sizeof(msg); ++k) { + uint8_t data = msg[k]; + uint16_t key = 0x3100; + for(int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) sum ^= key & 0xff; + // roll the key right + key = (key >> 1); + } + } + return ((sum ^ (uint8_t)((instance->decoder.decode_data >> 1) & 0xFF)) == 0x2D); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_gt_wt_03_remote_controller(WSBlockGeneric* instance) { + instance->id = instance->data >> 33; + instance->humidity = (instance->data >> 25) & 0xFF; + + if(instance->humidity <= 10) { // actually the sensors sends 10 below working range of 20% + instance->humidity = 0; + } else if(instance->humidity > 95) { // actually the sensors sends 110 above working range of 90% + instance->humidity = 100; + } + + instance->battery_low = (instance->data >> 24) & 1; + instance->btn = (instance->data >> 23) & 1; + instance->channel = ((instance->data >> 21) & 0x03) + 1; + + if(!((instance->data >> 20) & 1)) { + instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f; + } +} + +void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + + switch(instance->decoder.parser_step) { + case GT_WT03DecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2)) { + instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case GT_WT03DecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2)) { + //Found preambule + instance->header_count++; + } else if(instance->header_count == 4) { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) < + ws_protocol_gt_wt_03_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) < + ws_protocol_gt_wt_03_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 = GT_WT03DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) < + ws_protocol_gt_wt_03_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) < + ws_protocol_gt_wt_03_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 = GT_WT03DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + } + break; + + case GT_WT03DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = GT_WT03DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + break; + + case GT_WT03DecoderStepCheckDuration: + if(!level) { + if(((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < + ws_protocol_gt_wt_03_const.te_delta * 2))) { + if((instance->decoder.decode_count_bit == + ws_protocol_gt_wt_03_const.min_count_bit_for_found) && + ws_protocol_gt_wt_03_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_gt_wt_03_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->header_count = 1; + instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule; + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) < + ws_protocol_gt_wt_03_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) < + ws_protocol_gt_wt_03_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) < + ws_protocol_gt_wt_03_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) < + ws_protocol_gt_wt_03_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + } else { + instance->decoder.parser_step = GT_WT03DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_gt_wt_03_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderGT_WT03* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_gt_wt_03_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_gt_wt_03_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderGT_WT03* 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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.h b/applications/plugins/weather_station/protocols/gt_wt_03.h new file mode 100644 index 000000000..fd9536e34 --- /dev/null +++ b/applications/plugins/weather_station/protocols/gt_wt_03.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_GT_WT_03_NAME "GT-WT03" + +typedef struct WSProtocolDecoderGT_WT03 WSProtocolDecoderGT_WT03; +typedef struct WSProtocolEncoderGT_WT03 WSProtocolEncoderGT_WT03; + +extern const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder; +extern const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder; +extern const SubGhzProtocol ws_protocol_gt_wt_03; + +/** + * Allocate WSProtocolDecoderGT_WT03. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderGT_WT03* pointer to a WSProtocolDecoderGT_WT03 instance + */ +void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderGT_WT03. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + */ +void ws_protocol_decoder_gt_wt_03_free(void* context); + +/** + * Reset decoder WSProtocolDecoderGT_WT03. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + */ +void ws_protocol_decoder_gt_wt_03_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderGT_WT03. + * @param context Pointer to a WSProtocolDecoderGT_WT03 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_gt_wt_03_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderGT_WT03. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderGT_WT03 instance + * @param output Resulting text + */ +void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/infactory.c b/applications/plugins/weather_station/protocols/infactory.c new file mode 100644 index 000000000..2d444d981 --- /dev/null +++ b/applications/plugins/weather_station/protocols/infactory.c @@ -0,0 +1,296 @@ +#include "infactory.h" + +#define TAG "WSProtocolInfactory" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/infactory.c + * + * Analysis using Genuino (see http://gitlab.com/hp-uno, e.g. uno_log_433): + * Observed On-Off-Key (OOK) data pattern: + * preamble syncPrefix data...(40 bit) syncPostfix + * HHLL HHLL HHLL HHLL HLLLLLLLLLLLLLLLL (HLLLL HLLLLLLLL HLLLL HLLLLLLLL ....) HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL + * Breakdown: + * - four preamble pairs '1'/'0' each with a length of ca. 1000us + * - syncPre, syncPost, data0, data1 have a '1' start pulse of ca. 500us + * - syncPre pulse before dataPtr has a '0' pulse length of ca. 8000us + * - data0 (0-bits) have then a '0' pulse length of ca. 2000us + * - data1 (1-bits) have then a '0' pulse length of ca. 4000us + * - syncPost after dataPtr has a '0' pulse length of ca. 16000us + * This analysis is the reason for the new r_device definitions below. + * NB: pulse_slicer_ppm does not use .gap_limit if .tolerance is set. + * + * Outdoor sensor, transmits temperature and humidity data + * - inFactory NC-3982-913/NX-5817-902, Pearl (for FWS-686 station) + * - nor-tec 73383 (weather station + sensor), Schou Company AS, Denmark + * - DAY 73365 (weather station + sensor), Schou Company AS, Denmark + * Known brand names: inFactory, nor-tec, GreenBlue, DAY. Manufacturer in China. + * Transmissions includes an id. Every 60 seconds the sensor transmits 6 packets: + * 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 | 0110 0001 + * iiii iiii | cccc ub?? | tttt tttt | tttt hhhh | hhhh ??nn + * - i: identification; changes on battery switch + * - c: CRC-4; CCITT checksum, see below for computation specifics + * - u: unknown; (sometimes set at power-on, but not always) + * - b: battery low; flag to indicate low battery voltage + * - h: Humidity; BCD-encoded, each nibble is one digit, 'A0' means 100%rH + * - t: Temperature; in Β°F as binary number with one decimal place + 90 Β°F offset + * - n: Channel; Channel number 1 - 3 + * + */ + +static const SubGhzBlockConst ws_protocol_infactory_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 40, +}; + +struct WSProtocolDecoderInfactory { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderInfactory { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + InfactoryDecoderStepReset = 0, + InfactoryDecoderStepCheckPreambule, + InfactoryDecoderStepSaveDuration, + InfactoryDecoderStepCheckDuration, +} InfactoryDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_infactory_decoder = { + .alloc = ws_protocol_decoder_infactory_alloc, + .free = ws_protocol_decoder_infactory_free, + + .feed = ws_protocol_decoder_infactory_feed, + .reset = ws_protocol_decoder_infactory_reset, + + .get_hash_data = ws_protocol_decoder_infactory_get_hash_data, + .serialize = ws_protocol_decoder_infactory_serialize, + .deserialize = ws_protocol_decoder_infactory_deserialize, + .get_string = ws_protocol_decoder_infactory_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_infactory_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_infactory = { + .name = WS_PROTOCOL_INFACTORY_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_infactory_decoder, + .encoder = &ws_protocol_infactory_encoder, +}; + +void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderInfactory* instance = malloc(sizeof(WSProtocolDecoderInfactory)); + instance->base.protocol = &ws_protocol_infactory; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_infactory_free(void* context) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + free(instance); +} + +void ws_protocol_decoder_infactory_reset(void* context) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + instance->decoder.parser_step = InfactoryDecoderStepReset; +} + +static bool ws_protocol_infactory_check_crc(WSProtocolDecoderInfactory* instance) { + uint8_t msg[] = { + instance->decoder.decode_data >> 32, + (((instance->decoder.decode_data >> 24) & 0x0F) | (instance->decoder.decode_data & 0x0F) + << 4), + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8, + instance->decoder.decode_data}; + + uint8_t crc = + subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704 + crc ^= msg[4] >> 4; // last nibble is only XORed + return (crc == ((instance->decoder.decode_data >> 28) & 0x0F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_infactory_remote_controller(WSBlockGeneric* instance) { + instance->id = instance->data >> 32; + instance->battery_low = (instance->data >> 26) & 1; + instance->btn = WS_NO_BTN; + instance->temp = ws_block_generic_fahrenheit_to_celsius( + ((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f); + instance->humidity = + (((instance->data >> 8) & 0x0F) * 10) + ((instance->data >> 4) & 0x0F); // BCD, 'A0'=100%rH + instance->channel = instance->data & 0x03; +} + +void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + + switch(instance->decoder.parser_step) { + case InfactoryDecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) < + ws_protocol_infactory_const.te_delta * 2)) { + instance->decoder.parser_step = InfactoryDecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case InfactoryDecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short * 2) < + ws_protocol_infactory_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) < + ws_protocol_infactory_const.te_delta * 2)) { + //Found preambule + instance->header_count++; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < + ws_protocol_infactory_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 16) < + ws_protocol_infactory_const.te_delta * 8)) { + //Found syncPrefix + if(instance->header_count > 3) { + instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + } else { + instance->decoder.parser_step = InfactoryDecoderStepReset; + } + } + break; + + case InfactoryDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = InfactoryDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = InfactoryDecoderStepReset; + } + break; + + case InfactoryDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)ws_protocol_infactory_const.te_short * 30)) { + //Found syncPostfix + if((instance->decoder.decode_count_bit == + ws_protocol_infactory_const.min_count_bit_for_found) && + ws_protocol_infactory_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_infactory_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 = InfactoryDecoderStepReset; + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < + ws_protocol_infactory_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_infactory_const.te_long) < + ws_protocol_infactory_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < + ws_protocol_infactory_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_infactory_const.te_long * 2) < + ws_protocol_infactory_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = InfactoryDecoderStepReset; + } + } else { + instance->decoder.parser_step = InfactoryDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_infactory_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderInfactory* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_infactory_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_infactory_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderInfactory* 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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/infactory.h b/applications/plugins/weather_station/protocols/infactory.h new file mode 100644 index 000000000..2792ab987 --- /dev/null +++ b/applications/plugins/weather_station/protocols/infactory.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_INFACTORY_NAME "inFactory-TH" + +typedef struct WSProtocolDecoderInfactory WSProtocolDecoderInfactory; +typedef struct WSProtocolEncoderInfactory WSProtocolEncoderInfactory; + +extern const SubGhzProtocolDecoder ws_protocol_infactory_decoder; +extern const SubGhzProtocolEncoder ws_protocol_infactory_encoder; +extern const SubGhzProtocol ws_protocol_infactory; + +/** + * Allocate WSProtocolDecoderInfactory. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderInfactory* pointer to a WSProtocolDecoderInfactory instance + */ +void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderInfactory. + * @param context Pointer to a WSProtocolDecoderInfactory instance + */ +void ws_protocol_decoder_infactory_free(void* context); + +/** + * Reset decoder WSProtocolDecoderInfactory. + * @param context Pointer to a WSProtocolDecoderInfactory instance + */ +void ws_protocol_decoder_infactory_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderInfactory. + * @param context Pointer to a WSProtocolDecoderInfactory 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_infactory_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderInfactory. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderInfactory instance + * @param output Resulting text + */ +void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c new file mode 100644 index 000000000..e4b612250 --- /dev/null +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c @@ -0,0 +1,297 @@ +#include "lacrosse_tx141thbv2.h" + +#define TAG "WSProtocolLaCrosse_TX141THBv2" + +/* + * Help + * 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 + * - c: lfsr_digest8_reflect; + * - u: unknown; + * - b: battery low; flag to indicate low battery voltage + * - h: Humidity; + * - t: Temperature; in Β°F as binary number with one decimal place + 50 Β°F offset + * - n: Channel; Channel number 1 - 3 + */ + +static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = { + .te_short = 250, + .te_long = 500, + .te_delta = 120, + .min_count_bit_for_found = 41, +}; + +struct WSProtocolDecoderLaCrosse_TX141THBv2 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderLaCrosse_TX141THBv2 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + LaCrosse_TX141THBv2DecoderStepReset = 0, + LaCrosse_TX141THBv2DecoderStepCheckPreambule, + LaCrosse_TX141THBv2DecoderStepSaveDuration, + LaCrosse_TX141THBv2DecoderStepCheckDuration, +} LaCrosse_TX141THBv2DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_lacrosse_tx141thbv2_decoder = { + .alloc = ws_protocol_decoder_lacrosse_tx141thbv2_alloc, + .free = ws_protocol_decoder_lacrosse_tx141thbv2_free, + + .feed = ws_protocol_decoder_lacrosse_tx141thbv2_feed, + .reset = ws_protocol_decoder_lacrosse_tx141thbv2_reset, + + .get_hash_data = ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data, + .serialize = ws_protocol_decoder_lacrosse_tx141thbv2_serialize, + .deserialize = ws_protocol_decoder_lacrosse_tx141thbv2_deserialize, + .get_string = ws_protocol_decoder_lacrosse_tx141thbv2_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_lacrosse_tx141thbv2_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_lacrosse_tx141thbv2 = { + .name = WS_PROTOCOL_LACROSSE_TX141THBV2_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_lacrosse_tx141thbv2_decoder, + .encoder = &ws_protocol_lacrosse_tx141thbv2_encoder, +}; + +void* ws_protocol_decoder_lacrosse_tx141thbv2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = + malloc(sizeof(WSProtocolDecoderLaCrosse_TX141THBv2)); + instance->base.protocol = &ws_protocol_lacrosse_tx141thbv2; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_lacrosse_tx141thbv2_free(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + free(instance); +} + +void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; +} + +static bool + ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + instance->decoder.decode_data >> 33, + instance->decoder.decode_data >> 25, + instance->decoder.decode_data >> 17, + instance->decoder.decode_data >> 9}; + + uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4); + return (crc == ((instance->decoder.decode_data >> 1) & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) { + instance->id = instance->data >> 33; + instance->battery_low = (instance->data >> 32) & 1; + instance->btn = (instance->data >> 31) & 1; + instance->channel = ((instance->data >> 29) & 0x03) + 1; + instance->temp = ((float)((instance->data >> 17) & 0x0FFF) - 500.0f) / 10.0f; + instance->humidity = (instance->data >> 9) & 0xFF; +} + +void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + + switch(instance->decoder.parser_step) { + case LaCrosse_TX141THBv2DecoderStepReset: + if((level) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case LaCrosse_TX141THBv2DecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, + ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { + //Found preambule + instance->header_count++; + } else if(instance->header_count == 4) { + if((DURATION_DIFF( + instance->decoder.te_last, + ws_protocol_lacrosse_tx141thbv2_const.te_short) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_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 = LaCrosse_TX141THBv2DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, + ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) < + ws_protocol_lacrosse_tx141thbv2_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 = LaCrosse_TX141THBv2DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + } + break; + + case LaCrosse_TX141THBv2DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + break; + + case LaCrosse_TX141THBv2DecoderStepCheckDuration: + if(!level) { + if(((DURATION_DIFF( + instance->decoder.te_last, + ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) { + if((instance->decoder.decode_count_bit == + ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) && + ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_lacrosse_tx141thbv2_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->header_count = 1; + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_short) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_long) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) < + ws_protocol_lacrosse_tx141thbv2_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + } else { + instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_lacrosse_tx141thbv2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_lacrosse_tx141thbv2_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_lacrosse_tx141thbv2_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX141THBv2* 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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h new file mode 100644 index 000000000..941b01058 --- /dev/null +++ b/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_LACROSSE_TX141THBV2_NAME "TX141THBv2" + +typedef struct WSProtocolDecoderLaCrosse_TX141THBv2 WSProtocolDecoderLaCrosse_TX141THBv2; +typedef struct WSProtocolEncoderLaCrosse_TX141THBv2 WSProtocolEncoderLaCrosse_TX141THBv2; + +extern const SubGhzProtocolDecoder ws_protocol_lacrosse_tx141thbv2_decoder; +extern const SubGhzProtocolEncoder ws_protocol_lacrosse_tx141thbv2_encoder; +extern const SubGhzProtocol ws_protocol_lacrosse_tx141thbv2; + +/** + * Allocate WSProtocolDecoderLaCrosse_TX141THBv2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderLaCrosse_TX141THBv2* pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + */ +void* ws_protocol_decoder_lacrosse_tx141thbv2_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderLaCrosse_TX141THBv2. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + */ +void ws_protocol_decoder_lacrosse_tx141thbv2_free(void* context); + +/** + * Reset decoder WSProtocolDecoderLaCrosse_TX141THBv2. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + */ +void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderLaCrosse_TX141THBv2. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 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_lacrosse_tx141thbv2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderLaCrosse_TX141THBv2. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance + * @param output Resulting text + */ +void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/plugins/weather_station/protocols/nexus_th.c new file mode 100644 index 000000000..7d4a77aea --- /dev/null +++ b/applications/plugins/weather_station/protocols/nexus_th.c @@ -0,0 +1,260 @@ +#include "nexus_th.h" + +#define TAG "WSProtocolNexus_TH" + +/* + * Help + * 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, + * also infactory/FreeTec (Pearl) NX-3980 sensors for infactory/FreeTec NX-3974 station, + * also Solight TE82S sensors for Solight TE76/TE82/TE83/TE84 stations, + * also TFA 30.3209.02 temperature/humidity sensor. + * The sensor sends 36 bits 12 times, + * the packets are ppm modulated (distance coding) with a pulse of ~500 us + * followed by a short gap of ~1000 us for a 0 bit or a long ~2000 us gap for a + * 1 bit, the sync gap is ~4000 us. + * The data is grouped in 9 nibbles: + * [id0] [id1] [flags] [temp0] [temp1] [temp2] [const] [humi0] [humi1] + * - The 8-bit id changes when the battery is changed in the sensor. + * - flags are 4 bits B 0 C C, where B is the battery status: 1=OK, 0=LOW + * - and CC is the channel: 0=CH1, 1=CH2, 2=CH3 + * - temp is 12 bit signed scaled by 10 + * - const is always 1111 (0x0F) + * - humidity is 8 bits + * The sensors can be bought at Clas Ohlsen (Nexus) and Pearl (infactory/FreeTec). + * + */ + +#define NEXUS_TH_CONST_DATA 0b1111 + +static const SubGhzBlockConst ws_protocol_nexus_th_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 36, +}; + +struct WSProtocolDecoderNexus_TH { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderNexus_TH { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Nexus_THDecoderStepReset = 0, + Nexus_THDecoderStepSaveDuration, + Nexus_THDecoderStepCheckDuration, +} Nexus_THDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder = { + .alloc = ws_protocol_decoder_nexus_th_alloc, + .free = ws_protocol_decoder_nexus_th_free, + + .feed = ws_protocol_decoder_nexus_th_feed, + .reset = ws_protocol_decoder_nexus_th_reset, + + .get_hash_data = ws_protocol_decoder_nexus_th_get_hash_data, + .serialize = ws_protocol_decoder_nexus_th_serialize, + .deserialize = ws_protocol_decoder_nexus_th_deserialize, + .get_string = ws_protocol_decoder_nexus_th_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_nexus_th = { + .name = WS_PROTOCOL_NEXUS_TH_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_nexus_th_decoder, + .encoder = &ws_protocol_nexus_th_encoder, +}; + +void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderNexus_TH* instance = malloc(sizeof(WSProtocolDecoderNexus_TH)); + instance->base.protocol = &ws_protocol_nexus_th; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_nexus_th_free(void* context) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + free(instance); +} + +void ws_protocol_decoder_nexus_th_reset(void* context) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + instance->decoder.parser_step = Nexus_THDecoderStepReset; +} + +static bool ws_protocol_nexus_th_check(WSProtocolDecoderNexus_TH* instance) { + uint8_t type = (instance->decoder.decode_data >> 8) & 0x0F; + + if((type == NEXUS_TH_CONST_DATA) && ((instance->decoder.decode_data >> 4) != 0xffffffff)) { + return true; + } else { + return false; + } + return true; +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_nexus_th_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 28) & 0xFF; + instance->battery_low = !((instance->data >> 27) & 1); + instance->channel = ((instance->data >> 24) & 0x03) + 1; + instance->btn = WS_NO_BTN; + if(!((instance->data >> 23) & 1)) { + instance->temp = (float)((instance->data >> 12) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 12) & 0x07FF) + 1) / -10.0f; + } + + instance->humidity = instance->data & 0xFF; +} + +void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + + switch(instance->decoder.parser_step) { + case Nexus_THDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) < + ws_protocol_nexus_th_const.te_delta * 4)) { + //Found sync + instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case Nexus_THDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Nexus_THDecoderStepReset; + } + break; + + case Nexus_THDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) < + ws_protocol_nexus_th_const.te_delta * 4) { + //Found sync + instance->decoder.parser_step = Nexus_THDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_nexus_th_const.min_count_bit_for_found) && + ws_protocol_nexus_th_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_nexus_th_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) < + ws_protocol_nexus_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 2) < + ws_protocol_nexus_th_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) < + ws_protocol_nexus_th_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 4) < + ws_protocol_nexus_th_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Nexus_THDecoderStepReset; + } + } else { + instance->decoder.parser_step = Nexus_THDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_nexus_th_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderNexus_TH* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_nexus_th_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_nexus_th_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderNexus_TH* 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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/nexus_th.h b/applications/plugins/weather_station/protocols/nexus_th.h new file mode 100644 index 000000000..5450f0040 --- /dev/null +++ b/applications/plugins/weather_station/protocols/nexus_th.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_NEXUS_TH_NAME "Nexus-TH" + +typedef struct WSProtocolDecoderNexus_TH WSProtocolDecoderNexus_TH; +typedef struct WSProtocolEncoderNexus_TH WSProtocolEncoderNexus_TH; + +extern const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder; +extern const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder; +extern const SubGhzProtocol ws_protocol_nexus_th; + +/** + * Allocate WSProtocolDecoderNexus_TH. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderNexus_TH* pointer to a WSProtocolDecoderNexus_TH instance + */ +void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderNexus_TH. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + */ +void ws_protocol_decoder_nexus_th_free(void* context); + +/** + * Reset decoder WSProtocolDecoderNexus_TH. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + */ +void ws_protocol_decoder_nexus_th_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderNexus_TH. + * @param context Pointer to a WSProtocolDecoderNexus_TH 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_nexus_th_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderNexus_TH. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderNexus_TH instance + * @param output Resulting text + */ +void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/plugins/weather_station/protocols/oregon2.c new file mode 100644 index 000000000..d294548e6 --- /dev/null +++ b/applications/plugins/weather_station/protocols/oregon2.c @@ -0,0 +1,404 @@ +#include "oregon2.h" + +#include +#include +#include +#include +#include "ws_generic.h" + +#include +#include + +#define TAG "WSProtocolOregon2" + +static const SubGhzBlockConst ws_oregon2_const = { + .te_long = 1000, + .te_short = 500, + .te_delta = 200, + .min_count_bit_for_found = 32, +}; + +#define OREGON2_PREAMBLE_BITS 19 +#define OREGON2_PREAMBLE_MASK 0b1111111111111111111 +#define OREGON2_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) +#define OREGON2_CHECKSUM_BITS 8 + +// 15 ones + 0101 (inverted A) +#define OREGON2_PREAMBLE 0b1111111111111110101 + +// 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; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + ManchesterState manchester_state; + bool prev_bit; + bool have_bit; + + uint8_t var_bits; + uint32_t var_data; +}; + +typedef struct WSProtocolDecoderOregon2 WSProtocolDecoderOregon2; + +typedef enum { + Oregon2DecoderStepReset = 0, + Oregon2DecoderStepFoundPreamble, + Oregon2DecoderStepVarData, +} Oregon2DecoderStep; + +void* ws_protocol_decoder_oregon2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderOregon2* instance = malloc(sizeof(WSProtocolDecoderOregon2)); + instance->base.protocol = &ws_protocol_oregon2; + instance->generic.protocol_name = instance->base.protocol->name; + instance->generic.humidity = WS_NO_HUMIDITY; + instance->generic.temp = WS_NO_TEMPERATURE; + instance->generic.btn = WS_NO_BTN; + instance->generic.channel = WS_NO_CHANNEL; + instance->generic.battery_low = WS_NO_BATT; + instance->generic.id = WS_NO_ID; + return instance; +} + +void ws_protocol_decoder_oregon2_free(void* context) { + furi_assert(context); + WSProtocolDecoderOregon2* instance = context; + free(instance); +} + +void ws_protocol_decoder_oregon2_reset(void* context) { + furi_assert(context); + WSProtocolDecoderOregon2* instance = context; + instance->decoder.parser_step = Oregon2DecoderStepReset; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); + instance->have_bit = false; + instance->var_data = 0; + instance->var_bits = 0; +} + +static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { + bool is_long = false; + + if(DURATION_DIFF(duration, ws_oregon2_const.te_long) < ws_oregon2_const.te_delta) { + is_long = true; + } else if(DURATION_DIFF(duration, ws_oregon2_const.te_short) < ws_oregon2_const.te_delta) { + is_long = false; + } else { + return ManchesterEventReset; + } + + if(level) + return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh; + else + return is_long ? ManchesterEventLongLow : ManchesterEventShortLow; +} + +// 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 == ID_THR228N) return 16; + + if(sensor_id == ID_THGR122N) return 24; + + return 0; +} + +static void ws_oregon2_decode_const_data(WSBlockGeneric* ws_block) { + ws_block->id = OREGON2_SENSOR_ID(ws_block->data); + + uint8_t ch_bits = (ws_block->data >> 12) & 0xF; + ws_block->channel = 1; + while(ch_bits > 1) { + ws_block->channel++; + ch_bits >>= 1; + } + + ws_block->battery_low = (ws_block->data & OREGON2_FLAG_BAT_LOW) ? 1 : 0; +} + +uint16_t bcd_decode_short(uint32_t data) { + return (data & 0xF) * 10 + ((data >> 4) & 0xF); +} + +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) { + if(sensor_id == ID_THR228N) { + ws_b->temp = ws_oregon2_decode_temp(data); + ws_b->humidity = WS_NO_HUMIDITY; + return; + } + + if(sensor_id == ID_THGR122N) { + ws_b->humidity = bcd_decode_short(data); + ws_b->temp = ws_oregon2_decode_temp(data >> 8); + return; + } +} + +void ws_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderOregon2* instance = context; + // oregon v2.1 signal is inverted + ManchesterEvent event = level_and_duration_to_event(!level, duration); + bool data; + + // low-level bit sequence decoding + if(event == ManchesterEventReset) { + instance->decoder.parser_step = Oregon2DecoderStepReset; + instance->have_bit = false; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + } + if(manchester_advance(instance->manchester_state, event, &instance->manchester_state, &data)) { + if(instance->have_bit) { + if(!instance->prev_bit && data) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if(instance->prev_bit && !data) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + ws_protocol_decoder_oregon2_reset(context); + } + instance->have_bit = false; + } else { + instance->prev_bit = data; + instance->have_bit = true; + } + } + + switch(instance->decoder.parser_step) { + case Oregon2DecoderStepReset: + // waiting for fixed oregon2 preamble + if(instance->decoder.decode_count_bit >= OREGON2_PREAMBLE_BITS && + ((instance->decoder.decode_data & OREGON2_PREAMBLE_MASK) == OREGON2_PREAMBLE)) { + instance->decoder.parser_step = Oregon2DecoderStepFoundPreamble; + instance->decoder.decode_count_bit = 0; + instance->decoder.decode_data = 0UL; + } + break; + case Oregon2DecoderStepFoundPreamble: + // waiting for fixed oregon2 data + if(instance->decoder.decode_count_bit == 32) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + + // reverse nibbles in decoded data + instance->generic.data = (instance->generic.data & 0x55555555) << 1 | + (instance->generic.data & 0xAAAAAAAA) >> 1; + instance->generic.data = (instance->generic.data & 0x33333333) << 2 | + (instance->generic.data & 0xCCCCCCCC) >> 2; + + ws_oregon2_decode_const_data(&instance->generic); + instance->var_bits = + oregon2_sensor_id_var_bits(OREGON2_SENSOR_ID(instance->generic.data)); + + if(!instance->var_bits) { + // sensor is not supported, stop decoding, but showing the decoded fixed part + instance->decoder.parser_step = Oregon2DecoderStepReset; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } else { + instance->decoder.parser_step = Oregon2DecoderStepVarData; + } + } + break; + case Oregon2DecoderStepVarData: + // waiting for variable (sensor-specific data) + if(instance->decoder.decode_count_bit == instance->var_bits + OREGON2_CHECKSUM_BITS) { + instance->var_data = instance->decoder.decode_data & 0xFFFFFFFF; + + // reverse nibbles in var data + instance->var_data = (instance->var_data & 0x55555555) << 1 | + (instance->var_data & 0xAAAAAAAA) >> 1; + instance->var_data = (instance->var_data & 0x33333333) << 2 | + (instance->var_data & 0xCCCCCCCC) >> 2; + + ws_oregon2_decode_var_data( + &instance->generic, + OREGON2_SENSOR_ID(instance->generic.data), + instance->var_data >> OREGON2_CHECKSUM_BITS); + + instance->decoder.parser_step = Oregon2DecoderStepReset; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } +} + +uint8_t ws_protocol_decoder_oregon2_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderOregon2* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_oregon2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderOregon2* instance = context; + if(!ws_block_generic_serialize(&instance->generic, flipper_format, preset)) return false; + uint32_t temp = instance->var_bits; + if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { + FURI_LOG_E(TAG, "Error adding VarBits"); + return false; + } + if(!flipper_format_write_hex( + flipper_format, + "VarData", + (const uint8_t*)&instance->var_data, + sizeof(instance->var_data))) { + FURI_LOG_E(TAG, "Error adding VarData"); + return false; + } + return true; +} + +bool ws_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderOregon2* instance = context; + bool ret = false; + uint32_t temp_data; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { + FURI_LOG_E(TAG, "Missing VarLen"); + break; + } + instance->var_bits = (uint8_t)temp_data; + if(!flipper_format_read_hex( + flipper_format, + "VarData", + (uint8_t*)&instance->var_data, + sizeof(instance->var_data))) { + FURI_LOG_E(TAG, "Missing VarData"); + break; + } + if(instance->generic.data_count_bit != ws_oregon2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); + break; + } + ret = true; + } while(false); + return ret; +} + +static void oregon2_append_check_sum(uint32_t fix_data, uint32_t var_data, FuriString* output) { + uint8_t sum = fix_data & 0xF; + uint8_t ref_sum = var_data & 0xFF; + var_data >>= 8; + + for(uint8_t i = 1; i < 8; i++) { + fix_data >>= 4; + var_data >>= 4; + sum += (fix_data & 0xF) + (var_data & 0xF); + } + + // swap calculated sum nibbles + sum = (((sum >> 4) & 0xF) | (sum << 4)) & 0xFF; + if(sum == ref_sum) + furi_string_cat_printf(output, "Sum ok: 0x%hhX", ref_sum); + else + furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); +} + +void ws_protocol_decoder_oregon2_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderOregon2* instance = context; + furi_string_cat_printf( + output, + "%s\r\n" + "ID: 0x%04lX, ch: %d, bat: %d, rc: 0x%02lX\r\n", + instance->generic.protocol_name, + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (uint32_t)(instance->generic.data >> 4) & 0xFF); + + if(instance->var_bits > 0) { + furi_string_cat_printf( + output, + "Temp:%d.%d C Hum:%d%%", + (int16_t)instance->generic.temp, + abs( + ((int16_t)(instance->generic.temp * 10) - + (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); + oregon2_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); + } +} + +const SubGhzProtocolDecoder ws_protocol_oregon2_decoder = { + .alloc = ws_protocol_decoder_oregon2_alloc, + .free = ws_protocol_decoder_oregon2_free, + + .feed = ws_protocol_decoder_oregon2_feed, + .reset = ws_protocol_decoder_oregon2_reset, + + .get_hash_data = ws_protocol_decoder_oregon2_get_hash_data, + .serialize = ws_protocol_decoder_oregon2_serialize, + .deserialize = ws_protocol_decoder_oregon2_deserialize, + .get_string = ws_protocol_decoder_oregon2_get_string, +}; + +const SubGhzProtocol ws_protocol_oregon2 = { + .name = WS_PROTOCOL_OREGON2_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_oregon2_decoder, +}; diff --git a/applications/plugins/weather_station/protocols/oregon2.h b/applications/plugins/weather_station/protocols/oregon2.h new file mode 100644 index 000000000..cfe938e6d --- /dev/null +++ b/applications/plugins/weather_station/protocols/oregon2.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +#define WS_PROTOCOL_OREGON2_NAME "Oregon2" +extern const SubGhzProtocol ws_protocol_oregon2; diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c new file mode 100644 index 000000000..21768e042 --- /dev/null +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -0,0 +1,19 @@ +#include "protocol_items.h" + +const SubGhzProtocol* weather_station_protocol_registry_items[] = { + &ws_protocol_infactory, + &ws_protocol_thermopro_tx4, + &ws_protocol_nexus_th, + &ws_protocol_gt_wt_02, + &ws_protocol_gt_wt_03, + &ws_protocol_acurite_606tx, + &ws_protocol_acurite_609txc, + &ws_protocol_lacrosse_tx141thbv2, + &ws_protocol_oregon2, + &ws_protocol_acurite_592txr, + &ws_protocol_ambient_weather, +}; + +const SubGhzProtocolRegistry weather_station_protocol_registry = { + .items = weather_station_protocol_registry_items, + .size = COUNT_OF(weather_station_protocol_registry_items)}; \ No newline at end of file diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h new file mode 100644 index 000000000..aa064f044 --- /dev/null +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -0,0 +1,16 @@ +#pragma once +#include "../weather_station_app_i.h" + +#include "infactory.h" +#include "thermopro_tx4.h" +#include "nexus_th.h" +#include "gt_wt_02.h" +#include "gt_wt_03.h" +#include "acurite_606tx.h" +#include "acurite_609txc.h" +#include "lacrosse_tx141thbv2.h" +#include "oregon2.h" +#include "acurite_592txr.h" +#include "ambient_weather.h" + +extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.c b/applications/plugins/weather_station/protocols/thermopro_tx4.c new file mode 100644 index 000000000..0882bc33d --- /dev/null +++ b/applications/plugins/weather_station/protocols/thermopro_tx4.c @@ -0,0 +1,259 @@ +#include "thermopro_tx4.h" + +#define TAG "WSProtocolThermoPRO_TX4" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/thermopro_tx2.c + * + * The sensor sends 37 bits 6 times, before the first packet there is a sync pulse. + * The packets are ppm modulated (distance coding) with a pulse of ~500 us + * followed by a short gap of ~2000 us for a 0 bit or a long ~4000 us gap for a + * 1 bit, the sync gap is ~9000 us. + * The data is grouped in 9 nibbles + * [type] [id0] [id1] [flags] [temp0] [temp1] [temp2] [humi0] [humi1] + * - type: 4 bit fixed 1001 (9) or 0110 (5) + * - id: 8 bit a random id that is generated when the sensor starts, could include battery status + * the same batteries often generate the same id + * - flags(3): is 1 when the battery is low, otherwise 0 (ok) + * - flags(2): is 1 when the sensor sends a reading when pressing the button on the sensor + * - flags(1,0): the channel number that can be set by the sensor (1, 2, 3, X) + * - temp: 12 bit signed scaled by 10 + * - humi: 8 bit always 11001100 (0xCC) if no humidity sensor is available + * + */ + +#define THERMO_PRO_TX4_TYPE_1 0b1001 +#define THERMO_PRO_TX4_TYPE_2 0b0110 + +static const SubGhzBlockConst ws_protocol_thermopro_tx4_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 37, +}; + +struct WSProtocolDecoderThermoPRO_TX4 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderThermoPRO_TX4 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + ThermoPRO_TX4DecoderStepReset = 0, + ThermoPRO_TX4DecoderStepSaveDuration, + ThermoPRO_TX4DecoderStepCheckDuration, +} ThermoPRO_TX4DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder = { + .alloc = ws_protocol_decoder_thermopro_tx4_alloc, + .free = ws_protocol_decoder_thermopro_tx4_free, + + .feed = ws_protocol_decoder_thermopro_tx4_feed, + .reset = ws_protocol_decoder_thermopro_tx4_reset, + + .get_hash_data = ws_protocol_decoder_thermopro_tx4_get_hash_data, + .serialize = ws_protocol_decoder_thermopro_tx4_serialize, + .deserialize = ws_protocol_decoder_thermopro_tx4_deserialize, + .get_string = ws_protocol_decoder_thermopro_tx4_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_thermopro_tx4 = { + .name = WS_PROTOCOL_THERMOPRO_TX4_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_thermopro_tx4_decoder, + .encoder = &ws_protocol_thermopro_tx4_encoder, +}; + +void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderThermoPRO_TX4* instance = malloc(sizeof(WSProtocolDecoderThermoPRO_TX4)); + instance->base.protocol = &ws_protocol_thermopro_tx4; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_thermopro_tx4_free(void* context) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + free(instance); +} + +void ws_protocol_decoder_thermopro_tx4_reset(void* context) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; +} + +static bool ws_protocol_thermopro_tx4_check(WSProtocolDecoderThermoPRO_TX4* instance) { + uint8_t type = instance->decoder.decode_data >> 33; + + if((type == THERMO_PRO_TX4_TYPE_1) || (type == THERMO_PRO_TX4_TYPE_2)) { + return true; + } else { + return false; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_thermopro_tx4_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 25) & 0xFF; + instance->battery_low = (instance->data >> 24) & 1; + instance->btn = (instance->data >> 23) & 1; + instance->channel = ((instance->data >> 21) & 0x03) + 1; + + if(!((instance->data >> 20) & 1)) { + instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f; + } + + instance->humidity = (instance->data >> 1) & 0xFF; +} + +void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + + switch(instance->decoder.parser_step) { + case ThermoPRO_TX4DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) < + ws_protocol_thermopro_tx4_const.te_delta * 10)) { + //Found sync + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case ThermoPRO_TX4DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; + } + break; + + case ThermoPRO_TX4DecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) < + ws_protocol_thermopro_tx4_const.te_delta * 10) { + //Found sync + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_thermopro_tx4_const.min_count_bit_for_found) && + ws_protocol_thermopro_tx4_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_thermopro_tx4_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) < + ws_protocol_thermopro_tx4_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long) < + ws_protocol_thermopro_tx4_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) < + ws_protocol_thermopro_tx4_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long * 2) < + ws_protocol_thermopro_tx4_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; + } + } else { + instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_thermopro_tx4_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_thermopro_tx4_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_thermopro_tx4_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderThermoPRO_TX4* 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:%3.1f 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, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.h b/applications/plugins/weather_station/protocols/thermopro_tx4.h new file mode 100644 index 000000000..1feae58d3 --- /dev/null +++ b/applications/plugins/weather_station/protocols/thermopro_tx4.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_THERMOPRO_TX4_NAME "ThermoPRO-TX4" + +typedef struct WSProtocolDecoderThermoPRO_TX4 WSProtocolDecoderThermoPRO_TX4; +typedef struct WSProtocolEncoderThermoPRO_TX4 WSProtocolEncoderThermoPRO_TX4; + +extern const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder; +extern const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder; +extern const SubGhzProtocol ws_protocol_thermopro_tx4; + +/** + * Allocate WSProtocolDecoderThermoPRO_TX4. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderThermoPRO_TX4* pointer to a WSProtocolDecoderThermoPRO_TX4 instance + */ +void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderThermoPRO_TX4. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + */ +void ws_protocol_decoder_thermopro_tx4_free(void* context); + +/** + * Reset decoder WSProtocolDecoderThermoPRO_TX4. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + */ +void ws_protocol_decoder_thermopro_tx4_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderThermoPRO_TX4. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 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_thermopro_tx4_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderThermoPRO_TX4. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance + * @param output Resulting text + */ +void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/ws_generic.c b/applications/plugins/weather_station/protocols/ws_generic.c new file mode 100644 index 000000000..174531090 --- /dev/null +++ b/applications/plugins/weather_station/protocols/ws_generic.c @@ -0,0 +1,198 @@ +#include "ws_generic.h" +#include +#include +#include "../helpers/weather_station_types.h" + +#define TAG "WSBlockGeneric" + +void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) { + const char* preset_name_temp; + if(!strcmp(preset_name, "AM270")) { + preset_name_temp = "FuriHalSubGhzPresetOok270Async"; + } else if(!strcmp(preset_name, "AM650")) { + preset_name_temp = "FuriHalSubGhzPresetOok650Async"; + } else if(!strcmp(preset_name, "FM238")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async"; + } else if(!strcmp(preset_name, "FM476")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async"; + } else { + preset_name_temp = "FuriHalSubGhzPresetCustom"; + } + furi_string_set(preset_str, preset_name_temp); +} + +bool ws_block_generic_serialize( + WSBlockGeneric* instance, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(instance); + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, WS_KEY_FILE_TYPE, WS_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + ws_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + uint32_t temp_data = instance->id; + if(!flipper_format_write_uint32(flipper_format, "Id", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Id"); + break; + } + + temp_data = instance->data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> i * 8) & 0xFF; + } + + if(!flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + break; + } + + temp_data = instance->battery_low; + if(!flipper_format_write_uint32(flipper_format, "Batt", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Battery_low"); + break; + } + + temp_data = instance->humidity; + if(!flipper_format_write_uint32(flipper_format, "Hum", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Humidity"); + break; + } + + temp_data = instance->channel; + if(!flipper_format_write_uint32(flipper_format, "Ch", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Channel"); + break; + } + + temp_data = instance->btn; + if(!flipper_format_write_uint32(flipper_format, "Btn", &temp_data, 1)) { + FURI_LOG_E(TAG, "Unable to add Btn"); + break; + } + + float temp = instance->temp; + if(!flipper_format_write_float(flipper_format, "Temp", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Temperature"); + break; + } + + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format) { + furi_assert(instance); + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + if(!flipper_format_read_uint32(flipper_format, "Id", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Id"); + break; + } + instance->id = (uint32_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + instance->data_count_bit = (uint8_t)temp_data; + + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->data = instance->data << 8 | key_data[i]; + } + + if(!flipper_format_read_uint32(flipper_format, "Batt", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Battery_low"); + break; + } + instance->battery_low = (uint8_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "Hum", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Humidity"); + break; + } + instance->humidity = (uint8_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "Ch", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Channel"); + break; + } + instance->channel = (uint8_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "Btn", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Btn"); + break; + } + instance->btn = (uint8_t)temp_data; + + float temp; + if(!flipper_format_read_float(flipper_format, "Temp", (float*)&temp, 1)) { + FURI_LOG_E(TAG, "Missing Temperature"); + break; + } + instance->temp = temp; + + res = true; + } while(0); + + return res; +} + +float ws_block_generic_fahrenheit_to_celsius(float fahrenheit) { + return (fahrenheit - 32.0f) / 1.8f; +} \ No newline at end of file diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/plugins/weather_station/protocols/ws_generic.h new file mode 100644 index 000000000..b2a84df8e --- /dev/null +++ b/applications/plugins/weather_station/protocols/ws_generic.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +#include +#include "furi.h" +#include "furi_hal.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define WS_NO_ID 0xFFFFFFFF +#define WS_NO_BATT 0xFF +#define WS_NO_HUMIDITY 0xFF +#define WS_NO_CHANNEL 0xFF +#define WS_NO_BTN 0xFF +#define WS_NO_TEMPERATURE -273.0f + +typedef struct WSBlockGeneric WSBlockGeneric; + +struct WSBlockGeneric { + const char* protocol_name; + uint64_t data; + uint32_t id; + uint8_t data_count_bit; + uint8_t battery_low; + uint8_t humidity; + uint8_t channel; + uint8_t btn; + float temp; +}; + +/** + * Get name preset. + * @param preset_name name preset + * @param preset_str Output name preset + */ +void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str); + +/** + * Serialize data WSBlockGeneric. + * @param instance Pointer to a WSBlockGeneric 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_block_generic_serialize( + WSBlockGeneric* instance, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSBlockGeneric. + * @param instance Pointer to a WSBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format); + +float ws_block_generic_fahrenheit_to_celsius(float fahrenheit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/plugins/weather_station/scenes/weather_station_receiver.c b/applications/plugins/weather_station/scenes/weather_station_receiver.c new file mode 100644 index 000000000..670c8c386 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_receiver.c @@ -0,0 +1,207 @@ +#include "../weather_station_app_i.h" +#include "../views/weather_station_receiver.h" + +static const NotificationSequence subghs_sequence_rx = { + &message_green_255, + + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + + &message_delay_50, + NULL, +}; + +static const NotificationSequence subghs_sequence_rx_locked = { + &message_green_255, + + &message_display_backlight_on, + + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + + &message_delay_500, + + &message_display_backlight_off, + NULL, +}; + +static void weather_station_scene_receiver_update_statusbar(void* context) { + WeatherStationApp* app = context; + FuriString* history_stat_str; + history_stat_str = furi_string_alloc(); + if(!ws_history_get_text_space_left(app->txrx->history, history_stat_str)) { + FuriString* frequency_str; + FuriString* modulation_str; + + frequency_str = furi_string_alloc(); + modulation_str = furi_string_alloc(); + + ws_get_frequency_modulation(app, frequency_str, modulation_str); + + ws_view_receiver_add_data_statusbar( + app->ws_receiver, + furi_string_get_cstr(frequency_str), + furi_string_get_cstr(modulation_str), + furi_string_get_cstr(history_stat_str)); + + furi_string_free(frequency_str); + furi_string_free(modulation_str); + } else { + ws_view_receiver_add_data_statusbar( + app->ws_receiver, furi_string_get_cstr(history_stat_str), "", ""); + } + furi_string_free(history_stat_str); +} + +void weather_station_scene_receiver_callback(WSCustomEvent event, void* context) { + furi_assert(context); + WeatherStationApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +static void weather_station_scene_receiver_add_to_history_callback( + SubGhzReceiver* receiver, + SubGhzProtocolDecoderBase* decoder_base, + void* context) { + furi_assert(context); + WeatherStationApp* app = context; + FuriString* str_buff; + str_buff = furi_string_alloc(); + + if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) == + WSHistoryStateAddKeyNewDada) { + furi_string_reset(str_buff); + + ws_history_get_text_item_menu( + app->txrx->history, str_buff, ws_history_get_item(app->txrx->history) - 1); + ws_view_receiver_add_item_to_menu( + app->ws_receiver, + furi_string_get_cstr(str_buff), + ws_history_get_type_protocol( + app->txrx->history, ws_history_get_item(app->txrx->history) - 1)); + + weather_station_scene_receiver_update_statusbar(app); + notification_message(app->notifications, &sequence_blink_green_10); + if(app->lock != WSLockOn) { + notification_message(app->notifications, &subghs_sequence_rx); + } else { + notification_message(app->notifications, &subghs_sequence_rx_locked); + } + } + subghz_receiver_reset(receiver); + furi_string_free(str_buff); + app->txrx->rx_key_state = WSRxKeyStateAddKey; +} + +void weather_station_scene_receiver_on_enter(void* context) { + WeatherStationApp* app = context; + + FuriString* str_buff; + str_buff = furi_string_alloc(); + + if(app->txrx->rx_key_state == WSRxKeyStateIDLE) { + ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); + ws_history_reset(app->txrx->history); + app->txrx->rx_key_state = WSRxKeyStateStart; + } + + ws_view_receiver_set_lock(app->ws_receiver, app->lock); + + //Load history to receiver + ws_view_receiver_exit(app->ws_receiver); + for(uint8_t i = 0; i < ws_history_get_item(app->txrx->history); i++) { + furi_string_reset(str_buff); + ws_history_get_text_item_menu(app->txrx->history, str_buff, i); + ws_view_receiver_add_item_to_menu( + app->ws_receiver, + furi_string_get_cstr(str_buff), + ws_history_get_type_protocol(app->txrx->history, i)); + app->txrx->rx_key_state = WSRxKeyStateAddKey; + } + furi_string_free(str_buff); + weather_station_scene_receiver_update_statusbar(app); + + ws_view_receiver_set_callback(app->ws_receiver, weather_station_scene_receiver_callback, app); + subghz_receiver_set_rx_callback( + app->txrx->receiver, weather_station_scene_receiver_add_to_history_callback, app); + + if(app->txrx->txrx_state == WSTxRxStateRx) { + ws_rx_end(app); + }; + if((app->txrx->txrx_state == WSTxRxStateIDLE) || (app->txrx->txrx_state == WSTxRxStateSleep)) { + ws_begin( + app, + subghz_setting_get_preset_data_by_name( + app->setting, furi_string_get_cstr(app->txrx->preset->name))); + + ws_rx(app, app->txrx->preset->frequency); + } + + ws_view_receiver_set_idx_menu(app->ws_receiver, app->txrx->idx_menu_chosen); + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiver); +} + +bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case WSCustomEventViewReceiverBack: + // Stop CC1101 Rx + if(app->txrx->txrx_state == WSTxRxStateRx) { + ws_rx_end(app); + ws_sleep(app); + }; + app->txrx->hopper_state = WSHopperStateOFF; + app->txrx->idx_menu_chosen = 0; + subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); + + app->txrx->rx_key_state = WSRxKeyStateIDLE; + ws_preset_init( + app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, WeatherStationSceneStart); + consumed = true; + break; + case WSCustomEventViewReceiverOK: + app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver); + scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverInfo); + consumed = true; + break; + case WSCustomEventViewReceiverConfig: + app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver); + scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverConfig); + consumed = true; + break; + case WSCustomEventViewReceiverOffDisplay: + notification_message(app->notifications, &sequence_display_backlight_off); + consumed = true; + break; + case WSCustomEventViewReceiverUnlock: + app->lock = WSLockOff; + consumed = true; + break; + default: + break; + } + } else if(event.type == SceneManagerEventTypeTick) { + if(app->txrx->hopper_state != WSHopperStateOFF) { + ws_hopper_update(app); + weather_station_scene_receiver_update_statusbar(app); + } + if(app->txrx->txrx_state == WSTxRxStateRx) { + notification_message(app->notifications, &sequence_blink_cyan_10); + } + } + return consumed; +} + +void weather_station_scene_receiver_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/weather_station/scenes/weather_station_scene.c b/applications/plugins/weather_station/scenes/weather_station_scene.c new file mode 100644 index 000000000..f9306e5f4 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene.c @@ -0,0 +1,30 @@ +#include "../weather_station_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const weather_station_scene_on_enter_handlers[])(void*) = { +#include "weather_station_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const weather_station_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "weather_station_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const weather_station_scene_on_exit_handlers[])(void* context) = { +#include "weather_station_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers weather_station_scene_handlers = { + .on_enter_handlers = weather_station_scene_on_enter_handlers, + .on_event_handlers = weather_station_scene_on_event_handlers, + .on_exit_handlers = weather_station_scene_on_exit_handlers, + .scene_num = WeatherStationSceneNum, +}; diff --git a/applications/plugins/weather_station/scenes/weather_station_scene.h b/applications/plugins/weather_station/scenes/weather_station_scene.h new file mode 100644 index 000000000..8cee4ee60 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) WeatherStationScene##id, +typedef enum { +#include "weather_station_scene_config.h" + WeatherStationSceneNum, +} WeatherStationScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers weather_station_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "weather_station_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "weather_station_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "weather_station_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_about.c b/applications/plugins/weather_station/scenes/weather_station_scene_about.c new file mode 100644 index 000000000..d916dc76f --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_about.c @@ -0,0 +1,78 @@ +#include "../weather_station_app_i.h" +#include "../helpers/weather_station_types.h" + +void weather_station_scene_about_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + WeatherStationApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void weather_station_scene_about_on_enter(void* context) { + WeatherStationApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", WS_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", WS_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", WS_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, "Reading messages from\nweather stations that work\nwith SubGhz sensors\n\n"); + + furi_string_cat_printf(temp_str, "Supported protocols:\n"); + size_t i = 0; + const char* protocol_name = + subghz_environment_get_protocol_name_registry(app->txrx->environment, i++); + do { + furi_string_cat_printf(temp_str, "%s\n", protocol_name); + protocol_name = subghz_environment_get_protocol_name_registry(app->txrx->environment, i++); + } while(protocol_name != NULL); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! Weather station \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewWidget); +} + +bool weather_station_scene_about_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void weather_station_scene_about_on_exit(void* context) { + WeatherStationApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_config.h b/applications/plugins/weather_station/scenes/weather_station_scene_config.h new file mode 100644 index 000000000..0ba8ec013 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_config.h @@ -0,0 +1,5 @@ +ADD_SCENE(weather_station, start, Start) +ADD_SCENE(weather_station, about, About) +ADD_SCENE(weather_station, receiver, Receiver) +ADD_SCENE(weather_station, receiver_config, ReceiverConfig) +ADD_SCENE(weather_station, receiver_info, ReceiverInfo) diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c b/applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c new file mode 100644 index 000000000..fcd8f6d3e --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c @@ -0,0 +1,223 @@ +#include "../weather_station_app_i.h" + +enum WSSettingIndex { + WSSettingIndexFrequency, + WSSettingIndexHopping, + WSSettingIndexModulation, + WSSettingIndexLock, +}; + +#define HOPPING_COUNT 2 +const char* const hopping_text[HOPPING_COUNT] = { + "OFF", + "ON", +}; +const uint32_t hopping_value[HOPPING_COUNT] = { + WSHopperStateOFF, + WSHopperStateRunnig, +}; + +uint8_t weather_station_scene_receiver_config_next_frequency(const uint32_t value, void* context) { + furi_assert(context); + WeatherStationApp* app = context; + uint8_t index = 0; + for(uint8_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) { + if(value == subghz_setting_get_frequency(app->setting, i)) { + index = i; + break; + } else { + index = subghz_setting_get_frequency_default_index(app->setting); + } + } + return index; +} + +uint8_t weather_station_scene_receiver_config_next_preset(const char* preset_name, void* context) { + furi_assert(context); + WeatherStationApp* app = context; + uint8_t index = 0; + for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) { + if(!strcmp(subghz_setting_get_preset_name(app->setting, i), preset_name)) { + index = i; + break; + } else { + // index = subghz_setting_get_frequency_default_index(app ->setting); + } + } + return index; +} + +uint8_t weather_station_scene_receiver_config_hopper_value_index( + const uint32_t value, + const uint32_t values[], + uint8_t values_count, + void* context) { + furi_assert(context); + UNUSED(values_count); + WeatherStationApp* app = context; + + if(value == values[0]) { + return 0; + } else { + variable_item_set_current_value_text( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + " -----"); + return 1; + } +} + +static void weather_station_scene_receiver_config_set_frequency(VariableItem* item) { + WeatherStationApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + if(app->txrx->hopper_state == WSHopperStateOFF) { + char text_buf[10] = {0}; + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + subghz_setting_get_frequency(app->setting, index) / 1000000, + (subghz_setting_get_frequency(app->setting, index) % 1000000) / 10000); + variable_item_set_current_value_text(item, text_buf); + app->txrx->preset->frequency = subghz_setting_get_frequency(app->setting, index); + } else { + variable_item_set_current_value_index( + item, subghz_setting_get_frequency_default_index(app->setting)); + } +} + +static void weather_station_scene_receiver_config_set_preset(VariableItem* item) { + WeatherStationApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text( + item, subghz_setting_get_preset_name(app->setting, index)); + ws_preset_init( + app, + subghz_setting_get_preset_name(app->setting, index), + app->txrx->preset->frequency, + subghz_setting_get_preset_data(app->setting, index), + subghz_setting_get_preset_data_size(app->setting, index)); +} + +static void weather_station_scene_receiver_config_set_hopping_running(VariableItem* item) { + WeatherStationApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, hopping_text[index]); + if(hopping_value[index] == WSHopperStateOFF) { + char text_buf[10] = {0}; + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + subghz_setting_get_default_frequency(app->setting) / 1000000, + (subghz_setting_get_default_frequency(app->setting) % 1000000) / 10000); + variable_item_set_current_value_text( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + text_buf); + app->txrx->preset->frequency = subghz_setting_get_default_frequency(app->setting); + variable_item_set_current_value_index( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + subghz_setting_get_frequency_default_index(app->setting)); + } else { + variable_item_set_current_value_text( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + " -----"); + variable_item_set_current_value_index( + (VariableItem*)scene_manager_get_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig), + subghz_setting_get_frequency_default_index(app->setting)); + } + + app->txrx->hopper_state = hopping_value[index]; +} + +static void + weather_station_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { + furi_assert(context); + WeatherStationApp* app = context; + if(index == WSSettingIndexLock) { + view_dispatcher_send_custom_event(app->view_dispatcher, WSCustomEventSceneSettingLock); + } +} + +void weather_station_scene_receiver_config_on_enter(void* context) { + WeatherStationApp* app = context; + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + app->variable_item_list, + "Frequency:", + subghz_setting_get_frequency_count(app->setting), + weather_station_scene_receiver_config_set_frequency, + app); + value_index = + weather_station_scene_receiver_config_next_frequency(app->txrx->preset->frequency, app); + scene_manager_set_scene_state( + app->scene_manager, WeatherStationSceneReceiverConfig, (uint32_t)item); + variable_item_set_current_value_index(item, value_index); + char text_buf[10] = {0}; + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + subghz_setting_get_frequency(app->setting, value_index) / 1000000, + (subghz_setting_get_frequency(app->setting, value_index) % 1000000) / 10000); + variable_item_set_current_value_text(item, text_buf); + + item = variable_item_list_add( + app->variable_item_list, + "Hopping:", + HOPPING_COUNT, + weather_station_scene_receiver_config_set_hopping_running, + app); + value_index = weather_station_scene_receiver_config_hopper_value_index( + app->txrx->hopper_state, hopping_value, HOPPING_COUNT, app); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, hopping_text[value_index]); + + item = variable_item_list_add( + app->variable_item_list, + "Modulation:", + subghz_setting_get_preset_count(app->setting), + weather_station_scene_receiver_config_set_preset, + app); + value_index = weather_station_scene_receiver_config_next_preset( + furi_string_get_cstr(app->txrx->preset->name), app); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text( + item, subghz_setting_get_preset_name(app->setting, value_index)); + + variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL); + variable_item_list_set_enter_callback( + app->variable_item_list, + weather_station_scene_receiver_config_var_list_enter_callback, + app); + + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewVariableItemList); +} + +bool weather_station_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == WSCustomEventSceneSettingLock) { + app->lock = WSLockOn; + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + return consumed; +} + +void weather_station_scene_receiver_config_on_exit(void* context) { + WeatherStationApp* app = context; + variable_item_list_set_selected_item(app->variable_item_list, 0); + variable_item_list_reset(app->variable_item_list); +} diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c b/applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c new file mode 100644 index 000000000..b26661be3 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c @@ -0,0 +1,50 @@ +#include "../weather_station_app_i.h" +#include "../views/weather_station_receiver.h" + +void weather_station_scene_receiver_info_callback(WSCustomEvent event, void* context) { + furi_assert(context); + WeatherStationApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +static void weather_station_scene_receiver_info_add_to_history_callback( + SubGhzReceiver* receiver, + SubGhzProtocolDecoderBase* decoder_base, + void* context) { + furi_assert(context); + WeatherStationApp* app = context; + + if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) == + WSHistoryStateAddKeyUpdateData) { + ws_view_receiver_info_update( + app->ws_receiver_info, + ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen)); + subghz_receiver_reset(receiver); + + notification_message(app->notifications, &sequence_blink_green_10); + app->txrx->rx_key_state = WSRxKeyStateAddKey; + } +} + +void weather_station_scene_receiver_info_on_enter(void* context) { + WeatherStationApp* app = context; + + subghz_receiver_set_rx_callback( + app->txrx->receiver, weather_station_scene_receiver_info_add_to_history_callback, app); + ws_view_receiver_info_update( + app->ws_receiver_info, + ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen)); + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiverInfo); +} + +bool weather_station_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + return consumed; +} + +void weather_station_scene_receiver_info_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_start.c b/applications/plugins/weather_station/scenes/weather_station_scene_start.c new file mode 100644 index 000000000..56dd6fa86 --- /dev/null +++ b/applications/plugins/weather_station/scenes/weather_station_scene_start.c @@ -0,0 +1,58 @@ +#include "../weather_station_app_i.h" + +typedef enum { + SubmenuIndexWeatherStationReceiver, + SubmenuIndexWeatherStationAbout, +} SubmenuIndex; + +void weather_station_scene_start_submenu_callback(void* context, uint32_t index) { + WeatherStationApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void weather_station_scene_start_on_enter(void* context) { + UNUSED(context); + WeatherStationApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Read Weather Station", + SubmenuIndexWeatherStationReceiver, + weather_station_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + SubmenuIndexWeatherStationAbout, + weather_station_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WeatherStationSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewSubmenu); +} + +bool weather_station_scene_start_on_event(void* context, SceneManagerEvent event) { + WeatherStationApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexWeatherStationAbout) { + scene_manager_next_scene(app->scene_manager, WeatherStationSceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexWeatherStationReceiver) { + scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiver); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, WeatherStationSceneStart, event.event); + } + + return consumed; +} + +void weather_station_scene_start_on_exit(void* context) { + WeatherStationApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c new file mode 100644 index 000000000..61b152602 --- /dev/null +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -0,0 +1,436 @@ +#include "weather_station_receiver.h" +#include "../weather_station_app_i.h" +#include +#include + +#include +#include +#include + +#define FRAME_HEIGHT 12 +#define MAX_LEN_PX 100 +#define MENU_ITEMS 4u +#define UNLOCK_CNT 3 + +typedef struct { + FuriString* item_str; + uint8_t type; +} WSReceiverMenuItem; + +ARRAY_DEF(WSReceiverMenuItemArray, WSReceiverMenuItem, M_POD_OPLIST) + +#define M_OPL_WSReceiverMenuItemArray_t() ARRAY_OPLIST(WSReceiverMenuItemArray, M_POD_OPLIST) + +struct WSReceiverHistory { + WSReceiverMenuItemArray_t data; +}; + +typedef struct WSReceiverHistory WSReceiverHistory; + +static const Icon* ReceiverItemIcons[] = { + [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, + [SubGhzProtocolTypeStatic] = &I_Unlock_7x8, + [SubGhzProtocolTypeDynamic] = &I_Lock_7x8, + [SubGhzProtocolWeatherStation] = &I_station_icon, +}; + +typedef enum { + WSReceiverBarShowDefault, + WSReceiverBarShowLock, + WSReceiverBarShowToUnlockPress, + WSReceiverBarShowUnlock, +} WSReceiverBarShow; + +struct WSReceiver { + WSLock lock; + uint8_t lock_count; + FuriTimer* timer; + View* view; + WSReceiverCallback callback; + void* context; +}; + +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* history_stat_str; + WSReceiverHistory* history; + uint16_t idx; + uint16_t list_offset; + uint16_t history_item; + WSReceiverBarShow bar_show; +} WSReceiverModel; + +void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock lock) { + furi_assert(ws_receiver); + ws_receiver->lock_count = 0; + if(lock == WSLockOn) { + ws_receiver->lock = lock; + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowLock; }, + true); + furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000)); + } else { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowDefault; }, + true); + } +} + +void ws_view_receiver_set_callback( + WSReceiver* ws_receiver, + WSReceiverCallback callback, + void* context) { + furi_assert(ws_receiver); + furi_assert(callback); + ws_receiver->callback = callback; + ws_receiver->context = context; +} + +static void ws_view_receiver_update_offset(WSReceiver* ws_receiver) { + furi_assert(ws_receiver); + + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + size_t history_item = model->history_item; + uint16_t bounds = history_item > 3 ? 2 : history_item; + + if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) { + model->list_offset = model->idx - 3; + } else if(model->list_offset < model->idx - bounds) { + model->list_offset = + CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0); + } else if(model->list_offset > model->idx - bounds) { + model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0); + } + }, + true); +} + +void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type) { + furi_assert(ws_receiver); + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + WSReceiverMenuItem* item_menu = WSReceiverMenuItemArray_push_raw(model->history->data); + item_menu->item_str = furi_string_alloc_set(name); + item_menu->type = type; + if((model->idx == model->history_item - 1)) { + model->history_item++; + model->idx++; + } else { + model->history_item++; + } + }, + true); + ws_view_receiver_update_offset(ws_receiver); +} + +void ws_view_receiver_add_data_statusbar( + WSReceiver* ws_receiver, + const char* frequency_str, + const char* preset_str, + const char* history_stat_str) { + furi_assert(ws_receiver); + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + furi_string_set_str(model->frequency_str, frequency_str); + furi_string_set_str(model->preset_str, preset_str); + furi_string_set_str(model->history_stat_str, history_stat_str); + }, + true); +} + +static void ws_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1); + + canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); +} + +void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + elements_button_left(canvas, "Config"); + canvas_draw_line(canvas, 46, 51, 125, 51); + + bool scrollbar = model->history_item > 4; + FuriString* str_buff; + str_buff = furi_string_alloc(); + + WSReceiverMenuItem* item_menu; + + for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { + size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); + item_menu = WSReceiverMenuItemArray_get(model->history->data, idx); + furi_string_set(str_buff, item_menu->item_str); + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); + if(model->idx == idx) { + ws_view_receiver_draw_frame(canvas, i, scrollbar); + } else { + canvas_set_color(canvas, ColorBlack); + } + canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); + canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); + furi_string_reset(str_buff); + } + if(scrollbar) { + elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); + } + furi_string_free(str_buff); + + canvas_set_color(canvas, ColorBlack); + + if(model->history_item == 0) { + canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 63, 46, "Scanning..."); + canvas_draw_line(canvas, 46, 51, 125, 51); + canvas_set_font(canvas, FontSecondary); + } + + switch(model->bar_show) { + case WSReceiverBarShowLock: + canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); + canvas_draw_str(canvas, 74, 62, "Locked"); + break; + case WSReceiverBarShowToUnlockPress: + canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + canvas_set_font(canvas, FontSecondary); + elements_bold_rounded_frame(canvas, 14, 8, 99, 48); + elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); + canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42); + canvas_draw_dot(canvas, 17, 61); + break; + case WSReceiverBarShowUnlock: + canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8); + canvas_draw_str(canvas, 74, 62, "Unlocked"); + break; + default: + canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + break; + } +} + +static void ws_view_receiver_timer_callback(void* context) { + furi_assert(context); + WSReceiver* ws_receiver = context; + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowDefault; }, + true); + if(ws_receiver->lock_count < UNLOCK_CNT) { + ws_receiver->callback(WSCustomEventViewReceiverOffDisplay, ws_receiver->context); + } else { + ws_receiver->lock = WSLockOff; + ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context); + } + ws_receiver->lock_count = 0; +} + +bool ws_view_receiver_input(InputEvent* event, void* context) { + furi_assert(context); + WSReceiver* ws_receiver = context; + + if(ws_receiver->lock == WSLockOn) { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowToUnlockPress; }, + true); + if(ws_receiver->lock_count == 0) { + furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000)); + } + if(event->key == InputKeyBack && event->type == InputTypeShort) { + ws_receiver->lock_count++; + } + if(ws_receiver->lock_count >= UNLOCK_CNT) { + ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context); + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { model->bar_show = WSReceiverBarShowUnlock; }, + true); + ws_receiver->lock = WSLockOff; + furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(650)); + } + + return true; + } + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + ws_receiver->callback(WSCustomEventViewReceiverBack, ws_receiver->context); + } else if( + event->key == InputKeyUp && + (event->type == InputTypeShort || event->type == InputTypeRepeat)) { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + if(model->idx != 0) model->idx--; + }, + true); + } else if( + event->key == InputKeyDown && + (event->type == InputTypeShort || event->type == InputTypeRepeat)) { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + if(model->idx != model->history_item - 1) model->idx++; + }, + true); + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + ws_receiver->callback(WSCustomEventViewReceiverConfig, ws_receiver->context); + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + if(model->history_item != 0) { + ws_receiver->callback(WSCustomEventViewReceiverOK, ws_receiver->context); + } + }, + false); + } + + ws_view_receiver_update_offset(ws_receiver); + + return true; +} + +void ws_view_receiver_enter(void* context) { + furi_assert(context); +} + +void ws_view_receiver_exit(void* context) { + furi_assert(context); + WSReceiver* ws_receiver = context; + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + furi_string_reset(model->frequency_str); + furi_string_reset(model->preset_str); + furi_string_reset(model->history_stat_str); + for + M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) { + furi_string_free(item_menu->item_str); + item_menu->type = 0; + } + WSReceiverMenuItemArray_reset(model->history->data); + model->idx = 0; + model->list_offset = 0; + model->history_item = 0; + }, + false); + furi_timer_stop(ws_receiver->timer); +} + +WSReceiver* ws_view_receiver_alloc() { + WSReceiver* ws_receiver = malloc(sizeof(WSReceiver)); + + // View allocation and configuration + ws_receiver->view = view_alloc(); + + ws_receiver->lock = WSLockOff; + ws_receiver->lock_count = 0; + view_allocate_model(ws_receiver->view, ViewModelTypeLocking, sizeof(WSReceiverModel)); + view_set_context(ws_receiver->view, ws_receiver); + view_set_draw_callback(ws_receiver->view, (ViewDrawCallback)ws_view_receiver_draw); + view_set_input_callback(ws_receiver->view, ws_view_receiver_input); + view_set_enter_callback(ws_receiver->view, ws_view_receiver_enter); + view_set_exit_callback(ws_receiver->view, ws_view_receiver_exit); + + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + model->frequency_str = furi_string_alloc(); + model->preset_str = furi_string_alloc(); + model->history_stat_str = furi_string_alloc(); + model->bar_show = WSReceiverBarShowDefault; + model->history = malloc(sizeof(WSReceiverHistory)); + WSReceiverMenuItemArray_init(model->history->data); + }, + true); + ws_receiver->timer = + furi_timer_alloc(ws_view_receiver_timer_callback, FuriTimerTypeOnce, ws_receiver); + return ws_receiver; +} + +void ws_view_receiver_free(WSReceiver* ws_receiver) { + furi_assert(ws_receiver); + + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + furi_string_free(model->frequency_str); + furi_string_free(model->preset_str); + furi_string_free(model->history_stat_str); + for + M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) { + furi_string_free(item_menu->item_str); + item_menu->type = 0; + } + WSReceiverMenuItemArray_clear(model->history->data); + free(model->history); + }, + false); + furi_timer_free(ws_receiver->timer); + view_free(ws_receiver->view); + free(ws_receiver); +} + +View* ws_view_receiver_get_view(WSReceiver* ws_receiver) { + furi_assert(ws_receiver); + return ws_receiver->view; +} + +uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver) { + furi_assert(ws_receiver); + uint32_t idx = 0; + with_view_model( + ws_receiver->view, WSReceiverModel * model, { idx = model->idx; }, false); + return idx; +} + +void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx) { + furi_assert(ws_receiver); + with_view_model( + ws_receiver->view, + WSReceiverModel * model, + { + model->idx = idx; + if(model->idx > 2) model->list_offset = idx - 2; + }, + true); + ws_view_receiver_update_offset(ws_receiver); +} diff --git a/applications/plugins/weather_station/views/weather_station_receiver.h b/applications/plugins/weather_station/views/weather_station_receiver.h new file mode 100644 index 000000000..30c6516d5 --- /dev/null +++ b/applications/plugins/weather_station/views/weather_station_receiver.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "../helpers/weather_station_types.h" +#include "../helpers/weather_station_event.h" + +typedef struct WSReceiver WSReceiver; + +typedef void (*WSReceiverCallback)(WSCustomEvent event, void* context); + +void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock keyboard); + +void ws_view_receiver_set_callback( + WSReceiver* ws_receiver, + WSReceiverCallback callback, + void* context); + +WSReceiver* ws_view_receiver_alloc(); + +void ws_view_receiver_free(WSReceiver* ws_receiver); + +View* ws_view_receiver_get_view(WSReceiver* ws_receiver); + +void ws_view_receiver_add_data_statusbar( + WSReceiver* ws_receiver, + const char* frequency_str, + const char* preset_str, + const char* history_stat_str); + +void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type); + +uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver); + +void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx); + +void ws_view_receiver_exit(void* context); diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/plugins/weather_station/views/weather_station_receiver_info.c new file mode 100644 index 000000000..49b447f10 --- /dev/null +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -0,0 +1,160 @@ +#include "weather_station_receiver.h" +#include "../weather_station_app_i.h" +#include "weather_station_icons.h" +#include "../protocols/ws_generic.h" +#include +#include + +#define abs(x) ((x) > 0 ? (x) : -(x)) + +struct WSReceiverInfo { + View* view; +}; + +typedef struct { + FuriString* protocol_name; + WSBlockGeneric* generic; +} WSReceiverInfoModel; + +void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff) { + furi_assert(ws_receiver_info); + furi_assert(fff); + + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { + flipper_format_rewind(fff); + flipper_format_read_string(fff, "Protocol", model->protocol_name); + + ws_block_generic_deserialize(model->generic, fff); + }, + true); +} + +void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { + char buffer[64]; + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + snprintf( + buffer, + sizeof(buffer), + "%s %db", + furi_string_get_cstr(model->protocol_name), + model->generic->data_count_bit); + canvas_draw_str(canvas, 5, 8, buffer); + + if(model->generic->channel != WS_NO_CHANNEL) { + snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel); + canvas_draw_str(canvas, 105, 8, buffer); + } + + if(model->generic->id != WS_NO_ID) { + snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id); + canvas_draw_str(canvas, 5, 20, buffer); + } + + if(model->generic->btn != WS_NO_BTN) { + snprintf(buffer, sizeof(buffer), "Btn: %01d", model->generic->btn); + canvas_draw_str(canvas, 62, 20, buffer); + } + + if(model->generic->battery_low != WS_NO_BATT) { + snprintf( + buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low")); + canvas_draw_str(canvas, 90, 20, buffer); + } + + snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data); + canvas_draw_str(canvas, 5, 32, buffer); + + elements_bold_rounded_frame(canvas, 2, 37, 123, 25); + canvas_set_font(canvas, FontPrimary); + + if(model->generic->temp != WS_NO_TEMPERATURE) { + canvas_draw_icon(canvas, 18, 42, &I_Therm_7x16); + snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp); + canvas_draw_str_aligned(canvas, 63, 46, AlignRight, AlignTop, buffer); + canvas_draw_circle(canvas, 55, 45, 1); + } + + if(model->generic->humidity != WS_NO_HUMIDITY) { + canvas_draw_icon(canvas, 75, 42, &I_Humid_10x15); + snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity); + canvas_draw_str(canvas, 91, 54, buffer); + } +} + +bool ws_view_receiver_info_input(InputEvent* event, void* context) { + furi_assert(context); + //WSReceiverInfo* ws_receiver_info = context; + + if(event->key == InputKeyBack) { + return false; + } + + return true; +} + +void ws_view_receiver_info_enter(void* context) { + furi_assert(context); +} + +void ws_view_receiver_info_exit(void* context) { + furi_assert(context); + WSReceiverInfo* ws_receiver_info = context; + + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { furi_string_reset(model->protocol_name); }, + false); +} + +WSReceiverInfo* ws_view_receiver_info_alloc() { + WSReceiverInfo* ws_receiver_info = malloc(sizeof(WSReceiverInfo)); + + // View allocation and configuration + ws_receiver_info->view = view_alloc(); + + view_allocate_model(ws_receiver_info->view, ViewModelTypeLocking, sizeof(WSReceiverInfoModel)); + view_set_context(ws_receiver_info->view, ws_receiver_info); + view_set_draw_callback(ws_receiver_info->view, (ViewDrawCallback)ws_view_receiver_info_draw); + view_set_input_callback(ws_receiver_info->view, ws_view_receiver_info_input); + view_set_enter_callback(ws_receiver_info->view, ws_view_receiver_info_enter); + view_set_exit_callback(ws_receiver_info->view, ws_view_receiver_info_exit); + + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { + model->generic = malloc(sizeof(WSBlockGeneric)); + model->protocol_name = furi_string_alloc(); + }, + true); + + return ws_receiver_info; +} + +void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info) { + furi_assert(ws_receiver_info); + + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { + furi_string_free(model->protocol_name); + free(model->generic); + }, + false); + + view_free(ws_receiver_info->view); + free(ws_receiver_info); +} + +View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info) { + furi_assert(ws_receiver_info); + return ws_receiver_info->view; +} diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.h b/applications/plugins/weather_station/views/weather_station_receiver_info.h new file mode 100644 index 000000000..705434a23 --- /dev/null +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "../helpers/weather_station_types.h" +#include "../helpers/weather_station_event.h" +#include + +typedef struct WSReceiverInfo WSReceiverInfo; + +void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff); + +WSReceiverInfo* ws_view_receiver_info_alloc(); + +void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info); + +View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info); diff --git a/applications/plugins/weather_station/weather_station_10px.png b/applications/plugins/weather_station/weather_station_10px.png new file mode 100644 index 000000000..7d5cc318c Binary files /dev/null and b/applications/plugins/weather_station/weather_station_10px.png differ diff --git a/applications/plugins/weather_station/weather_station_app.c b/applications/plugins/weather_station/weather_station_app.c new file mode 100644 index 000000000..c4edc5975 --- /dev/null +++ b/applications/plugins/weather_station/weather_station_app.c @@ -0,0 +1,178 @@ +#include "weather_station_app_i.h" + +#include +#include +#include "protocols/protocol_items.h" + +static bool weather_station_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + WeatherStationApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool weather_station_app_back_event_callback(void* context) { + furi_assert(context); + WeatherStationApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void weather_station_app_tick_event_callback(void* context) { + furi_assert(context); + WeatherStationApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +WeatherStationApp* weather_station_app_alloc() { + WeatherStationApp* app = malloc(sizeof(WeatherStationApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&weather_station_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, weather_station_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, weather_station_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, weather_station_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Variable Item List + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + WeatherStationViewVariableItemList, + variable_item_list_get_view(app->variable_item_list)); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WeatherStationViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WeatherStationViewWidget, widget_get_view(app->widget)); + + // Receiver + app->ws_receiver = ws_view_receiver_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + WeatherStationViewReceiver, + ws_view_receiver_get_view(app->ws_receiver)); + + // Receiver Info + app->ws_receiver_info = ws_view_receiver_info_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + WeatherStationViewReceiverInfo, + ws_view_receiver_info_get_view(app->ws_receiver_info)); + + //init setting + app->setting = subghz_setting_alloc(); + + //ToDo FIX file name setting + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"), true); + + //init Worker & Protocol & History + app->lock = WSLockOff; + app->txrx = malloc(sizeof(WeatherStationTxRx)); + app->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); + app->txrx->preset->name = furi_string_alloc(); + ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); + + app->txrx->hopper_state = WSHopperStateOFF; + app->txrx->history = ws_history_alloc(); + app->txrx->worker = subghz_worker_alloc(); + app->txrx->environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry( + app->txrx->environment, (void*)&weather_station_protocol_registry); + app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment); + + subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable); + subghz_worker_set_overrun_callback( + app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); + subghz_worker_set_pair_callback( + app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); + subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); + + furi_hal_power_suppress_charge_enter(); + + scene_manager_next_scene(app->scene_manager, WeatherStationSceneStart); + + return app; +} + +void weather_station_app_free(WeatherStationApp* app) { + furi_assert(app); + + //CC1101 off + ws_sleep(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewSubmenu); + submenu_free(app->submenu); + + // Variable Item List + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewVariableItemList); + variable_item_list_free(app->variable_item_list); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewWidget); + widget_free(app->widget); + + // Receiver + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiver); + ws_view_receiver_free(app->ws_receiver); + + // Receiver Info + view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiverInfo); + ws_view_receiver_info_free(app->ws_receiver_info); + + //setting + subghz_setting_free(app->setting); + + //Worker & Protocol & History + subghz_receiver_free(app->txrx->receiver); + subghz_environment_free(app->txrx->environment); + ws_history_free(app->txrx->history); + subghz_worker_free(app->txrx->worker); + furi_string_free(app->txrx->preset->name); + free(app->txrx->preset); + free(app->txrx); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + furi_hal_power_suppress_charge_exit(); + + free(app); +} + +int32_t weather_station_app(void* p) { + UNUSED(p); + WeatherStationApp* weather_station_app = weather_station_app_alloc(); + + view_dispatcher_run(weather_station_app->view_dispatcher); + + weather_station_app_free(weather_station_app); + + return 0; +} diff --git a/applications/plugins/weather_station/weather_station_app_i.c b/applications/plugins/weather_station/weather_station_app_i.c new file mode 100644 index 000000000..052bb8533 --- /dev/null +++ b/applications/plugins/weather_station/weather_station_app_i.c @@ -0,0 +1,159 @@ +#include "weather_station_app_i.h" + +#define TAG "WeatherStation" +#include + +void ws_preset_init( + void* context, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size) { + furi_assert(context); + WeatherStationApp* app = context; + furi_string_set(app->txrx->preset->name, preset_name); + app->txrx->preset->frequency = frequency; + app->txrx->preset->data = preset_data; + app->txrx->preset->data_size = preset_data_size; +} + +bool ws_set_preset(WeatherStationApp* app, const char* preset) { + if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { + furi_string_set(app->txrx->preset->name, "AM270"); + } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { + furi_string_set(app->txrx->preset->name, "AM650"); + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { + furi_string_set(app->txrx->preset->name, "FM238"); + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { + furi_string_set(app->txrx->preset->name, "FM476"); + } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { + furi_string_set(app->txrx->preset->name, "CUSTOM"); + } else { + FURI_LOG_E(TAG, "Unknown preset"); + return false; + } + return true; +} + +void ws_get_frequency_modulation( + WeatherStationApp* app, + FuriString* frequency, + FuriString* modulation) { + furi_assert(app); + if(frequency != NULL) { + furi_string_printf( + frequency, + "%03ld.%02ld", + app->txrx->preset->frequency / 1000000 % 1000, + app->txrx->preset->frequency / 10000 % 100); + } + if(modulation != NULL) { + furi_string_printf(modulation, "%.2s", furi_string_get_cstr(app->txrx->preset->name)); + } +} + +void ws_begin(WeatherStationApp* app, uint8_t* preset_data) { + furi_assert(app); + UNUSED(preset_data); + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + furi_hal_subghz_load_custom_preset(preset_data); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + app->txrx->txrx_state = WSTxRxStateIDLE; +} + +uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency) { + furi_assert(app); + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + furi_crash("WeatherStation: Incorrect RX frequency."); + } + furi_assert( + app->txrx->txrx_state != WSTxRxStateRx && app->txrx->txrx_state != WSTxRxStateSleep); + + furi_hal_subghz_idle(); + uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_subghz_flush_rx(); + furi_hal_subghz_rx(); + + furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker); + subghz_worker_start(app->txrx->worker); + app->txrx->txrx_state = WSTxRxStateRx; + return value; +} + +void ws_idle(WeatherStationApp* app) { + furi_assert(app); + furi_assert(app->txrx->txrx_state != WSTxRxStateSleep); + furi_hal_subghz_idle(); + app->txrx->txrx_state = WSTxRxStateIDLE; +} + +void ws_rx_end(WeatherStationApp* app) { + furi_assert(app); + furi_assert(app->txrx->txrx_state == WSTxRxStateRx); + if(subghz_worker_is_running(app->txrx->worker)) { + subghz_worker_stop(app->txrx->worker); + furi_hal_subghz_stop_async_rx(); + } + furi_hal_subghz_idle(); + app->txrx->txrx_state = WSTxRxStateIDLE; +} + +void ws_sleep(WeatherStationApp* app) { + furi_assert(app); + furi_hal_subghz_sleep(); + app->txrx->txrx_state = WSTxRxStateSleep; +} + +void ws_hopper_update(WeatherStationApp* app) { + furi_assert(app); + + switch(app->txrx->hopper_state) { + case WSHopperStateOFF: + return; + break; + case WSHopperStatePause: + return; + break; + case WSHopperStateRSSITimeOut: + if(app->txrx->hopper_timeout != 0) { + app->txrx->hopper_timeout--; + return; + } + break; + default: + break; + } + float rssi = -127.0f; + if(app->txrx->hopper_state != WSHopperStateRSSITimeOut) { + // See RSSI Calculation timings in CC1101 17.3 RSSI + rssi = furi_hal_subghz_get_rssi(); + + // Stay if RSSI is high enough + if(rssi > -90.0f) { + app->txrx->hopper_timeout = 10; + app->txrx->hopper_state = WSHopperStateRSSITimeOut; + return; + } + } else { + app->txrx->hopper_state = WSHopperStateRunnig; + } + // Select next frequency + if(app->txrx->hopper_idx_frequency < + subghz_setting_get_hopper_frequency_count(app->setting) - 1) { + app->txrx->hopper_idx_frequency++; + } else { + app->txrx->hopper_idx_frequency = 0; + } + + if(app->txrx->txrx_state == WSTxRxStateRx) { + ws_rx_end(app); + }; + if(app->txrx->txrx_state == WSTxRxStateIDLE) { + subghz_receiver_reset(app->txrx->receiver); + app->txrx->preset->frequency = + subghz_setting_get_hopper_frequency(app->setting, app->txrx->hopper_idx_frequency); + ws_rx(app, app->txrx->preset->frequency); + } +} diff --git a/applications/plugins/weather_station/weather_station_app_i.h b/applications/plugins/weather_station/weather_station_app_i.h new file mode 100644 index 000000000..41e248112 --- /dev/null +++ b/applications/plugins/weather_station/weather_station_app_i.h @@ -0,0 +1,73 @@ +#pragma once + +#include "helpers/weather_station_types.h" + +#include "scenes/weather_station_scene.h" +#include +#include +#include +#include +#include +#include +#include +#include "views/weather_station_receiver.h" +#include "views/weather_station_receiver_info.h" +#include "weather_station_history.h" + +#include +#include +#include +#include +#include + +typedef struct WeatherStationApp WeatherStationApp; + +struct WeatherStationTxRx { + SubGhzWorker* worker; + + SubGhzEnvironment* environment; + SubGhzReceiver* receiver; + SubGhzRadioPreset* preset; + WSHistory* history; + uint16_t idx_menu_chosen; + WSTxRxState txrx_state; + WSHopperState hopper_state; + uint8_t hopper_timeout; + uint8_t hopper_idx_frequency; + WSRxKeyState rx_key_state; +}; + +typedef struct WeatherStationTxRx WeatherStationTxRx; + +struct WeatherStationApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + WeatherStationTxRx* txrx; + SceneManager* scene_manager; + NotificationApp* notifications; + VariableItemList* variable_item_list; + Submenu* submenu; + Widget* widget; + WSReceiver* ws_receiver; + WSReceiverInfo* ws_receiver_info; + WSLock lock; + SubGhzSetting* setting; +}; + +void ws_preset_init( + void* context, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size); +bool ws_set_preset(WeatherStationApp* app, const char* preset); +void ws_get_frequency_modulation( + WeatherStationApp* app, + FuriString* frequency, + FuriString* modulation); +void ws_begin(WeatherStationApp* app, uint8_t* preset_data); +uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency); +void ws_idle(WeatherStationApp* app); +void ws_rx_end(WeatherStationApp* app); +void ws_sleep(WeatherStationApp* app); +void ws_hopper_update(WeatherStationApp* app); diff --git a/applications/plugins/weather_station/weather_station_history.c b/applications/plugins/weather_station/weather_station_history.c new file mode 100644 index 000000000..b37009c46 --- /dev/null +++ b/applications/plugins/weather_station/weather_station_history.c @@ -0,0 +1,245 @@ +#include "weather_station_history.h" +#include +#include +#include +#include "protocols/ws_generic.h" + +#include + +#define WS_HISTORY_MAX 50 +#define TAG "WSHistory" + +typedef struct { + FuriString* item_str; + FlipperFormat* flipper_string; + uint8_t type; + uint32_t id; + SubGhzRadioPreset* preset; +} WSHistoryItem; + +ARRAY_DEF(WSHistoryItemArray, WSHistoryItem, M_POD_OPLIST) + +#define M_OPL_WSHistoryItemArray_t() ARRAY_OPLIST(WSHistoryItemArray, M_POD_OPLIST) + +typedef struct { + WSHistoryItemArray_t data; +} WSHistoryStruct; + +struct WSHistory { + uint32_t last_update_timestamp; + uint16_t last_index_write; + uint8_t code_last_hash_data; + FuriString* tmp_string; + WSHistoryStruct* history; +}; + +WSHistory* ws_history_alloc(void) { + WSHistory* instance = malloc(sizeof(WSHistory)); + instance->tmp_string = furi_string_alloc(); + instance->history = malloc(sizeof(WSHistoryStruct)); + WSHistoryItemArray_init(instance->history->data); + return instance; +} + +void ws_history_free(WSHistory* instance) { + furi_assert(instance); + furi_string_free(instance->tmp_string); + for + M_EACH(item, instance->history->data, WSHistoryItemArray_t) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + } + WSHistoryItemArray_clear(instance->history->data); + free(instance->history); + free(instance); +} + +uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + return item->preset->frequency; +} + +SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + return item->preset; +} + +const char* ws_history_get_preset(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + return furi_string_get_cstr(item->preset->name); +} + +void ws_history_reset(WSHistory* instance) { + furi_assert(instance); + furi_string_reset(instance->tmp_string); + for + M_EACH(item, instance->history->data, WSHistoryItemArray_t) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + } + WSHistoryItemArray_reset(instance->history->data); + instance->last_index_write = 0; + instance->code_last_hash_data = 0; +} + +uint16_t ws_history_get_item(WSHistory* instance) { + furi_assert(instance); + return instance->last_index_write; +} + +uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + return item->type; +} + +const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + flipper_format_rewind(item->flipper_string); + if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { + FURI_LOG_E(TAG, "Missing Protocol"); + furi_string_reset(instance->tmp_string); + } + return furi_string_get_cstr(instance->tmp_string); +} + +FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx) { + furi_assert(instance); + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + if(item->flipper_string) { + return item->flipper_string; + } else { + return NULL; + } +} +bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output) { + furi_assert(instance); + if(instance->last_index_write == WS_HISTORY_MAX) { + if(output != NULL) furi_string_printf(output, "Memory is FULL"); + return true; + } + if(output != NULL) + furi_string_printf(output, "%02u/%02u", instance->last_index_write, WS_HISTORY_MAX); + return false; +} + +void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx) { + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); + furi_string_set(output, item->item_str); +} + +WSHistoryStateAddKey + ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset) { + furi_assert(instance); + furi_assert(context); + + if(instance->last_index_write >= WS_HISTORY_MAX) return WSHistoryStateAddKeyOverflow; + + SubGhzProtocolDecoderBase* decoder_base = context; + if((instance->code_last_hash_data == + subghz_protocol_decoder_base_get_hash_data(decoder_base)) && + ((furi_get_tick() - instance->last_update_timestamp) < 500)) { + instance->last_update_timestamp = furi_get_tick(); + return WSHistoryStateAddKeyTimeOut; + } + + instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + instance->last_update_timestamp = furi_get_tick(); + + FlipperFormat* fff = flipper_format_string_alloc(); + uint32_t id = 0; + subghz_protocol_decoder_base_serialize(decoder_base, fff, preset); + + do { + if(!flipper_format_rewind(fff)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(fff, "Id", (uint32_t*)&id, 1)) { + FURI_LOG_E(TAG, "Missing Id"); + break; + } + } while(false); + flipper_format_free(fff); + + //Update record if found + bool sensor_found = false; + for(size_t i = 0; i < WSHistoryItemArray_size(instance->history->data); i++) { + WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, i); + if(item->id == id) { + sensor_found = true; + Stream* flipper_string_stream = flipper_format_get_raw_stream(item->flipper_string); + stream_clean(flipper_string_stream); + subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); + return WSHistoryStateAddKeyUpdateData; + } + } + + // or add new record + if(!sensor_found) { + WSHistoryItem* item = WSHistoryItemArray_push_raw(instance->history->data); + item->preset = malloc(sizeof(SubGhzRadioPreset)); + item->type = decoder_base->protocol->type; + item->preset->frequency = preset->frequency; + item->preset->name = furi_string_alloc(); + furi_string_set(item->preset->name, preset->name); + item->preset->data = preset->data; + item->preset->data_size = preset->data_size; + item->id = id; + + item->item_str = furi_string_alloc(); + item->flipper_string = flipper_format_string_alloc(); + subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); + + do { + if(!flipper_format_rewind(item->flipper_string)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_string( + item->flipper_string, "Protocol", instance->tmp_string)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + + if(!flipper_format_rewind(item->flipper_string)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(item->flipper_string, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + uint64_t data = 0; + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + data = (data << 8) | key_data[i]; + } + uint32_t temp_data = 0; + if(!flipper_format_read_uint32(item->flipper_string, "Ch", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Channel"); + break; + } + if(temp_data != WS_NO_CHANNEL) { + furi_string_cat_printf(instance->tmp_string, " Ch:%X", (uint8_t)temp_data); + } + + furi_string_printf( + item->item_str, "%s %llX", furi_string_get_cstr(instance->tmp_string), data); + + } while(false); + instance->last_index_write++; + return WSHistoryStateAddKeyNewDada; + } + return WSHistoryStateAddKeyUnknown; +} diff --git a/applications/plugins/weather_station/weather_station_history.h b/applications/plugins/weather_station/weather_station_history.h new file mode 100644 index 000000000..11601fe79 --- /dev/null +++ b/applications/plugins/weather_station/weather_station_history.h @@ -0,0 +1,112 @@ + +#pragma once + +#include +#include +#include +#include +#include + +typedef struct WSHistory WSHistory; + +/** History state add key */ +typedef enum { + WSHistoryStateAddKeyUnknown, + WSHistoryStateAddKeyTimeOut, + WSHistoryStateAddKeyNewDada, + WSHistoryStateAddKeyUpdateData, + WSHistoryStateAddKeyOverflow, +} WSHistoryStateAddKey; + +/** Allocate WSHistory + * + * @return WSHistory* + */ +WSHistory* ws_history_alloc(void); + +/** Free WSHistory + * + * @param instance - WSHistory instance + */ +void ws_history_free(WSHistory* instance); + +/** Clear history + * + * @param instance - WSHistory instance + */ +void ws_history_reset(WSHistory* instance); + +/** Get frequency to history[idx] + * + * @param instance - WSHistory instance + * @param idx - record index + * @return frequency - frequency Hz + */ +uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx); + +SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx); + +/** Get preset to history[idx] + * + * @param instance - WSHistory instance + * @param idx - record index + * @return preset - preset name + */ +const char* ws_history_get_preset(WSHistory* instance, uint16_t idx); + +/** Get history index write + * + * @param instance - WSHistory instance + * @return idx - current record index + */ +uint16_t ws_history_get_item(WSHistory* instance); + +/** Get type protocol to history[idx] + * + * @param instance - WSHistory instance + * @param idx - record index + * @return type - type protocol + */ +uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx); + +/** Get name protocol to history[idx] + * + * @param instance - WSHistory instance + * @param idx - record index + * @return name - const char* name protocol + */ +const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx); + +/** Get string item menu to history[idx] + * + * @param instance - WSHistory instance + * @param output - FuriString* output + * @param idx - record index + */ +void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx); + +/** Get string the remaining number of records to history + * + * @param instance - WSHistory instance + * @param output - FuriString* output + * @return bool - is FUUL + */ +bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output); + +/** Add protocol to history + * + * @param instance - WSHistory instance + * @param context - SubGhzProtocolCommon context + * @param preset - SubGhzRadioPreset preset + * @return WSHistoryStateAddKey; + */ +WSHistoryStateAddKey + ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset); + +/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data + * + * @param instance - WSHistory instance + * @param idx - record index + * @return SubGhzProtocolCommonLoad* + */ +FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx); diff --git a/applications/plugins/wifi_deauther/README.md b/applications/plugins/wifi_deauther/README.md deleted file mode 100644 index 87a419f90..000000000 --- a/applications/plugins/wifi_deauther/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# flipperzero_esp8266_deautherv2 -Flipper Zero esp8266 deauther app. - - -Based off the WiFi Marauder App from 0xchocolate. - -https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion - -https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/unleashed/applications/wifi_marauder_companion - -uses the Version 2 of the ESP8266 Deauther code. -https://github.com/SpacehuhnTech/esp8266_deauther/tree/v2/esp8266_deauther - -This is done so you can use the original deauther v2 firmware on the esp8266. -you can just flash the latest binary. - -also a shout out to https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module -This is already in the Roguemaster firmware and just needs to be enabled and compiled. unfortunatly I could not get this past the menu when I compiled his deauther source for the nodemcu. Nice menu though. - -I used a nodeMCU board. Wiring is simple. follow the wiring guide on https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module -On mine I connected one G to ground, VIN to 5V, RX to U_TX, TX to U_RX. - -NodeMCU---FlipperZero - -G---------GND - -VIN-------5V - -RX--------U_TX - -TX--------U_RX - - - -Video in action. -https://youtu.be/_RFzZyPkeR0 - -If you want to disable the built in WiFi access and web interface (only use flipper to serial send commands) then select "set webinterface false", "save settings" and "reboot". When it starts back up you wont see the pwned AP any more. - -I installed this into Roguemaster to test. - -git clone --recursive https://github.com/RogueMaster/flipperzero-firmware-wPlugins.git -cd flipperzero-firmware-wPlugins/ - -copy folder into applications. -add "APPS_wifi_deauther", to the meta/application.fam file. - -compile -./fbt resources icons -./fbt updater_package diff --git a/applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h b/applications/plugins/wifi_deauther_v1/FlipperZeroWiFiDeauthModuleDefines.h similarity index 100% rename from applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h rename to applications/plugins/wifi_deauther_v1/FlipperZeroWiFiDeauthModuleDefines.h diff --git a/applications/plugins/esp8266_deauth/application.fam b/applications/plugins/wifi_deauther_v1/application.fam similarity index 100% rename from applications/plugins/esp8266_deauth/application.fam rename to applications/plugins/wifi_deauther_v1/application.fam diff --git a/applications/plugins/esp8266_deauth/esp8266_deauth.c b/applications/plugins/wifi_deauther_v1/esp8266_deauth.c similarity index 97% rename from applications/plugins/esp8266_deauth/esp8266_deauth.c rename to applications/plugins/wifi_deauther_v1/esp8266_deauth.c index 8503ae65c..3cc61a588 100644 --- a/applications/plugins/esp8266_deauth/esp8266_deauth.c +++ b/applications/plugins/wifi_deauther_v1/esp8266_deauth.c @@ -18,7 +18,7 @@ #define DEAUTH_APP_DEBUG 0 #if DEAUTH_APP_DEBUG -#define APP_NAME_TAG "WiFi_Scanner" +#define APP_NAME_TAG "WiFi_Deauther" #define DEAUTH_APP_LOG_I(format, ...) FURI_LOG_I(APP_NAME_TAG, format, ##__VA_ARGS__) #define DEAUTH_APP_LOG_D(format, ...) FURI_LOG_D(APP_NAME_TAG, format, ##__VA_ARGS__) #define DEAUTH_APP_LOG_E(format, ...) FURI_LOG_E(APP_NAME_TAG, format, ##__VA_ARGS__) @@ -167,7 +167,7 @@ static void esp8266_deauth_module_render_callback(Canvas* const canvas, void* ct canvas_clear(canvas); canvas_set_font(canvas, FontSecondary); - const char* strInitializing = "Attach WiFi scanner module"; + const char* strInitializing = "Attach WiFi Deauther module"; canvas_draw_str( canvas, (u8g2_GetDisplayWidth(&canvas->fb) / 2) - @@ -365,8 +365,6 @@ int32_t esp8266_deauth_app(void* p) { DEAUTH_APP_LOG_I("Mutex created"); - app->m_rx_stream = furi_stream_buffer_alloc(1 * 1024, 1); - //app->m_notification = furi_record_open("notification"); ViewPort* view_port = view_port_alloc(); @@ -379,13 +377,7 @@ int32_t esp8266_deauth_app(void* p) { //notification_message(app->notification, &sequence_set_only_blue_255); - // Enable uart listener -#if DISABLE_CONSOLE - furi_hal_console_disable(); -#endif - furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app); - DEAUTH_APP_LOG_I("UART Listener created"); + app->m_rx_stream = furi_stream_buffer_alloc(1 * 1024, 1); app->m_worker_thread = furi_thread_alloc(); furi_thread_set_name(app->m_worker_thread, "WiFiDeauthModuleUARTWorker"); @@ -395,6 +387,14 @@ int32_t esp8266_deauth_app(void* p) { furi_thread_start(app->m_worker_thread); DEAUTH_APP_LOG_I("UART thread allocated"); + // Enable uart listener +#if DISABLE_CONSOLE + furi_hal_console_disable(); +#endif + furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app); + DEAUTH_APP_LOG_I("UART Listener created"); + SPluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); @@ -472,8 +472,6 @@ int32_t esp8266_deauth_app(void* p) { } } } - } else { - DEAUTH_APP_LOG_D("osMessageQueue: event timeout"); } #if ENABLE_MODULE_DETECTION @@ -497,6 +495,13 @@ int32_t esp8266_deauth_app(void* p) { DEAUTH_APP_LOG_I("Thread Deleted"); + // Reset GPIO pins to default state + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + #if DISABLE_CONSOLE furi_hal_console_enable(); #endif diff --git a/applications/plugins/esp8266_deauth/wifi_10px.png b/applications/plugins/wifi_deauther_v1/wifi_10px.png similarity index 100% rename from applications/plugins/esp8266_deauth/wifi_10px.png rename to applications/plugins/wifi_deauther_v1/wifi_10px.png diff --git a/applications/plugins/wifi_deauther_v2/LICENSE b/applications/plugins/wifi_deauther_v2/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/plugins/wifi_deauther_v2/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/plugins/wifi_deauther_v2/README.md b/applications/plugins/wifi_deauther_v2/README.md new file mode 100644 index 000000000..6c6d5751d --- /dev/null +++ b/applications/plugins/wifi_deauther_v2/README.md @@ -0,0 +1,51 @@ +# THIS IS OUTDATED +# flipperzero_esp8266_deautherv2 +Flipper Zero esp8266 deauther app. + + +Based off the WiFi Marauder App from 0xchocolate. + +https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion + +https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/unleashed/applications/wifi_marauder_companion + +uses the Version 2 of the ESP8266 Deauther code. +https://github.com/SpacehuhnTech/esp8266_deauther/tree/v2/esp8266_deauther + +This is done so you can use the original deauther v2 firmware on the esp8266. +you can just flash the latest binary. + +also a shout out to https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module +This is already in the Roguemaster firmware and just needs to be enabled and compiled. unfortunatly I could not get this past the menu when I compiled his deauther source for the nodemcu. Nice menu though. + +I used a nodeMCU board. Wiring is simple. follow the wiring guide on https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module +On mine I connected one G to ground, VIN to 5V, RX to U_TX, TX to U_RX. + +NodeMCU---FlipperZero + +G---------GND + +VIN-------5V + +RX--------U_TX + +TX--------U_RX + + + +Video in action. +https://youtu.be/_RFzZyPkeR0 + +If you want to disable the built in WiFi access and web interface (only use flipper to serial send commands) then select "set webinterface false", "save settings" and "reboot". When it starts back up you wont see the pwned AP any more. + +I installed this into Roguemaster to test. + +git clone --recursive https://github.com/RogueMaster/flipperzero-firmware-wPlugins.git +cd flipperzero-firmware-wPlugins/ + +copy folder into applications. +add "APPS_wifi_deauther", to the meta/application.fam file. + +compile +./fbt resources icons +./fbt updater_package diff --git a/applications/plugins/wifi_deauther/application.fam b/applications/plugins/wifi_deauther_v2/application.fam similarity index 100% rename from applications/plugins/wifi_deauther/application.fam rename to applications/plugins/wifi_deauther_v2/application.fam diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.c b/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene.c similarity index 100% rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.c rename to applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene.c diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.h b/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene.h similarity index 100% rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.h rename to applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene.h diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_config.h b/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_config.h similarity index 100% rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_config.h rename to applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_config.h diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_console_output.c b/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_console_output.c similarity index 100% rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_console_output.c rename to applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_console_output.c diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_start.c b/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_start.c similarity index 100% rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_start.c rename to applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_start.c diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_text_input.c b/applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_text_input.c similarity index 100% rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_text_input.c rename to applications/plugins/wifi_deauther_v2/scenes/wifi_deauther_scene_text_input.c diff --git a/applications/plugins/wifi_deauther/wifi_10px.png b/applications/plugins/wifi_deauther_v2/wifi_10px.png similarity index 100% rename from applications/plugins/wifi_deauther/wifi_10px.png rename to applications/plugins/wifi_deauther_v2/wifi_10px.png diff --git a/applications/plugins/wifi_deauther/wifi_deauther_app.c b/applications/plugins/wifi_deauther_v2/wifi_deauther_app.c similarity index 100% rename from applications/plugins/wifi_deauther/wifi_deauther_app.c rename to applications/plugins/wifi_deauther_v2/wifi_deauther_app.c diff --git a/applications/plugins/wifi_deauther/wifi_deauther_app.h b/applications/plugins/wifi_deauther_v2/wifi_deauther_app.h similarity index 100% rename from applications/plugins/wifi_deauther/wifi_deauther_app.h rename to applications/plugins/wifi_deauther_v2/wifi_deauther_app.h diff --git a/applications/plugins/wifi_deauther/wifi_deauther_app_i.h b/applications/plugins/wifi_deauther_v2/wifi_deauther_app_i.h similarity index 100% rename from applications/plugins/wifi_deauther/wifi_deauther_app_i.h rename to applications/plugins/wifi_deauther_v2/wifi_deauther_app_i.h diff --git a/applications/plugins/wifi_deauther/wifi_deauther_custom_event.h b/applications/plugins/wifi_deauther_v2/wifi_deauther_custom_event.h similarity index 100% rename from applications/plugins/wifi_deauther/wifi_deauther_custom_event.h rename to applications/plugins/wifi_deauther_v2/wifi_deauther_custom_event.h diff --git a/applications/plugins/wifi_deauther/wifi_deauther_uart.c b/applications/plugins/wifi_deauther_v2/wifi_deauther_uart.c similarity index 100% rename from applications/plugins/wifi_deauther/wifi_deauther_uart.c rename to applications/plugins/wifi_deauther_v2/wifi_deauther_uart.c diff --git a/applications/plugins/wifi_deauther/wifi_deauther_uart.h b/applications/plugins/wifi_deauther_v2/wifi_deauther_uart.h similarity index 100% rename from applications/plugins/wifi_deauther/wifi_deauther_uart.h rename to applications/plugins/wifi_deauther_v2/wifi_deauther_uart.h diff --git a/applications/plugins/wifi_marauder_companion/application.fam b/applications/plugins/wifi_marauder_companion/application.fam index 6e112c85b..e3185d50a 100644 --- a/applications/plugins/wifi_marauder_companion/application.fam +++ b/applications/plugins/wifi_marauder_companion/application.fam @@ -6,7 +6,7 @@ App( cdefines=["APP_WIFI_MARAUDER"], requires=["gui"], stack_size=1 * 1024, - order=10, + order=90, fap_icon="wifi_10px.png", fap_category="GPIO", ) diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c index c5afa6502..25ea0abb0 100644 --- a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c +++ b/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c @@ -34,7 +34,7 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { app->text_box_store_strlen = 0; if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { const char* help_msg = - "For app support/feedback,\nreach out to me:\n@cococode#6011 (discord)\n0xchocolate (github)\n"; + "Marauder companion v0.2.2\nFor app support/feedback,\nreach out to me:\n@cococode#6011 (discord)\n0xchocolate (github)\n"; furi_string_cat_str(app->text_box_store, help_msg); app->text_box_store_strlen += strlen(help_msg); } diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c index b5072417b..029614c5d 100644 --- a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -1,3 +1,5 @@ +//** Includes sniffbt and sniffskim for compatible ESP32-WROOM hardware. +//wifi_marauder_app_i.h also changed **// #include "../wifi_marauder_app_i.h" // For each command, define whether additional arguments are needed @@ -10,7 +12,7 @@ typedef enum { FOCUS_CONSOLE_END = 0, FOCUS_CONSOLE_START, FOCUS_CONSOLE_TOGGLE #define SHOW_STOPSCAN_TIP (true) #define NO_TIP (false) -#define MAX_OPTIONS (6) +#define MAX_OPTIONS (9) typedef struct { const char* item_string; const char* options_menu[MAX_OPTIONS]; @@ -26,7 +28,7 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { {"View Log from", {"start", "end"}, 2, {"", ""}, NO_ARGS, FOCUS_CONSOLE_TOGGLE, NO_TIP}, {"Scan AP", {""}, 1, {"scanap"}, NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, {"SSID", - {"add random", "add name", "remove"}, + {"add rand", "add name", "remove"}, 3, {"ssid -a -g", "ssid -a -n", "ssid -r"}, INPUT_ARGS, @@ -56,9 +58,17 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, {"Sniff", - {"beacon", "deauth", "esp", "pmkid", "pwn"}, - 5, - {"sniffbeacon", "sniffdeauth", "sniffesp", "sniffpmkid", "sniffpwn"}, + {"beacon", "deauth", "esp", "pmkid", "probe", "pwn", "raw", "bt", "skim"}, + 9, + {"sniffbeacon", + "sniffdeauth", + "sniffesp", + "sniffpmkid", + "sniffprobe", + "sniffpwn", + "sniffraw", + "sniffbt", + "sniffskim"}, NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h index 63d340bbc..f571a11a2 100644 --- a/applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h @@ -1,3 +1,5 @@ +//** Includes sniffbt and sniffskim for compatible ESP32-WROOM hardware. +// wifi_marauder_scene_start.c also changed **// #pragma once #include "wifi_marauder_app.h" diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c b/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c index 8c8d82eb6..228b0f83d 100644 --- a/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c +++ b/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c @@ -38,8 +38,6 @@ void wifi_marauder_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) static int32_t uart_worker(void* context) { WifiMarauderUart* uart = (void*)context; - uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); - while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); @@ -65,18 +63,19 @@ void wifi_marauder_uart_tx(uint8_t* data, size_t len) { WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app) { WifiMarauderUart* uart = malloc(sizeof(WifiMarauderUart)); - furi_hal_console_disable(); - furi_hal_uart_set_br(UART_CH, BAUDRATE); - furi_hal_uart_set_irq_cb(UART_CH, wifi_marauder_uart_on_irq_cb, uart); - uart->app = app; + uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); uart->rx_thread = furi_thread_alloc(); furi_thread_set_name(uart->rx_thread, "WifiMarauderUartRxThread"); furi_thread_set_stack_size(uart->rx_thread, 1024); furi_thread_set_context(uart->rx_thread, uart); furi_thread_set_callback(uart->rx_thread, uart_worker); - furi_thread_start(uart->rx_thread); + + furi_hal_console_disable(); + furi_hal_uart_set_br(UART_CH, BAUDRATE); + furi_hal_uart_set_irq_cb(UART_CH, wifi_marauder_uart_on_irq_cb, uart); + return uart; } diff --git a/applications/plugins/wifi_scanner/wifi_scanner.c b/applications/plugins/wifi_scanner/wifi_scanner.c index 4de68ee58..826048e26 100644 --- a/applications/plugins/wifi_scanner/wifi_scanner.c +++ b/applications/plugins/wifi_scanner/wifi_scanner.c @@ -674,8 +674,6 @@ int32_t wifi_scanner_app(void* p) { WIFI_APP_LOG_I("Mutex created"); - app->m_rx_stream = furi_stream_buffer_alloc(1 * 1024, 1); - app->m_notification = furi_record_open("notification"); ViewPort* view_port = view_port_alloc(); @@ -688,13 +686,7 @@ int32_t wifi_scanner_app(void* p) { //notification_message(app->notification, &sequence_set_only_blue_255); - // Enable uart listener -#if DISABLE_CONSOLE - furi_hal_console_disable(); -#endif - furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app); - WIFI_APP_LOG_I("UART Listener created"); + app->m_rx_stream = furi_stream_buffer_alloc(1 * 1024, 1); app->m_worker_thread = furi_thread_alloc(); furi_thread_set_name(app->m_worker_thread, "WiFiModuleUARTWorker"); @@ -704,6 +696,14 @@ int32_t wifi_scanner_app(void* p) { furi_thread_start(app->m_worker_thread); WIFI_APP_LOG_I("UART thread allocated"); + // Enable uart listener +#if DISABLE_CONSOLE + furi_hal_console_disable(); +#endif + furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app); + WIFI_APP_LOG_I("UART Listener created"); + // Because we assume that module was on before we launched the app. We need to ensure that module will be in initial state on app start send_serial_command(ESerialCommand_Restart); @@ -795,8 +795,6 @@ int32_t wifi_scanner_app(void* p) { } } } - } else { - WIFI_APP_LOG_D("osMessageQueue: event timeout"); } #if ENABLE_MODULE_DETECTION @@ -820,6 +818,9 @@ int32_t wifi_scanner_app(void* p) { WIFI_APP_LOG_I("Thread Deleted"); + // Reset GPIO pins to default state + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + #if DISABLE_CONSOLE furi_hal_console_enable(); #endif diff --git a/applications/plugins/wii_ec_anal/LICENSE b/applications/plugins/wii_ec_anal/LICENSE new file mode 100644 index 000000000..95e544a06 --- /dev/null +++ b/applications/plugins/wii_ec_anal/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 BlueChip + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/wii_ec_anal/README.md b/applications/plugins/wii_ec_anal/README.md new file mode 100644 index 000000000..8d439c7e0 --- /dev/null +++ b/applications/plugins/wii_ec_anal/README.md @@ -0,0 +1,234 @@ +# [FlipperZero] Wii Extension Controller Protocol Analyser +This Protocol Analyser offers a full Test and Calibrate system for Wii Extension Controllers. + +__Disclaimer:__ *Use of this plugin, and notably connecting an Extension Controller to the FlipperZero is performed entirely at your own risk.* + +# Notes +This plugin has (todate) only been tested with official Nintendo Nunchucks and Classic Controllers - namely Nunchucks and Classic Controllers. + +# Encryption +This plugin has SOME code to handle encryption, but it it unused, untested, and some of it is known to un-work. + +This plugin (currently) only works with Extension Controllers which implement the encryption-bypass strategy. IE. `i2c_write(0xf0, 0x55) ; i2c_write(0xfb, 0x00)` + +If you need this functionality, either raise an Issue or, better still, a Pull Request. + +# Screen: SPLASH +
+The SPLASH Screen is displayed when the Plugin starts. It can be cleared by pressing any key, else it will auto-clear after 3.5 seconds. + +# Screen: WAIT +   

+The WAIT screen will display which pins you need to connect between the flipper and the Wii Extension Controller. + +__Disclaimer:__ Use of this plugin, and notably connecting the Controller to the FlipperZero is performed entirely at your own risk. + +Looking in to the exposed side of the Extension Controller plug, with the notch on the bottom + +| EC Pin # | EC Position | EC Pin ID | Pin Function | FZ GPIO Pin Name | FZ GPIO Pin # | +| :---: | :---: | :---: | :---: | :---: | :---: | +| 1 | top-left | +3v3 | Power | 3v3 | 9 | +| 2 | bottom-left | SCL | i2c clock | C0 | 16 | +| 3 | top-centre | EN | ΒΏdetect? | | | +| 4 | bottom-centre | -x- | -none- | | | +| 5 | top-right | SDA | i2c data | C1 | 15 | +| 6 | bottom-right | Gnd | Power | Gnd | 18 | + +Keys: +* Left - Show splash screen +* Back - exit plugin + +The easiest way to connect a Wii Extension Controller to a FlipperZero is arguably with a ["WiiChuck"](https://www.ebay.co.uk/sch/?_nkw=wiichuck) or a ["Nunchucky"](https://www.solarbotics.com/product/31040)

+ + + + + +
WiiChuckNunchucky
+ +### ** WARNING ** +Neither the WiiChuck, nor the Nunchucky have a pin polarisation mechanism.
+If you plug the adaptor in the wrong way around you WILL apply voltage to the Controller the wrong way round!!
+I have no idea if THIS WILL PERMANENTLY KILL THE CONTROLLER ...Who wants to try it? + +On all the WiiChucks I have seen: +* The WiiChuck has THREE connectors on one side, and TWO connectors on the other. +* The side with TWO connectors should go against the side of the Controller plug with the big indent. +``` ++-------------+ +| _________ | +| | = = = | | +| |_=_____=_| | <-- notice missing pin +| ___ | +| | | | <-- notice indent ++----+ +----+ +``` +
+ +...BUT I *highly* recommend you check the pins on your adaptor to make sure everything goes well. + +I believe the unconnected pin on the top is a "presence detect" function, but I have not (yet) verified this.
+This feature is NOT required by this plugin, as the detection is performed by means of an i2c handshake. + +When a device is connected it will be immediately recognised. If it is not, either: +* The Controller is not correctly connected
+...This may be as simple as a broken wire. +* The controller board in the Controller is faulty.
+...Repair of which is beyond the scope of this document. + +To get the list of "known" Controllers, run `./info.sh`
+As of writing this, that returns: +```c +[PID_UNKNOWN ] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Unknown Perhipheral", SCENE_DUMP, +[PID_NUNCHUCK ] = { {0x00, 0x00, 0xA4, 0x20, 0x00, 0x00}, "Nunchuck", SCENE_NUNCHUCK, +[PID_CLASSIC ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x01}, "Classic Controller", SCENE_CLASSIC, +[PID_BALANCE ] = { {0x00, 0x00, 0xA4, 0x20, 0x04, 0x02}, "Balance Board", SCENE_DUMP, +[PID_GH_GUITAR ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero Guitar", SCENE_DUMP, +[PID_GH_DRUMS ] = { {0x01, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero World Tour Drums", SCENE_DUMP, +[PID_TURNTABLE ] = { {0x03, 0x00, 0xA4, 0x20, 0x01, 0x03}, "DJ Hero Turntable", SCENE_DUMP, +[PID_TAIKO_DRUMS] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x11}, "Taiko Drum Controller)", SCENE_DUMP, + +``` + +You can see that there are EIGHT known devices. One is the default for an unknown controller; SEVEN devices are known by name; and TWO (of those seven) have bespoke "scenes" (ie. SCENE_NUNCHUCK & SCENE_CLASSIC). + +# Screen: NUNCHUCK - MAIN +
+When you connect a Nunchuck, you will see a screen displaying: +* Accelerometer{X,Y,Z} values +* Joystick{X,Y} values +* Joystick graphic +* Button{C,Z} + +Keys: +* Left - Go to the DUMP screen +* Right - Go to the NUNCHUCK_ACC accelerometers screen +* Up/Down/OK - [qv. Peak Meters] +* Short-Back - Reset controller +* Long-Back - Exit plugin + +# Screen: NUNCHUCK - ACCELEROMETERS + +   
+ +| Axis | Movement | Lower | Higher | +| :---: | :---: | :---: | :---: | +| X | Left / Right | Left | Right | +| Y | Fwd / Bkwd | Fwd | Bkwd | +| Z | Down / Up | Down | Up | + +* Movement in the direction of an axis changes that axis reading +* Twisting/tilting around an axis changes the other two readings +* EG. + * Move left (along the X axis) will effect X + * Turn left (a rotation around the Y axis) will effect X and Z + +Keys: +* Left - go to the main NUNCHUCK screen +* Up + * Auto-Pause Disabled --> Enable Auto-Pause + * Paused at the end of a page --> Restart scanner + * Running with Auto-Pause Enabled --> Disable Auto-Pause +* Nunchuck-Z - Toggle pause +* Nunchuck-C - Toggle auto-pause +* Long-OK - Enter Software Calibration mode [qv. Calibration] + * Calibration mode on the Accelerometer screen will ONLY calibrate the accelerometer +* Short-OK - Leave Software Calibration mode *and* Calibrate CENTRE position(s) +* Short-Back - Reset controller +* Long-Back - Exit plugin + +NB. Code DOES exist to scroll the display, but the LCD refresh rate is too low, and it looks awful + +# Screen: CLASSIC +
+When you connect a Classic Controller [Pro], you will see a screen displaying a Classic Controller +* The Classic Controller will animate in line with controller events +* The scan rate is set to 30fps, but in reality there is a bit of lag with the LCD screen, so YMMV. + +Keys: +* Left - go to the DUMP screen +* Right - show analogue readings (Left to hide them again) +* Up/Down/OK - [qv. Peak Meters] +* Short-Back - Reset controller +* Long-Back - Exit plugin + +# Screen: DUMP +
+The Dump screen will show you the raw readings from the device.
+If you connect a device which does not have a bespoke `_decode()` function (etc.), you will see (only) this screen. +* SID - String ID - human-readable name (from the `info` table) +* PID - Peripheral ID - The 6 bytes which identify the device. +* Cal - Calibration data - 16 bytes +* The bottom row of hex shows the SIX bytes of Controller data + * Below each hex digit is the binary representation of that digit + * By example. With a Nunchuck connected, click the Z button, and watch the bit on the far right + +Keys: +* Right - return to controller-specific screen (if there is one) +* Short-Back - Reset controller +* Long-Back - Exit plugin + +# Peak Meters (Calibration values) + +On any Controller-specific screen with a Peak/Trough menu displayed: +* Up - [toggle] only show peak values +* Down - [toggle] only show trough values +* Long-OK - Enter Software Calibration mode [qv. Calibration] +* Short-OK - Leave Software Calibration mode / Calibrate CENTRE position(s) + +# Calibration +
+ +* __This project handles Calibration of Analogue Controls, but has NO understanding of Accelerometer values (yet).__ + +Digital buttons do NOT require Calibration. + +Some Calibration data is calculated at the factory, and stored in memory (ΒΏOTP?) on the Controller. + +Each device has a different way to interpret the Calibration Data.
+EG. A Nunchuck has one joystick, and an accelerometer ...whereas a Classic Controller has 2 joysticks and 2 analogue buttons. + +I have personally found the calibration data to be inaccurate (when compared to actual readings), I guess Controllers drift over the yearsβ€½ +If the factory-values LIMIT movement, this is easily resolved - by expanding them on-the-fly.
+BUT, I have seen Controllers with factory calibration data that suggests the limits are FURTHER than the joystick can reach ...and this requires a full re-calibration of the Controller! + +Probably the best way to calibrate is to: +* Take a/some reading(s) while the Controller is 'at rest', IE. perfectly still and level. +* Move the Controller to all extremes and store the extreme {peak/trough} values. + +Nintendo (allegedly) take the 'at rest' reading immediately after the Controller is connected, and a 're-calibration' can be performed at any time by pressing {`A`, `B`, `+`, `-`} at the same time, for at least 3 seconds. Although I have no details on what this actually does. + +### This tool calibrates as such: +* When the Controller is first recognised + * The factory Calibration data is used to decide the Centre/Middle position and extreme values (eg. far-left & far-right) for each analogue Control +* Long-OK button press (on the FlipperZero) ...Do NOT touch ANY of the analogue controllers while you are pressing Long-OK + * Start the calibrate button flashing + * Take the current reading as the Centre position + * Set the range limits to "no range" + * You must now move the Control between its extremes, so the code can work out the new Calibration/range/peak+trough values + * When done, press Short-OK to end Software Calibration mode +* Short-OK button press (on the FlipperZero) ...Do NOT touch ANY of the analogue controllers while you are pressing Short-OK + * Stop the calibrate button flashing + * Calibrate the centre position of all analogue controls (accelerometers not supported (yet)) + +# Screen: DEBUG +
+On any screen (except SPLASH) you may press Long-Down to enter Debug mode. + +You can (at any time) attach to the FlipperZero (via USB) with a serial console {`minicom`, `putty`, whatever} and start the `log` function to see the debug messages. + +When you enter the DEBUG screen, the real-time scanner will be stopped. And the following keys made available: +* Up - Attempt to initialise the attached Controller +* OK - Take a reading from the attached Controller +* Long-Down - Restart the real-time scanner and return to the WAIT screen + +You can limit the messages at compile-time [see `./info.sh`], or at runtime [FZ->Settings->System->LogLevel]
+ +[This is probably irrelevant since the introduction of FAP support]
+If you have memory issues, limiting the messages at compile-time will make the plugin smaller.
+But (ΒΏobviously?) the more you limit the messsages, the less debug information will be sent to the logger. + +# TODO + +* FZ Bug: At the time of writing this, there are problems with the i2c FZ functions [qv `i2c_workaround.c`] + diff --git a/applications/plugins/wii_ec_anal/README.txt b/applications/plugins/wii_ec_anal/README.txt new file mode 100644 index 000000000..e7ebe7a4c --- /dev/null +++ b/applications/plugins/wii_ec_anal/README.txt @@ -0,0 +1,67 @@ + ,-------. +---( Files )--- + `-------' + + README.md - User Manual : Body [github markdown] + _images/ - User Manual : Images + _images/GIMP/ - User Manual : GIMP image masters + + LICENSE - Tech Docs : MIT Licence file + README.txt - Tech Docs : Dev notes + notes.txt - Tech Docs : Random dev notes + info.sh - Tech Docs : Retrieve info from source code + + application.fam - FAP : Header file + WiiEC.png - FAP : Icon {10x10} + + gfx/ - Analyser : Images [generated by bc_image_tool] + wii_anal.c|h - Analyser : Main application + wii_anal_ec.c|h - Analyser : Extension controller actions + wii_anal_keys.c|h - Analyser : Keyboard handling + wii_anal_lcd.c|h - Analyser : LCD handling + + i2c_workaround.h - Temporary workaround for i2c bug in FZ code + err.h - Errors + bc_logging.h - Logging macros - especially LOG_LEVEL + + wii_i2c.c|h - i2c functionality + + wii_ec.c|h - Extension Controller basic functions + wii_ec_macros.h - Bespoke Extension Controller handy-dandy MACROs + wii_ec_classic.c|h - EC: Classic Controller Pro scene + wii_ec_nunchuck.c|h - EC: Nunchuck scene + wii_ec_udraw.c|h - EC: UDraw scene - not written + + ,----------------------------------. +---( Adding a new Extension Controller )--- + `----------------------------------' + +//! I'll finish this when I write the UDraw code + +Create a new Extension Controller called "mydev" + +Create wii_ec_mydev.c and wii_ec_mydev.h + +In wii_ec_mydev.c|h + Create the functions [& prototypes] + bool mydev_init (wiiEC_t* const) ; // Additional initialisation code + void mydev_decode (wiiEC_t* const) ; // Decode controller input data + void mydev_msg (wiiEC_t* const, FuriMessageQueue* const) ; // Put event messages in the event queue + void mydev_calib (wiiEC_t* const, ecCalib_t) ; // Controller calibration function + void mydev_show (Canvas* const, state_t* const) ; // Scene LCD display + bool mydev_key (const eventMsg_t* const, state_t* const) ; // Scene key controls + +In wii_ec.h + Include the new header + #include "wii_ec_mydev.h" + Add a perhipheral id to enum ecPid + PID_MYDEV + +In wii_anal.h + As a scene name to enum scene + SCENE_MYDEV + +In wii_ec.c + Add the device definition to the ecId[] array + [PID_MYDEV] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "My Device", SCENE_MYDEV, + mydev_init, mydev_decode, mydev_msg, mydev_calib, mydev_show, mydev_key }, diff --git a/applications/plugins/wii_ec_anal/WiiEC.png b/applications/plugins/wii_ec_anal/WiiEC.png new file mode 100644 index 000000000..6e1afcb0c Binary files /dev/null and b/applications/plugins/wii_ec_anal/WiiEC.png differ diff --git a/applications/plugins/wii_ec_anal/_image_tool/LICENSE b/applications/plugins/wii_ec_anal/_image_tool/LICENSE new file mode 100644 index 000000000..95e544a06 --- /dev/null +++ b/applications/plugins/wii_ec_anal/_image_tool/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 BlueChip + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/wii_ec_anal/_image_tool/README b/applications/plugins/wii_ec_anal/_image_tool/README new file mode 100644 index 000000000..979605a08 --- /dev/null +++ b/applications/plugins/wii_ec_anal/_image_tool/README @@ -0,0 +1,30 @@ +1. Prepare the image + a. Open your *black and white* image in GIMP + b. File -> Export As + filename: EXAMPLE.c + Type : "C source code" + [Export] + prefixed name: gimp_image + Comment : + [x] Use GLib types + [ ] <> + Opacity : 100% + [Export] + +2. Prepare conversion tool [stored in (eg.) /path/] + a. cp _convert*.* /path/ + b. cp EXAMPLE.c /path/ + +3. Run the conversion tool + a. cd /path/ + b. ./_convert.sh EXAMPLE.c + +4. All being well, you will see an ascii version of your image. + If not, then you're gonna have to submit a bug report + +5. You should now have a directory called img_/ + In that directory should be + img_EXAMPLE.c - The data for your new image + img_*.c - The data for other images + images.h - A header for ALL images that have been created in this directory + images.c - A sample FlipperZero show() function [not optimised] diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert.c b/applications/plugins/wii_ec_anal/_image_tool/_convert.c new file mode 100644 index 000000000..57deeb083 --- /dev/null +++ b/applications/plugins/wii_ec_anal/_image_tool/_convert.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + const unsigned char* pp = NULL; + uint32_t pix = 0; + int bit = 0; + + uint8_t b = 0; + uint8_t bcnt = 0; + + unsigned int lcnt = 0; + static const int lmax = 16; // max hex values per line + + uint8_t* buf = NULL; + uint8_t* bp = NULL; + unsigned int blen = 0; + + uint8_t* cmp = NULL; + uint8_t* cp = NULL; + unsigned int clen = 0; + uint8_t ctag = 0xFF; + uint32_t tag[256] = {0}; + uint32_t tmax = UINT32_MAX; + + unsigned int x, y, z; + + const char* name = argv[1]; + FILE* fh = fopen(argv[2], "wb"); + + uint32_t white = 0xFF; + + int rv = 0; // assume success + + // allocate buffers + blen = ((img.w * img.h) + 0x7) >> 3; + bp = (buf = calloc(blen + 1, 1)); + cp = (cmp = calloc(blen + 4, 1)); + + // sanity check + if(!fh || !buf || !cmp) { + printf("! fopen() or malloc() fail.\n"); + rv = 255; + goto bail; + } + + // Find white value + for(x = 1; x < img.bpp; x++) white = (white << 8) | 0xFF; + + // build bit pattern + // create the comment as we go + for(pp = img.b, y = 0; y < img.h; y++) { + fprintf(fh, "// "); + for(x = 0; x < img.w; x++) { + // read pixel + for(pix = 0, z = 0; z < img.bpp; pix = (pix << 8) | *pp++, z++) + ; + // get bit and draw + if(pix < white) { + b = (b << 1) | 1; + fprintf(fh, "##"); + } else { + b <<= 1; + fprintf(fh, ".."); + } + // got byte + if((++bcnt) == 8) { + *bp++ = b; + tag[b]++; + bcnt = (b = 0); + } + } + fprintf(fh, "\n"); + } + fprintf(fh, "\n"); + // padding + if(bcnt) { + b <<= (bcnt = 8 - bcnt); + *bp++ = b; + tag[b]++; + } + // Kill the compression + *bp = ~bp[-1]; // https://youtube.com/clip/Ugkx-JZIr16hETy7hz_H6yIdKPtxVe8C5w_V + + // Byte run length compression + // Find a good tag + for(x = 0; tmax && (x < 256); x++) { + if(tag[x] < tmax) { + tmax = tag[x]; + ctag = x; + } + } + + // compress the data + for(bp = buf, x = 0; (clen < blen) && (x < blen); x++) { + // need at least 4 the same to be worth it + // must compress tag (if it occurs) + if((bp[x] == bp[x + 1]) && (bp[x] == bp[x + 2]) && (bp[x] == bp[x + 3]) || + (bp[x] == ctag)) { + for(y = 1; (y < 255) && (bp[x] == bp[x + y]); y++) + ; + *cp++ = ctag; // tag + *cp++ = y; // length + *cp++ = bp[x]; // byte + x += y - 1; + clen += 3; + } else { + *cp++ = bp[x]; + clen++; + } + } + + // create struct + fprintf(fh, "#include \"images.h\"\n\n"); + fprintf(fh, "const image_t img_%s = { %d, %d, ", name, img.w, img.h); + + if(clen < blen) { // dump compressed? + fprintf( + fh, + "true, %d, 0x%02X, { // orig:%d, comp:%.2f%%\n\t", + clen, + ctag, + blen, + 100.0 - ((clen * 100.0) / blen)); + for(x = 0; x < clen; x++) + if(x == clen - 1) + fprintf(fh, "0x%02X\n}};\n", cmp[x]); + else + fprintf(fh, "0x%02X%s", cmp[x], (!((x + 1) % 16)) ? ",\n\t" : ", "); + + } else { // dump UNcompressed + fprintf(fh, "false, %d, 0, {\n\t", blen); + for(x = 0; x < blen; x++) + if(x == blen - 1) + fprintf(fh, "0x%02X\n}};\n", buf[x]); + else + fprintf(fh, "0x%02X%s", buf[x], (!((x + 1) % 16)) ? ",\n\t" : ", "); + } + +bail: + if(fh) fclose(fh); + if(buf) free(buf); + if(cmp) free(cmp); + + return rv; +} diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert.sh b/applications/plugins/wii_ec_anal/_image_tool/_convert.sh new file mode 100644 index 000000000..aaa7977b5 --- /dev/null +++ b/applications/plugins/wii_ec_anal/_image_tool/_convert.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +[ -z $1 ] && { + echo "Specify an image" + echo "gimp -> export -> c source file -> [x] gunit names" + exit 2 +} + +echo $* + +for N in $* ; do + + [ ! -f $N ] && { + echo "!! File missing $N" + continue + } + + # filename (sans extension) + FN=$(basename -- "$N") + EXT="${FN##*.}" + NAME="${FN%.*}" + + OUTDIR=img_/ + mkdir -p ${OUTDIR} + + HDR=${OUTDIR}/images.h + SRC=${OUTDIR}/images.c + + OUT=${OUTDIR}/img_${NAME}.c + + echo -e "\nΒ¦${N}Β¦ == Β¦${NAME}Β¦ -> Β¦${OUT}Β¦" + + TESTX=test_${NAME} + TESTC=test_${NAME}.c + + # compile name + CONV=${NAME}_ + + # clean up gimp output + sed -e "s/gimp_image/img/g" \ + -e 's/guint8/unsigned char/g' \ + -e 's/width/w/g' \ + -e 's/height/h/g' \ + -e 's/bytes_per_pixel/bpp/g' \ + -e 's/pixel_data/b/g' \ + -e 's/guint/unsigned int/g' \ + $N \ + | grep -v ^/ \ + | grep -v ^$ \ + > ${CONV}.c + + # append conversion code + cat _convert.c >> ${CONV}.c + + # compile & run converter + rm -f ${CONV} + gcc ${CONV}.c -DIMGTEST -o ${CONV} + ./${CONV} ${NAME} ${OUT} + rm -f ${CONV} ${CONV}.c + + # (create &) update header + [[ ! -f ${HDR} ]] && cp _convert_images.h ${HDR} + sed -i "/ img_${NAME};/d" ${HDR} + sed -i "s#//\[TAG\]#//\[TAG\]\nextern const image_t img_${NAME};#" ${HDR} + + # sample FZ code + [[ ! -f images.c ]] && cp _convert_images.c ${SRC} + + # test + ROOT=${PWD} + pushd ${OUTDIR} >/dev/null + sed "s/zzz/${NAME}/" ${ROOT}/_convert_test.c > ${TESTC} + rm -f ${TESTX} + gcc ${TESTC} ${OUT##*/} -DIMGTEST -o ${TESTX} + ./${TESTX} + rm -f ${TESTX} ${TESTC} + popd >/dev/null + +done diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_images.c b/applications/plugins/wii_ec_anal/_image_tool/_convert_images.c new file mode 100644 index 000000000..e8ab899f7 --- /dev/null +++ b/applications/plugins/wii_ec_anal/_image_tool/_convert_images.c @@ -0,0 +1,137 @@ +#include // GUI (screen/keyboard) API + +#include "images.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +static Canvas* _canvas; +static uint8_t _tlx; +static uint8_t _tly; + +static uint8_t _x; +static uint8_t _y; + +static const image_t* _img; + +static bool _blk; +static Color _set; +static Color _clr; + +//+============================================================================ +static void _showByteSet(const uint8_t b) { + for(uint8_t m = 0x80; m; m >>= 1) { + if(b & m) // plot only SET bits + canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); + if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; + } +} + +//+============================================================================ +static void _showByteClr(const uint8_t b) { + for(uint8_t m = 0x80; m; m >>= 1) { + if(!(b & m)) // plot only CLR bits + canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); + if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; + } +} + +//+============================================================================ +static void _showByteAll(const uint8_t b) { + for(uint8_t m = 0x80; m; m >>= 1) { + if((!!(b & m)) ^ _blk) { // Change colour only when required + canvas_set_color(_canvas, ((b & m) ? _set : _clr)); + _blk = !_blk; + } + canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); + if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; + } +} + +//+============================================================================ +// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK +// SHOW_XOR - same as SET_BLACK +// SHOW_SET_WHT - plot image pixels that are SET in WHITE +// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK +// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE +// SHOW_ALL - plot all images pixels as they are +// SHOW_ALL_INV - plot all images pixels inverted +// +void show( + Canvas* const canvas, + const uint8_t tlx, + const uint8_t tly, + const image_t* img, + const showMode_t mode) { + void (*fnShow)(const uint8_t) = NULL; + + const uint8_t* bp = img->data; + + // code size optimisation + switch(mode & SHOW_INV_) { + case SHOW_NRM_: + _set = ColorBlack; + _clr = ColorWhite; + break; + + case SHOW_INV_: + _set = ColorWhite; + _clr = ColorBlack; + break; + + case SHOW_BLK_: + canvas_set_color(canvas, ColorBlack); + break; + + case SHOW_WHT_: + canvas_set_color(canvas, ColorWhite); + break; + } + switch(mode & SHOW_INV_) { + case SHOW_NRM_: + case SHOW_INV_: + fnShow = _showByteAll; + canvas_set_color(canvas, ColorWhite); + _blk = 0; + break; + + case SHOW_BLK_: + case SHOW_WHT_: + switch(mode & SHOW_ALL_) { + case SHOW_SET_: + fnShow = _showByteSet; + break; + case SHOW_CLR_: + fnShow = _showByteClr; + break; + } + break; + } + furi_check(fnShow); + + // I want nested functions! + _canvas = canvas; + _img = img; + _tlx = tlx; + _tly = tly; + _x = 0; + _y = 0; + + // Compressed + if(img->c) { + for(unsigned int i = 0; i < img->len; i++, bp++) { + // Compressed data? {tag, length, value} + if(*bp == img->tag) { + for(uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]); + bp += 3 - 1; + i += 3 - 1; + + // Uncompressed byte + } else { + fnShow(*bp); + } + } + + // Not compressed + } else { + for(unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp); + } +} diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_images.h b/applications/plugins/wii_ec_anal/_image_tool/_convert_images.h new file mode 100644 index 000000000..1743cb409 --- /dev/null +++ b/applications/plugins/wii_ec_anal/_image_tool/_convert_images.h @@ -0,0 +1,53 @@ +#ifndef IMAGES_H_ +#define IMAGES_H_ + +#include +#include + +//----------------------------------------------------------------------------- ---------------------------------------- +typedef enum showMode { + // {INV:--:WHT:BLK::--:--:CLR:SET} + SHOW_SET_ = 0x01, + SHOW_CLR_ = 0x02, + SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_, + + SHOW_BLK_ = 0x10, + SHOW_WHT_ = 0x20, + SHOW_NRM_ = 0x00, + SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_, + + SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_, + SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_, + + SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_, + SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_, + + SHOW_ALL = SHOW_ALL_ | SHOW_NRM_, + SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_, +} showMode_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +typedef struct image { + uint8_t w; // width + uint8_t h; // height + bool c; // compressed? + uint16_t len; // image data length + uint8_t tag; // rle tag + uint8_t data[]; // image data +} image_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +//[TAG] + +//----------------------------------------------------------------------------- ---------------------------------------- +#ifndef IMGTEST +#include +void show( + Canvas* const canvas, + const uint8_t tlx, + const uint8_t tly, + const image_t* img, + const showMode_t mode); +#endif + +#endif //IMAGES_H_ diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_test.c b/applications/plugins/wii_ec_anal/_image_tool/_convert_test.c new file mode 100644 index 000000000..fdc2ee946 --- /dev/null +++ b/applications/plugins/wii_ec_anal/_image_tool/_convert_test.c @@ -0,0 +1,59 @@ +#include +#include + +#include "images.h" + +//----------------------------------------------------------------------------- +// This will be the plot function out of your graphics library +// +#define PLOT(x, y, c) \ + do { \ + printf("%s", (c ? "#" : ".")); \ + if(x == img->w - 1) printf("\n"); \ + } while(0) + +//+============================================================================ +// The pain we endure to avoid code duplication cleanly +// +#define PLOTBYTE(b) \ + do { \ + for(uint8_t m = 0x80; m; m >>= 1) { \ + PLOT(x, y, (b & m)); \ + if(((++x) == img->w) && !(x = 0) && ((++y) == img->h)) break; \ + } \ + } while(0) + +void show(const image_t* img) { + // Some variables + const uint8_t* bp = img->data; + unsigned int x = 0; + unsigned int y = 0; + + // Compressed + if(img->c) { + for(unsigned int i = 0; i < img->len; i++, bp++) { + // Compressed data? {tag, length, value} + if(*bp == img->tag) { + for(uint16_t c = 0; c < bp[1]; c++) PLOTBYTE(bp[2]); + bp += 3 - 1; + i += 3 - 1; + + // Uncompressed byte + } else { + PLOTBYTE(*bp); + } + } + + // Not compressed + } else { + for(unsigned int i = 0; i < img->len; i++, bp++) PLOTBYTE(*bp); + } +} + +#undef PLOTBYTE + +//+============================================================================ +int main(void) { + show(&img_zzz); + return 0; +} diff --git a/applications/plugins/wii_ec_anal/_images/CLASSIC.png b/applications/plugins/wii_ec_anal/_images/CLASSIC.png new file mode 100644 index 000000000..aa5318b33 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/CLASSIC.png differ diff --git a/applications/plugins/wii_ec_anal/_images/CLASSIC_N.png b/applications/plugins/wii_ec_anal/_images/CLASSIC_N.png new file mode 100644 index 000000000..24f4ac225 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/CLASSIC_N.png differ diff --git a/applications/plugins/wii_ec_anal/_images/DEBUG.png b/applications/plugins/wii_ec_anal/_images/DEBUG.png new file mode 100644 index 000000000..bca35c693 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/DEBUG.png differ diff --git a/applications/plugins/wii_ec_anal/_images/DUMP.png b/applications/plugins/wii_ec_anal/_images/DUMP.png new file mode 100644 index 000000000..dc9328aab Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/DUMP.png differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf new file mode 100644 index 000000000..67f70139e Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/RIP.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/RIP.xcf new file mode 100644 index 000000000..0058fe9c8 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/RIP.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/Wiring.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/Wiring.xcf new file mode 100644 index 000000000..aa8078db8 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/Wiring.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/classic.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/classic.xcf new file mode 100644 index 000000000..6fd152675 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/classic.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/csLogo.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/csLogo.xcf new file mode 100644 index 000000000..f4e33844a Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/csLogo.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/fonts.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/fonts.xcf new file mode 100644 index 000000000..d05d03fc7 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/fonts.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/frame.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/frame.xcf new file mode 100644 index 000000000..31705cf72 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/frame.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/port.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/port.xcf new file mode 100644 index 000000000..10fcd2de2 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/port.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/GIMP/social.xcf b/applications/plugins/wii_ec_anal/_images/GIMP/social.xcf new file mode 100644 index 000000000..377eaa63b Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/GIMP/social.xcf differ diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK.png b/applications/plugins/wii_ec_anal/_images/NUNCHUCK.png new file mode 100644 index 000000000..bc31ae386 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK.png differ diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK_acc.png b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_acc.png new file mode 100644 index 000000000..895c85e4c Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_acc.png differ diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK_anal.png b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_anal.png new file mode 100644 index 000000000..e821d7ee2 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_anal.png differ diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.gif b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.gif new file mode 100644 index 000000000..72d807a54 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.gif differ diff --git a/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.png b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.png new file mode 100644 index 000000000..f9d34bb93 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/NUNCHUCK_cal.png differ diff --git a/applications/plugins/wii_ec_anal/_images/Nunchucky.png b/applications/plugins/wii_ec_anal/_images/Nunchucky.png new file mode 100644 index 000000000..3af395da6 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/Nunchucky.png differ diff --git a/applications/plugins/wii_ec_anal/_images/RIP.png b/applications/plugins/wii_ec_anal/_images/RIP.png new file mode 100644 index 000000000..0acfe0c00 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/RIP.png differ diff --git a/applications/plugins/wii_ec_anal/_images/SPLASH.png b/applications/plugins/wii_ec_anal/_images/SPLASH.png new file mode 100644 index 000000000..a5c3f093a Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/SPLASH.png differ diff --git a/applications/plugins/wii_ec_anal/_images/WAIT.png b/applications/plugins/wii_ec_anal/_images/WAIT.png new file mode 100644 index 000000000..776edc3f1 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/WAIT.png differ diff --git a/applications/plugins/wii_ec_anal/_images/WiiChuck.png b/applications/plugins/wii_ec_anal/_images/WiiChuck.png new file mode 100644 index 000000000..532ce3096 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/WiiChuck.png differ diff --git a/applications/plugins/wii_ec_anal/_images/Wiring.png b/applications/plugins/wii_ec_anal/_images/Wiring.png new file mode 100644 index 000000000..300c07ee4 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/Wiring.png differ diff --git a/applications/plugins/wii_ec_anal/_images/plug.png b/applications/plugins/wii_ec_anal/_images/plug.png new file mode 100644 index 000000000..c418f43b1 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/plug.png differ diff --git a/applications/plugins/wii_ec_anal/_images/social.png b/applications/plugins/wii_ec_anal/_images/social.png new file mode 100644 index 000000000..1d3eddcc5 Binary files /dev/null and b/applications/plugins/wii_ec_anal/_images/social.png differ diff --git a/applications/plugins/wii_ec_anal/application.fam b/applications/plugins/wii_ec_anal/application.fam new file mode 100644 index 000000000..ac11d260e --- /dev/null +++ b/applications/plugins/wii_ec_anal/application.fam @@ -0,0 +1,36 @@ +# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md + +App( + # --- App Info + appid="wii_ec_anal", + name="Wii EC Analyser", + + # --- Entry point + apptype=FlipperAppType.EXTERNAL, + entry_point="wii_ec_anal", + + # --- Interaction + cdefines=["APP_WII_EC_ANAL"], + requires=[ + "gui", + ], + +# conflicts="", +# sdk_headers="", + + # --- Run-time info + stack_size=2 * 1024, + order=20, + + # --- FAP details + sources=["wii_*.c", "gfx/*.c"], + +# fap_weburl="https://github.com/csBlueChip/FlipperZero_plugin_WiiChuck/", +# fap_author="BlueChip", + +# fap_description="Wii Extension Controller Protocol Analyser", +# fap_version=(1,0), + + fap_icon="WiiEC.png", + fap_category="Misc", +) diff --git a/applications/plugins/wii_ec_anal/bc_logging.h b/applications/plugins/wii_ec_anal/bc_logging.h new file mode 100644 index 000000000..73dda80bd --- /dev/null +++ b/applications/plugins/wii_ec_anal/bc_logging.h @@ -0,0 +1,70 @@ +#ifndef BC_LOGGING_H_ +#define BC_LOGGING_H_ + +#include +#include "err.h" // appName + +//! WARNING: There is a bug in Furi such that if you crank LOG_LEVEL up to 6=TRACE +//! AND you have menu->settings->system->logLevel = trace +//! THEN this program will cause the FZ to crash when the plugin exits! +#define LOG_LEVEL 4 + +//----------------------------------------------------------------------------- ---------------------------------------- +// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time +// ... LOG_LEVEL lets you limit it at COMPILE-time +// +// FURI logging has 6 levels (numbered 1 thru 6} +// 1. None +// 2. Errors FURI_LOG_E +// 3. Warnings FURI_LOG_W +// 4. Information FURI_LOG_I +// 5. Debug FURI_LOG_D +// 6. Trace FURI_LOG_T +// +// --> furi/core/log.h +// + +// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time +// This lets you limit it at COMPILE-time +#ifndef LOG_LEVEL +#define LOG_LEVEL 6 // default = full logging +#endif + +#if(LOG_LEVEL < 2) +#undef FURI_LOG_E +#define FURI_LOG_E(tag, fmt, ...) +#endif + +#if(LOG_LEVEL < 3) +#undef FURI_LOG_W +#define FURI_LOG_W(tag, fmt, ...) +#endif + +#if(LOG_LEVEL < 4) +#undef FURI_LOG_I +#define FURI_LOG_I(tag, fmt, ...) +#endif + +#if(LOG_LEVEL < 5) +#undef FURI_LOG_D +#define FURI_LOG_D(tag, fmt, ...) +#endif + +#if(LOG_LEVEL < 6) +#undef FURI_LOG_T +#define FURI_LOG_T(tag, fmt, ...) +#endif + +//---------------------------------------------------------- +// Logging helper macros +// +#define ERROR(fmt, ...) FURI_LOG_E(appName, fmt __VA_OPT__(, ) __VA_ARGS__) +#define WARN(fmt, ...) FURI_LOG_W(appName, fmt __VA_OPT__(, ) __VA_ARGS__) +#define INFO(fmt, ...) FURI_LOG_I(appName, fmt __VA_OPT__(, ) __VA_ARGS__) +#define DEBUG(fmt, ...) FURI_LOG_D(appName, fmt __VA_OPT__(, ) __VA_ARGS__) +#define TRACE(fmt, ...) FURI_LOG_T(appName, fmt __VA_OPT__(, ) __VA_ARGS__) + +#define ENTER TRACE("(+) %s", __func__) +#define LEAVE TRACE("(-) %s", __func__) + +#endif //BC_LOGGING_H_ diff --git a/applications/plugins/wii_ec_anal/err.h b/applications/plugins/wii_ec_anal/err.h new file mode 100644 index 000000000..5a25c93f8 --- /dev/null +++ b/applications/plugins/wii_ec_anal/err.h @@ -0,0 +1,72 @@ +// Avoid circular/nested/mulitple inclusion +#ifndef ERR_H_ +#define ERR_H_ + +//----------------------------------------------------------------------------- ---------------------------------------- +// Application name +// +static const char* const appName = "Wii_i2c"; //$ Name used in log files + +//----------------------------------------------------------------------------- ---------------------------------------- +// Error codes and messages +// + +// You should only ever (need to) edit this list +// ...Watch out for extraneous whitespace after the terminating backslashes +#define FOREACH_ES(esPrial) \ + /* The first line MUST define 'ERR_OK = 0' */ \ + esPrial(0, ERR_OK, "OK (no error)") \ + \ + esPrial(1, ERR_MALLOC_QUEUE, "malloc() fail - queue") esPrial( \ + 2, \ + ERR_MALLOC_STATE, \ + "malloc() fail - state") esPrial(3, ERR_MALLOC_TEXT, "malloc() fail - text") \ + esPrial(4, ERR_MALLOC_VIEW, "malloc() fail - viewport") esPrial( \ + 5, ERR_NO_MUTEX, "Cannot create mutex") esPrial(6, ERR_NO_GUI, "Cannot open GUI") \ + esPrial(7, ERR_NO_TIMER, "Cannot create timer") esPrial( \ + 8, ERR_NO_NOTIFY, "Cannot acquire notifications handle") \ + \ + esPrial(10, ERR_MUTEX_BLOCK, "Mutex block failed") esPrial( \ + 11, ERR_MUTEX_RELEASE, "Mutex release failed") \ + \ + esPrial(20, ERR_QUEUE_RTOS, "queue - Undefined RTOS error") \ + esPrial(21, DEBUG_QUEUE_TIMEOUT, "queue - Timeout") esPrial( \ + 22, ERR_QUEUE_RESOURCE, "queue - Resource not available") \ + esPrial(23, ERR_QUEUE_BADPRM, "queue - Bad parameter") esPrial( \ + 24, ERR_QUEUE_NOMEM, "queue - Out of memory") \ + esPrial(25, ERR_QUEUE_ISR, "queue - Banned in ISR") esPrial( \ + 26, ERR_QUEUE_UNK, "queue - Unknown") \ + \ + esPrial(30, WARN_SCAN_START, "Scan - Already started") \ + esPrial(31, WARN_SCAN_STOP, "Scan - Already stopped") \ + esPrial( \ + 32, \ + ERR_TIMER_START, \ + "Scan - Cannot start timer") \ + esPrial( \ + 33, \ + ERR_TIMER_STOP, \ + "Scan - Cannot stop timer") //[EOT] + +// Declare list extraction macros +#define ES_ENUM(num, ename, string) ename = num, +#define ES_STRING(num, ename, string) string "\r\n", + +// Build the enum +typedef enum err { FOREACH_ES(ES_ENUM) } err_t; + +// You need to '#define ERR_C_' in precisely ONE source file +#ifdef ERR_C_ +// Build the string list +const char* const wii_errs[] = {FOREACH_ES(ES_STRING)}; +#else +// Give access to string list +extern const char* const wii_errs[]; +#endif + +// This is a header file, clean up +#undef ES_ENUM +#undef ES_STRING +#undef FOREACH_ES + +#endif // ERR_H_ diff --git a/applications/plugins/wii_ec_anal/gfx/images.c b/applications/plugins/wii_ec_anal/gfx/images.c new file mode 100644 index 000000000..e8ab899f7 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/images.c @@ -0,0 +1,137 @@ +#include // GUI (screen/keyboard) API + +#include "images.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +static Canvas* _canvas; +static uint8_t _tlx; +static uint8_t _tly; + +static uint8_t _x; +static uint8_t _y; + +static const image_t* _img; + +static bool _blk; +static Color _set; +static Color _clr; + +//+============================================================================ +static void _showByteSet(const uint8_t b) { + for(uint8_t m = 0x80; m; m >>= 1) { + if(b & m) // plot only SET bits + canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); + if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; + } +} + +//+============================================================================ +static void _showByteClr(const uint8_t b) { + for(uint8_t m = 0x80; m; m >>= 1) { + if(!(b & m)) // plot only CLR bits + canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); + if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; + } +} + +//+============================================================================ +static void _showByteAll(const uint8_t b) { + for(uint8_t m = 0x80; m; m >>= 1) { + if((!!(b & m)) ^ _blk) { // Change colour only when required + canvas_set_color(_canvas, ((b & m) ? _set : _clr)); + _blk = !_blk; + } + canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); + if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; + } +} + +//+============================================================================ +// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK +// SHOW_XOR - same as SET_BLACK +// SHOW_SET_WHT - plot image pixels that are SET in WHITE +// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK +// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE +// SHOW_ALL - plot all images pixels as they are +// SHOW_ALL_INV - plot all images pixels inverted +// +void show( + Canvas* const canvas, + const uint8_t tlx, + const uint8_t tly, + const image_t* img, + const showMode_t mode) { + void (*fnShow)(const uint8_t) = NULL; + + const uint8_t* bp = img->data; + + // code size optimisation + switch(mode & SHOW_INV_) { + case SHOW_NRM_: + _set = ColorBlack; + _clr = ColorWhite; + break; + + case SHOW_INV_: + _set = ColorWhite; + _clr = ColorBlack; + break; + + case SHOW_BLK_: + canvas_set_color(canvas, ColorBlack); + break; + + case SHOW_WHT_: + canvas_set_color(canvas, ColorWhite); + break; + } + switch(mode & SHOW_INV_) { + case SHOW_NRM_: + case SHOW_INV_: + fnShow = _showByteAll; + canvas_set_color(canvas, ColorWhite); + _blk = 0; + break; + + case SHOW_BLK_: + case SHOW_WHT_: + switch(mode & SHOW_ALL_) { + case SHOW_SET_: + fnShow = _showByteSet; + break; + case SHOW_CLR_: + fnShow = _showByteClr; + break; + } + break; + } + furi_check(fnShow); + + // I want nested functions! + _canvas = canvas; + _img = img; + _tlx = tlx; + _tly = tly; + _x = 0; + _y = 0; + + // Compressed + if(img->c) { + for(unsigned int i = 0; i < img->len; i++, bp++) { + // Compressed data? {tag, length, value} + if(*bp == img->tag) { + for(uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]); + bp += 3 - 1; + i += 3 - 1; + + // Uncompressed byte + } else { + fnShow(*bp); + } + } + + // Not compressed + } else { + for(unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp); + } +} diff --git a/applications/plugins/wii_ec_anal/gfx/images.h b/applications/plugins/wii_ec_anal/gfx/images.h new file mode 100644 index 000000000..d21909176 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/images.h @@ -0,0 +1,134 @@ +#ifndef IMAGES_H_ +#define IMAGES_H_ + +#include +#include + +//----------------------------------------------------------------------------- ---------------------------------------- +typedef enum showMode { + // {INV:--:WHT:BLK::--:--:CLR:SET} + SHOW_SET_ = 0x01, + SHOW_CLR_ = 0x02, + SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_, + + SHOW_BLK_ = 0x10, + SHOW_WHT_ = 0x20, + SHOW_NRM_ = 0x00, + SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_, + + SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_, + SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_, + + SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_, + SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_, + + SHOW_ALL = SHOW_ALL_ | SHOW_NRM_, + SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_, +} showMode_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +typedef struct image { + uint8_t w; // width + uint8_t h; // height + bool c; // compressed? + uint16_t len; // image data length + uint8_t tag; // rle tag + uint8_t data[]; // image data +} image_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +//[TAG] +extern const image_t img_csLogo_Small; +extern const image_t img_3x5_v; +extern const image_t img_3x5_9; +extern const image_t img_3x5_8; +extern const image_t img_3x5_7; +extern const image_t img_3x5_6; +extern const image_t img_3x5_5; +extern const image_t img_3x5_4; +extern const image_t img_3x5_3; +extern const image_t img_3x5_2; +extern const image_t img_3x5_1; +extern const image_t img_3x5_0; +extern const image_t img_key_Ui; +extern const image_t img_key_OKi; +extern const image_t img_RIP; +extern const image_t img_cc_trg_R4; +extern const image_t img_cc_trg_R3; +extern const image_t img_cc_trg_R2; +extern const image_t img_cc_trg_R1; +extern const image_t img_cc_trg_L4; +extern const image_t img_cc_trg_L3; +extern const image_t img_cc_trg_L2; +extern const image_t img_cc_trg_L1; +extern const image_t img_cc_Joy; +extern const image_t img_cc_Main; +extern const image_t img_cc_Cable; +extern const image_t img_key_Back; +extern const image_t img_key_OK; +extern const image_t img_6x8_Z; +extern const image_t img_6x8_Y; +extern const image_t img_6x8_X; +extern const image_t img_key_U; +extern const image_t img_key_D; +extern const image_t img_csLogo_FULL; +extern const image_t img_6x8_7; +extern const image_t img_key_R; +extern const image_t img_key_L; +extern const image_t img_5x7_7; +extern const image_t img_5x7_F; +extern const image_t img_5x7_E; +extern const image_t img_5x7_D; +extern const image_t img_5x7_C; +extern const image_t img_5x7_B; +extern const image_t img_5x7_A; +extern const image_t img_5x7_9; +extern const image_t img_5x7_8; +extern const image_t img_5x7_6; +extern const image_t img_5x7_5; +extern const image_t img_5x7_4; +extern const image_t img_5x7_3; +extern const image_t img_5x7_2; +extern const image_t img_5x7_1; +extern const image_t img_5x7_0; +extern const image_t img_6x8_v; +extern const image_t img_6x8_n; +extern const image_t img_6x8_G; +extern const image_t img_6x8_F; +extern const image_t img_6x8_E; +extern const image_t img_6x8_d; +extern const image_t img_6x8_C; +extern const image_t img_6x8_B; +extern const image_t img_6x8_A; +extern const image_t img_6x8_9; +extern const image_t img_6x8_8; +extern const image_t img_6x8_6; +extern const image_t img_6x8_5; +extern const image_t img_6x8_4; +extern const image_t img_6x8_3; +extern const image_t img_6x8_2; +extern const image_t img_6x8_1; +extern const image_t img_6x8_0; +extern const image_t img_ecp_SDA; +extern const image_t img_ecp_SCL; +extern const image_t img_ecp_port; +extern const image_t img_cc_pad_UD1; +extern const image_t img_cc_pad_LR1; +extern const image_t img_cc_btn_Y1; +extern const image_t img_cc_btn_X1; +extern const image_t img_cc_btn_B1; +extern const image_t img_cc_btn_A1; +extern const image_t img_6x8_D; + +//----------------------------------------------------------------------------- ---------------------------------------- +#ifndef IMGTEST +#include +void show( + Canvas* const canvas, + const uint8_t tlx, + const uint8_t tly, + const image_t* img, + const showMode_t mode); +#endif + +#endif //IMAGES_H_ diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_0.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_0.c new file mode 100644 index 000000000..8fc8e0e14 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_0.c @@ -0,0 +1,9 @@ +// ###### +// ##..## +// ##..## +// ##..## +// ###### + +#include "images.h" + +const image_t img_3x5_0 = {3, 5, false, 2, 0, {0xF6, 0xDE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_1.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_1.c new file mode 100644 index 000000000..8b7d4cf80 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_1.c @@ -0,0 +1,9 @@ +// ####.. +// ..##.. +// ..##.. +// ..##.. +// ###### + +#include "images.h" + +const image_t img_3x5_1 = {3, 5, false, 2, 0, {0xC9, 0x2E}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_2.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_2.c new file mode 100644 index 000000000..89a81c75e --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_2.c @@ -0,0 +1,9 @@ +// ###### +// ....## +// ###### +// ##.... +// ###### + +#include "images.h" + +const image_t img_3x5_2 = {3, 5, false, 2, 0, {0xE7, 0xCE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_3.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_3.c new file mode 100644 index 000000000..97ff0478a --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_3.c @@ -0,0 +1,9 @@ +// ###### +// ....## +// ..#### +// ....## +// ###### + +#include "images.h" + +const image_t img_3x5_3 = {3, 5, false, 2, 0, {0xE5, 0x9E}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_4.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_4.c new file mode 100644 index 000000000..2bbd9ef42 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_4.c @@ -0,0 +1,9 @@ +// ##.... +// ##..## +// ###### +// ....## +// ....## + +#include "images.h" + +const image_t img_3x5_4 = {3, 5, false, 2, 0, {0x97, 0x92}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_5.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_5.c new file mode 100644 index 000000000..e0466f37a --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_5.c @@ -0,0 +1,9 @@ +// ###### +// ##.... +// ###### +// ....## +// ###### + +#include "images.h" + +const image_t img_3x5_5 = {3, 5, false, 2, 0, {0xF3, 0x9E}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_6.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_6.c new file mode 100644 index 000000000..1b62caf72 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_6.c @@ -0,0 +1,9 @@ +// ####.. +// ##.... +// ###### +// ##..## +// ###### + +#include "images.h" + +const image_t img_3x5_6 = {3, 5, false, 2, 0, {0xD3, 0xDE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_7.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_7.c new file mode 100644 index 000000000..acfe57cf8 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_7.c @@ -0,0 +1,9 @@ +// ###### +// ....## +// ..##.. +// ..##.. +// ..##.. + +#include "images.h" + +const image_t img_3x5_7 = {3, 5, false, 2, 0, {0xE5, 0x24}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_8.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_8.c new file mode 100644 index 000000000..31f32af52 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_8.c @@ -0,0 +1,9 @@ +// ###### +// ##..## +// ###### +// ##..## +// ###### + +#include "images.h" + +const image_t img_3x5_8 = {3, 5, false, 2, 0, {0xF7, 0xDE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_9.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_9.c new file mode 100644 index 000000000..4b1ba1e09 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_9.c @@ -0,0 +1,9 @@ +// ###### +// ##..## +// ###### +// ....## +// ..#### + +#include "images.h" + +const image_t img_3x5_9 = {3, 5, false, 2, 0, {0xF7, 0x96}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_v.c b/applications/plugins/wii_ec_anal/gfx/img_3x5_v.c new file mode 100644 index 000000000..2282e1697 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_3x5_v.c @@ -0,0 +1,9 @@ +// ...... +// ...... +// ##..## +// ##..## +// ..##.. + +#include "images.h" + +const image_t img_3x5_v = {3, 5, false, 2, 0, {0x02, 0xD4}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_0.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_0.c new file mode 100644 index 000000000..7ae2186b3 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_0.c @@ -0,0 +1,11 @@ +// ..######.. +// ##......## +// ##....#### +// ##..##..## +// ####....## +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_0 = {5, 7, false, 5, 0, {0x74, 0x67, 0x5C, 0xC5, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_1.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_1.c new file mode 100644 index 000000000..c1a9cec74 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_1.c @@ -0,0 +1,11 @@ +// ..####.... +// ##..##.... +// ....##.... +// ....##.... +// ....##.... +// ....##.... +// ########## + +#include "images.h" + +const image_t img_5x7_1 = {5, 7, false, 5, 0, {0x65, 0x08, 0x42, 0x13, 0xE0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_2.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_2.c new file mode 100644 index 000000000..7fab90010 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_2.c @@ -0,0 +1,11 @@ +// ..######.. +// ##......## +// ........## +// ......##.. +// ....##.... +// ..##...... +// ########## + +#include "images.h" + +const image_t img_5x7_2 = {5, 7, false, 5, 0, {0x74, 0x42, 0x22, 0x23, 0xE0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_3.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_3.c new file mode 100644 index 000000000..2099bf795 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_3.c @@ -0,0 +1,11 @@ +// ..######.. +// ##......## +// ........## +// ....####.. +// ........## +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_3 = {5, 7, false, 5, 0, {0x74, 0x42, 0x60, 0xC5, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_4.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_4.c new file mode 100644 index 000000000..1eee4f07d --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_4.c @@ -0,0 +1,11 @@ +// ##........ +// ##........ +// ##....##.. +// ##....##.. +// ########## +// ......##.. +// ......##.. + +#include "images.h" + +const image_t img_5x7_4 = {5, 7, false, 5, 0, {0x84, 0x25, 0x2F, 0x88, 0x40}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_5.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_5.c new file mode 100644 index 000000000..be1e54681 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_5.c @@ -0,0 +1,11 @@ +// ########## +// ##........ +// ##........ +// ########.. +// ........## +// ........## +// ########.. + +#include "images.h" + +const image_t img_5x7_5 = {5, 7, false, 5, 0, {0xFC, 0x21, 0xE0, 0x87, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_6.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_6.c new file mode 100644 index 000000000..da155c1b5 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_6.c @@ -0,0 +1,11 @@ +// ..######.. +// ##........ +// ##........ +// ########.. +// ##......## +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_6 = {5, 7, false, 5, 0, {0x74, 0x21, 0xE8, 0xC5, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_7.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_7.c new file mode 100644 index 000000000..fde7e8ea2 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_7.c @@ -0,0 +1,11 @@ +// ########## +// ........## +// ......##.. +// ......##.. +// ....##.... +// ....##.... +// ....##.... + +#include "images.h" + +const image_t img_5x7_7 = {5, 7, false, 5, 0, {0xF8, 0x44, 0x22, 0x10, 0x80}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_8.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_8.c new file mode 100644 index 000000000..aff178282 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_8.c @@ -0,0 +1,11 @@ +// ..######.. +// ##......## +// ##......## +// ..######.. +// ##......## +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_8 = {5, 7, false, 5, 0, {0x74, 0x62, 0xE8, 0xC5, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_9.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_9.c new file mode 100644 index 000000000..2417c57e8 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_9.c @@ -0,0 +1,11 @@ +// ..######.. +// ##......## +// ##......## +// ..######## +// ........## +// ........## +// ..######.. + +#include "images.h" + +const image_t img_5x7_9 = {5, 7, false, 5, 0, {0x74, 0x62, 0xF0, 0x85, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_A.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_A.c new file mode 100644 index 000000000..910c034a2 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_A.c @@ -0,0 +1,11 @@ +// ..######.. +// ##......## +// ##......## +// ########## +// ##......## +// ##......## +// ##......## + +#include "images.h" + +const image_t img_5x7_A = {5, 7, false, 5, 0, {0x74, 0x63, 0xF8, 0xC6, 0x20}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_B.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_B.c new file mode 100644 index 000000000..93808fee2 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_B.c @@ -0,0 +1,11 @@ +// ########.. +// ##......## +// ##......## +// ##..####.. +// ##......## +// ##......## +// ########.. + +#include "images.h" + +const image_t img_5x7_B = {5, 7, false, 5, 0, {0xF4, 0x63, 0x68, 0xC7, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_C.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_C.c new file mode 100644 index 000000000..1438eaf44 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_C.c @@ -0,0 +1,11 @@ +// ..######.. +// ##......## +// ##........ +// ##........ +// ##........ +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_C = {5, 7, false, 5, 0, {0x74, 0x61, 0x08, 0x45, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_D.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_D.c new file mode 100644 index 000000000..9c6b590ee --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_D.c @@ -0,0 +1,11 @@ +// ..######.. +// ##..##..## +// ....##..## +// ....##..## +// ....##..## +// ##..##..## +// ..######.. + +#include "images.h" + +const image_t img_5x7_D = {5, 7, false, 5, 0, {0x75, 0x4A, 0x52, 0xD5, 0xC0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_E.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_E.c new file mode 100644 index 000000000..bc15fb240 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_E.c @@ -0,0 +1,11 @@ +// ########## +// ##........ +// ##........ +// ######.... +// ##........ +// ##........ +// ########## + +#include "images.h" + +const image_t img_5x7_E = {5, 7, false, 5, 0, {0xFC, 0x21, 0xC8, 0x43, 0xE0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_F.c b/applications/plugins/wii_ec_anal/gfx/img_5x7_F.c new file mode 100644 index 000000000..e4ad0db69 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_5x7_F.c @@ -0,0 +1,11 @@ +// ########## +// ##........ +// ##........ +// ######.... +// ##........ +// ##........ +// ##........ + +#include "images.h" + +const image_t img_5x7_F = {5, 7, false, 5, 0, {0xFC, 0x21, 0xC8, 0x42, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_0.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_0.c new file mode 100644 index 000000000..952cf34d8 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_0.c @@ -0,0 +1,12 @@ +// ..########.. +// ############ +// ####....#### +// ####....#### +// ####....#### +// ####....#### +// ############ +// ..########.. + +#include "images.h" + +const image_t img_6x8_0 = {6, 8, false, 6, 0, {0x7B, 0xFC, 0xF3, 0xCF, 0x3F, 0xDE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_1.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_1.c new file mode 100644 index 000000000..846a6876c --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_1.c @@ -0,0 +1,12 @@ +// ..######.... +// ########.... +// ....####.... +// ....####.... +// ....####.... +// ....####.... +// ############ +// ############ + +#include "images.h" + +const image_t img_6x8_1 = {6, 8, false, 6, 0, {0x73, 0xC3, 0x0C, 0x30, 0xCF, 0xFF}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_2.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_2.c new file mode 100644 index 000000000..4534bb67c --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_2.c @@ -0,0 +1,12 @@ +// ..########.. +// ############ +// ........#### +// ......###### +// ....####.... +// ..####...... +// ############ +// ############ + +#include "images.h" + +const image_t img_6x8_2 = {6, 8, false, 6, 0, {0x7B, 0xF0, 0xC7, 0x31, 0x8F, 0xFF}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_3.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_3.c new file mode 100644 index 000000000..7e79eb03a --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_3.c @@ -0,0 +1,12 @@ +// ..########.. +// ############ +// ........#### +// ....######## +// ....######## +// ........#### +// ############ +// ..########.. + +#include "images.h" + +const image_t img_6x8_3 = {6, 8, false, 6, 0, {0x7B, 0xF0, 0xCF, 0x3C, 0x3F, 0xDE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_4.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_4.c new file mode 100644 index 000000000..324b036ce --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_4.c @@ -0,0 +1,12 @@ +// ####........ +// ####........ +// ####..####.. +// ####..####.. +// ############ +// ############ +// ......####.. +// ......####.. + +#include "images.h" + +const image_t img_6x8_4 = {6, 8, false, 6, 0, {0xC3, 0x0D, 0xB6, 0xFF, 0xF1, 0x86}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_5.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_5.c new file mode 100644 index 000000000..cdfda5f2b --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_5.c @@ -0,0 +1,12 @@ +// ############ +// ############ +// ####........ +// ##########.. +// ############ +// ........#### +// ############ +// ##########.. + +#include "images.h" + +const image_t img_6x8_5 = {6, 8, false, 6, 0, {0xFF, 0xFC, 0x3E, 0xFC, 0x3F, 0xFE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_6.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_6.c new file mode 100644 index 000000000..781a060f1 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_6.c @@ -0,0 +1,12 @@ +// ..########.. +// ##########.. +// ####........ +// ##########.. +// ############ +// ####....#### +// ############ +// ..########.. + +#include "images.h" + +const image_t img_6x8_6 = {6, 8, false, 6, 0, {0x7B, 0xEC, 0x3E, 0xFF, 0x3F, 0xDE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_7.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_7.c new file mode 100644 index 000000000..fec5f4bf4 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_7.c @@ -0,0 +1,12 @@ +// ############ +// ############ +// ........#### +// ......####.. +// ......####.. +// ....####.... +// ....####.... +// ....####.... + +#include "images.h" + +const image_t img_6x8_7 = {6, 8, false, 6, 0, {0xFF, 0xF0, 0xC6, 0x18, 0xC3, 0x0C}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_8.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_8.c new file mode 100644 index 000000000..a5b21c375 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_8.c @@ -0,0 +1,12 @@ +// ..########.. +// ############ +// ####....#### +// ..########.. +// ############ +// ####....#### +// ############ +// ..########.. + +#include "images.h" + +const image_t img_6x8_8 = {6, 8, false, 6, 0, {0x7B, 0xFC, 0xDE, 0xFF, 0x3F, 0xDE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_9.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_9.c new file mode 100644 index 000000000..f7707c0df --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_9.c @@ -0,0 +1,12 @@ +// ..########.. +// ############ +// ####....#### +// ############ +// ..########## +// ........#### +// ..########## +// ..########.. + +#include "images.h" + +const image_t img_6x8_9 = {6, 8, false, 6, 0, {0x7B, 0xFC, 0xFF, 0x7C, 0x37, 0xDE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_A.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_A.c new file mode 100644 index 000000000..1bb65c902 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_A.c @@ -0,0 +1,12 @@ +// ..########.. +// ############ +// ####....#### +// ####....#### +// ############ +// ############ +// ####....#### +// ####....#### + +#include "images.h" + +const image_t img_6x8_A = {6, 8, false, 6, 0, {0x7B, 0xFC, 0xF3, 0xFF, 0xFC, 0xF3}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_B.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_B.c new file mode 100644 index 000000000..00e012d53 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_B.c @@ -0,0 +1,12 @@ +// ##########.. +// ############ +// ####....#### +// ##########.. +// ##########.. +// ####....#### +// ############ +// ##########.. + +#include "images.h" + +const image_t img_6x8_B = {6, 8, false, 6, 0, {0xFB, 0xFC, 0xFE, 0xFB, 0x3F, 0xFE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_C.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_C.c new file mode 100644 index 000000000..694901009 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_C.c @@ -0,0 +1,12 @@ +// ..########## +// ############ +// ####........ +// ####........ +// ####........ +// ####........ +// ############ +// ..########## + +#include "images.h" + +const image_t img_6x8_C = {6, 8, false, 6, 0, {0x7F, 0xFC, 0x30, 0xC3, 0x0F, 0xDF}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_D.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_D.c new file mode 100644 index 000000000..a95e760eb --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_D.c @@ -0,0 +1,12 @@ +// ##########.. +// ############ +// ..####..#### +// ..####..#### +// ..####..#### +// ..####..#### +// ############ +// ##########.. + +#include "images.h" + +const image_t img_6x8_D = {6, 8, false, 6, 0, {0xFB, 0xF6, 0xDB, 0x6D, 0xBF, 0xFE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_E.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_E.c new file mode 100644 index 000000000..f49503f00 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_E.c @@ -0,0 +1,12 @@ +// ############ +// ############ +// ####........ +// ########.... +// ########.... +// ####........ +// ############ +// ############ + +#include "images.h" + +const image_t img_6x8_E = {6, 8, false, 6, 0, {0xFF, 0xFC, 0x3C, 0xF3, 0x0F, 0xFF}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_F.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_F.c new file mode 100644 index 000000000..0037b2544 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_F.c @@ -0,0 +1,12 @@ +// ############ +// ############ +// ####........ +// ########.... +// ########.... +// ####........ +// ####........ +// ####........ + +#include "images.h" + +const image_t img_6x8_F = {6, 8, false, 6, 0, {0xFF, 0xFC, 0x3C, 0xF3, 0x0C, 0x30}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_G.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_G.c new file mode 100644 index 000000000..f30bc9952 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_G.c @@ -0,0 +1,12 @@ +// ..########## +// ############ +// ####........ +// ####........ +// ####..###### +// ####....#### +// ############ +// ..########## + +#include "images.h" + +const image_t img_6x8_G = {6, 8, false, 6, 0, {0x7F, 0xFC, 0x30, 0xDF, 0x3F, 0xDF}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_X.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_X.c new file mode 100644 index 000000000..4735e82a1 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_X.c @@ -0,0 +1,12 @@ +// ####....#### +// ####....#### +// ..####..##.. +// ....######.. +// ..######.... +// ..##..####.. +// ####....#### +// ####....#### + +#include "images.h" + +const image_t img_6x8_X = {6, 8, false, 6, 0, {0xCF, 0x36, 0x8E, 0x71, 0x6C, 0xF3}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_Y.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_Y.c new file mode 100644 index 000000000..508e786bd --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_Y.c @@ -0,0 +1,12 @@ +// ####....#### +// ####....#### +// ####....#### +// ####....#### +// ..########.. +// ....####.... +// ....####.... +// ....####.... + +#include "images.h" + +const image_t img_6x8_Y = {6, 8, false, 6, 0, {0xCF, 0x3C, 0xF3, 0x78, 0xC3, 0x0C}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_Z.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_Z.c new file mode 100644 index 000000000..c42d560ac --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_Z.c @@ -0,0 +1,12 @@ +// ############ +// ############ +// ........#### +// ......####.. +// ....####.... +// ..####...... +// ############ +// ############ + +#include "images.h" + +const image_t img_6x8_Z = {6, 8, false, 6, 0, {0xFF, 0xF0, 0xC6, 0x31, 0x8F, 0xFF}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_d_.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_d_.c new file mode 100644 index 000000000..1f8123a6c --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_d_.c @@ -0,0 +1,12 @@ +// ........#### +// ........#### +// ........#### +// ..########## +// ############ +// ####....#### +// ############ +// ..########## + +#include "images.h" + +const image_t img_6x8_d = {6, 8, false, 6, 0, {0x0C, 0x30, 0xDF, 0xFF, 0x3F, 0xDF}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_n_.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_n_.c new file mode 100644 index 000000000..15d403d28 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_n_.c @@ -0,0 +1,12 @@ +// ............ +// ............ +// ..########.. +// ############ +// ####....#### +// ####....#### +// ####....#### +// ####....#### + +#include "images.h" + +const image_t img_6x8_n = {6, 8, false, 6, 0, {0x00, 0x07, 0xBF, 0xCF, 0x3C, 0xF3}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_v_.c b/applications/plugins/wii_ec_anal/gfx/img_6x8_v_.c new file mode 100644 index 000000000..1229701a1 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_6x8_v_.c @@ -0,0 +1,12 @@ +// ............ +// ............ +// ##........## +// ####....#### +// ####....#### +// ############ +// ..########.. +// ....####.... + +#include "images.h" + +const image_t img_6x8_v = {6, 8, false, 6, 0, {0x00, 0x08, 0x73, 0xCF, 0xF7, 0x8C}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_RIP.c b/applications/plugins/wii_ec_anal/gfx/img_RIP.c new file mode 100644 index 000000000..c20877ef0 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_RIP.c @@ -0,0 +1,130 @@ +// ################################################################################################################################################################################################################################################################ +// ################################################################################################################################################################################################################################################################ +// ####........................................................................................................................................................................................................................................................#### +// ####..##..##........................................................................................................................................................................................................................................##..##..#### +// ####....##....................##############..........########........##################........................##############......##..........##......##############......##############......##############......##############....................##....#### +// ####..##..##..................##############............######..........################........................##############....######......######....##############......##############......##############......##############..................##..##..#### +// ####....................................######..........##..##......................######....................####........######..######......######..####........######..######......######..............######..######......######........................#### +// ####........................######......######..........##..##..........######......##..##....................######......##..##..##..##......##..##..######......######..######......######..######......######..######......##..##........................#### +// ####........................######......##..##..........##..##..........######......##..##....................##..##......######..##..##......##..##..######......##..##..##..##......##..##..######......##..##..##..##......##..##........................#### +// ####........................##..##......##..##..........##..##..........##..##......##..##....................##..##..............##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##........................#### +// ####........................##..##......##..##..........##..##..........##..##......##..##....................##..##..............##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##..##..##......######........................#### +// ####........................##..##......######..........##..##..........##..##......######....................##..##..............##..##......##..##..##..##......######..##..##..##..##..##..##..##......######..##..##....................................#### +// ####........................##..##########..............##..##..........##..##########........................##..##..............######......######..##..##########......##..##......##..##..##..##########......##..##....................................#### +// ####........................##..##########..............##..##..........##..##########........................##..##................##############....##..##########......##..##......##..##..##..##########......##..##..########..........................#### +// ####........................##..##....######............##..##..........##..##................................##..##..................####..####......##..##......######..##..##..##..##..##..##..##....######....##..##..##########........................#### +// ####........................##..##....##..##............##..##..........##..##................................##..##....................##..##........##..##......##..##..##..##......##..##..##..##....##..##....##..##......##..##........................#### +// ####........................##..##....##..##............##..##..........##..##................................##..##....................##..##........##..##......##..##..##..##......##..##..##..##....##..##....##..##......##..##........................#### +// ####........................##..##....##..##............##..##..........##..##................................######......######........##..##........##..##......##..##..##..##......##..##..##..##....##..##....######......##..##........................#### +// ####........................##..##....##..##............##..##..........##..##................................######......##..##........##..##........######......######..######......######..##..##....##..##....######......##..##........................#### +// ####........................##..##....##..##............##..##..........##..##................................####........######........######........######......######..######......######..##..##....##..##....####........######........................#### +// ####........................######....######....####....######..####....######..####............................##############..........######..........##############......##############....######....######......##############..........................#### +// ####........................######......######..####..########..####..########..####............................##############........##########........##############......##############....######......######....##############..........................#### +// ####........................................................................................................................................................................................................................................................#### +// ####........................................................................................................................................................................................................................................................#### +// ####..........................................................................................................................................................................................####......####..............##############....................#### +// ####......................................................................................................................................##................................................##....##..##....##........####..............####................#### +// ####..........######..##..##..######......######..##..##..######..######..######..####..####..######......##......##..######..##....##..##..######......######..######......................####..##..####..##......##......................##..............#### +// ####............##....##..##..##..........##......##..##..##........##....##......##..##..##..##..........##......##..##..##..####..##........##........##..##..##............................####......####......##......##########..........##............#### +// ####............##....######..####........######....##....######....##....####....##..##..##..######......##..##..##..##..##..##..####........##........####....####..............................##..##........##......##..........##........##............#### +// ####............##....##..##..##..............##....##........##....##....##......##......##......##......##..##..##..##..##..##....##........##........##..##..##..................................##..........##....##..............##......##............#### +// ####............##....##..##..######......######....##....######....##....######..##......##..######......####..####..######..##....##........##........######..######............................######........##....##......####....##......##............#### +// ####............................................................................................................................................................................................##......##......##....##....##....##..##......##............#### +// ####..........................................................................................................................................................................................##........##......##....##....##..##....##......##............#### +// ####..........................................................................................................................................................................................##........##......##....##....##........##......##............#### +// ####..........######..##..##..######......######..######..####..####..######......##......##..######..######..##..##..######..##..##..######......##..##..######..##..##........................##........######......##......########......##..............#### +// ####............##....##..##..##..........##......##..##..##..##..##..##..........##......##....##......##....##..##..##..##..##..##....##........##..##..##..##..##..##........................##..............##......##..................####............#### +// ####............##....######..####........######..######..##..##..##..####........##..##..##....##......##....######..##..##..##..##....##..........##....##..##..##..##..........................##..............##########..............##....##..........#### +// ####............##....##..##..##..............##..##..##..##......##..##..........##..##..##....##......##....##..##..##..##..##..##....##..........##....##..##..##..##..........................##......................################........##........#### +// ####............##....##..##..######......######..##..##..##......##..######......####..####..######....##....##..##..######..######....##..........##....######..######............................####..........................................##........#### +// ####....................................................................................................................................................................................................##########################################..........#### +// ####........................................................................................................................................................................................................................................................#### +// ####........................................................................................................................................................................................................................................................#### +// ####......................................##########......##############......##############......##############............................##############......##############......##############......##############......................................#### +// ####......................................##########......##############......##############......##############............................##############......##############......##############......##############......................................#### +// ####......................................##..##..##....######......######..######......######..######......######........................######......######..######......######..######......######..######......######....................................#### +// ####..........................................##..##....######......######..##..##......######..######......######........................##..##......##..##..######......######..##..##......##..##..##..##......##..##....................................#### +// ####..........................................##..##....##..##......##..##..######......##..##..##..##......##..##........................######......##..##..##..##......##..##..######......##..##..######......##..##....................................#### +// ####..........................................##..##....##..##......##..##..............##..##..##..##......##..##....................................##..##..##..##......##..##..............##..##..............##..##....................................#### +// ####..........................................##..##....##..##......##..##..............##..##..##..##....####..##....................................##..##..##..##....####..##..............##..##..............##..##....................................#### +// ####..........................................##..##....######......##..##..............######..##..##....####..##....................................######..##..##....####..##..............######..............######....................................#### +// ####..........................................######........##########..##..............######..##..##..##..##..##....################........############....##..##..##..##..##......############........############......................................#### +// ####..........................................######........##########..##..........######......##..##..##..##..##....##............##......############......##..##..##..##..##....############........############........................................#### +// ####..........................................##..##................##..##..........######......##..####....##..##....################....######..............##..####....##..##..######..............######................................................#### +// ####..........................................##..##................##..##..........##..##......##..####....##..##........................##..##..............##..####....##..##..##..##..............##..##................................................#### +// ####..........................................##..##................##..##..........##..##......##..##......##..##........................##..##..............##..##......##..##..##..##..............##..##................................................#### +// ####..........................................##..##................##..##..........##..##......##..##......##..##........................##..##..............##..##......##..##..##..##..............##..##................................................#### +// ####..........................................##..##................######..........##..##......######......######........................##..##..............######......######..##..##..............##..##................................................#### +// ####..........................................##..##................######..........##..##......######......######........................##..##........####..######......######..##..##........####..##..##........####....................................#### +// ####..##..##..............................##############....############............######........##############..........................##################....##############....##################..##################............................##..##..#### +// ####....##..............................##################..############............######........##############..........................##################....##############....##################..##################..............................##....#### +// ####..##..##........................................................................................................................................................................................................................................##..##..#### +// ####........................................................................................................................................................................................................................................................#### +// ################################################################################################################################################################################################################################################################ +// ################################################################################################################################################################################################################################################################ + +#include "images.h" + +const image_t img_RIP = { + 128, + 64, + true, + 837, + 0x06, + {// orig:1024, comp:18.26% + 0x06, 0x20, 0xFF, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xD4, 0x06, 0x0E, 0x00, 0x2B, 0xC8, 0x01, + 0xFC, 0x1E, 0x1F, 0xF0, 0x00, 0xFE, 0x20, 0x8F, 0xE3, 0xF8, 0xFE, 0x3F, 0x80, 0x13, 0xD4, + 0x01, 0xFC, 0x0E, 0x0F, 0xF0, 0x00, 0xFE, 0x71, 0xCF, 0xE3, 0xF8, 0xFE, 0x3F, 0x80, 0x2B, + 0xC0, 0x00, 0x0E, 0x0A, 0x00, 0x38, 0x01, 0x87, 0x71, 0xD8, 0x77, 0x1C, 0x07, 0x71, 0xC0, + 0x03, 0xC0, 0x03, 0x8E, 0x0A, 0x0E, 0x28, 0x01, 0xC5, 0x51, 0x5C, 0x77, 0x1D, 0xC7, 0x71, + 0x40, 0x03, 0xC0, 0x03, 0x8A, 0x0A, 0x0E, 0x28, 0x01, 0x47, 0x51, 0x5C, 0x55, 0x15, 0xC5, + 0x51, 0x40, 0x03, 0xC0, 0x02, 0x8A, 0x0A, 0x0A, 0x28, 0x01, 0x40, 0x51, 0x54, 0x55, 0x15, + 0x45, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x8A, 0x0A, 0x0A, 0x28, 0x01, 0x40, 0x51, 0x54, 0x55, + 0x15, 0x45, 0x51, 0xC0, 0x03, 0xC0, 0x02, 0x8E, 0x0A, 0x0A, 0x38, 0x01, 0x40, 0x51, 0x54, + 0x75, 0x55, 0x47, 0x50, 0x00, 0x03, 0xC0, 0x02, 0xF8, 0x0A, 0x0B, 0xE0, 0x01, 0x40, 0x71, + 0xD7, 0xC5, 0x15, 0x7C, 0x50, 0x00, 0x03, 0xC0, 0x02, 0xF8, 0x0A, 0x0B, 0xE0, 0x01, 0x40, + 0x3F, 0x97, 0xC5, 0x15, 0x7C, 0x57, 0x80, 0x03, 0xC0, 0x02, 0x9C, 0x0A, 0x0A, 0x00, 0x01, + 0x40, 0x1B, 0x14, 0x75, 0x55, 0x4E, 0x57, 0xC0, 0x03, 0xC0, 0x02, 0x94, 0x0A, 0x0A, 0x00, + 0x01, 0x40, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x94, 0x0A, 0x0A, + 0x00, 0x01, 0x40, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x94, 0x0A, + 0x0A, 0x00, 0x01, 0xC7, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x71, 0x40, 0x03, 0xC0, 0x02, 0x94, + 0x0A, 0x0A, 0x00, 0x01, 0xC5, 0x0A, 0x1C, 0x77, 0x1D, 0x4A, 0x71, 0x40, 0x03, 0xC0, 0x02, + 0x94, 0x0A, 0x0A, 0x00, 0x01, 0x87, 0x0E, 0x1C, 0x77, 0x1D, 0x4A, 0x61, 0xC0, 0x03, 0xC0, + 0x03, 0x9C, 0xCE, 0xCE, 0xC0, 0x00, 0xFE, 0x0E, 0x0F, 0xE3, 0xF9, 0xCE, 0x3F, 0x80, 0x03, + 0xC0, 0x03, 0x8E, 0xDE, 0xDE, 0xC0, 0x00, 0xFE, 0x1F, 0x0F, 0xE3, 0xF9, 0xC7, 0x3F, 0x80, + 0x03, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xC0, 0x06, 0x0A, 0x00, + 0x01, 0x8C, 0x07, 0xF0, 0x03, 0xC0, 0x06, 0x07, 0x00, 0x04, 0x00, 0x00, 0x02, 0x52, 0x18, + 0x0C, 0x03, 0xC1, 0xD5, 0xC7, 0x57, 0x77, 0x6D, 0xC4, 0x5D, 0x2B, 0x8E, 0xE0, 0x03, 0x5A, + 0x20, 0x02, 0x03, 0xC0, 0x95, 0x04, 0x54, 0x24, 0x55, 0x04, 0x55, 0xA1, 0x0A, 0x80, 0x01, + 0x8C, 0x47, 0xC1, 0x03, 0xC0, 0x9D, 0x87, 0x27, 0x26, 0x55, 0xC5, 0x55, 0x61, 0x0C, 0xC0, + 0x00, 0x50, 0x88, 0x21, 0x03, 0xC0, 0x95, 0x01, 0x21, 0x24, 0x44, 0x45, 0x55, 0x21, 0x0A, + 0x80, 0x00, 0x20, 0x90, 0x11, 0x03, 0xC0, 0x95, 0xC7, 0x27, 0x27, 0x45, 0xC6, 0xDD, 0x21, + 0x0E, 0xE0, 0x00, 0x70, 0x91, 0x91, 0x03, 0xC0, 0x06, 0x0B, 0x00, 0x88, 0x92, 0x51, 0x03, + 0xC0, 0x06, 0x0A, 0x00, 0x01, 0x08, 0x92, 0x91, 0x03, 0xC0, 0x06, 0x0A, 0x00, 0x01, 0x08, + 0x92, 0x11, 0x03, 0xC1, 0xD5, 0xC7, 0x76, 0xDC, 0x45, 0xDD, 0x5D, 0x5C, 0x57, 0x50, 0x00, + 0x87, 0x11, 0xE2, 0x03, 0xC0, 0x95, 0x04, 0x55, 0x50, 0x44, 0x89, 0x55, 0x48, 0x55, 0x50, + 0x00, 0x80, 0x88, 0x03, 0x03, 0xC0, 0x9D, 0x87, 0x75, 0x58, 0x54, 0x89, 0xD5, 0x48, 0x25, + 0x50, 0x00, 0x40, 0x7C, 0x04, 0x83, 0xC0, 0x95, 0x01, 0x54, 0x50, 0x54, 0x89, 0x55, 0x48, + 0x25, 0x50, 0x00, 0x40, 0x07, 0xF8, 0x43, 0xC0, 0x95, 0xC7, 0x54, 0x5C, 0x6D, 0xC9, 0x5D, + 0xC8, 0x27, 0x70, 0x00, 0x30, 0x00, 0x00, 0x43, 0xC0, 0x06, 0x0B, 0x00, 0x0F, 0xFF, 0xFF, + 0x83, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xC0, 0x00, 0x07, 0xC7, + 0xF1, 0xFC, 0x7F, 0x00, 0x03, 0xF8, 0xFE, 0x3F, 0x8F, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x07, + 0xC7, 0xF1, 0xFC, 0x7F, 0x00, 0x03, 0xF8, 0xFE, 0x3F, 0x8F, 0xE0, 0x00, 0x03, 0xC0, 0x00, + 0x05, 0x4E, 0x3B, 0x8E, 0xE3, 0x80, 0x07, 0x1D, 0xC7, 0x71, 0xDC, 0x70, 0x00, 0x03, 0xC0, + 0x00, 0x01, 0x4E, 0x3A, 0x8E, 0xE3, 0x80, 0x05, 0x15, 0xC7, 0x51, 0x54, 0x50, 0x00, 0x03, + 0xC0, 0x00, 0x01, 0x4A, 0x2B, 0x8A, 0xA2, 0x80, 0x07, 0x15, 0x45, 0x71, 0x5C, 0x50, 0x00, + 0x03, 0xC0, 0x00, 0x01, 0x4A, 0x28, 0x0A, 0xA2, 0x80, 0x00, 0x15, 0x45, 0x01, 0x40, 0x50, + 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4A, 0x28, 0x0A, 0xA6, 0x80, 0x00, 0x15, 0x4D, 0x01, 0x40, + 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4E, 0x28, 0x0E, 0xA6, 0x80, 0x00, 0x1D, 0x4D, 0x01, + 0xC0, 0x70, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xC3, 0xE8, 0x0E, 0xAA, 0x9F, 0xE1, 0xF9, 0x55, + 0x1F, 0x87, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xC3, 0xE8, 0x38, 0xAA, 0x90, 0x23, 0xF1, + 0x55, 0x3F, 0x0F, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x38, 0xB2, 0x9F, 0xE7, + 0x01, 0x65, 0x70, 0x1C, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, 0xB2, 0x80, + 0x05, 0x01, 0x65, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, 0xA2, + 0x80, 0x05, 0x01, 0x45, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, + 0xA2, 0x80, 0x05, 0x01, 0x45, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x38, + 0x28, 0xE3, 0x80, 0x05, 0x01, 0xC7, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, + 0x38, 0x28, 0xE3, 0x80, 0x05, 0x0D, 0xC7, 0x50, 0xD4, 0x30, 0x00, 0x03, 0xD4, 0x00, 0x07, + 0xF3, 0xF0, 0x38, 0x7F, 0x00, 0x07, 0xFC, 0xFE, 0x7F, 0xDF, 0xF0, 0x00, 0x2B, 0xC8, 0x00, + 0x0F, 0xFB, 0xF0, 0x38, 0x7F, 0x00, 0x07, 0xFC, 0xFE, 0x7F, 0xDF, 0xF0, 0x00, 0x13, 0xD4, + 0x06, 0x0E, 0x00, 0x2B, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0x06, 0x20, 0xFF}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Cable.c b/applications/plugins/wii_ec_anal/gfx/img_cc_Cable.c new file mode 100644 index 000000000..f4ac26173 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_Cable.c @@ -0,0 +1,25 @@ +// ####..## +// ##..#### +// ####..## +// ##..#### +// ####..## +// ##..#### +// ####..## +// ##..#### +// ####..## +// ##..#### +// ####..## + +#include "images.h" + +const image_t img_cc_Cable = { + 4, + 11, + true, + 4, + 0x00, + {// orig:6, comp:33.33% + 0x00, + 0x05, + 0xDB, + 0xD0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Joy.c b/applications/plugins/wii_ec_anal/gfx/img_cc_Joy.c new file mode 100644 index 000000000..5054103b3 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_Joy.c @@ -0,0 +1,25 @@ +// ................##................ +// ............##########............ +// ....############..############.... +// ....######..............######.... +// ....####..................####.... +// ....##......................##.... +// ..####......................####.. +// ..####......................####.. +// ####..........................#### +// ..####......................####.. +// ..####......................####.. +// ....##......................##.... +// ....####..................####.... +// ....######..............######.... +// ....############..############.... +// ............##########............ +// ................##................ + +#include "images.h" + +const image_t img_cc_Joy = {17, 17, false, 37, 0, {0x00, 0x80, 0x01, 0xF0, 0x0F, 0xDF, 0x87, 0x01, + 0xC3, 0x00, 0x61, 0x00, 0x11, 0x80, 0x0C, 0xC0, + 0x06, 0xC0, 0x01, 0xB0, 0x01, 0x98, 0x00, 0xC4, + 0x00, 0x43, 0x00, 0x61, 0xC0, 0x70, 0xFD, 0xF8, + 0x07, 0xC0, 0x00, 0x80, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Main.c b/applications/plugins/wii_ec_anal/gfx/img_cc_Main.c new file mode 100644 index 000000000..b29a9ab57 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_Main.c @@ -0,0 +1,100 @@ +// ..................................................##################................................................................................................##################.................................................. +// ......................................############................##............##########....................................................##########............##................############...................................... +// ..................................####............................##..........##..........##......................####......................##..........##..........##............................####.................................. +// ................................##................................##############..........####################################################..........##############................................##................................ +// ............................######................................##..........##..........##......................####......................##..........##..........##................................######............................ +// ..........................##..##....................################..........##..........##......................####......................##..........##..........################....................##..##.......................... +// ........................##....##........############............................##########....................................................##########............................############........##....##........................ +// ......................##......##########........................................................................................................................................................##########......##...................... +// ....................##..........########################################################################################################################################################################..........##.................... +// ..................##......########....................................................................................................................................................................########......##.................. +// ................##....######................................................................................................................................................................................######....##................ +// ..............##....####........................................................................................................................................................................................####....##.............. +// ............##########............................................................................................................................................................................................##########............ +// ............######......................................................................................####......####..####..####....................................................................................######............ +// ..........######......................##################................................................####..##..####..................................................................######..........................######.......... +// ..........####........................##################................................................####..##..####..####..####....................................................##########..........................####.......... +// ........####..........................####..........####................................................####..##..####..####..####..................................................####......####..........................####........ +// ......######..........................####..........####..................................................####..####....####..####................................................####..........####........................######...... +// ......####............................####....##....####........................................................................................................................####....##..##....####........................####...... +// ......##..............................####....##....####........................................................................................................................####......##......####..........................####.... +// ....####..............................####....##....####........................................................................................................................####....##..##....####..........................####.... +// ....##..................##################..........##################............................................................................................######..........####..........####..........######..............##.... +// ..####..................##################..........##################..........................................................................................##########..........####......####..........##########............####.. +// ..##....................####......................................####..........................########........########........########......................####......####..........##########..........####......####............##.. +// ..##....................####......................................####........................####....####....####....####....####....####..................####..........####..........######..........####....##....####..........##.. +// ####....................####....######..................######....####........................##........##....##........##....##........##................####....##..##....####......................####....##..##....####........#### +// ##......................####......................................####........................##........##....##........##....##........##................####....######....####......................####....######....####..........## +// ##......................####......................................####........................####....####....####....####....####....####................####........##....####......................####....##..##....####..........## +// ##......................##################..........##################..........................########........########........########....................####....##....####..........######..........####..........####............## +// ##......................##################..........##################........................................................................................####......####..........##########..........####......####..............## +// ##....................................####....##....####........................................................................................................##########..........####......####..........##########................## +// ##....................................####....##....####..........................................................................................................######..........####..##......####..........######..................## +// ##....................................####....##....####........................................................................................................................####....##........####................................## +// ##....................................####..........####........................................................................................................................####....####......####................................## +// ##....................................####..........####........................................................................................................................####....##..##....####................................## +// ####..................................##################..........................................................................................................................####....####..####................................#### +// ..##..................................##################............................................................................................................................####......####..................................##.. +// ..##..................................................................................................................................................................................##########....................................##.. +// ..####..................................................................................................................................................................................######....................................####.. +// ....##............................................................................................................................................................................................................................##.... +// ....##............................................................................................................................................................................................................................##.... +// ....####........................................................................................................................................................................................................................####.... +// ......##........................................................................................................................................................................................................................##...... +// ......####....................................................................................................................................................................................................................####...... +// ........####................................................................................................................................................................................................................####........ +// ..........####............................................................................................................................................................................................................####.......... +// ............####........................................................................................................................................................................................................####............ +// ..............####....................................................................................................................................................................................................####.............. +// ................######............................................................................................................................................................................................######................ +// ....................####........................................................................................................................................................................................####.................... +// ......................######................................................................................................................................................................................######...................... +// ..........................########....................................................................................................................................................................########.......................... +// ................................########################################################################################################################################################################................................ + +#include "images.h" + +const image_t img_cc_Main = { + 116, + 53, + true, + 542, + 0x05, + {// orig:769, comp:29.52% + 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x05, 0x05, 0x00, 0x3F, 0xE0, 0x05, 0x04, 0x00, 0x01, 0xF8, + 0x04, 0x0F, 0x80, 0x00, 0x00, 0x1F, 0x02, 0x01, 0xF8, 0x05, 0x04, 0x00, 0x60, 0x00, 0x41, + 0x04, 0x00, 0x60, 0x02, 0x08, 0x20, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0xF0, + 0x7F, 0xFF, 0xFF, 0xE0, 0xFE, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x41, 0x04, + 0x00, 0x60, 0x02, 0x08, 0x20, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x50, 0x03, 0xFC, 0x10, 0x40, + 0x06, 0x00, 0x20, 0x83, 0xFC, 0x00, 0xA0, 0x00, 0x00, 0x09, 0x0F, 0xC0, 0x00, 0xF8, 0x00, + 0x00, 0x01, 0xF0, 0x00, 0x3F, 0x09, 0x00, 0x00, 0x01, 0x1F, 0x05, 0x09, 0x00, 0x0F, 0x88, + 0x00, 0x00, 0x20, 0x05, 0x0A, 0xFF, 0xF0, 0x40, 0x00, 0x04, 0x78, 0x05, 0x09, 0x00, 0x01, + 0xE2, 0x00, 0x00, 0x9C, 0x05, 0x0A, 0x00, 0x03, 0x90, 0x00, 0x13, 0x05, 0x0B, 0x00, 0x0C, + 0x80, 0x03, 0xE0, 0x05, 0x0B, 0x00, 0x7C, 0x00, 0x38, 0x05, 0x05, 0x00, 0xC6, 0xD8, 0x05, + 0x04, 0x00, 0x01, 0xC0, 0x07, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x0E, 0x00, 0x60, 0x01, 0xFF, 0x00, 0x00, 0x00, 0xD6, 0xD8, 0x00, 0x00, 0x01, + 0xF0, 0x00, 0x60, 0x0C, 0x00, 0x18, 0x30, 0x00, 0x00, 0x0D, 0x6D, 0x80, 0x00, 0x00, 0x31, + 0x80, 0x03, 0x01, 0xC0, 0x01, 0x83, 0x00, 0x00, 0x00, 0x6C, 0xD8, 0x00, 0x00, 0x06, 0x0C, + 0x00, 0x38, 0x18, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00, 0xCA, 0x60, 0x01, 0x81, 0x00, 0x01, + 0x93, 0x05, 0x07, 0x00, 0x0C, 0x46, 0x00, 0x0C, 0x30, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00, + 0xCA, 0x60, 0x00, 0xC2, 0x00, 0xFF, 0x83, 0xFE, 0x05, 0x05, 0x00, 0x07, 0x06, 0x0C, 0x1C, + 0x04, 0x60, 0x0F, 0xF8, 0x3F, 0xE0, 0x05, 0x05, 0x00, 0xF8, 0x31, 0x83, 0xE0, 0x64, 0x00, + 0xC0, 0x00, 0x06, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x18, 0xC1, 0xF0, 0x63, 0x02, 0x40, 0x0C, + 0x00, 0x00, 0x60, 0x01, 0x99, 0x99, 0x98, 0x03, 0x06, 0x0E, 0x0C, 0x98, 0x2C, 0x00, 0xCE, + 0x00, 0xE6, 0x00, 0x10, 0x90, 0x90, 0x80, 0x65, 0x30, 0x01, 0x94, 0xC3, 0x80, 0x0C, 0x00, + 0x00, 0x60, 0x01, 0x09, 0x09, 0x08, 0x06, 0x73, 0x00, 0x19, 0xCC, 0x18, 0x00, 0xC0, 0x00, + 0x06, 0x00, 0x19, 0x99, 0x99, 0x80, 0x61, 0x30, 0x01, 0x94, 0xC1, 0x80, 0x0F, 0xF8, 0x3F, + 0xE0, 0x00, 0xF0, 0xF0, 0xF0, 0x03, 0x26, 0x0E, 0x0C, 0x18, 0x18, 0x00, 0xFF, 0x83, 0xFE, + 0x05, 0x05, 0x00, 0x18, 0xC1, 0xF0, 0x63, 0x01, 0x80, 0x00, 0x19, 0x30, 0x05, 0x06, 0x00, + 0xF8, 0x31, 0x83, 0xE0, 0x18, 0x00, 0x01, 0x93, 0x05, 0x06, 0x00, 0x07, 0x06, 0x8C, 0x1C, + 0x01, 0x80, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00, 0xC8, 0x60, 0x00, 0x18, 0x00, 0x01, 0x83, + 0x05, 0x07, 0x00, 0x0C, 0xC6, 0x00, 0x01, 0x80, 0x00, 0x18, 0x30, 0x05, 0x07, 0x00, 0xCA, + 0x60, 0x00, 0x1C, 0x00, 0x01, 0xFF, 0x05, 0x07, 0x00, 0x06, 0x6C, 0x00, 0x03, 0x40, 0x00, + 0x1F, 0xF0, 0x05, 0x07, 0x00, 0x31, 0x80, 0x00, 0x24, 0x05, 0x0A, 0x00, 0x01, 0xF0, 0x00, + 0x02, 0x60, 0x05, 0x0A, 0x00, 0x0E, 0x00, 0x00, 0x62, 0x05, 0x0D, 0x00, 0x04, 0x20, 0x05, + 0x0D, 0x00, 0x43, 0x05, 0x0D, 0x00, 0x0C, 0x10, 0x05, 0x0D, 0x00, 0x81, 0x80, 0x05, 0x0C, + 0x00, 0x18, 0x0C, 0x05, 0x0C, 0x00, 0x03, 0x00, 0x60, 0x05, 0x0C, 0x00, 0x60, 0x03, 0x05, + 0x0C, 0x00, 0x0C, 0x00, 0x18, 0x05, 0x0B, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x05, 0x0B, 0x00, + 0x70, 0x00, 0x03, 0x05, 0x0B, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x05, 0x0A, 0x00, 0x03, 0x80, + 0x00, 0x00, 0x78, 0x05, 0x09, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x05, 0x0A, 0xFF, 0xF0, + 0x00, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_A1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_A1.c new file mode 100644 index 000000000..0889b2a08 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_A1.c @@ -0,0 +1,11 @@ +// ############## +// ######..###### +// ####..##..#### +// ####......#### +// ####..##..#### +// ############## +// ############## + +#include "images.h" + +const image_t img_cc_btn_A1 = {7, 7, false, 7, 0, {0xFF, 0xDF, 0x5E, 0x3D, 0x7F, 0xFF, 0x80}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_B1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_B1.c new file mode 100644 index 000000000..bbf5fba1a --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_B1.c @@ -0,0 +1,11 @@ +// ############## +// ####..######## +// ####..######## +// ####....###### +// ####..##..#### +// ######....#### +// ############## + +#include "images.h" + +const image_t img_cc_btn_B1 = {7, 7, false, 7, 0, {0xFF, 0xBF, 0x7E, 0x7D, 0x7C, 0xFF, 0x80}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_X1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_X1.c new file mode 100644 index 000000000..2352ba695 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_X1.c @@ -0,0 +1,11 @@ +// ############## +// ############## +// ####..##..#### +// ######..###### +// ####..##..#### +// ############## +// ############## + +#include "images.h" + +const image_t img_cc_btn_X1 = {7, 7, false, 7, 0, {0xFF, 0xFF, 0x5F, 0x7D, 0x7F, 0xFF, 0x80}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_Y1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_Y1.c new file mode 100644 index 000000000..d7192e3e7 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_btn_Y1.c @@ -0,0 +1,11 @@ +// ############## +// ############## +// ####..##..#### +// ####......#### +// ########..#### +// ######..###### +// ############## + +#include "images.h" + +const image_t img_cc_btn_Y1 = {7, 7, false, 7, 0, {0xFF, 0xFF, 0x5E, 0x3F, 0x7D, 0xFF, 0x80}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_pad_LR1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_pad_LR1.c new file mode 100644 index 000000000..300ed5eee --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_pad_LR1.c @@ -0,0 +1,9 @@ +// ############## +// ############## +// ####......#### +// ############## +// ############## + +#include "images.h" + +const image_t img_cc_pad_LR1 = {7, 5, false, 5, 0, {0xFF, 0xFF, 0x1F, 0xFF, 0xE0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_pad_UD1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_pad_UD1.c new file mode 100644 index 000000000..feb32d283 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_pad_UD1.c @@ -0,0 +1,11 @@ +// ########## +// ########## +// ####..#### +// ####..#### +// ####..#### +// ########## +// ########## + +#include "images.h" + +const image_t img_cc_pad_UD1 = {5, 7, false, 5, 0, {0xFF, 0xF7, 0xBD, 0xFF, 0xE0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L1.c new file mode 100644 index 000000000..c70e35334 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L1.c @@ -0,0 +1,16 @@ +// ......##############....##....##.. +// ..####..##....##....##....##....## +// ##....##....##....##....##....##.. +// ##..##....##....##....##....##.... +// ..##....##....##....############## +// ##....##############.............. + +#include "images.h" + +const image_t img_cc_trg_L1 = { + 17, + 6, + false, + 13, + 0, + {0x1F, 0xC9, 0x34, 0x92, 0x64, 0x92, 0x54, 0x92, 0x44, 0x93, 0xFC, 0xFE, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L2.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L2.c new file mode 100644 index 000000000..47561ab98 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L2.c @@ -0,0 +1,28 @@ +// ......##############..##..##..##.. +// ..####..##..##..##..##..##..##..## +// ####..##..##..##..##..##..##..##.. +// ##..##..##..##..##..##..##..##..## +// ..##..##..##..##..################ +// ##..##..############.............. + +#include "images.h" + +const image_t img_cc_trg_L2 = { + 17, + 6, + true, + 12, + 0x01, + {// orig:13, comp:7.69% + 0x1F, + 0xD5, + 0x35, + 0x55, + 0x75, + 0x01, + 0x04, + 0x55, + 0x57, + 0xFD, + 0x7E, + 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L3.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L3.c new file mode 100644 index 000000000..0b51bed35 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L3.c @@ -0,0 +1,16 @@ +// ......############..####..####..## +// ..######..####..####..####..####.. +// ######..####..####..####..####..## +// ####..####..####..####..####..#### +// ##..####..####..################## +// ..####..############.............. + +#include "images.h" + +const image_t img_cc_trg_L3 = { + 17, + 6, + false, + 13, + 0, + {0x1F, 0xB6, 0xBB, 0x6D, 0xBB, 0x6D, 0xBB, 0x6D, 0xBB, 0x6F, 0xFB, 0x7E, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L4.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L4.c new file mode 100644 index 000000000..062caca77 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L4.c @@ -0,0 +1,24 @@ +// ......############################ +// ..################################ +// ################################## +// ################################## +// ################################## +// ####################.............. + +#include "images.h" + +const image_t img_cc_trg_L4 = { + 17, + 6, + true, + 8, + 0x01, + {// orig:13, comp:38.46% + 0x1F, + 0xFF, + 0xBF, + 0x01, + 0x08, + 0xFF, + 0xFE, + 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R1.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R1.c new file mode 100644 index 000000000..6f08886d3 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R1.c @@ -0,0 +1,16 @@ +// ..##....##....##############...... +// ##....##....##....##....##..####.. +// ..##....##....##....##....##....## +// ....##....##....##....##....##..## +// ##############....##....##....##.. +// ..............##############....## + +#include "images.h" + +const image_t img_cc_trg_R1 = { + 17, + 6, + false, + 13, + 0, + {0x49, 0xFC, 0x49, 0x25, 0x92, 0x49, 0x24, 0x92, 0x5F, 0xE4, 0x90, 0x0F, 0xE4}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R2.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R2.c new file mode 100644 index 000000000..d85e45761 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R2.c @@ -0,0 +1,16 @@ +// ..##..##..##..##############...... +// ##..##..##..##..##..##..##..####.. +// ..##..##..##..##..##..##..##..#### +// ##..##..##..##..##..##..##..##..## +// ################..##..##..##..##.. +// ..............############..##..## + +#include "images.h" + +const image_t img_cc_trg_R2 = { + 17, + 6, + false, + 13, + 0, + {0x55, 0xFC, 0x55, 0x55, 0x95, 0x55, 0x75, 0x55, 0x5F, 0xF5, 0x50, 0x0F, 0xD4}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R3.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R3.c new file mode 100644 index 000000000..082d160e2 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R3.c @@ -0,0 +1,16 @@ +// ##..####..####..############...... +// ..####..####..####..####..######.. +// ##..####..####..####..####..###### +// ####..####..####..####..####..#### +// ##################..####..####..## +// ..............############..####.. + +#include "images.h" + +const image_t img_cc_trg_R3 = { + 17, + 6, + false, + 13, + 0, + {0xB6, 0xFC, 0x36, 0xDB, 0xAD, 0xB6, 0xFB, 0x6D, 0xBF, 0xFB, 0x68, 0x0F, 0xD8}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R4.c b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R4.c new file mode 100644 index 000000000..0395058b8 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R4.c @@ -0,0 +1,27 @@ +// ############################...... +// ################################.. +// ################################## +// ################################## +// ################################## +// ..............#################### + +#include "images.h" + +const image_t img_cc_trg_R4 = { + 17, + 6, + true, + 11, + 0x00, + {// orig:13, comp:15.38% + 0xFF, + 0xFC, + 0x7F, + 0xFF, + 0xBF, + 0x00, + 0x05, + 0xFF, + 0xF8, + 0x0F, + 0xFC}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_csLogo_FULL.c b/applications/plugins/wii_ec_anal/gfx/img_csLogo_FULL.c new file mode 100644 index 000000000..a8c030fa2 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_csLogo_FULL.c @@ -0,0 +1,89 @@ +// ....##########################################........##..........##........##############........##############........##############........##############............................................................................................ +// ....##########################################......######......######......##############........##############........##############........##############............................................................................................ +// ############..............................######....######......######....####........######....######......######................######....######......######..............######..######..######...................................................... +// ############..............................##..##....##..##......##..##....######......######....######......######....######......######....######......##..##..............##......##........##........................................................ +// ############..............................##..##....##..##......##..##....######......##..##....##..##......##..##....######......##..##....##..##......##..##..............####....######....##........................................................ +// ####....####..............................##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##..............##..........##....##........................................................ +// ############..............................######....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......######..............######..######....##..##.................................................... +// ############........................................##..##......##..##....##..##......######....##..##..##..##..##....##..##......######....##..##...................................................................................................... +// ####....####........................................######......######....##..##########........##..##......##..##....##..##########........##..##........................####..######..######..##...................................................... +// ######..####........####..............................##############......##..##########........##..##......##..##....##..##########........##..##..########................##..##..##..##..##..##..##.................................................. +// ####..######........####................................####..####........##..##......######....##..##..##..##..##....##..##....######......##..##..##########..............##..######..######..######.................................................. +// ######..####................####..........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......##..##......##..##..............##......##..##..##......##.................................................. +// ####..######..............##....##........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......##..##......##..##............######....##..######......##..................................##.............. +// ######..####..............##....##........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......######......##..##........................................................................##..##............ +// ####..######................####..........................##..##..........######......######....######......######....##..##....##..##......######......##..##..................................................................######......##.......... +// ####....####..............................................######..........######......######....######......######....##..##....##..##......####........######..................................................................####....##....##........ +// ####....####..............................................######............##############........##############......######....######........##############....................................................................##....##..##....##...... +// ####....####............................................##########..........##############........##############......######......######......##############..............................................................######....##......##....##.... +// ####....####..............................................................................................................................................................................................................####........##..##..##....##.. +// ####....####..............................................................................................................................................................................................................##....##......##......##....## +// ####....####....................##############........##..........##........##############......##################......##############........##############........##############..................................######....##..........##..##....#### +// ####....####....................##############......######......######......##############......##################......##############........##############........##############..................................####....##......##......##....###### +// ####..######..................####........######....######......######....####........######....####..######..####....####....##########....##################....####........######................................##........##..##..##........######## +// ######..####..................######......##..##....##..##......##..##....######......##..##..........##..##..........######................######..##..######....######......##..##..........................######....##......##......##....########.. +// ####..######..................##..##......##..##....##..##......##..##....##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##..........................####....##..##..........##....##########.. +// ######..####..................##..##......##..##....##..##......##..##....##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##..........................##....##......##......##....############## +// ####..######..................##..##......######....##..##......##..##....##..##......######..........##..##..........##..##................##..##..##..##..##....##..##......######....................######............##..##........########....#### +// ######..####..................######................##..##......##..##....######......................##..##..........##..##....######......##..##..##..##..##....######................................####....................##....##########........ +// ####....####....................############........######......######......############..............##..##..........##..########..........##..##..##..##..##......############........................##....##..............##....##############...... +// ############........................##########........##############............##########............##..##..........##..########..........##..##..##..##..##..........##########....................##....##..##..........##....########....####...... +// ############..............................######........####..####....................######..........##..##..........##..##....######......##..##..##..##..##................######................##..##........##............##########.............. +// ####....####..................######......##..##..........##..##..........######......##..##..........##..##..........##..##................##..##......##..##....######......##..##................####............##..##....##############............ +// ############..................##..##......##..##..........##..##..........##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##................####....##........##....########....####............ +// ############..........####....##..##......##..##..........##..##..........##..##......##..##..........##..##..........##..##................##..##......##..##....##..##......##..##..................##########....##....##########.................... +// ############..........####....##..##......######..........##..##..........##..##......######..........##..##..........######................##..##......##..##....##..##......######....................######..........##############.................. +// ############..........####....######........####..........######..........######........####..........##..##..........####....##########....##..##......##..##....######........####......................####........########....####.................. +// ....######################......##############............######............##############............######............##############......######......######......##############..........................######..##########.......................... +// ....######################......##############..........##########..........##############............######............##############......######......######......##############............................##################........................ +// ................................................................................................................................................................................................................########....####........................ +// ..................................................................................................................................................................................................................####.................................. + +#include "images.h" + +const image_t img_csLogo_FULL = { + 124, + 40, + true, + 571, + 0x0B, + {// orig:620, comp:7.90% + 0x3F, 0xFF, 0xFE, 0x10, 0x43, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, 0x0B, 0x05, 0x00, 0x03, 0xFF, + 0xFF, 0xE3, 0x8E, 0x3F, 0x87, 0xF0, 0xFE, 0x1F, 0xC0, 0x0B, 0x05, 0x00, 0xFC, 0x00, 0x07, + 0x38, 0xE6, 0x1C, 0xE3, 0x80, 0x73, 0x8E, 0x03, 0xBB, 0x80, 0x00, 0x00, 0x0F, 0xC0, 0x00, + 0x52, 0x8A, 0x71, 0xCE, 0x39, 0xC7, 0x38, 0xA0, 0x22, 0x10, 0x00, 0x00, 0x00, 0xFC, 0x00, + 0x05, 0x28, 0xA7, 0x14, 0xA2, 0x9C, 0x52, 0x8A, 0x03, 0x39, 0x00, 0x00, 0x00, 0x0C, 0xC0, + 0x00, 0x52, 0x8A, 0x51, 0x4A, 0x29, 0x45, 0x28, 0xA0, 0x20, 0x90, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x07, 0x28, 0xA5, 0x14, 0xA2, 0x94, 0x52, 0x8E, 0x03, 0xB9, 0x40, 0x00, 0x00, 0x0F, + 0xC0, 0x00, 0x02, 0x8A, 0x51, 0xCA, 0xA9, 0x47, 0x28, 0x0B, 0x06, 0x00, 0xCC, 0x00, 0x00, + 0x38, 0xE5, 0xF0, 0xA2, 0x97, 0xC2, 0x80, 0x06, 0xEE, 0x80, 0x00, 0x00, 0x0E, 0xC3, 0x00, + 0x01, 0xFC, 0x5F, 0x0A, 0x29, 0x7C, 0x2B, 0xC0, 0x2A, 0xAA, 0x00, 0x00, 0x00, 0xDC, 0x30, + 0x00, 0x0D, 0x85, 0x1C, 0xAA, 0x94, 0xE2, 0xBE, 0x02, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xC0, + 0x30, 0x00, 0x50, 0x51, 0x4A, 0x29, 0x4A, 0x28, 0xA0, 0x22, 0xA2, 0x00, 0x00, 0x00, 0xDC, + 0x04, 0x80, 0x05, 0x05, 0x14, 0xA2, 0x94, 0xA2, 0x8A, 0x07, 0x2E, 0x20, 0x00, 0x08, 0x0E, + 0xC0, 0x48, 0x00, 0x50, 0x51, 0x4A, 0x29, 0x4A, 0x38, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x40, + 0xDC, 0x03, 0x00, 0x05, 0x07, 0x1C, 0xE3, 0x94, 0xA3, 0x8A, 0x0B, 0x04, 0x00, 0xE2, 0x0C, + 0xC0, 0x00, 0x00, 0x70, 0x71, 0xCE, 0x39, 0x4A, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x0C, 0x90, + 0xCC, 0x00, 0x00, 0x07, 0x03, 0xF8, 0x7F, 0x1C, 0xE1, 0xFC, 0x0B, 0x04, 0x00, 0x94, 0x8C, + 0xC0, 0x00, 0x00, 0xF8, 0x3F, 0x87, 0xF1, 0xC7, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x72, 0x24, + 0xCC, 0x0B, 0x0C, 0x00, 0x06, 0x15, 0x2C, 0xC0, 0x0B, 0x0C, 0x00, 0x48, 0x89, 0xCC, 0x00, + 0xFE, 0x10, 0x43, 0xF8, 0xFF, 0x8F, 0xE1, 0xFC, 0x3F, 0x80, 0x00, 0x39, 0x05, 0x3C, 0xC0, + 0x0F, 0xE3, 0x8E, 0x3F, 0x8F, 0xF8, 0xFE, 0x1F, 0xC3, 0xF8, 0x00, 0x03, 0x22, 0x27, 0xDC, + 0x01, 0x87, 0x38, 0xE6, 0x1C, 0xDD, 0x99, 0xF3, 0xFE, 0x61, 0xC0, 0x00, 0x21, 0x50, 0xFE, + 0xC0, 0x1C, 0x52, 0x8A, 0x71, 0x41, 0x41, 0xC0, 0x3A, 0xE7, 0x14, 0x00, 0x1C, 0x88, 0x9E, + 0xDC, 0x01, 0x45, 0x28, 0xA5, 0x14, 0x14, 0x14, 0x02, 0xAA, 0x51, 0x40, 0x01, 0x94, 0x13, + 0xEE, 0xC0, 0x14, 0x52, 0x8A, 0x51, 0x41, 0x41, 0x40, 0x2A, 0xA5, 0x14, 0x00, 0x12, 0x22, + 0x7F, 0xDC, 0x01, 0x47, 0x28, 0xA5, 0x1C, 0x14, 0x14, 0x02, 0xAA, 0x51, 0xC0, 0x0E, 0x05, + 0x0F, 0x3E, 0xC0, 0x1C, 0x02, 0x8A, 0x70, 0x01, 0x41, 0x4E, 0x2A, 0xA7, 0x00, 0x00, 0xC0, + 0x09, 0xF0, 0xCC, 0x00, 0xFC, 0x38, 0xE3, 0xF0, 0x14, 0x17, 0x82, 0xAA, 0x3F, 0x00, 0x09, + 0x01, 0x3F, 0x8F, 0xC0, 0x03, 0xE1, 0xFC, 0x0F, 0x81, 0x41, 0x78, 0x2A, 0xA0, 0xF8, 0x01, + 0x28, 0x27, 0x98, 0xFC, 0x00, 0x07, 0x0D, 0x80, 0x1C, 0x14, 0x14, 0xE2, 0xAA, 0x01, 0xC0, + 0x28, 0x40, 0xF8, 0x0C, 0xC0, 0x1C, 0x50, 0x50, 0x71, 0x41, 0x41, 0x40, 0x28, 0xA7, 0x14, + 0x03, 0x02, 0x9F, 0xC0, 0xFC, 0x01, 0x45, 0x05, 0x05, 0x14, 0x14, 0x14, 0x02, 0xAA, 0x51, + 0x40, 0x32, 0x13, 0xCC, 0x0F, 0xC1, 0x94, 0x50, 0x50, 0x51, 0x41, 0x41, 0x40, 0x28, 0xA5, + 0x14, 0x01, 0xF2, 0x7C, 0x00, 0xFC, 0x19, 0x47, 0x05, 0x05, 0x1C, 0x14, 0x1C, 0x02, 0x8A, + 0x51, 0xC0, 0x0E, 0x0F, 0xE0, 0x0F, 0xC1, 0x9C, 0x30, 0x70, 0x70, 0xC1, 0x41, 0x9F, 0x28, + 0xA7, 0x0C, 0x00, 0x61, 0xE6, 0x00, 0x3F, 0xF8, 0xFE, 0x07, 0x03, 0xF8, 0x1C, 0x0F, 0xE3, + 0x8E, 0x3F, 0x80, 0x03, 0xBE, 0x00, 0x03, 0xFF, 0x8F, 0xE0, 0xF8, 0x3F, 0x81, 0xC0, 0xFE, + 0x38, 0xE3, 0xF8, 0x00, 0x1F, 0xF0, 0x0B, 0x0E, 0x00, 0xF3, 0x0B, 0x0E, 0x00, 0x06, 0x00, + 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_csLogo_Small.c b/applications/plugins/wii_ec_anal/gfx/img_csLogo_Small.c new file mode 100644 index 000000000..71debc2ff --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_csLogo_Small.c @@ -0,0 +1,22 @@ +// ################## +// ################## +// ####..........#### +// ####..........#### +// ####..##.......... +// ####......######## +// ####......##....## +// ####......##...... +// ####......######## +// ####............## +// ########..##....## +// ########..######## + +#include "images.h" + +const image_t img_csLogo_Small = { + 9, + 12, + false, + 14, + 0, + {0xFF, 0xFF, 0xF0, 0x78, 0x3D, 0x06, 0x3F, 0x13, 0x88, 0xC7, 0xE0, 0x7D, 0x3E, 0xF0}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_SCL.c b/applications/plugins/wii_ec_anal/gfx/img_ecp_SCL.c new file mode 100644 index 000000000..e3622a626 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_ecp_SCL.c @@ -0,0 +1,17 @@ +// ....##############......######## +// ....##############......######## +// ....####......####......####.... +// ....####......####......####.... +// ....####......####......####.... +// ########......##############.... +// ########......##############.... + +#include "images.h" + +const image_t img_ecp_SCL = { + 16, + 7, + false, + 14, + 0, + {0x3F, 0x8F, 0x3F, 0x8F, 0x31, 0x8C, 0x31, 0x8C, 0x31, 0x8C, 0xF1, 0xFC, 0xF1, 0xFC}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_SDA.c b/applications/plugins/wii_ec_anal/gfx/img_ecp_SDA.c new file mode 100644 index 000000000..5ce0cbec4 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_ecp_SDA.c @@ -0,0 +1,19 @@ +// ......##.......................... +// ....####.......................... +// ..####............................ +// ######################............ +// ######################....##...... +// ..####....................####.... +// ....####....................####.. +// ......##....###################### +// ............###################### +// ............................####.. +// ..........................####.... +// ..........................##...... + +#include "images.h" + +const image_t img_ecp_SDA = {17, 12, false, 26, 0, {0x10, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1F, + 0xFC, 0x0F, 0xFE, 0x43, 0x00, 0x30, 0xC0, + 0x0C, 0x27, 0xFF, 0x03, 0xFF, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x00, 0x80}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_port.c b/applications/plugins/wii_ec_anal/gfx/img_ecp_port.c new file mode 100644 index 000000000..60f535458 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_ecp_port.c @@ -0,0 +1,72 @@ +// ....................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..## +// ..................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##.. +// ................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..## +// ..............##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##.. +// ............##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..## +// ..........##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##.. +// ........##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..## +// ......######################################################################################################################..##..##..##.. +// ......########################################################################################################################..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####..............................................................................................................######..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####..............................................................................................................######..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####........##############################################################################################........######..##..##..## +// ......####........##############################################################################################........####..##..##..##.. +// ......####........####........####..........####........####..........####........####..........####........####........######..##..##..## +// ......####........####........####..........####........####..........####........####..........####........####........####..##..##..##.. +// ......####........####........##################........##################........##################........####........######..##..##..## +// ......####........####........##################........##################........##################........####........####..##..##..##.. +// ......####........####......................................................................................####........######..##..##..## +// ..########........####......................................................................................####........####..##########.. +// ##########........####......................................................................................####........################## +// ##########........####......................................................................................####........####..##########.. +// ..########........####......................................................................................####........################## +// ......####........####........##################..................................##################........####........####..##..##..##.. +// ......####........####........##################..................................##################........####........######..##..##..## +// ......####........####........####..........####........##################........####..........####........####........####..##..##..##.. +// ......####........####........####..........####........##..............##........####..........####........####........######..##..##..## +// ......####........##############################################################################################........####..##..##..##.. +// ......####........##############################################################################################........######..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####..............................................................................................................######..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####....................................######################################....................................######..##..##..## +// ......####....................................######################################....................................####..##..##..##.. +// ......####....................................####..##..##..##..................####....................................######..##..##.... +// ......####....................................######..##..##....................####....................................####..##..##...... +// ......####....................................####..##..##......................####....................................######..##........ +// ......####....................................######..##........................####....................................####..##.......... +// ......############################################..##..........................##############################################............ +// ......##############################################............................############################################.............. + +#include "images.h" + +const image_t img_ecp_port = { + 69, + 42, + true, + 290, + 0x04, + {// orig:363, comp:20.11% + 0x00, 0x2A, 0x04, 0x06, 0xAA, 0xA8, 0x02, 0x04, 0x07, 0xAA, 0x80, 0x2A, 0x04, 0x07, 0xAA, + 0x02, 0x04, 0x07, 0xAA, 0xA0, 0x2A, 0x04, 0x07, 0xAA, 0x82, 0x04, 0x07, 0xAA, 0xA8, 0x2A, + 0x04, 0x07, 0xAA, 0xA3, 0x04, 0x07, 0xFF, 0xAA, 0x1F, 0x04, 0x06, 0xFF, 0xFE, 0xA8, 0xC0, + 0x04, 0x06, 0x00, 0x6A, 0x86, 0x04, 0x06, 0x00, 0x03, 0xAA, 0x30, 0x04, 0x06, 0x00, 0x1A, + 0xA1, 0x80, 0x04, 0x06, 0x00, 0xEA, 0x8C, 0x04, 0x06, 0x00, 0x06, 0xA8, 0x61, 0x04, 0x05, + 0xFF, 0xFC, 0x3A, 0xA3, 0x0F, 0x04, 0x05, 0xFF, 0xE1, 0xAA, 0x18, 0x61, 0x83, 0x0C, 0x18, + 0x60, 0xC3, 0x0E, 0xA8, 0xC3, 0x0C, 0x18, 0x60, 0xC3, 0x06, 0x18, 0x6A, 0x86, 0x18, 0x7F, + 0xC3, 0xFE, 0x1F, 0xF0, 0xC3, 0xAA, 0x30, 0xC3, 0xFE, 0x1F, 0xF0, 0xFF, 0x86, 0x1A, 0xA1, + 0x86, 0x04, 0x05, 0x00, 0x30, 0xEA, 0xBC, 0x30, 0x04, 0x04, 0x00, 0x01, 0x86, 0xFB, 0xE1, + 0x80, 0x04, 0x04, 0x00, 0x0C, 0x3F, 0xFF, 0x0C, 0x04, 0x05, 0x00, 0x61, 0xBE, 0x78, 0x60, + 0x04, 0x04, 0x00, 0x03, 0x0F, 0xF8, 0xC3, 0x0F, 0xF8, 0x00, 0x03, 0xFE, 0x18, 0x6A, 0x86, + 0x18, 0x7F, 0xC0, 0x00, 0x1F, 0xF0, 0xC3, 0xAA, 0x30, 0xC3, 0x06, 0x1F, 0xF0, 0xC1, 0x86, + 0x1A, 0xA1, 0x86, 0x18, 0x30, 0x80, 0x86, 0x0C, 0x30, 0xEA, 0x8C, 0x3F, 0x04, 0x05, 0xFF, + 0x86, 0xA8, 0x61, 0x04, 0x05, 0xFF, 0xFC, 0x3A, 0xA3, 0x04, 0x06, 0x00, 0x01, 0xAA, 0x18, + 0x04, 0x06, 0x00, 0x0E, 0xA8, 0xC0, 0x04, 0x06, 0x00, 0x6A, 0x86, 0x00, 0x00, 0x7F, 0xFF, + 0xF0, 0x00, 0x03, 0xAA, 0x30, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x1A, 0xA1, 0x80, 0x00, + 0x1A, 0xA0, 0x0C, 0x00, 0x00, 0xEA, 0x0C, 0x00, 0x00, 0xEA, 0x00, 0x60, 0x00, 0x06, 0xA0, + 0x60, 0x00, 0x06, 0xA0, 0x03, 0x00, 0x00, 0x3A, 0x03, 0x00, 0x00, 0x3A, 0x00, 0x18, 0x00, + 0x01, 0xA0, 0x1F, 0xFF, 0xFF, 0xA0, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFE, 0x00, + 0x07, 0xFF, 0xFF, 0xE0, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_Back.c b/applications/plugins/wii_ec_anal/gfx/img_key_Back.c new file mode 100644 index 000000000..23c17fe2b --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_key_Back.c @@ -0,0 +1,14 @@ +// ..##############.. +// ################## +// ######..########## +// ####........###### +// ######..####..#### +// ############..#### +// ########....###### +// ################## +// ....############.. + +#include "images.h" + +const image_t img_key_Back = + {9, 9, false, 11, 0, {0x7F, 0x7F, 0xFB, 0xF8, 0x7E, 0xDF, 0xEF, 0xCF, 0xFF, 0x3F, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_D.c b/applications/plugins/wii_ec_anal/gfx/img_key_D.c new file mode 100644 index 000000000..689b9148c --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_key_D.c @@ -0,0 +1,13 @@ +// ..##############.. +// ################## +// ################## +// ####..........#### +// ######......###### +// ########..######## +// ################## +// ..##############.. + +#include "images.h" + +const image_t img_key_D = + {9, 8, false, 9, 0, {0x7F, 0x7F, 0xFF, 0xF8, 0x3E, 0x3F, 0xBF, 0xFE, 0xFE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_L.c b/applications/plugins/wii_ec_anal/gfx/img_key_L.c new file mode 100644 index 000000000..a5fca1a21 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_key_L.c @@ -0,0 +1,14 @@ +// ..############.. +// ################ +// ########..###### +// ######....###### +// ####......###### +// ######....###### +// ########..###### +// ################ +// ..############.. + +#include "images.h" + +const image_t img_key_L = + {8, 9, false, 9, 0, {0x7E, 0xFF, 0xF7, 0xE7, 0xC7, 0xE7, 0xF7, 0xFF, 0x7E}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_OK.c b/applications/plugins/wii_ec_anal/gfx/img_key_OK.c new file mode 100644 index 000000000..926d91c2e --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_key_OK.c @@ -0,0 +1,14 @@ +// ..##############.. +// ################## +// ######......###### +// ####..........#### +// ####..........#### +// ####..........#### +// ######......###### +// ################## +// ....############.. + +#include "images.h" + +const image_t img_key_OK = + {9, 9, false, 11, 0, {0x7F, 0x7F, 0xF8, 0xF8, 0x3C, 0x1E, 0x0F, 0x8F, 0xFF, 0x3F, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_OKi.c b/applications/plugins/wii_ec_anal/gfx/img_key_OKi.c new file mode 100644 index 000000000..aa6f9e692 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_key_OKi.c @@ -0,0 +1,14 @@ +// ..##############.. +// ####..........#### +// ##....######....## +// ##..##########..## +// ##..##########..## +// ##..##########..## +// ##....######....## +// ####..........#### +// ..##############.. + +#include "images.h" + +const image_t img_key_OKi = + {9, 9, false, 11, 0, {0x7F, 0x60, 0xE7, 0x37, 0xDB, 0xED, 0xF6, 0x73, 0x83, 0x7F, 0x00}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_R.c b/applications/plugins/wii_ec_anal/gfx/img_key_R.c new file mode 100644 index 000000000..8b97c7b48 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_key_R.c @@ -0,0 +1,14 @@ +// ..############.. +// ################ +// ######..######## +// ######....###### +// ######......#### +// ######....###### +// ######..######## +// ################ +// ..############.. + +#include "images.h" + +const image_t img_key_R = + {8, 9, false, 9, 0, {0x7E, 0xFF, 0xEF, 0xE7, 0xE3, 0xE7, 0xEF, 0xFF, 0x7E}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_U.c b/applications/plugins/wii_ec_anal/gfx/img_key_U.c new file mode 100644 index 000000000..65f4cd9e0 --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_key_U.c @@ -0,0 +1,13 @@ +// ..##############.. +// ################## +// ########..######## +// ######......###### +// ####..........#### +// ################## +// ################## +// ..##############.. + +#include "images.h" + +const image_t img_key_U = + {9, 8, false, 9, 0, {0x7F, 0x7F, 0xFD, 0xFC, 0x7C, 0x1F, 0xFF, 0xFE, 0xFE}}; diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_Ui.c b/applications/plugins/wii_ec_anal/gfx/img_key_Ui.c new file mode 100644 index 000000000..30c60c66e --- /dev/null +++ b/applications/plugins/wii_ec_anal/gfx/img_key_Ui.c @@ -0,0 +1,13 @@ +// ..##############.. +// ####..........#### +// ##......##......## +// ##....######....## +// ##..##########..## +// ##..............## +// ####..........#### +// ..##############.. + +#include "images.h" + +const image_t img_key_Ui = + {9, 8, false, 9, 0, {0x7F, 0x60, 0xE2, 0x33, 0x9B, 0xEC, 0x07, 0x06, 0xFE}}; diff --git a/applications/plugins/wii_ec_anal/i2c_workaround.h b/applications/plugins/wii_ec_anal/i2c_workaround.h new file mode 100644 index 000000000..b24efaf48 --- /dev/null +++ b/applications/plugins/wii_ec_anal/i2c_workaround.h @@ -0,0 +1,131 @@ +/* + As of the date of releasing this code, there is (seemingly) a bug in the FZ i2c library code + It is described here: https://github.com/flipperdevices/flipperzero-firmware/issues/1670 + + This is a short-term workaround so I can keep developing while we get to the bottom of the issue + + FYI. *something* in the following code is the fix + +void furi_hal_i2c_acquire (FuriHalI2cBusHandle* handle) +{ + // 1. Disable the power/backlight (it uses i2c) + furi_hal_power_insomnia_enter(); + // 2. Lock bus access + handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); + // 3. Ensuree that no active handle set + furi_check(handle->bus->current_handle == NULL); + // 4. Set current handle + handle->bus->current_handle = handle; + // 5. Activate bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate); + // 6. Activate handle + handle->callback(handle, FuriHalI2cBusHandleEventActivate); +} + +void furi_hal_i2c_release (FuriHalI2cBusHandle* handle) +{ + // Ensure that current handle is our handle + furi_check(handle->bus->current_handle == handle); + // 6. Deactivate handle + handle->callback(handle, FuriHalI2cBusHandleEventDeactivate); + // 5. Deactivate bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate); + // 3,4. Reset current handle + handle->bus->current_handle = NULL; + // 2. Unlock bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock); + // 1. Re-enable the power system + furi_hal_power_insomnia_exit(); +} + +*/ + +#ifndef I2C_WORKAROUND_H_ +#define I2C_WORKAROUND_H_ + +#include + +#define ENABLE_WORKAROUND 1 + +#if ENABLE_WORKAROUND == 1 +//+============================================================================ ======================================== +static inline bool furi_hal_Wi2c_is_device_ready( + FuriHalI2cBusHandle* const bus, + const uint8_t addr, + const uint32_t tmo) { + furi_hal_i2c_acquire(bus); + bool rv = furi_hal_i2c_is_device_ready(bus, addr, tmo); + furi_hal_i2c_release(bus); + return rv; +} + +//+============================================================================ +static inline bool furi_hal_Wi2c_tx( + FuriHalI2cBusHandle* const bus, + const uint8_t addr, + const void* buf, + const size_t len, + const uint32_t tmo) { + furi_hal_i2c_acquire(bus); + bool rv = furi_hal_i2c_tx(bus, addr, buf, len, tmo); + furi_hal_i2c_release(bus); + return rv; +} + +//+============================================================================ +static inline bool furi_hal_Wi2c_rx( + FuriHalI2cBusHandle* const bus, + const uint8_t addr, + void* buf, + const size_t len, + const uint32_t tmo) { + furi_hal_i2c_acquire(bus); + bool rv = furi_hal_i2c_rx(bus, addr, buf, len, tmo); + furi_hal_i2c_release(bus); + return rv; +} + +//+============================================================================ +static inline bool furi_hal_Wi2c_trx( + FuriHalI2cBusHandle* const bus, + const uint8_t addr, + const void* tx, + const size_t txlen, + void* rx, + const size_t rxlen, + const uint32_t tmo) { + bool rv = furi_hal_Wi2c_tx(bus, addr, tx, txlen, tmo); + if(rv) rv = furi_hal_Wi2c_rx(bus, addr, rx, rxlen, tmo); + return rv; +} + +//----------------------------------------------------------------------------- ---------------------------------------- +#define furi_hal_i2c_is_device_ready(...) furi_hal_Wi2c_is_device_ready(__VA_ARGS__) +#define furi_hal_i2c_tx(...) furi_hal_Wi2c_tx(__VA_ARGS__) +#define furi_hal_i2c_rx(...) furi_hal_Wi2c_rx(__VA_ARGS__) +#define furi_hal_i2c_trx(...) furi_hal_Wi2c_trx(__VA_ARGS__) + +#endif //ENABLE_WORKAROUND + +//+============================================================================ ======================================== +// Some devices take a moment to respond to read requests +// The puts a delay between the address being set and the data being read +// +static inline bool furi_hal_i2c_trxd( + FuriHalI2cBusHandle* const bus, + const uint8_t addr, + const void* tx, + const size_t txlen, + void* rx, + const size_t rxlen, + const uint32_t tmo, + const uint32_t us) { + bool rv = furi_hal_i2c_tx(bus, addr, tx, txlen, tmo); + if(rv) { + furi_delay_us(us); + rv = furi_hal_i2c_rx(bus, addr, rx, rxlen, tmo); + } + return rv; +} + +#endif //I2C_WORKAROUND_H_ diff --git a/applications/plugins/wii_ec_anal/info.sh b/applications/plugins/wii_ec_anal/info.sh new file mode 100644 index 000000000..e009eb118 --- /dev/null +++ b/applications/plugins/wii_ec_anal/info.sh @@ -0,0 +1,11 @@ +echo "MARKED AS TODO" +echo "==============" +grep //! *.c *.h + +echo -e "\nSUPPORTED CONTROLLERS" +echo "=====================" +grep '\[PID_.*{ {' wii_ec.c | head -n -3 | sed 's/\s*\(.*\)/\1/' + +echo -e "\nLOGGING" +echo "=======" +grep LOG_LEVEL *.h | grep -v '#if ' diff --git a/applications/plugins/wii_ec_anal/notes.txt b/applications/plugins/wii_ec_anal/notes.txt new file mode 100644 index 000000000..61b6e29af --- /dev/null +++ b/applications/plugins/wii_ec_anal/notes.txt @@ -0,0 +1,87 @@ +//+============================================================================ ======================================== +// Select font +// A full list of u8g2 fonts can be found here: +// https://github.com/olikraus/u8g2/wiki/fntlistall +// ...and here are the ones available in FZ (currently: all of them): +// grep -P '.*u8g2.*\[[0-9]*\]' lib/u8g2/u8g2_fonts.c | sed 's/.*\(u8g2_.*\)\[.*/\1/' +// +#if 0 //! Extra fonts is just too memory hungry +#include +void setFont (Canvas* const canvas, const uint8_t* font) +{ + u8g2_SetFontMode(&canvas->fb, 1); // no idea - but canvas.c does it + u8g2_SetFont(&canvas->fb, font); +} +#endif + +litui : @BlueChip for posterity, the function to break at is flipper_application_spawn. At that point, you can set new breakpoints in your fap code and continue. + +/* + +This is wrong on quite a few levels! +https://training.ti.com/introduction-i2c-reserved-addresses + +void doit (void) +{ + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + printf("Scanning external i2c on PC0(SCL)/PC1(SDA)\r\n" + "Clock: 100khz, 7bit address\r\n" + "\r\n"); + printf(" | 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"); + printf("--+--------------------------------\r\n"); + for(uint8_t row = 0; row < 0x8; row++) { + printf("%x | ", row); + for(uint8_t column = 0; column <= 0xF; column++) { + bool ret = furi_hal_i2c_is_device_ready( + &furi_hal_i2c_handle_external, ((row << 4) + column) << 1, 2); + printf("%c ", ret ? '#' : '-'); + } + printf("\r\n"); + } + furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} +*/ + + +region locking : firmware/targets/f7/furi_hal/furi_hal_region.c + + +# if 0 //! scrolling works beautifully, but the LCD refresh can't keep up :( + // Waveform + if (cnt) { // start + for (int a = ACC_1; a < ACC_N; a++) { + canvas_draw_dot(canvas, x,y[a]+v[a][idx]); + for (int i = 1; i < aw -cnt; i++) { + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + } + } + } else { // scroll + for (int a = ACC_1; a < ACC_N; a++) { + for (int i = 0; i < aw; i++) { + int off = (idx +i) %aw; + int prev = off ? off-1 : aw-1; + canvas_draw_line(canvas, x+i,y[a]+v[a][prev] , x+i,y[a]+v[a][off]); + } + } + } + +# else + int end = idx ? idx : aw; + for (int a = ACC_1; a < ACC_N; a++) { + canvas_draw_dot(canvas, x,y[a]+v[a][idx]); + if (state->apause) { + for (int i = 1; i < end; i++) + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + } else { + for (int i = 1; i < end; i++) + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + for (int i = end+10; i < aw -cnt; i++) + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + } + } + + // Wipe bar + if (end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); + if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); + if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); +# endif diff --git a/applications/plugins/wii_ec_anal/wii_anal.c b/applications/plugins/wii_ec_anal/wii_anal.c new file mode 100644 index 000000000..f0af1c9c5 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal.c @@ -0,0 +1,543 @@ +//----------------------------------------------------------------------------- ---------------------------------------- +// Includes +// + +// System libs +#include // malloc +#include // uint32_t +#include // __VA_ARGS__ +#include +#include + +// FlipperZero libs +#include // Core API +#include // GUI (screen/keyboard) API +#include // GUI Input extensions +#include + +// Do this first! +#define ERR_C_ // Do this in precisely ONE file +#include "err.h" // Error numbers & messages + +#include "bc_logging.h" + +// Local headers +#include "wii_anal.h" // Various enums and struct declarations +#include "wii_i2c.h" // Wii i2c functions +#include "wii_ec.h" // Wii Extension Controller functions (eg. draw) +#include "wii_anal_keys.h" // key mappings +#include "gfx/images.h" // Images +#include "wii_anal_lcd.h" // Drawing functions +#include "wii_anal_ec.h" // Wii controller events + +#include "wii_anal_ver.h" // Version number + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// OOOOO // SSSSS CCCCC AAA L L BBBB AAA CCCC K K SSSSS +// O O /// S C A A L L B B A A C K K S +// O O /// SSSSS C AAAAA L L BBBB AAAAA C KKK SSSSS +// O O /// S C A A L L B B A A C K K S +// OOOOO // SSSSS CCCCC A A LLLLL LLLLL BBBB A A CCCC K K SSSSS +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//+============================================================================ ======================================== +// OS Callback : Timer tick +// We register this function to be called when the OS signals a timer 'tick' event +// +static void cbTimer(FuriMessageQueue* queue) { + ENTER; + furi_assert(queue); + + eventMsg_t message = {.id = EVID_TICK}; + furi_message_queue_put(queue, &message, 0); + + LEAVE; + return; +} + +//+============================================================================ ======================================== +// OS Callback : Keypress +// We register this function to be called when the OS detects a keypress +// +static void cbInput(InputEvent* event, FuriMessageQueue* queue) { + ENTER; + furi_assert(queue); + furi_assert(event); + + // Put an "input" event message on the message queue + eventMsg_t message = {.id = EVID_KEY, .input = *event}; + furi_message_queue_put(queue, &message, FuriWaitForever); + + LEAVE; + return; +} + +//+============================================================================ +// Show version number +// +static void showVer(Canvas* const canvas) { + show(canvas, 0, 59, &img_3x5_v, SHOW_SET_BLK); + show(canvas, 4, 59, VER_MAJ, SHOW_SET_BLK); + canvas_draw_frame(canvas, 8, 62, 2, 2); + show(canvas, 11, 59, VER_MIN, SHOW_SET_BLK); +} + +//+============================================================================ +// OS Callback : Draw request +// We register this function to be called when the OS requests that the screen is redrawn +// +// We actually instruct the OS to perform this request, after we update the interface +// I guess it's possible that this instruction may able be issued by other threads !? +// +static void cbDraw(Canvas* const canvas, void* ctx) { + ENTER; + furi_assert(canvas); + furi_assert(ctx); + + state_t* state = NULL; + + // Try to acquire the mutex for the plugin state variables, timeout = 25mS + if(!(state = (state_t*)acquire_mutex((ValueMutex*)ctx, 25))) return; + + switch(state->scene) { + //--------------------------------------------------------------------- + case SCENE_SPLASH: + show(canvas, 2, 0, &img_csLogo_FULL, SHOW_SET_BLK); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 43, AlignCenter, AlignTop, "Wii Extension Controller"); + canvas_draw_str_aligned(canvas, 64, 55, AlignCenter, AlignTop, "Protocol Analyser"); + + showVer(canvas); + + break; + + //--------------------------------------------------------------------- + case SCENE_RIP: + show(canvas, 0, 0, &img_RIP, SHOW_SET_BLK); + break; + + //--------------------------------------------------------------------- + case SCENE_WAIT: +#define xo 2 + + show(canvas, 3 + xo, 10, &img_ecp_port, SHOW_SET_BLK); + + BOX_TL(22 + xo, 6, 82 + xo, 23); // 3v3 + BOX_TL(48 + xo, 21, 82 + xo, 23); // C1 + BOX_BL(22 + xo, 41, 82 + xo, 58); // C0 + BOX_BL(48 + xo, 41, 82 + xo, 44); // Gnd + + show(canvas, 90 + xo, 3, &img_6x8_3, SHOW_SET_BLK); // 3v3 + show(canvas, 97 + xo, 3, &img_6x8_v, SHOW_SET_BLK); + show(canvas, 104 + xo, 3, &img_6x8_3, SHOW_SET_BLK); + + show(canvas, 90 + xo, 18, &img_6x8_C, SHOW_SET_BLK); // C1 <-> + show(canvas, 98 + xo, 18, &img_6x8_1, SHOW_SET_BLK); + show(canvas, 107 + xo, 16, &img_ecp_SDA, SHOW_SET_BLK); + + show(canvas, 90 + xo, 40, &img_6x8_G, SHOW_SET_BLK); // Gnd + show(canvas, 97 + xo, 40, &img_6x8_n, SHOW_SET_BLK); + show(canvas, 104 + xo, 40, &img_6x8_d, SHOW_SET_BLK); + + show(canvas, 90 + xo, 54, &img_6x8_C, SHOW_SET_BLK); // C0 _-_- + show(canvas, 98 + xo, 54, &img_6x8_0, SHOW_SET_BLK); + show(canvas, 108 + xo, 54, &img_ecp_SCL, SHOW_SET_BLK); + + show(canvas, 0, 0, &img_csLogo_Small, SHOW_SET_BLK); + showVer(canvas); + +#undef xo + break; + + //--------------------------------------------------------------------- + case SCENE_DEBUG: + canvas_set_font(canvas, FontSecondary); + + show(canvas, 0, 0, &img_key_U, SHOW_SET_BLK); + canvas_draw_str_aligned(canvas, 11, 0, AlignLeft, AlignTop, "Initialise Perhipheral"); + + show(canvas, 0, 11, &img_key_OK, SHOW_SET_BLK); + canvas_draw_str_aligned(canvas, 11, 11, AlignLeft, AlignTop, "Read values [see log]"); + + show(canvas, 0, 23, &img_key_D, SHOW_SET_BLK); + canvas_draw_str_aligned(canvas, 11, 22, AlignLeft, AlignTop, "Restart Scanner"); + + show(canvas, 0, 33, &img_key_Back, SHOW_SET_BLK); + canvas_draw_str_aligned(canvas, 11, 33, AlignLeft, AlignTop, "Exit"); + + break; + + //--------------------------------------------------------------------- + default: + if(state->ec.pidx >= PID_ERROR) { + ERROR("%s : bad PID = %d", __func__, state->ec.pidx); + } else { + if((state->scene == SCENE_DUMP) || !ecId[state->ec.pidx].show) + ecId[PID_UNKNOWN].show(canvas, state); + else + ecId[state->ec.pidx].show(canvas, state); + } + break; + } + + // Release the mutex + release_mutex((ValueMutex*)ctx, state); + + LEAVE; + return; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// SSSSS TTTTT AAA TTTTT EEEEE V V AAA RRRR IIIII AAA BBBB L EEEEE SSSSS +// S T A A T E V V A A R R I A A B B L E S +// SSSSS T AAAAA T EEE V V AAAAA RRRR I AAAAA BBBB L EEE SSSSS +// S T A A T E V V A A R R I A A B B L E S +// SSSSS T A A T EEEEE V A A R R IIIII A A BBBB LLLLL EEEEE SSSSS +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//+============================================================================ ======================================== +// Initialise plugin state variables +// +static inline bool stateInit(state_t* const state) { + ENTER; + furi_assert(state); + + bool rv = true; // assume success + + // Enable the main loop + state->run = true; + + // Timer + state->timerEn = false; + state->timer = NULL; + state->timerHz = furi_kernel_get_tick_frequency(); + state->fps = 30; + + // Scene + state->scene = SCENE_SPLASH; + state->scenePrev = SCENE_NONE; + state->scenePegg = SCENE_NONE; + + state->hold = 0; // show hold meters (-1=lowest, 0=current, +1=highest} + state->calib = CAL_TRACK; + state->pause = false; // animation running + state->apause = false; // auto-pause animation + + // Notifications + state->notify = NULL; + + // Perhipheral + state->ec.init = false; + state->ec.pidx = PID_UNKNOWN; + state->ec.sid = ecId[state->ec.pidx].name; + + // Controller data + memset(state->ec.pid, 0xC5, PID_LEN); // Cyborg 5ystems + memset(state->ec.calF, 0xC5, CAL_LEN); + memset(state->ec.joy, 0xC5, JOY_LEN); + + // Encryption details + state->ec.encrypt = false; + memset(state->ec.encKey, 0x00, ENC_LEN); + + // Seed the PRNG + // CYCCNT --> lib/STM32CubeWB/Drivers/CMSIS/Include/core_cm7.h + // srand(DWT->CYCCNT); + + LEAVE; + return rv; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// MM MM AAA IIIII N N +// M M M A A I NN N +// M M M AAAAA I N N N +// M M A A I N NN +// M M A A IIIII N N +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//+============================================================================ ======================================== +// Enable/Disable scanning +// +void timerEn(state_t* state, bool on) { + ENTER; + furi_assert(state); + + // ENable scanning + if(on) { + if(state->timerEn) { + WARN(wii_errs[WARN_SCAN_START]); + } else { + // Set the timer to fire at 'fps' times/second + if(furi_timer_start(state->timer, state->timerHz / state->fps) == FuriStatusOk) { + state->timerEn = true; + INFO("%s : monitor started", __func__); + } else { + ERROR(wii_errs[ERR_TIMER_START]); + } + } + + // DISable scanning + } else { + if(!state->timerEn) { + WARN(wii_errs[WARN_SCAN_STOP]); + } else { + // Stop the timer + if(furi_timer_stop(state->timer) == FuriStatusOk) { + state->timerEn = false; + INFO("%s : monitor stopped", __func__); + } else { + ERROR(wii_errs[ERR_TIMER_STOP]); + } + } + } + + LEAVE; + return; +} + +//+============================================================================ ======================================== +// Plugin entry point +// +int32_t wii_ec_anal(void) { + ENTER; + + // ===== Variables ===== + err_t error = 0; // assume success + Gui* gui = NULL; + ViewPort* vpp = NULL; + state_t* state = NULL; + ValueMutex mutex = {0}; + FuriMessageQueue* queue = NULL; + const uint32_t queueSz = 20; // maximum messages in queue + uint32_t tmo = (3.5f * 1000); // timeout splash screen after N seconds + + // The queue will contain plugin event-messages + // --> local + eventMsg_t msg = {0}; + + INFO("BEGIN"); + + // ===== Message queue ===== + // 1. Create a message queue (for up to 8 (keyboard) event messages) + if(!(queue = furi_message_queue_alloc(queueSz, sizeof(msg)))) { + ERROR(wii_errs[(error = ERR_MALLOC_QUEUE)]); + goto bail; + } + + // ===== Create GUI Interface ===== + // 2. Create a GUI interface + if(!(gui = furi_record_open("gui"))) { + ERROR(wii_errs[(error = ERR_NO_GUI)]); + goto bail; + } + + // ===== Plugin state variables ===== + // 3. Allocate space on the heap for the plugin state variables + if(!(state = malloc(sizeof(state_t)))) { + ERROR(wii_errs[(error = ERR_MALLOC_STATE)]); + goto bail; + } + // 4. Initialise the plugin state variables + if(!stateInit(state)) { + // error message(s) is/are output by stateInit() + error = 15; + goto bail; + } + // 5. Create a mutex for (reading/writing) the plugin state variables + if(!init_mutex(&mutex, state, sizeof(state))) { + ERROR(wii_errs[(error = ERR_NO_MUTEX)]); + goto bail; + } + + // ===== Viewport ===== + // 6. Allocate space on the heap for the viewport + if(!(vpp = view_port_alloc())) { + ERROR(wii_errs[(error = ERR_MALLOC_VIEW)]); + goto bail; + } + // 7a. Register a callback for input events + view_port_input_callback_set(vpp, cbInput, queue); + // 7b. Register a callback for draw events + view_port_draw_callback_set(vpp, cbDraw, &mutex); + + // ===== Start GUI Interface ===== + // 8. Attach the viewport to the GUI + gui_add_view_port(gui, vpp, GuiLayerFullscreen); + + // ===== Timer ===== + // 9. Allocate a timer + if(!(state->timer = furi_timer_alloc(cbTimer, FuriTimerTypePeriodic, queue))) { + ERROR(wii_errs[(error = ERR_NO_TIMER)]); + goto bail; + } + + // === System Notifications === + // 10. Acquire a handle for the system notification queue + if(!(state->notify = furi_record_open(RECORD_NOTIFICATION))) { + ERROR(wii_errs[(error = ERR_NO_NOTIFY)]); + goto bail; + } + patBacklight(state); // Turn on the backlight [qv. remote FAP launch] + + INFO("INITIALISED"); + + // ==================== Main event loop ==================== + + if(state->run) do { + bool redraw = false; + FuriStatus status = FuriStatusErrorTimeout; + + // Wait for a message + // while ((status = furi_message_queue_get(queue, &msg, tmo)) == FuriStatusErrorTimeout) ; + status = furi_message_queue_get(queue, &msg, tmo); + + // Clear splash screen + if((state->scene == SCENE_SPLASH) && + (state->scenePrev == SCENE_NONE) && // Initial splash + ((status == FuriStatusErrorTimeout) || // timeout + ((msg.id == EVID_KEY) && (msg.input.type == InputTypeShort))) // or key-short + ) { + tmo = 60 * 1000; // increase message-wait timeout to 60secs + timerEn(state, true); // start scanning the i2c bus + status = FuriStatusOk; // pass status check + msg.id = EVID_NONE; // valid msg ID + sceneSet(state, SCENE_WAIT); // move to wait screen + } + + // Check for queue errors + if(status != FuriStatusOk) { + switch(status) { + case FuriStatusErrorTimeout: + DEBUG(wii_errs[DEBUG_QUEUE_TIMEOUT]); + continue; + case FuriStatusError: + ERROR(wii_errs[(error = ERR_QUEUE_RTOS)]); + goto bail; + case FuriStatusErrorResource: + ERROR(wii_errs[(error = ERR_QUEUE_RESOURCE)]); + goto bail; + case FuriStatusErrorParameter: + ERROR(wii_errs[(error = ERR_QUEUE_BADPRM)]); + goto bail; + case FuriStatusErrorNoMemory: + ERROR(wii_errs[(error = ERR_QUEUE_NOMEM)]); + goto bail; + case FuriStatusErrorISR: + ERROR(wii_errs[(error = ERR_QUEUE_ISR)]); + goto bail; + default: + ERROR(wii_errs[(error = ERR_QUEUE_UNK)]); + goto bail; + } + } + // Read successful + + // *** Try to lock the plugin state variables *** + if(!(state = (state_t*)acquire_mutex_block(&mutex))) { + ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]); + goto bail; + } + + // *** Handle events *** + switch(msg.id) { + //--------------------------------------------- + case EVID_TICK: // Timer events + //! I would prefer to have ecPoll() called by cbTimer() + //! ...but how does cbTimer() get the required access to the state variables? Namely: 'state->ec' + //! So, for now, the timer pushes a message to call ecPoll() + //! which, in turn, will push WIIEC event meesages! + ecPoll(&state->ec, queue); + break; + + //--------------------------------------------- + case EVID_WIIEC: // WiiMote Perhipheral + if(evWiiEC(&msg, state)) redraw = true; + break; + + //--------------------------------------------- + case EVID_KEY: // Key events + patBacklight(state); + if(evKey(&msg, state)) redraw = true; + break; + + //--------------------------------------------- + case EVID_NONE: + break; + + //--------------------------------------------- + default: // Unknown event + WARN("Unknown message.ID [%d]", msg.id); + break; + } + + // *** Update the GUI screen via the viewport *** + if(redraw) view_port_update(vpp); + + // *** Try to release the plugin state variables *** + if(!release_mutex(&mutex, state)) { + ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]); + goto bail; + } + } while(state->run); + + // ===== Game Over ===== + INFO("USER EXIT"); + +bail: + // 10. Release system notification queue + if(state->notify) { + furi_record_close(RECORD_NOTIFICATION); + state->notify = NULL; + } + + // 9. Stop the timer + if(state->timer) { + (void)furi_timer_stop(state->timer); + furi_timer_free(state->timer); + state->timer = NULL; + state->timerEn = false; + } + + // 8. Detach the viewport + gui_remove_view_port(gui, vpp); + + // 7. No need to unreqgister the callbacks + // ...they will go when the viewport is destroyed + + // 6. Destroy the viewport + if(vpp) { + view_port_enabled_set(vpp, false); + view_port_free(vpp); + vpp = NULL; + } + + // 5. Free the mutex + if(mutex.mutex) { + delete_mutex(&mutex); + mutex.mutex = NULL; + } + + // 4. Free up state pointer(s) + // none + + // 3. Free the plugin state variables + if(state) { + free(state); + state = NULL; + } + + // 2. Close the GUI + furi_record_close("gui"); + + // 1. Destroy the message queue + if(queue) { + furi_message_queue_free(queue); + queue = NULL; + } + + INFO("CLEAN EXIT ... Exit code: %d", error); + LEAVE; + return (int32_t)error; +} diff --git a/applications/plugins/wii_ec_anal/wii_anal.h b/applications/plugins/wii_ec_anal/wii_anal.h new file mode 100644 index 000000000..3aae61fdc --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal.h @@ -0,0 +1,89 @@ +#ifndef WII_ANAL_H_ +#define WII_ANAL_H_ + +#include // Core API +#include // GUI Input extensions +#include + +//----------------------------------------------------------------------------- ---------------------------------------- +// GUI scenes +// +typedef enum scene { + SCENE_NONE = 0, + SCENE_SPLASH = 1, + SCENE_RIP = 2, + SCENE_WAIT = 3, + SCENE_DEBUG = 4, + SCENE_DUMP = 5, + SCENE_CLASSIC = 6, + SCENE_CLASSIC_N = 7, + SCENE_NUNCHUCK = 8, + SCENE_NUNCHUCK_ACC = 9, +} scene_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +#include "wii_i2c.h" +#include "wii_ec.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// A list of event IDs handled by this plugin +// +typedef enum eventID { + EVID_NONE, + EVID_UNKNOWN, + + // A full list of events can be found with: `grep -r --color "void.*set_.*_callback" applications/gui/*` + // ...A free gift to you from the makers of well written code that conforms to a good coding standard + EVID_KEY, // keypad + EVID_TICK, // tick + EVID_WIIEC, // wii extension controller +} eventID_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// An item in the event message-queue +// +typedef struct eventMsg { + eventID_t id; + union { + InputEvent input; // --> applications/input/input.h + wiiEcEvent_t wiiEc; // --> local + }; +} eventMsg_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// State variables for this plugin +// An instance of this is allocated on the heap, and the pointer is passed back to the OS +// Access to this memory is controlled by mutex +// +typedef struct state { + bool run; // true : plugin is running + + bool timerEn; // controller scanning enabled + FuriTimer* timer; // the timer + uint32_t timerHz; // system ticks per second + int fps; // poll/refresh [frames]-per-second + + int cnvW; // canvas width + int cnvH; // canvas height + scene_t scene; // current scene + scene_t scenePrev; // previous scene + scene_t scenePegg; // previous scene for easter eggs + int flash; // flash counter (flashing icons) + + int hold; // hold type: {-1=tough-peak, 0=none, +1=peak-hold} + ecCalib_t calib; // Software calibration mode + + bool pause; // Accelerometer animation pause + bool apause; // Accelerometer animation auto-pause + + NotificationApp* notify; // OS nitifcation queue (for patting the backlight watchdog timer) + + wiiEC_t ec; // Extension Controller details +} state_t; + +//============================================================================= ======================================== +// Function prototypes +// +void timerEn(state_t* state, bool on); + +#endif //WII_ANAL_H_ diff --git a/applications/plugins/wii_ec_anal/wii_anal_ec.c b/applications/plugins/wii_ec_anal/wii_anal_ec.c new file mode 100644 index 000000000..62268a219 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal_ec.c @@ -0,0 +1,113 @@ +#include +#include + +#include "wii_anal.h" +#include "wii_anal_lcd.h" +#include "wii_anal_keys.h" + +//+============================================================================ ======================================== +// Handle Wii Extension Controller events +// +bool evWiiEC(const eventMsg_t* const msg, state_t* const state) { + bool redraw = false; + +#if LOG_LEVEL >= 4 + { + const char* s = NULL; + switch(msg->wiiEc.type) { + case WIIEC_NONE: + s = "Error"; + break; + case WIIEC_CONN: + s = "Connect"; + break; + case WIIEC_DISCONN: + s = "Disconnect"; + break; + case WIIEC_PRESS: + s = "Press"; + break; + case WIIEC_RELEASE: + s = "Release"; + break; + case WIIEC_ANALOG: + s = "Analog"; + break; + case WIIEC_ACCEL: + s = "Accel"; + break; + default: + s = "Bug"; + break; + } + INFO( + "WIIP : %s '%c' = %d", + s, + (isprint((int)msg->wiiEc.in) ? msg->wiiEc.in : '_'), + msg->wiiEc.val); + if((msg->wiiEc.type == WIIEC_CONN) || (msg->wiiEc.type == WIIEC_DISCONN)) + INFO("...%d=\"%s\"", msg->wiiEc.val, ecId[msg->wiiEc.val].name); + } +#endif + + switch(msg->wiiEc.type) { + case WIIEC_CONN: + patBacklight(state); + state->hold = 0; + state->calib = CAL_TRACK; + sceneSet(state, ecId[msg->wiiEc.val].scene); + redraw = true; + +// #if 1 // Workaround for Classic Controller Pro, which shows 00's for Factory Calibration Data!? + // if(state->ec.pidx == PID_CLASSIC_PRO) { + // Simulate a Long-OK keypress, to start Software Calibration mode + // eventMsg_t msg = {input.type = InputTypeLong, input.key = InputKeyOk}; + // key_calib(&msg, state); + // } +// #endif + break; + + case WIIEC_DISCONN: + patBacklight(state); + sceneSet(state, SCENE_WAIT); + redraw = true; + break; + + case WIIEC_PRESS: + if(state->scene == SCENE_NUNCHUCK_ACC) switch(msg->wiiEc.in) { + case 'z': // un-pause + state->pause = !state->pause; + break; + case 'c': // toggle auto-pause + state->pause = false; + state->apause = !state->apause; + break; + default: + break; + } + +#if 1 //! factory calibration method not known for classic triggers - this will set the digital switch point + if((state->ec.pidx == PID_CLASSIC) || (state->ec.pidx == PID_CLASSIC_PRO)) { + if(msg->wiiEc.in == 'l') state->ec.calS.classic[2].trgZL = msg->wiiEc.val; + if(msg->wiiEc.in == 'r') state->ec.calS.classic[2].trgZR = msg->wiiEc.val; + } +#endif + __attribute__((fallthrough)); + + case WIIEC_RELEASE: + patBacklight(state); + redraw = true; + break; + + case WIIEC_ANALOG: + case WIIEC_ACCEL: + ecCalibrate(&state->ec, state->calib); + redraw = true; + break; + + default: + break; + } + + return redraw; +} diff --git a/applications/plugins/wii_ec_anal/wii_anal_ec.h b/applications/plugins/wii_ec_anal/wii_anal_ec.h new file mode 100644 index 000000000..eec6b523c --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal_ec.h @@ -0,0 +1,14 @@ +#ifndef WII_ANAL_EC_H_ +#define WII_ANAL_EC_H_ + +#include + +//============================================================================= ======================================== +// Function prototypes +// +typedef struct eventMsg eventMsg_t; +typedef struct state state_t; + +bool evWiiEC(const eventMsg_t* const msg, state_t* const state); + +#endif //WII_ANAL_EC_H_ diff --git a/applications/plugins/wii_ec_anal/wii_anal_keys.c b/applications/plugins/wii_ec_anal/wii_anal_keys.c new file mode 100644 index 000000000..8c5c99b4e --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal_keys.c @@ -0,0 +1,299 @@ +#include + +#include "bc_logging.h" + +#include "wii_anal.h" + +//+============================================================================ ======================================== +// Stop Calibration mode +// +static void calStop(state_t* const state) { + state->hold = 0; // stop calibration mode + state->calib &= ~(CAL_RANGE | CAL_NOTJOY); // ... +} + +//+============================================================================ ======================================== +// Change to another scene +// +void sceneSet(state_t* const state, const scene_t scene) { + calStop(state); // Stop software calibration + state->scenePrev = state->scene; // Remember where we came from + state->scene = scene; // Go to new scene + INFO("Scene : %d -> %d", state->scenePrev, state->scene); +} + +//+============================================================================ ======================================== +// Change to an easter egg scene +// +static void sceneSetEgg(state_t* const state, const scene_t scene) { + calStop(state); // Stop software calibration + state->scenePegg = state->scene; // Remember where we came from + state->scene = scene; // Go to new scene + INFO("Scene* : %d => %d", state->scenePegg, state->scene); +} + +//+============================================================================ ======================================== +// Several EC screens have 'peak-hold' and 'calibration' features +// Enabling peak-hold on screen with no peak meters will have no effect +// So, to avoid code duplication... +// +bool key_calib(const eventMsg_t* const msg, state_t* const state) { + int used = false; // assume key is NOT-handled + + switch(msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyUp: //# hold = (state->hold == +1) ? 0 : +1; // toggle peak hold + used = true; + break; + + case InputKeyDown: //# hold = (state->hold == -1) ? 0 : -1; // toggle trough hold + used = true; + break; + + case InputKeyOk: //# calib & CAL_RANGE) + calStop(state); // STOP softare calibration + else + ecCalibrate(&state->ec, CAL_CENTRE); // perform centre calibration + used = true; + break; + + default: + break; + } + break; + + case InputTypeLong: //# >! After INPUT_LONG_PRESS interval, asynch to InputTypeRelease + switch(msg->input.key) { + case InputKeyOk: //# >O [ LONG-OK ] + ecCalibrate(&state->ec, CAL_RESET | CAL_CENTRE); // START software calibration + state->hold = 0; + state->calib |= CAL_RANGE; + state->flash = 8; // start with flash ON + used = true; + break; + + default: + break; + } + break; + + default: + break; + } + + return used; +} + +//+============================================================================ ======================================== +// WAIT screen +// +static inline bool wait_key(const eventMsg_t* const msg, state_t* const state) { + int used = false; // assume key is NOT-handled + + if(msg->input.type == InputTypeShort) { + switch(msg->input.key) { + case InputKeyLeft: //# run = false; + used = true; + break; + + default: + break; + } + } + + return used; +} + +//+============================================================================ ======================================== +// DEBUG screen +// +static inline bool debug_key(const eventMsg_t* const msg, state_t* const state) { + int used = false; // assume key is NOT-handled + + switch(msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyUp: { //# ec, NULL); // Initialise the controller //! NULL = no encryption + (void)init; // in case INFO is optimised out + INFO("%s : %s", __func__, (init ? "init OK" : "init fail")); + used = true; + break; + } + + case InputKeyOk: //# ec) == 0) { // Read the controller + INFO( + "%s : joy: {%02X,%02X,%02X,%02X,%02X,%02X}", + __func__, + state->ec.joy[0], + state->ec.joy[1], + state->ec.joy[2], + state->ec.joy[3], + state->ec.joy[4], + state->ec.joy[5]); + } + used = true; + break; + + case InputKeyDown: //# scenePrev); + used = true; + break; + + case InputKeyBack: //# run = false; + used = true; + break; + + default: + break; //# input.key == InputKeyBack) && (state->scenePrev == SCENE_NONE)) state->run = false; + + // ANY-other-KEY press + if(msg->input.type == InputTypeShort) { + timerEn(state, true); // Restart the timer + state->scene = state->scenePegg; + } + + return true; +} + +//+============================================================================ ======================================== +// "_pre" allows the plugin to use the key before the active scene gets a chance +// +static inline bool key_pre(const eventMsg_t* const msg, state_t* const state) { + (void)msg; + (void)state; + + return false; +} + +//+============================================================================ ======================================== +// "_post" allows the plugin to use a key if it was not used by the active scene +// +static inline bool key_post(const eventMsg_t* const msg, state_t* const state) { + int used = false; // assume key is NOT-handled + + if(msg->input.key == InputKeyBack) { + if(msg->input.type == InputTypeShort) { //# ec.init = false; // reset/disconnect the controller + sceneSet(state, SCENE_WAIT); + used = true; + + } else if(msg->input.type == InputTypeLong) { //# >B [LONG-BACK] + state->run = false; // Signal the plugin to exit + used = true; + } + } + + // Easter eggs + switch(state->scene) { + case SCENE_SPLASH: // Scenes that do NOT offer Easter eggs + case SCENE_RIP: + case SCENE_DEBUG: + break; + default: + if(msg->input.type == InputTypeLong) { + switch(msg->input.key) { + case InputKeyDown: //# >D [LONG-DOWN] + timerEn(state, false); // Stop the timer + sceneSetEgg(state, SCENE_DEBUG); + used = true; + break; + + case InputKeyLeft: //# >L [ LONG-LEFT ] + timerEn(state, false); // Stop the timer + sceneSetEgg(state, SCENE_SPLASH); + used = true; + break; + + case InputKeyUp: //# >U [ LONG-UP ] + timerEn(state, false); // Stop the timer + sceneSetEgg(state, SCENE_RIP); + used = true; + break; + + default: + break; + } + } + break; + } + + return used; +} + +//+============================================================================ ======================================== +// Handle a key press event +// +bool evKey(const eventMsg_t* const msg, state_t* const state) { + furi_assert(msg); + furi_assert(state); + + bool used = key_pre(msg, state); + + if(!used) switch(state->scene) { + case SCENE_SPLASH: //... + case SCENE_RIP: + used = splash_key(msg, state); + break; + + case SCENE_WAIT: + used = wait_key(msg, state); + break; + case SCENE_DEBUG: + used = debug_key(msg, state); + break; + + default: + if(state->ec.pidx >= PID_ERROR) { + ERROR("%s : bad PID = %d", __func__, state->ec.pidx); + } else { + if((state->scene == SCENE_DUMP) || !ecId[state->ec.pidx].keys) + ecId[PID_UNKNOWN].keys(msg, state); + else + ecId[state->ec.pidx].keys(msg, state); + } + break; + + case SCENE_NONE: + break; + } + + if(!used) used = key_post(msg, state); + + return used; +} diff --git a/applications/plugins/wii_ec_anal/wii_anal_keys.h b/applications/plugins/wii_ec_anal/wii_anal_keys.h new file mode 100644 index 000000000..c10fcd1ef --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal_keys.h @@ -0,0 +1,16 @@ +#ifndef WII_ANAL_KEYS_H_ +#define WII_ANAL_KEYS_H_ + +//============================================================================= ======================================== +// Function prototypes +// +#include // bool +typedef struct eventMsg eventMsg_t; +typedef struct state state_t; +typedef enum scene scene_t; + +void sceneSet(state_t* const state, const scene_t scene); +bool key_calib(const eventMsg_t* const msg, state_t* const state); +bool evKey(const eventMsg_t* const msg, state_t* const state); + +#endif //WII_ANAL_KEYS_H_ diff --git a/applications/plugins/wii_ec_anal/wii_anal_lcd.c b/applications/plugins/wii_ec_anal/wii_anal_lcd.c new file mode 100644 index 000000000..921a3708e --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal_lcd.c @@ -0,0 +1,282 @@ +#include "wii_anal.h" +#include "gfx/images.h" // Images + +//----------------------------------------------------------------------------- ---------------------------------------- +// A couple of monospaced hex fonts +// +const image_t* img_6x8[16] = { + &img_6x8_0, + &img_6x8_1, + &img_6x8_2, + &img_6x8_3, + &img_6x8_4, + &img_6x8_5, + &img_6x8_6, + &img_6x8_7, + &img_6x8_8, + &img_6x8_9, + &img_6x8_A, + &img_6x8_B, + &img_6x8_C, + &img_6x8_D, + &img_6x8_E, + &img_6x8_F, +}; + +const image_t* img_5x7[16] = { + &img_5x7_0, + &img_5x7_1, + &img_5x7_2, + &img_5x7_3, + &img_5x7_4, + &img_5x7_5, + &img_5x7_6, + &img_5x7_7, + &img_5x7_8, + &img_5x7_9, + &img_5x7_A, + &img_5x7_B, + &img_5x7_C, + &img_5x7_D, + &img_5x7_E, + &img_5x7_F, +}; + +//+============================================================================ ======================================== +// void backlightOn (void) +// { +// // Acquire a handle for the system notification queue +// // Do this ONCE ... at plugin startup +// NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); +// +// // Pat the backlight watchdog +// // Send the (predefined) message sequence {backlight_on, end} +// // --> applications/notification/*.c +// notification_message(notifications, &sequence_display_backlight_on); +// +// // Release the handle for the system notification queue +// // Do this ONCE ... at plugin quit +// furi_record_close(RECORD_NOTIFICATION); +// } +void patBacklight(state_t* state) { + notification_message(state->notify, &sequence_display_backlight_on); +} + +//============================================================================= ======================================== +// Show a hex number in an inverted box (for ananlogue readings) +// +void showHex( + Canvas* const canvas, + uint8_t x, + uint8_t y, + const uint32_t val, + const uint8_t cnt, + const int b) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, x++, y++, 1 + (cnt * (6 + 1)), 10); + + // thicken border + if(b == 2) canvas_draw_frame(canvas, x - 2, y - 2, 1 + (cnt * (6 + 1)) + 2, 10 + 2); + + for(int i = (cnt - 1) * 4; i >= 0; i -= 4, x += 6 + 1) + show(canvas, x, y, img_6x8[(val >> i) & 0xF], SHOW_SET_WHT); +} + +//============================================================================= ======================================== +// Show the up/down "peak hold" controls in the bottom right +// +void showPeakHold(state_t* const state, Canvas* const canvas, const int hold) { + switch(hold) { + case 0: + show(canvas, 119, 51, &img_key_U, SHOW_CLR_BLK); + show(canvas, 119, 56, &img_key_D, SHOW_CLR_BLK); + break; + case +1: + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 120, 52, 7, 6); + show(canvas, 119, 51, &img_key_U, SHOW_CLR_WHT); + show(canvas, 119, 56, &img_key_D, SHOW_CLR_BLK); + break; + case -1: + show(canvas, 119, 51, &img_key_U, SHOW_CLR_BLK); + canvas_draw_box(canvas, 120, 57, 7, 6); + show(canvas, 119, 56, &img_key_D, SHOW_CLR_WHT); + break; + default: + break; + } + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 119, 51, 9, 13); + + // calibration indicator + show( + canvas, + 108, + 55, + ((state->calib & CAL_RANGE) && (++state->flash & 8)) ? &img_key_OKi : &img_key_OK, + SHOW_SET_BLK); +} + +//============================================================================= ======================================== +// This code performs a FULL calibration on the device EVERY time it draws a joystick +//...This is NOT a good way forward for anything other than a test tool. +// +// Realistically you would do all the maths when the controller is connected +// or, if you prefer (and it IS a good thing), have a "calibrate controller" menu option +// ...and then just use a lookup table, or trivial formual +// +// THIS algorithm chops the joystick in to one of 9 zones +// Eg. {FullLeft, Left3, Left2, Left1, Middle, Right1, Right2, Right3, FullRight} +// FullLeft and FullRight have a deadzone of N [qv. xDead] ..a total of N+1 positions +// Middle has a deadzone of N EACH WAY ...a total of 2N+1 positions +// +// If the remaining range does not divide evenly in to three zones, +// the first remainder is added to zone3, +// and the second remainder (if there is one) is added to zone2 +// ...giving finer control near the centre of the joystick +// +// The value of the deadzone is based on the number of bits in the +// joystcik {x,y} values - the larger the range, the larger the deadzone. +// +// 03 15 29 +// |<<| Calibration points |==| |>>| +// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F +// |---| |________________________| |------| |______________________________| |---| +// |r=2| | range = 9 | | r=3 | | range = 11 | |r=2| +// Zones: |-4 | |-3 |-2 |-1 | |0 | |+1 |+2 |+3 | |+4 | +// +// This is not "the right way to do it" ...this is "one way to do it" +// Consider you application, and what the user is trying to achieve +// Aim a gun - probably need to be more accurate +// Turn and object - this is probably good enough +// Start slowly & pick up speed - how about a log or sine curve? +// +void showJoy( + Canvas* const canvas, + const uint8_t x, + const uint8_t y, // x,y is the CENTRE of the Joystick + const uint8_t xMin, + const uint8_t xMid, + const uint8_t xMax, + const uint8_t yMin, + const uint8_t yMid, + const uint8_t yMax, + const uint8_t xPos, + const uint8_t yPos, + const uint8_t bits) { + int xOff = 0; // final offset of joystick hat image + int yOff = 0; + + int xDead = (bits < 7) ? (1 << 0) : (1 << 3); // dead zone (centre & limits) + int yDead = xDead; + + // This code is NOT optimised ...and it's still barely readable! + if((xPos >= (xMid - xDead)) && (xPos <= (xMid + xDead))) + xOff = 0; // centre [most likely] + else if(xPos <= (xMin + xDead)) + xOff = -4; // full left + else if(xPos >= (xMax - xDead)) + xOff = +4; // full right + else if(xPos < (xMid - xDead)) { // part left + // very much hard-coded for 3 interim positions + int lo = (xMin + xDead) + 1; // lowest position + int hi = (xMid - xDead) - 1; // highest position + + // this is the only duplicated bit of code + int range = (hi - lo) + 1; // range covered + int div = range / 3; // each division (base amount, eg. 17/3==5) + int rem = range - (div * 3); // remainder (ie. range%3) + + // int hi1 = hi; // lowest value for zone #-1 + // int lo1 = hi1 -div +1; // highest value for zone #-1 + // int hi2 = lo1 -1; // lowest value for zone #-2 + // int lo2 = hi2 -div +1 -(rem==2); // highest value for zone #-2 expand out remainder + // int hi3 = lo2 -1; // lowest value for zone #-3 + // int lo3 = hi3 -div +1 -(rem>=1); // highest value for zone #-3 expand out remainder + + int lo1 = hi - div + 1; // (in brevity) + int hi3 = hi - div - div - (rem == 2); // ... + + if(xPos <= hi3) + xOff = -3; // zone #-3 + else if(xPos >= lo1) + xOff = -1; // zone #-1 + else + xOff = -2; // zone #-2 + + } else /*if (xPos > (xMid +xDead))*/ { // part right + // very much hard-coded for 3 interim positions + int lo = (xMid + xDead) + 1; // lowest position + int hi = (xMax - xDead) - 1; // highest position + + int range = (hi - lo) + 1; // range covered + int div = range / 3; // each division (base amount, eg. 17/3==5) + int rem = range - (div * 3); // remainder (ie. range%3) + + // int lo1 = lo; // lowest value for zone #+1 + // int hi1 = lo +div -1; // highest value for zone #+1 + // int lo2 = hi1 +1; // lowest value for zone #+2 + // int hi2 = lo2 +div -1 +(rem==2); // highest value for zone #+2 expand out remainder + // int lo3 = hi2 +1; // lowest value for zone #+3 + // int hi3 = lo3 +div -1 +(rem>=1); // highest value for zone #+3 expand out remainder + + int hi1 = lo + div - 1; // (in brevity) + int lo3 = lo + div + div + (rem == 2); // ... + + if(xPos <= hi1) + xOff = 1; // zone #1 + else if(xPos >= lo3) + xOff = 3; // zone #3 + else + xOff = 2; // zone #2 + } + + // All this to print a 3x3 square (in the right place) - LOL! + if((yPos >= (yMid - yDead)) && (yPos <= (yMid + yDead))) + yOff = 0; // centre [most likely] + else if(yPos <= (yMin + yDead)) + yOff = +4; // full down + else if(yPos >= (yMax - yDead)) + yOff = -4; // full up + else if(yPos < (yMid - yDead)) { // part down + int lo = (yMin + yDead) + 1; // lowest position + int hi = (yMid - yDead) - 1; // highest position + + int range = (hi - lo) + 1; // range covered + int div = range / 3; // each division (base amount, eg. 17/3==5) + int rem = range - (div * 3); // remainder (ie. range%3) + + int lo1 = hi - div + 1; // (in brevity) + int hi3 = hi - div - div - (rem == 2); // ... + + if(yPos <= hi3) + yOff = +3; // zone #3 + else if(yPos >= lo1) + yOff = +1; // zone #1 + else + yOff = +2; // zone #2 + + } else /*if (yPos > (yMid +yDead))*/ { // part up + int lo = (yMid + yDead) + 1; // lowest position + int hi = (yMax - yDead) - 1; // highest position + + int range = (hi - lo) + 1; // range covered + int div = range / 3; // each division (base amount, eg. 17/3==5) + int rem = range - (div * 3); // remainder (ie. range%3) + + int hi1 = lo + div - 1; // (in brevity) + int lo3 = lo + div + div + (rem == 2); // ... + + if(yPos <= hi1) + yOff = -1; // zone #-1 + else if(yPos >= lo3) + yOff = -3; // zone #-3 + else + yOff = -2; // zone #-2 + } + + show(canvas, x - (img_cc_Joy.w / 2), y - (img_cc_Joy.h / 2), &img_cc_Joy, SHOW_SET_BLK); + + // All ^that^ for v-this-v - LOL!! + canvas_draw_box(canvas, (x - 1) + xOff, (y - 1) + yOff, 3, 3); +} diff --git a/applications/plugins/wii_ec_anal/wii_anal_lcd.h b/applications/plugins/wii_ec_anal/wii_anal_lcd.h new file mode 100644 index 000000000..e52a3adc6 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal_lcd.h @@ -0,0 +1,57 @@ +#ifndef WII_ANAL_LCD_H_ +#define WII_ANAL_LCD_H_ + +//----------------------------------------------------------------------------- ---------------------------------------- +// A couple of monospaced hex fonts +// +#include "gfx/images.h" + +extern const image_t* img_6x8[]; +extern const image_t* img_5x7[]; + +//============================================================================= ======================================== +// macros to draw only two sides of a box +// these are used for drawing the wires on the WAIT screen +// +#define BOX_TL(x1, y1, x2, y2) \ + do { \ + canvas_draw_frame(canvas, x1, y1, x2 - x1 + 1, 2); \ + canvas_draw_frame(canvas, x1, y1 + 2, 2, y2 - y1 + 1 - 2); \ + } while(0) + +#define BOX_BL(x1, y1, x2, y2) \ + do { \ + canvas_draw_frame(canvas, x1, y2 - 1, x2 - x1 + 1, 2); \ + canvas_draw_frame(canvas, x1, y1, 2, y2 - y1 + 1 - 2); \ + } while(0) + +//============================================================================= ======================================== +// Function prototypes +// +void patBacklight(state_t* state); + +void showHex( + Canvas* const canvas, + uint8_t x, + uint8_t y, + const uint32_t val, + const uint8_t cnt, + const int b); + +void showPeakHold(state_t* const state, Canvas* const canvas, const int hold); + +void showJoy( + Canvas* const canvas, + const uint8_t x, + const uint8_t y, // x,y is the CENTRE of the Joystick + const uint8_t xMin, + const uint8_t xMid, + const uint8_t xMax, + const uint8_t yMin, + const uint8_t yMid, + const uint8_t yMax, + const uint8_t xPos, + const uint8_t yPos, + const uint8_t bits); + +#endif //WII_ANAL_LCD_H_ diff --git a/applications/plugins/wii_ec_anal/wii_anal_ver.h b/applications/plugins/wii_ec_anal/wii_anal_ver.h new file mode 100644 index 000000000..3f2c8c0e6 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_anal_ver.h @@ -0,0 +1,9 @@ +#ifndef WII_ANAL_VER_H_ +#define WII_ANAL_VER_H_ + +#include "gfx/images.h" + +#define VER_MAJ &img_3x5_1 +#define VER_MIN &img_3x5_0 + +#endif //WII_ANAL_VER_H_ diff --git a/applications/plugins/wii_ec_anal/wii_ec.c b/applications/plugins/wii_ec_anal/wii_ec.c new file mode 100644 index 000000000..00dcbf922 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_ec.c @@ -0,0 +1,298 @@ +#include +#include // Core API + +#include "wii_anal.h" +#include "wii_i2c.h" +#include "wii_ec.h" +#include "bc_logging.h" + +#include "gfx/images.h" // Images +#include "wii_anal_lcd.h" // Drawing functions +#include "wii_anal_keys.h" // key mappings + +//----------------------------------------------------------------------------- ---------------------------------------- +// List of known perhipherals +// +// More perhipheral ID codes here: https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way +// +const ecId_t ecId[PID_CNT] = { + [PID_UNKNOWN] = + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + "Unknown Perhipheral", + SCENE_DUMP, + NULL, + NULL, + NULL, + NULL, + ec_show, + ec_key}, + + // If you're wise, ONLY edit this bit + [PID_NUNCHUCK] = + {{0x00, 0x00, 0xA4, 0x20, 0x00, 0x00}, + "Nunchuck", + SCENE_NUNCHUCK, + NULL, + nunchuck_decode, + nunchuck_msg, + nunchuck_calib, + nunchuck_show, + nunchuck_key}, + + [PID_NUNCHUCK_R2] = + {{0xFF, 0x00, 0xA4, 0x20, 0x00, 0x00}, + "Nunchuck (rev2)", + SCENE_NUNCHUCK, + NULL, + nunchuck_decode, + nunchuck_msg, + nunchuck_calib, + nunchuck_show, + nunchuck_key}, + + [PID_CLASSIC] = + {{0x00, 0x00, 0xA4, 0x20, 0x01, 0x01}, + "Classic Controller", + SCENE_CLASSIC, + NULL, + classic_decode, + classic_msg, + classic_calib, + classic_show, + classic_key}, + + [PID_CLASSIC_PRO] = + {{0x01, 0x00, 0xA4, 0x20, 0x01, 0x01}, + "Classic Controller Pro", + SCENE_CLASSIC, + NULL, + classic_decode, + classic_msg, + classic_calib, + classic_show, + classic_key}, + + [PID_BALANCE] = + {{0x00, 0x00, 0xA4, 0x20, 0x04, 0x02}, + "Balance Board", + SCENE_DUMP, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, + + [PID_GH_GUITAR] = + {{0x00, 0x00, 0xA4, 0x20, 0x01, 0x03}, + "Guitar Hero Guitar", + SCENE_DUMP, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, + + [PID_GH_DRUMS] = + {{0x01, 0x00, 0xA4, 0x20, 0x01, 0x03}, + "Guitar Hero World Tour Drums", + SCENE_DUMP, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, + + [PID_TURNTABLE] = + {{0x03, 0x00, 0xA4, 0x20, 0x01, 0x03}, + "DJ Hero Turntable", + SCENE_DUMP, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, + + [PID_TAIKO_DRUMS] = + {{0x00, 0x00, 0xA4, 0x20, 0x01, 0x11}, + "Taiko Drum Controller)", + SCENE_DUMP, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, // Taiko no Tatsujin TaTaCon (Drum controller) + + [PID_UDRAW] = + {{0xFF, 0x00, 0xA4, 0x20, 0x00, 0x13}, + "uDraw Tablet", + SCENE_DUMP, + udraw_init, + NULL, + NULL, + NULL, + NULL, + NULL}, //! same as drawsome? + // ----- + + [PID_ERROR] = + {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + "Read Error", + SCENE_NONE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, + + [PID_NULL] = {{0}, NULL, SCENE_NONE, NULL, NULL, NULL, NULL, NULL, NULL} // last entry +}; + +//+============================================================================ ======================================== +void ecDecode(wiiEC_t* pec) { + if(ecId[pec->pidx].decode) ecId[pec->pidx].decode(pec); +} + +//+============================================================================ ======================================== +void ecCalibrate(wiiEC_t* const pec, ecCalib_t c) { + if(ecId[pec->pidx].calib) ecId[pec->pidx].calib(pec, c); +} + +//+============================================================================ ======================================== +void ecPoll(wiiEC_t* const pec, FuriMessageQueue* const queue) { + ENTER; + furi_assert(queue); + + if(!pec->init) { + // Attempt to initialise + if(ecInit(pec, NULL)) { //! need a way to auto-start with encryption enabled + eventMsg_t msg = { + .id = EVID_WIIEC, .wiiEc = {.type = WIIEC_CONN, .in = '<', .val = pec->pidx}}; + furi_message_queue_put(queue, &msg, 0); + } + + } else { + // Attempt to read + switch(ecRead(pec)) { + case 2: { // device gone + eventMsg_t msg = { + .id = EVID_WIIEC, .wiiEc = {.type = WIIEC_DISCONN, .in = '>', .val = pec->pidx}}; + furi_message_queue_put(queue, &msg, 0); + break; + } + + case 0: { // read OK + void (*fn)(wiiEC_t*, FuriMessageQueue*) = ecId[pec->pidx].check; + if(fn) fn(pec, queue); + break; + } + + case 3: // read fail + // this is probably temporary just ignore it + break; + + default: // bug: unknown + case 1: // bug: not initialised - should never happen + ERROR("%s : read bug", __func__); + break; + } + } + + LEAVE; + return; +} + +//+============================================================================ ======================================== +// This is the screen drawn for an unknown controller +// It is also available by pressing LEFT (at least once) on a "known controller" screen +// +void ec_show(Canvas* const canvas, state_t* const state) { + wiiEC_t* pec = &state->ec; + int h = 11; // line height + int x = 1; // (initial) offset for bits + int y = -h; // previous y value + int yb = 0; // y for bit patterns + int c2 = 17; // column 2 + + // Headings + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "SID:"); + canvas_draw_str_aligned(canvas, c2, 0, AlignLeft, AlignTop, pec->sid); + + canvas_draw_str_aligned(canvas, 0, 11, AlignLeft, AlignTop, "PID:"); + canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Cal:"); + + // PID + x = c2; + for(int i = 0; i < 6; i++) { + show(canvas, x, 11, img_5x7[pec->pid[i] >> 4], SHOW_SET_BLK); + x += 5 + 1; + show(canvas, x, 11, img_5x7[pec->pid[i] & 0xF], SHOW_SET_BLK); + x += 5 + 1 + 2; + } + + // Calibrations data + y = 11; + for(int j = 0; j <= 8; j += 8) { + x = c2; + y += 11; + for(int i = 0; i < 8; i++) { + show(canvas, x, y, img_5x7[pec->calF[i + j] >> 4], SHOW_SET_BLK); + x += 5 + 1; + show(canvas, x, y, img_5x7[pec->calF[i + j] & 0xF], SHOW_SET_BLK); + x += 5 + 1 + 2; + } + } + + // Reading + x = 1; + y++; + yb = (y += h) + h + 2; + + canvas_draw_line(canvas, x, y - 1, x, yb + 4); + x += 2; + + for(int i = 0; i < JOY_LEN; i++) { + show(canvas, x + 1, y, img_6x8[pec->joy[i] >> 4], SHOW_SET_BLK); + show(canvas, x + 11, y, img_6x8[pec->joy[i] & 0xF], SHOW_SET_BLK); + + // bits + for(int m = 0x80; m; m >>= 1) { + x += 2 * !!(m & 0x08); // nybble step + canvas_draw_box(canvas, x, yb + (2 * !(pec->joy[i] & m)), 2, 2); + x += 2; // bit step + } + + // byte step + x += 1; + canvas_draw_line(canvas, x, y - 1, x, yb + 4); + x += 2; + } + + // Scene navigation + if(state->scenePrev != SCENE_WAIT) show(canvas, 120, 0, &img_key_R, SHOW_SET_BLK); +} + +//+============================================================================ ======================================== +// The DUMP screen is +// +bool ec_key(const eventMsg_t* const msg, state_t* const state) { + int used = false; // assume key is NOT-handled + + if(state->scenePrev != SCENE_WAIT) { + //# input.type == InputTypeShort) && (msg->input.key == InputKeyRight)) { + sceneSet(state, state->scenePrev); + used = true; + } + } + + return used; +} diff --git a/applications/plugins/wii_ec_anal/wii_ec.h b/applications/plugins/wii_ec_anal/wii_ec.h new file mode 100644 index 000000000..a28453740 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_ec.h @@ -0,0 +1,161 @@ +#ifndef WII_EC_H_ +#define WII_EC_H_ + +#include + +#include + +#include "wii_ec_nunchuck.h" +#include "wii_ec_classic.h" +#include "wii_ec_udraw.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// Crypto key (PSK), base register : {0x40..0x4F}[2][8] +#define ENC_LEN (2 * 8) + +// Controller State data, base register : {0x00..0x05}[6] +#define JOY_LEN (6) + +// Calibration data, base register : {0x20..0x2F}[16] +#define CAL_LEN (16) + +// Controller ID, base register : {0xFA..0xFF}[6] +#define PID_LEN (6) + +//----------------------------------------------------------------------------- ---------------------------------------- +// Perhipheral specific parameters union +// +typedef union ecDec { + ecDecNunchuck_t nunchuck; + ecDecClassic_t classic; +} ecDec_t; + +//----------------------------------------------------------------------------- +typedef union ecCal { + // 0=lowest seen ; 1=min ; 2=mid ; 3=max ; 4=highest seen + ecCalNunchuck_t nunchuck[5]; + ecCalClassic_t classic[5]; +} ecCal_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// Wii Extension Controller events +// +typedef enum wiiEcEventType { + WIIEC_NONE, + WIIEC_CONN, // Connect + WIIEC_DISCONN, // Disconnect + WIIEC_PRESS, // Press button + WIIEC_RELEASE, // Release button + WIIEC_ANALOG, // Analogue change (Joystick/Trigger) + WIIEC_ACCEL, // Accelerometer change +} wiiEcEventType_t; + +//----------------------------------------------------------------------------- +typedef struct wiiEcEvent { + wiiEcEventType_t type; // event type + char in; // input (see device specific options) + uint32_t val; // new value - meaningless for digital button presses +} wiiEcEvent_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// Known perhipheral types +// +typedef enum ecPid { + PID_UNKNOWN = 0, + PID_FIRST = 1, + PID_NUNCHUCK = PID_FIRST, + + // If you're wise, ONLY edit this section + PID_NUNCHUCK_R2, + PID_CLASSIC, + PID_CLASSIC_PRO, + PID_BALANCE, + PID_GH_GUITAR, + PID_GH_DRUMS, + PID_TURNTABLE, + PID_TAIKO_DRUMS, + PID_UDRAW, //! same as drawsome? + // ----- + + PID_ERROR, + PID_NULL, + PID_CNT, +} ecPid_t; + +//----------------------------------------------------------------------------- +// Calibration strategies +// +typedef enum ecCalib { + CAL_FACTORY = 0x01, // (re)set to factory defaults + CAL_TRACK = 0x02, // track maximum and minimum values seen + CAL_RESET = 0x04, // initialise ready for software calibration + CAL_RANGE = 0x08, // perform software calibration step + CAL_CENTRE = 0x10, // reset centre point of joystick + CAL_NOTJOY = 0x20, // do NOT calibrate the joystick +} ecCalib_t; + +//----------------------------------------------------------------------------- +// ecId table entry +// +typedef struct ecId { + uint8_t id[6]; // 6 byte ID string returned by Extension Controller + char* name; // Friendly name + scene_t scene; // Default scene + bool (*init)(wiiEC_t*); // Additional initialisation code + void (*decode)(wiiEC_t*); // Decode function + void (*check)(wiiEC_t*, FuriMessageQueue*); // check (for action) function + void (*calib)(wiiEC_t*, ecCalib_t); // calibrate analogue controllers [SOFTWARE] + void (*show)(Canvas* const, state_t* const); // Draw scene + bool (*keys)(const eventMsg_t* const, state_t* const); // Interpret keys +} ecId_t; + +//----------------------------------------------------------------------------- +// List of known perhipherals +// +// More perhipheral ID codes here: https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way +// +extern const ecId_t ecId[PID_CNT]; + +//----------------------------------------------------------------------------- ---------------------------------------- +// Data pertaining to a single Perhipheral instance +// +typedef struct wiiEC { + // Perhipheral state + bool init; // Initialised? + + uint8_t pid[PID_LEN]; // PID string - eg. {0x00, 0x00, 0xA4, 0x20, 0x00, 0x00} + ecPid_t pidx; // Index in to ecId table + const char* sid; // just for convenience + + bool encrypt; // encryption enabled? + uint8_t encKey[ENC_LEN]; // encryption key + + uint8_t calF[CAL_LEN]; // factory calibration data (not software) + uint8_t joy[JOY_LEN]; // Perhipheral raw data + + ecDec_t dec[2]; // device specific decode (two, so we can spot changes) + int decN; // which decode set is most recent {0, 1} + ecCal_t calS; // software calibration data +} wiiEC_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// Function prototypes +// +// top level calls will work out which sub-function to call +// top level check() function will handle connect/disconnect messages +// + +#include // Canvas +typedef struct wiiEC wiiEC_t; +typedef enum ecCalib ecCalib_t; +typedef struct state state_t; +typedef struct eventMsg eventMsg_t; + +void ecDecode(wiiEC_t* const pec); +void ecPoll(wiiEC_t* const pec, FuriMessageQueue* const queue); +void ecCalibrate(wiiEC_t* const pec, ecCalib_t c); + +void ec_show(Canvas* const canvas, state_t* const state); +bool ec_key(const eventMsg_t* const msg, state_t* const state); + +#endif //WII_EC_H_ diff --git a/applications/plugins/wii_ec_anal/wii_ec_classic.c b/applications/plugins/wii_ec_anal/wii_ec_classic.c new file mode 100644 index 000000000..5bd3398ca --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_ec_classic.c @@ -0,0 +1,439 @@ +#include +#include // Core API + +#include "wii_anal.h" +#include "wii_ec.h" +#include "bc_logging.h" + +//#include "gfx/images.h" // Images +#include "wii_anal_lcd.h" // Drawing functions +#include "wii_anal_keys.h" // key mappings + +// ** If you want to see what this source code looks like with all the MACROs expanded +// ** grep -v '#include ' wii_i2c_classic.c | gcc -E -o /dev/stdout -xc - +#include "wii_ec_macros.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// Classic Controller ... Classic Controller Pro is electronically the same +// +// ANA{l} ANA{r} +// BTN{l} BTN{L} BTN{R} BTN{r} +// ,--------. ,-, ,-, .--------, +// .----------------------------------------------------------. +// | | +// | BTN{W} BTN{x} | +// | BTN{A} BTN{D} BTN{-} BTN{h} BTN{+} BTN{y} BTN{a} | +// | BTN{S} BTN{b} | +// | | +// | ANA{y} ANA{Y} | +// | ANA{x} ANA{X} | +// | | +// `----------------------------------------------------------' + +//+============================================================================ ======================================== +// https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller +// I think a LOT of drugs went in to "designing" this layout +// ...And yes, the left-joystick has an extra 'bit' of precision! +// ...Also: trgZ{L|R} WILL continue to increase after btnZ{L|R} has gone active +// +void classic_decode(wiiEC_t* const pec) { + ecDecClassic_t* p = &pec->dec[(pec->decN = !pec->decN)].classic; + uint8_t* joy = pec->joy; + + p->trgZL = ((joy[2] >> 2) & 0x18) | ((joy[3] >> 5) & 0x07); // {5} + p->btnZL = !(joy[4] & 0x20); // !{1} + + p->trgZR = joy[3] & 0x1F; // {5} + p->btnZR = !(joy[4] & 0x02); // !{1} + + p->btnL = !(joy[5] & 0x80); // !{1} + p->btnR = !(joy[5] & 0x04); // !{1} + + p->padU = !(joy[5] & 0x01); // !{1} + p->padD = !(joy[4] & 0x40); // !{1} + p->padL = !(joy[5] & 0x02); // !{1} + p->padR = !(joy[4] & 0x80); // !{1} + + p->btnM = !(joy[4] & 0x10); // !{1} + p->btnH = !(joy[4] & 0x08); // !{1} + p->btnP = !(joy[4] & 0x04); // !{1} + + p->btnX = !(joy[5] & 0x08); // !{1} + p->btnY = !(joy[5] & 0x20); // !{1} + + p->btnA = !(joy[5] & 0x10); // !{1} + p->btnB = !(joy[5] & 0x40); // !{1} + + p->joyLX = joy[0] & 0x3F; // {6} + p->joyLY = joy[1] & 0x3F; // {6} + + p->joyRX = ((joy[0] >> 3) & 0x18) | ((joy[1] >> 5) & 0x06) | ((joy[2] >> 7) & 0x01); // {5} + p->joyRY = joy[2] & 0x1F; // {5} + + DEBUG( + ">%d> ZL{%02X}%c, L:%c, R:%c, ZR{%02X}%c", + pec->decN, + p->trgZL, + (p->btnZL ? '#' : '.'), + (p->btnL ? '#' : '.'), + (p->btnR ? '#' : '.'), + p->trgZR, + (p->btnZR ? '#' : '.')); + DEBUG( + ">%d> D:{%c,%c,%c,%c}, H:{%c,%c,%c}, B:{%c,%c,%c,%c}", + pec->decN, + (p->padU ? 'U' : '.'), + (p->padD ? 'D' : '.'), + (p->padL ? 'L' : '.'), + (p->padR ? 'R' : '.'), + (p->btnM ? '-' : '.'), + (p->btnH ? 'H' : '.'), + (p->btnP ? '+' : '.'), + (p->btnX ? 'X' : '.'), + (p->btnY ? 'Y' : '.'), + (p->btnA ? 'A' : '.'), + (p->btnB ? 'B' : '.')); + DEBUG( + ">%d> JoyL{x:%02X, y:%02X}, JoyR{x:%02X, y:%02X}", + pec->decN, + p->joyLX, + p->joyLY, + p->joyRX, + p->joyRY); +} + +//+============================================================================ ======================================== +// Give each button a unique character identifier +// +void classic_msg(wiiEC_t* const pec, FuriMessageQueue* const queue) { + ecDecClassic_t* new = &pec->dec[pec->decN].classic; + ecDecClassic_t* old = &pec->dec[!pec->decN].classic; + + eventMsg_t msg = { + .id = EVID_WIIEC, + .wiiEc = { + .type = WIIEC_NONE, + .in = ' ', + .val = 0, + }}; + + ANALOG(trgZL, 'l'); // FIVE bit value + ANABTN(btnZL, trgZL, 'l'); + + BUTTON(btnL, 'L'); + BUTTON(btnR, 'R'); + + ANALOG(trgZR, 'r'); // FIVE bit value + ANABTN(btnZR, trgZR, 'r'); + + BUTTON(padU, 'W'); + BUTTON(padL, 'A'); + BUTTON(padD, 'S'); + BUTTON(padR, 'D'); + + BUTTON(btnM, '-'); + BUTTON(btnH, 'h'); + BUTTON(btnP, '+'); + + BUTTON(btnX, 'x'); + BUTTON(btnY, 'y'); + BUTTON(btnA, 'a'); + BUTTON(btnB, 'b'); + + ANALOG(joyLX, 'x'); // SIX bit values + ANALOG(joyLY, 'y'); + + ANALOG(joyRX, 'X'); // FIVE bit values + ANALOG(joyRY, 'Y'); +} + +//+============================================================================ ======================================== +// https://web.archive.org/web/20090415045219/http://www.wiili.org/index.php/Wiimote/Extension_Controllers/Classic_Controller#Calibration_data +// +// Calibration data +// 0..2 left analog stick X axis {maximum, minimum, center} ... JoyL is 6bits, so >>2 to compare to readings +// 3..5 left analog stick Y axis {maximum, minimum, center} ... JoyL is 6bits, so >>2 to compare to readings +// 6..8 right analog stick X axis {maximum, minimum, center} ... JoyR is 5bits, so >>3 to compare to readings +// 9..11 right analog stick Y axis {maximum, minimum, center} ... JoyR is 5bits, so >>3 to compare to readings +// 12..15 somehow describe the shoulder {5bit} button values!? +// +void classic_calib(wiiEC_t* const pec, ecCalib_t c) { + ecDecClassic_t* src = &pec->dec[pec->decN].classic; // from input + ecCalClassic_t* dst = pec->calS.classic; // to calibration data + + if(c & CAL_RESET) { // initialise ready for software calibration + // LO is set to the MAXIMUM value (so it can be reduced) + // HI is set to ZERO (so it can be increased) + RESET_LO_HI(trgZL, 5); // 5bit value + RESET_LO_HI(trgZR, 5); // 5bit value + + RESET_LO_MID_HI(joyLX, 6); // 6bit value + RESET_LO_MID_HI(joyLY, 6); // 6bit value + + RESET_LO_MID_HI(joyRX, 5); // 5bit value + RESET_LO_MID_HI(joyRY, 5); // 5bit value + } + if(c & CAL_FACTORY) { // (re)set to factory defaults + //! strategy for factory calibration for classic controller [pro] triggers is (currently) unknown + //! FACTORY_LO( trgZL, pec->calF[12..15]); + //! FACTORY_MID(trgZL, pec->calF[12..15]); + //! FACTORY_HI( trgZL, pec->calF[12..15]); + + //! FACTORY_LO( trgZR, pec->calF[12..15]); + //! FACTORY_MID(trgZR, pec->calF[12..15]); + //! FACTORY_HI( trgZR, pec->calF[12..15]); + +#if 1 + FACTORY_LO(trgZL, 0x03); + FACTORY_LO(trgZR, 0x03); + + FACTORY_MID(trgZL, 0x1B); //! these will be set every time the digital switch changes to ON + FACTORY_MID(trgZR, 0x1B); +#endif + + FACTORY_LO(joyLX, pec->calF[1] >> 2); + FACTORY_MID(joyLX, pec->calF[2] >> 2); + FACTORY_HI(joyLX, pec->calF[0] >> 2); + + FACTORY_LO(joyLY, pec->calF[4] >> 2); + FACTORY_MID(joyLY, pec->calF[5] >> 2); + FACTORY_HI(joyLY, pec->calF[3] >> 2); + + FACTORY_LO(joyRX, pec->calF[7] >> 3); + FACTORY_MID(joyRX, pec->calF[8] >> 3); + FACTORY_HI(joyRX, pec->calF[6] >> 3); + + FACTORY_LO(joyRY, pec->calF[10] >> 3); + FACTORY_MID(joyRY, pec->calF[11] >> 3); + FACTORY_HI(joyRY, pec->calF[9] >> 3); + } + if(c & CAL_TRACK) { // track maximum and minimum values seen + TRACK_LO_HI(trgZL); + TRACK_LO_HI(trgZR); + + TRACK_LO_HI(joyLX); + TRACK_LO_HI(joyLY); + + TRACK_LO_HI(joyRX); + TRACK_LO_HI(joyRY); + } + if(c & CAL_RANGE) { // perform software calibration step + RANGE_LO_HI(trgZL); + RANGE_LO_HI(trgZR); + + RANGE_LO_HI(joyLX); + RANGE_LO_HI(joyLY); + + RANGE_LO_HI(joyRX); + RANGE_LO_HI(joyRY); + } + if(c & CAL_CENTRE) { // reset centre point of joystick + CENTRE(joyLX); + CENTRE(joyLY); + + CENTRE(joyRX); + CENTRE(joyRY); + } +} + +//+============================================================================ ======================================== +// bits that are common to both screens +// +static void classic_show_(Canvas* const canvas, state_t* const state) { + ecDecClassic_t* d = &state->ec.dec[state->ec.decN].classic; + ecCalClassic_t* js = state->ec.calS.classic; + + static const int dead = 1; // trigger deadzone + const image_t* img = NULL; // trigger image + + show(canvas, 6, 0, &img_cc_Main, SHOW_SET_BLK); + show(canvas, 62, 53, &img_cc_Cable, SHOW_SET_BLK); + + // classic triggers + if(d->trgZL >= js[2].trgZL) + img = &img_cc_trg_L4; + else if(d->trgZL <= js[1].trgZL + dead) + img = NULL; + else { + // copied from the joystick calibration code + int lo = js[1].trgZL + dead + 1; + int hi = js[2].trgZL - 1; + int range = hi - lo + 1; + int div = range / 3; // each division (base amount, eg. 17/3==5) + int rem = range - (div * 3); // remainder (ie. range%3) + int hi1 = lo + div - 1; // (in brevity) + int lo3 = lo + div + div + (rem == 2); // ... + + if(d->trgZL <= hi1) + img = &img_cc_trg_L1; // zone #1 + else if(d->trgZL >= lo3) + img = &img_cc_trg_L3; // zone #3 + else + img = &img_cc_trg_L2; // zone #2 + } + if(img) show(canvas, 22, 1, img, SHOW_SET_BLK); + + if(d->trgZR >= js[2].trgZR) + img = &img_cc_trg_R4; + else if(d->trgZR <= js[1].trgZR + dead) + img = NULL; + else { + // copied from the joystick calibration code + int lo = js[1].trgZR + dead + 1; + int hi = js[2].trgZR - 1; + int range = hi - lo + 1; + int div = range / 3; // each division (base amount, eg. 17/3==5) + int rem = range - (div * 3); // remainder (ie. range%3) + int hi1 = lo + div - 1; // (in brevity) + int lo3 = lo + div + div + (rem == 2); // ... + + if(d->trgZR <= hi1) + img = &img_cc_trg_R1; // zone #1 + else if(d->trgZR >= lo3) + img = &img_cc_trg_R3; // zone #3 + else + img = &img_cc_trg_R2; // zone #2 + } + if(img) show(canvas, 89, 1, img, SHOW_SET_BLK); + + if(d->padU) show(canvas, 27, 16, &img_cc_pad_UD1, SHOW_ALL); + if(d->padL) show(canvas, 20, 23, &img_cc_pad_LR1, SHOW_ALL); + if(d->padD) show(canvas, 27, 28, &img_cc_pad_UD1, SHOW_ALL); + if(d->padR) show(canvas, 32, 23, &img_cc_pad_LR1, SHOW_ALL); + + if(d->btnX) show(canvas, 96, 16, &img_cc_btn_X1, SHOW_ALL); + if(d->btnY) show(canvas, 85, 23, &img_cc_btn_Y1, SHOW_ALL); + if(d->btnA) show(canvas, 107, 23, &img_cc_btn_A1, SHOW_ALL); + if(d->btnB) show(canvas, 96, 30, &img_cc_btn_B1, SHOW_ALL); + + canvas_set_color(canvas, ColorBlack); + if(d->btnL) canvas_draw_box(canvas, 46, 2, 5, 4); + if(d->btnR) canvas_draw_box(canvas, 77, 2, 5, 4); + + if(d->btnM) canvas_draw_box(canvas, 54, 24, 4, 4); + if(d->btnH) canvas_draw_box(canvas, 62, 24, 4, 4); + if(d->btnP) canvas_draw_box(canvas, 70, 24, 4, 4); + + // Show joysticks + showJoy( + canvas, + 48, + 42, + js[1].joyLX, + js[2].joyLX, + js[3].joyLX, + js[1].joyLY, + js[2].joyLY, + js[3].joyLY, + d->joyLX, + d->joyLY, + 6); + showJoy( + canvas, + 78, + 42, + js[1].joyRX, + js[2].joyRX, + js[3].joyRX, + js[1].joyRY, + js[2].joyRY, + js[3].joyRY, + d->joyRX, + d->joyRY, + 5); + + show(canvas, 0, 55, &img_key_L, SHOW_SET_BLK); +} + +//+============================================================================ ======================================== +static void classic_showN(Canvas* const canvas, state_t* const state) { + ecCalClassic_t* c = (state->hold) ? + &state->ec.calS.classic[(state->hold < 0) ? 0 : 4] : + (ecCalClassic_t*)(&state->ec.dec[state->ec.decN].classic); //! danger + + classic_show_(canvas, state); + + showHex(canvas, 0, 0, c->trgZL, 2, 1); // 5bits + showHex(canvas, 113, 0, c->trgZR, 2, 1); // 5bits + + showHex(canvas, 24, 41, c->joyLX, 2, 1); // 6bits + showHex(canvas, 41, 54, c->joyLY, 2, 1); // 6bits + + showHex(canvas, 88, 41, c->joyRX, 2, 1); // 5bits + showHex(canvas, 71, 54, c->joyRY, 2, 1); // 5bits + + showPeakHold(state, canvas, state->hold); // peak keys +} + +//+============================================================================ ======================================== +void classic_show(Canvas* const canvas, state_t* const state) { + // Classic controllers have TWO scenes + if(state->scene == SCENE_CLASSIC_N) return classic_showN(canvas, state); + + // Default scene + classic_show_(canvas, state); + show(canvas, 9, 55, &img_key_R, SHOW_SET_BLK); + + show( + canvas, + 119, + 55, + ((state->calib & CAL_RANGE) && (++state->flash & 8)) ? &img_key_OKi : &img_key_OK, + SHOW_SET_BLK); +} + +//+============================================================================ ======================================== +static bool classic_keyN(const eventMsg_t* const msg, state_t* const state) { + int used = false; // assume key is NOT-handled + + if((msg->input.type == InputTypeShort) && (msg->input.key == InputKeyLeft)) { + sceneSet(state, SCENE_CLASSIC); + used = true; + } + + // Calibration keys + if(!used) used = key_calib(msg, state); + + return used; +} + +//+============================================================================ ======================================== +bool classic_key(const eventMsg_t* const msg, state_t* const state) { + // Classic controllers have TWO scenes + if(state->scene == SCENE_CLASSIC_N) return classic_keyN(msg, state); + + // Default scene + int used = false; // assume key is NOT-handled + + switch(msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyUp: //# +#include + +//----------------------------------------------------------------------------- ---------------------------------------- +// Classic Controller ... Classic Controller Pro is electronically the same +// +// ANA{l} ANA{r} +// BTN{l} BTN{L} BTN{R} BTN{r} +// ,--------. ,-, ,-, .--------, +// .----------------------------------------------------------. +// | | +// | BTN{W} BTN{x} | +// | BTN{A} BTN{D} BTN{-} BTN{h} BTN{+} BTN{y} BTN{a} | +// | BTN{S} BTN{b} | +// | | +// | ANA{y} ANA{Y} | +// | ANA{x} ANA{X} | +// | | +// `----------------------------------------------------------' +// + +//----------------------------------------------------------------------------- ---------------------------------------- +// Controllers which have calibration must have their calibratable controls here +//! Is there a better way to get the start of the decode struct to match the calibration struct ? +#define CLASSIC_ANALOGUE \ + uint8_t trgZL, trgZR; /* ANA{l, l} lowercase=trigger 5bit values {5} */ \ + uint8_t joyLX, joyLY; /* ANA{x, y} left=lowercase 6bit values {6}<-- */ \ + uint8_t joyRX, joyRY; /* ANA{X, Y} 5bit values {5} */ + +//----------------------------------------------------------------------------- +// Calibratable controls +// +typedef struct ecCalClassic { + CLASSIC_ANALOGUE +} ecCalClassic_t; + +//----------------------------------------------------------------------------- +// All controls +// +typedef struct ecDecClassic { + CLASSIC_ANALOGUE // MUST be first + + // Digital controls + bool btnZL, + btnZR; // BTN{l, l} + + bool btnL, btnR; // BTN{L, R} upperrcase=shoulder + + bool padU, padL, padD, padR; // BTN{W, A, S, D} + + bool btnM, btnH, btnP; // BTN{-, h, +} + + bool btnX, btnY; // BTN{x, y} + bool btnA, btnB; // BTN{a, b} + +} ecDecClassic_t; + +#undef CLASSIC_ANALOGUE + +//============================================================================= ======================================== +// Function prototypes +// +#include // Canvas +typedef struct wiiEC wiiEC_t; +typedef enum ecCalib ecCalib_t; +typedef struct state state_t; +typedef struct eventMsg eventMsg_t; + +void classic_decode(wiiEC_t* const pec); +void classic_msg(wiiEC_t* const pec, FuriMessageQueue* const queue); +void classic_calib(wiiEC_t* const pec, ecCalib_t c); + +void classic_show(Canvas* const canvas, state_t* const state); +bool classic_key(const eventMsg_t* const msg, state_t* const state); + +#endif //WII_EC_CLASSIC_H_ diff --git a/applications/plugins/wii_ec_anal/wii_ec_macros.h b/applications/plugins/wii_ec_anal/wii_ec_macros.h new file mode 100644 index 000000000..00ab9825b --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_ec_macros.h @@ -0,0 +1,138 @@ +#ifndef WII_EC_MACROS_H_ +#define WII_EC_MACROS_H_ + +//----------------------------------------------------------------------------- ---------------------------------------- +// CHECK MACROS +// +// I don't generally like this style of coding - it just (generally) makes things nightmarish to debug +// However, on this occasion I think it's a good choice (to make adding controllers LESS bug-prone) +// + +//if (furi_message_queue_get_count(queue) > 18) WARN("queue high %d", furi_message_queue_get_count(queue)); +#define MSGQ(lbl) \ + do { \ + msg.wiiEc.in = lbl; \ + furi_message_queue_put(queue, &msg, 0); \ + } while(0) + +// A 'standard' "button" is an independent SPST switch +// Eg. Nunchuck 'Z' button +// The "value" will always be 0 +#define BUTTON(btn, lbl) \ + do { \ + if(new->btn != old->btn) { \ + msg.wiiEc.type = (new->btn) ? WIIEC_PRESS : WIIEC_RELEASE; \ + msg.wiiEc.val = 0; \ + MSGQ(lbl); \ + } \ + } while(0) + +// An "analogue button" is an SPST coupled with an ananlogue 'switch' +// Eg. The "bottom out" switches on the triggers of the classic controller +// The "value" will be the value of the associated analogue controller +#define ANABTN(btn, ana, lbl) \ + do { \ + if(new->btn != old->btn) { \ + msg.wiiEc.type = (new->btn) ? WIIEC_PRESS : WIIEC_RELEASE; \ + msg.wiiEc.val = new->ana; \ + MSGQ(lbl); \ + } \ + } while(0) + +#define ANALOG(ana, lbl) \ + do { \ + if(new->ana != old->ana) { \ + msg.wiiEc.type = WIIEC_ANALOG; \ + msg.wiiEc.val = new->ana; \ + MSGQ(lbl); \ + } \ + } while(0) + +#define ACCEL(acc, lbl) \ + do { \ + if(new->acc != old->acc) { \ + msg.wiiEc.type = WIIEC_ACCEL; \ + msg.wiiEc.val = new->acc; \ + MSGQ(lbl); \ + } \ + } while(0) + +//----------------------------------------------------------------------------- ---------------------------------------- +// CALIBRATION MACROS +// +// Again ...I totally agree with anyone who says "MACRO coding" is (gernally) a poor choice of programming style +// But something about this code is making it soooo appealing +// +// ... v=variable, n=number +// +#define FACTORY_LO(v, n) \ + do { \ + (dst[1].v) = n; \ + } while(0) +#define FACTORY_MID(v, n) \ + do { \ + (dst[2].v) = n; \ + } while(0) +#define FACTORY_HI(v, n) \ + do { \ + (dst[3].v) = n; \ + } while(0) + +#define TRACK_LO(v) \ + do { \ + if((src->v) < (dst[0].v)) (dst[0].v) = (src->v); \ + } while(0) +#define TRACK_HI(v) \ + do { \ + if((src->v) > (dst[4].v)) (dst[4].v) = (src->v); \ + } while(0) +#define TRACK_LO_HI(v) \ + do { \ + TRACK_LO(v); \ + TRACK_HI(v); \ + } while(0) + +#define RESET_LO(v, b) \ + do { \ + (dst[0].v) = (dst[1].v) = ((1 << (b)) - 1); \ + } while(0) +#define RESET_HI(v) \ + do { \ + (dst[4].v) = (dst[3].v) = 0; \ + } while(0) +#define RESET_MID(v) \ + do { \ + (dst[2].v) = (src->v); \ + } while(0) +#define RESET_LO_HI(v, b) \ + do { \ + RESET_LO(v, b); \ + RESET_HI(v); \ + } while(0) +#define RESET_LO_MID_HI(v, b) \ + do { \ + RESET_LO(v, b); \ + RESET_MID(v); \ + RESET_HI(v); \ + } while(0) + +#define RANGE_LO(v) \ + do { \ + if((src->v) < (dst[1].v)) (dst[1].v) = (src->v); \ + } while(0) +#define RANGE_HI(v) \ + do { \ + if((src->v) > (dst[3].v)) (dst[3].v) = (src->v); \ + } while(0) +#define RANGE_LO_HI(v) \ + do { \ + RANGE_LO(v); \ + RANGE_HI(v); \ + } while(0) + +#define CENTRE(v) \ + do { \ + (dst[2].v) = (src->v); \ + } while(0) + +#endif //WII_EC_MACROS_H_ diff --git a/applications/plugins/wii_ec_anal/wii_ec_nunchuck.c b/applications/plugins/wii_ec_anal/wii_ec_nunchuck.c new file mode 100644 index 000000000..d88d535b6 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_ec_nunchuck.c @@ -0,0 +1,476 @@ +#include +#include // Core API + +#include "wii_anal.h" +#include "wii_i2c.h" +#include "bc_logging.h" + +#include "gfx/images.h" // Images +#include "wii_anal_lcd.h" // Drawing functions +#include "wii_anal_keys.h" // key mappings + +// ** If you want to see what this source code looks like with all the MACROs expanded +// ** grep -v '#include ' wii_ec_nunchuck.c | gcc -E -o /dev/stdout -xc - +#include "wii_ec_macros.h" + +//+============================================================================ ======================================== +// Standard Nunchuck : 2 buttons, 1 analogue joystick, 1 3-axis accelerometer +// +void nunchuck_decode(wiiEC_t* const pec) { + ecDecNunchuck_t* p = &pec->dec[(pec->decN = !pec->decN)].nunchuck; + uint8_t* joy = pec->joy; + + p->btnC = !(joy[5] & 0x02); // !{1} + p->btnZ = !(joy[5] & 0x01); // !{1} + + p->joyX = joy[0]; // {8} + p->joyY = joy[1]; // {8} + + p->accX = ((uint16_t)joy[2] << 2) | ((joy[5] >> 2) & 0x03); // {10} + p->accY = ((uint16_t)joy[3] << 2) | ((joy[5] >> 4) & 0x03); // {10} + p->accZ = ((uint16_t)joy[4] << 2) | ((joy[5] >> 6) & 0x03); // {10} + + DEBUG( + ">%d> C:%c, Z:%c, Joy{x:%02X, y:%02X}, Acc{x:%03X, y:%03X, z:%03X}", + pec->decN, + (p->btnC ? '#' : '.'), + (p->btnZ ? '#' : '.'), + p->joyX, + p->joyY, + p->accX, + p->accY, + p->accZ); +} + +//+============================================================================ ======================================== +// Give each button a unique character identifier +// +void nunchuck_msg(wiiEC_t* const pec, FuriMessageQueue* const queue) { + ecDecNunchuck_t* new = &pec->dec[pec->decN].nunchuck; + ecDecNunchuck_t* old = &pec->dec[!pec->decN].nunchuck; + + eventMsg_t msg = { + .id = EVID_WIIEC, + .wiiEc = { + .type = WIIEC_NONE, + .in = ' ', + .val = 0, + }}; + + BUTTON(btnC, 'c'); + BUTTON(btnZ, 'z'); + + ANALOG(joyX, 'x'); + ANALOG(joyY, 'y'); + + ACCEL(accX, 'x'); + ACCEL(accY, 'y'); + ACCEL(accZ, 'z'); +} + +//+============================================================================ ======================================== +// https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254#toc-5--read-actual-calibration-data-from-the-device-14 +// +void nunchuck_calib(wiiEC_t* const pec, ecCalib_t c) { + ecDecNunchuck_t* src = &pec->dec[pec->decN].nunchuck; // from input + ecCalNunchuck_t* dst = pec->calS.nunchuck; // to calibration data + + if(c & CAL_RESET) { // initialise ready for software calibration + // LO is set to the MAXIMUM value (so it can be reduced) + // HI is set to ZERO (so it can be increased) + RESET_LO_HI(accX, 10); // 10bit value + RESET_LO_HI(accY, 10); // 10bit value + RESET_LO_HI(accZ, 10); // 10bit value + + RESET_LO_HI(joyX, 8); // 8bit value + RESET_LO_HI(joyY, 8); // 8bit value + } + if(c & CAL_FACTORY) { // (re)set to factory defaults + //! "[4] LSB of Zero value of X,Y,Z axes" ...helpful! + //! ...Well, my test nunchuck has bits set in the bottom 6 bits, so let's guess ;) + + // No value available - annecdotal tests suggest 8 is reasonable + FACTORY_LO(accX, 8); + FACTORY_LO(accY, 8); + FACTORY_LO(accZ, 8); + + // @ 0G + FACTORY_MID(accX, ((pec->calF[0] << 2) | ((pec->calF[3] >> 4) & 0x3))); + FACTORY_MID(accY, ((pec->calF[1] << 2) | ((pec->calF[3] >> 2) & 0x3))); + FACTORY_MID(accZ, ((pec->calF[2] << 2) | ((pec->calF[3]) & 0x3))); + + // @ 1G + FACTORY_HI(accX, ((pec->calF[4] << 2) | ((pec->calF[7] >> 4) & 0x3))); + FACTORY_HI(accY, ((pec->calF[5] << 2) | ((pec->calF[7] >> 2) & 0x3))); + FACTORY_HI(accZ, ((pec->calF[6] << 2) | ((pec->calF[7]) & 0x3))); + + // Joysticks + FACTORY_LO(joyX, pec->calF[9]); + FACTORY_MID(joyX, pec->calF[10]); + FACTORY_HI(joyX, pec->calF[8]); + + FACTORY_LO(joyY, pec->calF[12]); + FACTORY_MID(joyY, pec->calF[13]); + FACTORY_HI(joyY, pec->calF[11]); + } + if(c & CAL_TRACK) { // track maximum and minimum values seen + TRACK_LO_HI(accX); + TRACK_LO_HI(accY); + TRACK_LO_HI(accZ); + + TRACK_LO_HI(joyX); + TRACK_LO_HI(joyY); + } + if(c & CAL_RANGE) { // perform software calibration step + RANGE_LO_HI(accX); + RANGE_LO_HI(accY); + RANGE_LO_HI(accZ); + + if(!(c & CAL_NOTJOY)) { // double negative! + RANGE_LO_HI(joyX); + RANGE_LO_HI(joyY); + } + } + if(c & CAL_CENTRE) { // reset centre point of joystick + CENTRE(accX); + CENTRE(accY); + CENTRE(accZ); + + CENTRE(joyX); + CENTRE(joyY); + } +} + +//============================================================================= ======================================== +// Accelerometer screen ...might this be useful for other controllers? +// +// https://bootlin.com/labs/doc/nunchuk.pdf +// X : Move Left/Right : -left / +right +// Y : Move Fwd/Bkwd : -fwd / +bkwd +// Z : Move Down/Up : -down / +up +// +// Movement in the direction of an axis changes that axis reading +// Twisting/tilting around an axis changes the other two readings +// +// EG. Move left will effect X ; turn left will effect Y & Z +// +#define aw 110 // axis width +#define ah 15 // height {0......7......14} +#define am 7 // midpoint { 7 } +#define ar 7 // range {1234567 1234567} + +enum { + ACC_X = 0, + ACC_Y = 1, + ACC_Z = 2, + ACC_CNT = 3, + ACC_1 = ACC_X, // first + ACC_N = ACC_Z, // last +}; + +//+============================================================================ +static void nunchuck_showAcc(Canvas* const canvas, state_t* const state) { + ecDecNunchuck_t* d = &state->ec.dec[state->ec.decN].nunchuck; + ecCalNunchuck_t* lo = &state->ec.calS.nunchuck[1]; + ecCalNunchuck_t* mid = &state->ec.calS.nunchuck[2]; + ecCalNunchuck_t* hi = &state->ec.calS.nunchuck[3]; + + int y[ACC_CNT] = {0, 0 + (ah + 4), 0 + ((ah + 4) * 2)}; + int x = 10; + + static uint16_t v[ACC_CNT][aw] = {0}; + // static uint16_t tv[ACC_CNT][aw] = {0}; + + static uint16_t idx = 0; + static uint16_t cnt = aw - 1; + + // Only record when scanner NOT-paused + if(!state->pause) { + uint16_t dead = (1 << 5); + + // Find axes y-offsets + for(int a = ACC_1; a <= ACC_N; a++) { + uint16_t* dp = NULL; // data value (current reading) + uint16_t* lp = NULL; // lo value + uint16_t* mp = NULL; // mid value + uint16_t* hp = NULL; // hi value + uint16_t* vp = NULL; // value (result) + + switch(a) { + case ACC_X: + dp = &d->accX; // data (input) + lp = &lo->accX; // low \. + mp = &mid->accX; // mid > calibration + hp = &hi->accX; // high / + vp = &v[ACC_X][idx]; // value (where to store the result) + break; + case ACC_Y: + dp = &d->accY; + lp = &lo->accY; + mp = &mid->accY; + hp = &hi->accY; + vp = &v[ACC_Y][idx]; + break; + case ACC_Z: + dp = &d->accZ; + lp = &lo->accZ; + mp = &mid->accZ; + hp = &hi->accZ; + vp = &v[ACC_Z][idx]; + break; + default: + break; + } + + // Again - qv. the joysick calibration: + // This is not the "right way" to do this, it is just "one way" to do it + // ...mid point and extreme zones have a deadzone + // ...the rest is evenly divided by the amount of space on the graph + if((*dp >= (*mp - dead)) && (*dp <= (*mp + dead))) + *vp = ar; + else if(*dp >= (*hp - dead)) + *vp = ah - 1; + else if(*dp <= (*lp + dead)) + *vp = 0; + else if(*dp < *mp) { + uint16_t min = ((*lp + dead) + 1); + uint16_t max = ((*mp - dead) - 1); + float range = (max - min) + 1; + float m = range / (ar - 1); // 6 evenly(/fairly) divided zones + *vp = ((int)((*dp - min) / m)) + 1; + + } else { //if (*dp > *mp) + uint16_t min = ((*mp + dead) + 1); + uint16_t max = ((*hp - dead) - 1); + float range = (max - min) + 1; + float m = range / (ar - 1); // 6 evenly(/fairly) divided zones + *vp = ((int)((*dp - min) / m)) + 1 + ar; + } + } + + //! If we decide to offer "export to CSV" + //! I suggest we keep a second array of true-values, rather than do all the maths every time + //! Also - the data will need to me moved to the 'state' table - so a.n.other function can save it off + // tv[ACC_X][idx] = d->accX; + // tv[ACC_Y][idx] = d->accY; + // tv[ACC_Z][idx] = d->accZ; + + // Prepare for the next datapoint + if(++idx >= aw) idx = 0; + if(cnt) cnt--; + } + + // Auto-pause + if(state->apause && !idx) state->pause = true; + + // *** Draw axes *** + show(canvas, 0, y[ACC_X] + ((ah - img_6x8_X.h) / 2), &img_6x8_X, SHOW_SET_BLK); + show(canvas, 0, y[ACC_Y] + ((ah - img_6x8_Y.h) / 2), &img_6x8_Y, SHOW_SET_BLK); + show(canvas, 0, y[ACC_Z] + ((ah - img_6x8_Z.h) / 2), &img_6x8_Z, SHOW_SET_BLK); + + canvas_set_color(canvas, ColorBlack); + for(int a = ACC_1; a <= ACC_N; a++) { + canvas_draw_line(canvas, x - 1, y[a], x - 1, y[a] + ah); + canvas_draw_line(canvas, x, y[a] + ah, x + aw - 1, y[a] + ah); + + // Mid & Peak lines + for(int i = 1; i < aw; i += 3) { + canvas_draw_dot(canvas, x + i, y[a]); + canvas_draw_dot(canvas, x + i, y[a] + (ah / 2)); + } + } + + // Data (wiper display - see notes.txt for scrolling algorithm) + int end = idx ? idx : aw; + for(int a = ACC_1; a <= ACC_N; a++) { + canvas_draw_dot(canvas, x, y[a] + v[a][idx]); + for(int i = 1; i < end; i++) + canvas_draw_line(canvas, x + i, y[a] + v[a][i - 1], x + i, y[a] + v[a][i]); + if(!state->apause) + for(int i = end + 10; i < aw - cnt; i++) + canvas_draw_line(canvas, x + i, y[a] + v[a][i - 1], x + i, y[a] + v[a][i]); + } + // Wipe bar + if(end < aw) canvas_draw_line(canvas, x + end, y[0], x + end, y[2] + ah - 1); + if(++end < aw) canvas_draw_line(canvas, x + end, y[0], x + end, y[2] + ah - 1); + if(++end < aw) canvas_draw_line(canvas, x + end, y[0], x + end, y[2] + ah - 1); + + // *** Mode buttons *** + show(canvas, 0, 55, &img_key_L, SHOW_SET_BLK); // mode key + + if((state->calib & CAL_RANGE) || state->pause) state->flash++; + + // -pause- ...yeah, this got a little out of hand! LOL! + if(state->pause || state->apause) { + if(state->pause && state->apause && !idx) { + if(state->flash & 8) { + show(canvas, 108, 56, &img_key_U, SHOW_SET_BLK); + } else { + show(canvas, 108, 56, &img_key_Ui, SHOW_SET_BLK); + canvas_draw_line(canvas, x + aw, y[0], x + aw, y[2] + ah - 1); + } + } else { + show(canvas, 108, 56, &img_key_Ui, SHOW_SET_BLK); + } + } else { + show(canvas, 108, 56, &img_key_U, SHOW_SET_BLK); // pause + } + + // -calibration- + if(state->calib & CAL_RANGE) { + show(canvas, 119, 55, (state->flash & 8) ? &img_key_OKi : &img_key_OK, SHOW_SET_BLK); + } else { + show(canvas, 119, 55, &img_key_OK, SHOW_SET_BLK); + } +} + +#undef aw +#undef ah +#undef am +#undef ar + +//+============================================================================ ======================================== +// Default nunchuck screen +// +void nunchuck_show(Canvas* const canvas, state_t* const state) { + // Nunchucks have TWO scenes + if(state->scene == SCENE_NUNCHUCK_ACC) return nunchuck_showAcc(canvas, state); + + // Default scene + ecDecNunchuck_t* d = &state->ec.dec[state->ec.decN].nunchuck; + ecCalNunchuck_t* c = (state->hold) ? &state->ec.calS.nunchuck[(state->hold < 0) ? 0 : 4] : + (ecCalNunchuck_t*)d; //! danger will robinson! + ecCalNunchuck_t* js = state->ec.calS.nunchuck; + + // X, Y, Z + show(canvas, 42, 0, &img_6x8_X, SHOW_SET_BLK); + show(canvas, 73, 0, &img_6x8_Y, SHOW_SET_BLK); + show(canvas, 104, 0, &img_6x8_Z, SHOW_SET_BLK); + + canvas_draw_str_aligned(canvas, 0, 14, AlignLeft, AlignTop, "Accel"); + canvas_draw_str_aligned(canvas, 0, 28, AlignLeft, AlignTop, "Joy"); + + // accel values + showHex(canvas, 34, 12, c->accX, 3, 2); + showHex(canvas, 65, 12, c->accY, 3, 2); + showHex(canvas, 96, 12, c->accZ, 3, 2); + // Joy values + showHex(canvas, 38, 27, c->joyX, 2, 2); + showHex(canvas, 69, 27, c->joyY, 2, 2); + + showJoy( + canvas, + 103, + 32, + js[1].joyX, + js[2].joyX, + js[3].joyX, + js[1].joyY, + js[2].joyY, + js[3].joyY, + d->joyX, + d->joyY, + 8); + + // buttons + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 0, 44, AlignLeft, AlignTop, "Button"); + + if(!d->btnC) { + canvas_draw_rframe(canvas, 36, 42, 18, 12, 6); + show(canvas, 42, 44, &img_6x8_C, SHOW_SET_BLK); + } else { + canvas_draw_rbox(canvas, 36, 42, 18, 12, 6); + show(canvas, 42, 44, &img_6x8_C, SHOW_SET_WHT); + canvas_set_color(canvas, ColorBlack); + } + + if(!d->btnZ) { + canvas_draw_rframe(canvas, 64, 40, 24, 16, 2); + show(canvas, 73, 44, &img_6x8_Z, SHOW_SET_BLK); + } else { + canvas_draw_rbox(canvas, 64, 40, 24, 16, 2); + show(canvas, 73, 44, &img_6x8_Z, SHOW_SET_WHT); + } + + // Navigation + showPeakHold(state, canvas, state->hold); // peak keys + show(canvas, 0, 55, &img_key_L, SHOW_SET_BLK); // mode keys + show(canvas, 9, 55, &img_key_R, SHOW_SET_BLK); +} + +//+============================================================================ ======================================== +static bool nunchuck_keyAcc(const eventMsg_t* const msg, state_t* const state) { + int used = false; // assume key is NOT-handled + + switch(msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyDown: //# pause) + state->pause = false; // Paused? Restart + else + state->apause = !state->apause; // No? toggle auto-pause + used = true; + break; + + case InputKeyLeft: //# calib &= ~CAL_NOTJOY; // DO calibrate joystick in NUNCHUCK mode + used = true; + break; + + default: + break; //# scene == SCENE_NUNCHUCK_ACC) return nunchuck_keyAcc(msg, state); + + // Default scene + int used = false; // assume key is NOT-handled + + switch(msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyLeft: //# calib |= CAL_NOTJOY; // do NOT calibrate joystick in _ACC mode + used = true; + break; + default: + break; //# +#include + +//----------------------------------------------------------------------------- +// Controllers which have calibration must have their calibratable controls here +//! Is there a better way to get the start of the decode struct to match the calibration struct ? +#define NUNCHUCK_ANALOGUE \ + uint8_t joyX, joyY; \ + uint16_t accX, accY, accZ; + +//----------------------------------------------------------------------------- +// Calibratable controls +// +typedef struct ecCalNunchuck { + NUNCHUCK_ANALOGUE +} ecCalNunchuck_t; + +//----------------------------------------------------------------------------- +// All controls +// +typedef struct ecDecNunchuck { + NUNCHUCK_ANALOGUE // MUST be first + + // Digital controls + bool btnC, + btnZ; // BTN{c, z} +} ecDecNunchuck_t; + +#undef NUNCHUCK_ANALOGUE + +//============================================================================= +// Function prototypes +// +#include // Canvas +typedef struct wiiEC wiiEC_t; +typedef enum ecCalib ecCalib_t; +typedef struct state state_t; +typedef struct eventMsg eventMsg_t; + +void nunchuck_decode(wiiEC_t* const pec); +void nunchuck_msg(wiiEC_t* const pec, FuriMessageQueue* const queue); +void nunchuck_calib(wiiEC_t* const pec, ecCalib_t c); + +void nunchuck_show(Canvas* const canvas, state_t* const state); +bool nunchuck_key(const eventMsg_t* const msg, state_t* const state); + +#endif //WII_EC_NUNCHUCK_H_ diff --git a/applications/plugins/wii_ec_anal/wii_ec_udraw.c b/applications/plugins/wii_ec_anal/wii_ec_udraw.c new file mode 100644 index 000000000..82987b205 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_ec_udraw.c @@ -0,0 +1,149 @@ +//! udraw support is NOT written - this is just notes about the init function +#include +#include // Core API + +#include "wii_anal.h" +#include "wii_ec.h" +#include "bc_logging.h" + +#include "i2c_workaround.h" //! temporary workaround for a bug in furi i2c [see header] + +// ** If you want to see what this source code looks like with all the MACROs expanded +// ** grep -v '#include ' wii_ec_udraw.c | gcc -E -o /dev/stdout -xc - +#include "wii_ec_macros.h" + +//+============================================================================ ======================================== +// https://github.com/madhephaestus/WiiChuck/blob/master/src/Drawsome.cpp#L3 +// Gratuitously stolen ... never tested (don't own one) - just bought one on ebay +// although it seems like the UK version is a "uDraw" and MIGHT contain a different chipset :/ +// +// read 6 bytes starting from 0x20 +// read 6 bytes starting from 0x28 +// read 6 bytes starting from 0x30 +// read 6 bytes starting from 0x38 +// read 6 bytes starting from 0x00 (#1) +// read 6 bytes starting from 0x00 (#2) +// write 1 byte [0x01] to 0xFB +// read 6 bytes starting from 0x00 (#3) +// read 6 bytes starting from 0x00 (#4) +// +bool udraw_init(wiiEC_t* const pec) { + ENTER; + bool rv = true; + + (void)pec; + /* +//! this is the Drawsome code, NOT the uDraw code !! + static const uint8_t reg[9] = {0x20, 0x28, 0x30, 0x38, 0x00, 0x00, 0xFB, 0x00, 0x00}; // 0..8 + const uint8_t* p = reg; + uint8_t buf[6] = {0}; + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 0 + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 1 + furi_delay_ms(100); + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 2 + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 3 + furi_delay_ms(100); + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 4 + furi_delay_ms(100); + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 5 + furi_delay_ms(100); + + buf[0] = *p++; + buf[1] = 0x01; + if (!furi_hal_i2c_tx(bus,addr, buf,2, timeout)) goto fail ; // 6 + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 7 + furi_delay_ms(100); + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 8 + furi_delay_ms(100); + + TRACE("%s : OK #%d", __func__, (p-reg)); + goto done; + +fail: + ERROR("%s : fail #%d", __func__, (p -reg) -1); + rv = false; + +done: +*/ + LEAVE; + return rv; +} + +//+============================================================================ ======================================== +bool udraw_key(const eventMsg_t* const msg, state_t* const state) { + (void)state; + bool run = true; + + switch(msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyUp: //# ! After INPUT_LONG_PRESS interval, asynch to InputTypeRelease + switch(msg->input.key) { + case InputKeyUp: //# >U [ LONG-UP ] + case InputKeyDown: //# >D [ LONG-DOWN ] + case InputKeyLeft: //# >L [ LONG-LEFT ] + case InputKeyRight: //# >R [ LONG-RIGHT ] + case InputKeyOk: //# >O [ LONG-OK ] + case InputKeyBack: //# >B [ LONG-BACK ] + default: + break; //# >? + } + break; + case InputTypePress: //# +! After debounce + switch(msg->input.key) { + case InputKeyUp: //# +U [ SHORT-UP ] + case InputKeyDown: //# +D [ SHORT-DOWN ] + case InputKeyLeft: //# +L [ SHORT-LEFT ] + case InputKeyRight: //# +R [ SHORT-RIGHT ] + case InputKeyOk: //# +O [ SHORT-OK ] + case InputKeyBack: //# +B [ SHORT-BACK ] + default: + break; //# +? + } + break; + case InputTypeRepeat: //# *! With INPUT_REPEATE_PRESS period after InputTypeLong event + switch(msg->input.key) { + case InputKeyUp: //# *U [ REPEAT-UP ] + case InputKeyDown: //# *D [ REPEAT-DOWN ] + case InputKeyLeft: //# *L [ REPEAT-LEFT ] + case InputKeyRight: //# *R [ REPEAT-RIGHT ] + case InputKeyOk: //# *O [ REPEAT-OK ] + case InputKeyBack: //# *B [ REPEAT-BACK ] + default: + break; //# *? + } + break; + case InputTypeRelease: //# -! After debounce + switch(msg->input.key) { + case InputKeyUp: //# -U [ RELEASE-UP ] + case InputKeyDown: //# -D [ RELEASE-DOWN ] + case InputKeyLeft: //# -L [ RELEASE-LEFT ] + case InputKeyRight: //# -R [ RELEASE-RIGHT ] + case InputKeyOk: //# -O [ RELEASE-OK ] + case InputKeyBack: //# -B [ RELEASE-BACK ] + default: + break; //# -? + } + break; + default: + return true; + } + + return run; +} diff --git a/applications/plugins/wii_ec_anal/wii_ec_udraw.h b/applications/plugins/wii_ec_anal/wii_ec_udraw.h new file mode 100644 index 000000000..9283fd95d --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_ec_udraw.h @@ -0,0 +1,18 @@ +#ifndef WII_EC_UDRAW_H_ +#define WII_EC_UDRAW_H_ + +#include +#include + +//============================================================================= ======================================= +// Function prototypes +// +typedef struct wiiEC wiiEC_t; +typedef enum ecCalib ecCalib_t; +typedef struct eventMsg eventMsg_t; +typedef struct state state_t; + +bool udraw_init(wiiEC_t* const pec); +bool udraw_key(const eventMsg_t* const msg, state_t* const state); + +#endif //WII_EC_UDRAW_H_ diff --git a/applications/plugins/wii_ec_anal/wii_i2c.c b/applications/plugins/wii_ec_anal/wii_i2c.c new file mode 100644 index 000000000..f5d6840d9 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_i2c.c @@ -0,0 +1,301 @@ +//----------------------------------------------------------------------------- ---------------------------------------- +// Biblio: [standing on the shoulders of giants] +// https://bootlin.com/labs/doc/nunchuk.pdf +// https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254#toc-i2c-protocol-9 +// https://web.archive.org/web/20220000000000*/https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254 +// https://github.com/madhephaestus/WiiChuck/blob/master/src/Accessory.cpp#L14 +// https://wiibrew.org/wiki/Wiimote/Extension_Controllers +// https://www.best-microcontroller-projects.com/i2c-tutorial.html +// +// WiiMote Extension Controller: +// Bus Address : 0x52 +// Register autoincrements after each (byte is) read +// 0x00..0x05 ( 6 bytes) ... [r] Controller Data +// 0x20..0x2F (16 bytes) ... [r] Calibration Data +// 0x30..0x3F (16 bytes) ... [r] (A copy of the) Calibration Data +// 0x40..0x4F (16 bytes) ... [w] Encryption key(s) +// 0xFA..0xFF ( 6 bytes) ... [r] Perhipheral ID + +//----------------------------------------------------------------------------- ---------------------------------------- +#include +#include +#include + +#include +#include +#include + +#include "i2c_workaround.h" //! temporary workaround for a bug in furi i2c [see header] + +#include "wii_anal.h" +#include "wii_i2c.h" +#include "wii_ec.h" + +#include "bc_logging.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// Wii Extension Controller i2c Bus address +static const uint8_t ec_i2cAddr = 0x52; + +// Initialise for UNencrypted comms +static const uint8_t regInit1 = 0xF0; +static const uint8_t regInit2 = 0xFB; +static const uint8_t cmdInit1[] = {regInit1, 0x55}; +static const uint8_t cmdInit2[] = {regInit2, 0x00}; + +// Initialise for ENcrypted comms +static const uint8_t regInitEnc = 0x40; +static const uint8_t cmdInitEnc[] = {regInitEnc, 0x00}; + +// Crypto key (PSK), base register : {0x40..0x4F}[2][8] +static const uint8_t regEnc = 0x40; // ENC_LEN + +// Controller State data, base register : {0x00..0x05}[6] +static const uint8_t regJoy = 0x00; // JOY_LEN + +// Calibration data, base register : {0x20..0x2F}[16] +static const uint8_t regCal = 0x20; // CAL_LEN + +// Controller ID, base register : {0xFA..0xFF}[6] +static const uint8_t regPid = 0xFA; // PID_LEN + +//+============================================================================ ======================================== +// Hexdump a buffer to the logfile +// +#if LOG_LEVEL >= 4 // INFO + +static void dump(const uint8_t* buf, const unsigned int len, const char* id) { + // snprintf() would be useful! + char s[128] = {0}; + char* p = NULL; + + strcpy(s, id); + p = s + strlen(s); + *p++ = ':'; + *p++ = ' '; + *p++ = '{'; + + for(unsigned int i = 0; i < len; i++) { + uint8_t hi = (buf[i] & 0xF0) >> 4; + uint8_t lo = (buf[i] & 0x0F); + + hi = hi + ((hi > 9) ? ('A' - 10) : '0'); + lo = lo + ((lo > 9) ? ('A' - 10) : '0'); + + *p++ = (char)hi; + *p++ = (char)lo; + *p++ = ','; + } + *p = '\0'; + *--p = '}'; + INFO(s); +} + +#else +#define dump(...) +#endif + +//+============================================================================ ======================================== +// +//! -W-A-R-N-I-N-G- : THIS ENCRYPTION CODE SHOULD NEVER BE REQUIRED ... AS SUCH, I'VE NEVER TESTED IT +// +static void decrypt(uint8_t* buf, const uint8_t* encKey, const uint8_t reg, unsigned int len) { +#if 1 // Use standard algorithm + // decrypted_byte = (encrypted_byte XOR encKey[1][address%8]) + encKey[2][address%8] + for(uint8_t* p = buf; p < buf + len; p++) + *p = (*p ^ encKey[(reg + (p - buf)) % 8]) + encKey[8 + ((reg + (p - buf)) % 8)]; + +#else //! This is (I think) a shortcut for an all-zero key [not tested] + (void)encKey; + (void)reg; + for(uint8_t* p = buf; p < buf + len; p++) *p = (*p ^ 0x17) + 0x17; +#endif +} + +//+============================================================================ ======================================== +// Read the Extension Controller state +// ...and decode it in to something sane +// +// Returns: {0:OK, >0:Error} +// +int ecRead(wiiEC_t* pec) { + ENTER; + int rv = 0; // assume success + + if(!pec->init) { + WARN("%s : device not initialised", __func__); + rv = 1; + goto bail; + } + + if(!furi_hal_i2c_is_device_ready(i2cBus, i2cAddr, i2cTimeout)) { + INFO("%s : device disconnected", __func__); + pec->init = false; + rv = 2; + goto bail; + } + + if(!furi_hal_i2c_trxd( + i2cBus, i2cAddr, ®Joy, 1, pec->joy, JOY_LEN, i2cTimeout, i2cReadWait)) { + ERROR("%s : trxd fail", __func__); + rv = 3; + goto bail; + } + + if(pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN); + + // Decode the readings (according to Controller type) + ecDecode(pec); + +bail: + LEAVE; + return rv; +} + +//+============================================================================ ======================================== +// Initialise an Extension Controller +// +//! To disable encryption, pass a NULL encryption key <-- this is currently ALWAYS the case +// +bool ecInit(wiiEC_t* pec, const uint8_t* encKey) { + ENTER; + + bool rv = false; // assume failure + +#if 0 //! i2c workaround + //! I think this is done during OS startup - long before the plugin starts + furi_hal_i2c_init(); +#endif + +#if 0 //! i2c workaround + // May become relevant when the i2c issues are resolved + // Take control of the i2c bus [which returns void !?] + // --> firmware/targets/f7/furi_hal/furi_hal_i2c.c + furi_hal_i2c_acquire(i2cBus); +#endif + + pec->init = false; // assume failure + + // === See if the device is alive === + if(!furi_hal_i2c_is_device_ready(i2cBus, i2cAddr, i2cTimeout)) { + TRACE("%s : waiting for device", __func__); + goto bail; + } + INFO("%s : device connected", __func__); + + // === Initialise the device === + pec->init = false; // This goes true AFTER the (optional) controller-specific init code + + // === Start the Extension Controller === + if(encKey) { //! start in encrypted mode + + //! todo - should this happen here, or AFTER we've got the ID ? + + } else { + if(!furi_hal_i2c_tx(i2cBus, i2cAddr, cmdInit1, sizeof(cmdInit1), i2cTimeout)) { + ERROR("%s : init fail (dec1)", __func__); + goto bail; + } + TRACE("%s : init OK1", __func__); + + if(!furi_hal_i2c_tx(i2cBus, i2cAddr, cmdInit2, sizeof(cmdInit2), i2cTimeout)) { + ERROR("%s : init fail (dec2)", __func__); + goto bail; + } + TRACE("%s : init OK2", __func__); + } + + // === Retrieve the Extension Controller ID === + if(!furi_hal_i2c_trx(i2cBus, i2cAddr, ®Pid, 1, pec->pid, PID_LEN, i2cTimeout)) { + ERROR("%s : T(R)x fail (pid)", __func__); + goto bail; + } + if(pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN); + dump(pec->pid, PID_LEN, "pid"); // debug INFO + + // Find the StringID in the lookup table + for(pec->pidx = PID_FIRST; pec->pidx < PID_ERROR; pec->pidx++) + if(memcmp(pec->pid, ecId[pec->pidx].id, PID_LEN) == 0) break; + if(pec->pidx == PID_ERROR) pec->pidx = PID_UNKNOWN; + pec->sid = ecId[pec->pidx].name; + INFO("sid: %s", pec->sid); + + // === (optionally) Enable encryption === + if(!encKey) { + pec->encrypt = false; + + } else { // Controller WILL encrypt ALL tranmissions + //! this encryption code fails - should it be done earlier? + //! as it is probably never of any use, I'm kinda loathed to spend time on it + //! https://github.com/madhephaestus/WiiChuck/blob/master/src/Accessory.cpp#L138 + uint8_t encTx[1 + ENC_LEN] = {0}; + uint8_t* ep = encTx; + + pec->encrypt = true; + + // ** Start the Controller in ENcrytped mode + if(!furi_hal_i2c_tx(i2cBus, i2cAddr, cmdInitEnc, sizeof(cmdInitEnc), i2cTimeout)) { + ERROR("%s : init fail (enc)", __func__); + goto bail; + } + + // Copy the (symmetric) encryption key to the controller state table + if(pec->encKey != encKey) memcpy(pec->encKey, encKey, ENC_LEN); + + // Build the encryption key packet + *ep++ = regEnc; + memcpy(ep, pec->encKey, ENC_LEN); + + // ** Send encryption key (PSK) + if(!furi_hal_i2c_tx(i2cBus, i2cAddr, encTx, (1 + ENC_LEN), i2cTimeout)) { + ERROR("%s : key fail", __func__); + goto bail; + } + + TRACE("%s : init OK (enc)", __func__); + } + + // === Some devices [eg. Drawsome/uDraw] require additional init code === + if(ecId[pec->init].init && (ecId[pec->init].init(pec) == false)) goto bail; + pec->init = true; + + // === Read calibration data === + if(!furi_hal_i2c_trx(i2cBus, i2cAddr, ®Cal, 1, pec->calF, CAL_LEN, i2cTimeout)) { + ERROR("%s : trx fail (cal)", __func__); + goto bail; + } + if(pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN); + dump(pec->calF, CAL_LEN, "cal"); + + ecCalibrate(pec, CAL_RESET | CAL_FACTORY); // Load factory default calibration + + // === Initialise decode buffers === + pec->decN = 0; // read in to decode[1] (yes, N=0 -> read in to dec[1]) + switch(ecRead(pec)) { + case 0: // read OK + memcpy(&pec->dec[0], &pec->dec[1], sizeof(pec->dec[0])); + dump(pec->joy, JOY_LEN, "joy"); + break; + + default: // bug: unknown + case 1: // bug: not initialised - should never happen + ERROR("%s : read bug", __func__); + break; + + case 2: // device gone + case 3: // read fail + // Logging done by ecRead() + pec->init = false; + goto bail; + } + + rv = true; // yay :) + +bail: +#if 0 //! i2c workaround + furi_hal_i2c_release(i2cBus); +#endif + + LEAVE; + return rv; +} diff --git a/applications/plugins/wii_ec_anal/wii_i2c.h b/applications/plugins/wii_ec_anal/wii_i2c.h new file mode 100644 index 000000000..efebefcf9 --- /dev/null +++ b/applications/plugins/wii_ec_anal/wii_i2c.h @@ -0,0 +1,42 @@ +#ifndef WII_I2C_H_ +#define WII_I2C_H_ + +#include + +//#include "wii_ec.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// i2c bus details +// +// https://www.best-microcontroller-projects.com/i2c-tutorial.html +// https://web.archive.org/web/20220000000000*/https://www.best-microcontroller-projects.com/i2c-tutorial.html +// https://training.ti.com/introduction-i2c-reserved-addresses +// +// After the (special) START "bit"... +// the first 8bits (byte) of i2c data are the 7bit i2c Address, +// FOLLOWED by 1bit to signify a READ or WRITE {0=write, 1=read} +// The data is transmitted BIG-Endian, IE. MSb first [human readable] +// So the address actually lives in the TOP (MSb's) of the first "byte", (with bit0 being used as the read/write flag) +// +// The read() and write() functions on the FZ will set the LSb appropriately, +// BUT they do NOT shift the address left to make room for it! +// So the address you give to read/write() MUST be given as (7bitAddress << 1) +// +// When we read: After we send the read command, we wait for i2cReadWait uS before reading the data +// + +// firmware/targets/f7/furi_hal/furi_hal_i2c_types.h +#define i2cBus (&furi_hal_i2c_handle_external) // FZ external i2c bus +#define i2cAddr (ec_i2cAddr << 1) +#define i2cTimeout (3) // in mS +#define i2cReadWait (300) //! 300uS: how low can we take this? + +//----------------------------------------------------------------------------- ---------------------------------------- +// public functions +// +typedef struct wiiEC wiiEC_t; + +bool ecInit(wiiEC_t* const pec, const uint8_t* encKey); +int ecRead(wiiEC_t* const pec); + +#endif //WII_I2C_H_ diff --git a/applications/services/application.fam b/applications/services/application.fam index 8d7d8168a..47601a436 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -10,6 +10,8 @@ App( "loader", "power", "ibuttonsrv", + "infraredsrv", + "lfrfidsrv", "namechangersrv", ], ) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index aeb2beec9..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" @@ -165,6 +166,11 @@ static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context ret = rpc_session_get_available_size(bt->rpc_session); } else if(event.event == SerialServiceEventTypeDataSent) { furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_BUFF_SENT); + } else if(event.event == SerialServiceEventTypesBleResetRequest) { + FURI_LOG_I(TAG, "BLE restart request received"); + BtMessage message = {.type = BtMessageTypeSetProfile, .data.profile = BtProfileSerial}; + furi_check( + furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); } return ret; } @@ -226,6 +232,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { rpc_session_set_context(bt->rpc_session, bt); furi_hal_bt_serial_set_event_callback( RPC_BUFFER_SIZE, bt_serial_event_callback, bt); + furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatusActive); } else { FURI_LOG_W(TAG, "RPC is busy, failed to open new session"); } @@ -241,6 +248,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { } else if(event.type == GapEventTypeDisconnected) { if(bt->profile == BtProfileSerial && bt->rpc_session) { FURI_LOG_I(TAG, "Close RPC connection"); + furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatusNotActive); furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); rpc_session_close(bt->rpc_session); furi_hal_bt_serial_set_event_callback(0, NULL, NULL); @@ -330,14 +338,20 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { } furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); bt->profile = message->data.profile; - *message->result = true; + if(message->result) { + *message->result = true; + } } else { FURI_LOG_E(TAG, "Failed to start Bt App"); - *message->result = false; + if(message->result) { + *message->result = false; + } } } else { bt_show_warning(bt, "Radio stack doesn't support this app"); - *message->result = false; + if(message->result) { + *message->result = false; + } } furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); } diff --git a/applications/services/cli/cli_command_gpio.c b/applications/services/cli/cli_command_gpio.c index 54671eda4..0b29f4853 100644 --- a/applications/services/cli/cli_command_gpio.c +++ b/applications/services/cli/cli_command_gpio.c @@ -40,7 +40,7 @@ static bool pin_name_to_int(FuriString* pin_name, size_t* result) { bool debug = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { if(!furi_string_cmp(pin_name, cli_command_gpio_pins[i].name)) { - if(!cli_command_gpio_pins[i].debug || (cli_command_gpio_pins[i].debug && debug)) { + if(!cli_command_gpio_pins[i].debug || debug) { *result = i; found = true; break; @@ -55,21 +55,26 @@ static void gpio_print_pins(void) { printf("Wrong pin name. Available pins: "); bool debug = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(!cli_command_gpio_pins[i].debug || (cli_command_gpio_pins[i].debug && debug)) { + if(!cli_command_gpio_pins[i].debug || debug) { printf("%s ", cli_command_gpio_pins[i].name); } } } -typedef enum { OK, ERR_CMD_SYNTAX, ERR_PIN, ERR_VALUE } GpioParseError; +typedef enum { + GpioParseReturnOk, + GpioParseReturnCmdSyntaxError, + GpioParseReturnPinError, + GpioParseReturnValueError +} GpioParseReturn; -static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) { +static GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) { FuriString* pin_name; pin_name = furi_string_alloc(); size_t ws = furi_string_search_char(args, ' '); if(ws == FURI_STRING_FAILURE) { - return ERR_CMD_SYNTAX; + return GpioParseReturnCmdSyntaxError; } furi_string_set_n(pin_name, args, 0, ws); @@ -78,7 +83,7 @@ static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint if(!pin_name_to_int(pin_name, pin_num)) { furi_string_free(pin_name); - return ERR_PIN; + return GpioParseReturnPinError; } furi_string_free(pin_name); @@ -88,10 +93,10 @@ static GpioParseError gpio_command_parse(FuriString* args, size_t* pin_num, uint } else if(!furi_string_cmp(args, "1")) { *value = 1; } else { - return ERR_VALUE; + return GpioParseReturnValueError; } - return OK; + return GpioParseReturnOk; } void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { @@ -101,22 +106,22 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { size_t num = 0; uint8_t value = 255; - GpioParseError err = gpio_command_parse(args, &num, &value); + GpioParseReturn err = gpio_command_parse(args, &num, &value); - if(ERR_CMD_SYNTAX == err) { + if(err == GpioParseReturnCmdSyntaxError) { cli_print_usage("gpio mode", " <0|1>", furi_string_get_cstr(args)); return; - } else if(ERR_PIN == err) { + } else if(err == GpioParseReturnPinError) { gpio_print_pins(); return; - } else if(ERR_VALUE == err) { + } else if(err == GpioParseReturnValueError) { printf("Value is invalid. Enter 1 for input or 0 for output"); return; } if(cli_command_gpio_pins[num].debug) { printf( - "Changeing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); + "Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); char c = cli_getc(cli); if(c != 'y' && c != 'Y') { printf("Cancelled.\r\n"); @@ -161,15 +166,15 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { size_t num = 0; uint8_t value = 0; - GpioParseError err = gpio_command_parse(args, &num, &value); + GpioParseReturn err = gpio_command_parse(args, &num, &value); - if(ERR_CMD_SYNTAX == err) { + if(err == GpioParseReturnCmdSyntaxError) { cli_print_usage("gpio set", " <0|1>", furi_string_get_cstr(args)); return; - } else if(ERR_PIN == err) { + } else if(err == GpioParseReturnPinError) { gpio_print_pins(); return; - } else if(ERR_VALUE == err) { + } else if(err == GpioParseReturnValueError) { printf("Value is invalid. Enter 1 for high or 0 for low"); return; } diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 239434b7a..d6d9da54c 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -7,6 +7,7 @@ #include #include #include +#include // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" @@ -135,6 +136,16 @@ void cli_command_date(Cli* cli, FuriString* args, void* context) { } } +void cli_command_src(Cli* cli, FuriString* args, void* context) { + // Quality of life feaature for people exploring CLI on lab.flipper.net/cli + // By Yousef AK + UNUSED(cli); + UNUSED(args); + UNUSED(context); + + printf("https://github.com/RogueMaster/flipperzero-firmware-wPlugins"); +} + #define CLI_COMMAND_LOG_RING_SIZE 2048 #define CLI_COMMAND_LOG_BUFFER_SIZE 64 @@ -192,6 +203,83 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { furi_stream_buffer_free(ring); } +void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + if(!furi_string_cmp(args, "0")) { + furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); + loader_update_menu(); + printf("Debug disabled."); + } else if(!furi_string_cmp(args, "1")) { + furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); + loader_update_menu(); + printf("Debug enabled."); + } else { + cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); + } +} + +void cli_command_sysctl_heap_track(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + if(!furi_string_cmp(args, "none")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone); + printf("Heap tracking disabled"); + } else if(!furi_string_cmp(args, "main")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeMain); + printf("Heap tracking enabled for application main thread"); +#if FURI_DEBUG + } else if(!furi_string_cmp(args, "tree")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeTree); + printf("Heap tracking enabled for application main and child threads"); + } else if(!furi_string_cmp(args, "all")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeAll); + printf("Heap tracking enabled for all threads"); +#endif + } else { + cli_print_usage("sysctl heap_track", "", furi_string_get_cstr(args)); + } +} + +void cli_command_sysctl_print_usage() { + printf("Usage:\r\n"); + printf("sysctl \r\n"); + printf("Cmd list:\r\n"); + + printf("\tdebug <0|1>\t - Enable or disable system debug\r\n"); +#if FURI_DEBUG + printf("\theap_track \t - Set heap allocation tracking mode\r\n"); +#else + printf("\theap_track \t - Set heap allocation tracking mode\r\n"); +#endif +} + +void cli_command_sysctl(Cli* cli, FuriString* args, void* context) { + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + cli_command_sysctl_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "debug") == 0) { + cli_command_sysctl_debug(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "heap_track") == 0) { + cli_command_sysctl_heap_track(cli, args, context); + break; + } + + cli_command_sysctl_print_usage(); + } while(false); + + furi_string_free(cmd); +} + void cli_command_vibro(Cli* cli, FuriString* args, void* context) { UNUSED(cli); UNUSED(context); @@ -208,22 +296,6 @@ void cli_command_vibro(Cli* cli, FuriString* args, void* context) { } } -void cli_command_debug(Cli* cli, FuriString* args, void* context) { - UNUSED(cli); - UNUSED(context); - if(!furi_string_cmp(args, "0")) { - furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); - loader_update_menu(); - printf("Debug disabled."); - } else if(!furi_string_cmp(args, "1")) { - furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); - loader_update_menu(); - printf("Debug enabled."); - } else { - cli_print_usage("debug", "<1|0>", furi_string_get_cstr(args)); - } -} - void cli_command_led(Cli* cli, FuriString* args, void* context) { UNUSED(cli); UNUSED(context); @@ -350,13 +422,15 @@ void cli_command_i2c(Cli* cli, FuriString* args, void* context) { void cli_commands_init(Cli* cli) { cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_device_info, NULL); cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_device_info, NULL); + cli_add_command(cli, "src", CliCommandFlagParallelSafe, cli_command_src, NULL); + cli_add_command(cli, "source", CliCommandFlagParallelSafe, cli_command_src, NULL); cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL); cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL); cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); - cli_add_command(cli, "debug", CliCommandFlagDefault, cli_command_debug, NULL); + cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL); cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL); cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL); cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL); diff --git a/applications/services/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c index 1e27e185b..ad26dca47 100644 --- a/applications/services/cli/cli_vcp.c +++ b/applications/services/cli/cli_vcp.c @@ -68,10 +68,7 @@ static void cli_vcp_init() { vcp->connected = false; - vcp->thread = furi_thread_alloc(); - furi_thread_set_name(vcp->thread, "CliVcpWorker"); - furi_thread_set_stack_size(vcp->thread, 1024); - furi_thread_set_callback(vcp->thread, vcp_worker); + vcp->thread = furi_thread_alloc_ex("CliVcpWorker", 1024, vcp_worker, NULL); furi_thread_start(vcp->thread); FURI_LOG_I(TAG, "Init OK"); @@ -103,7 +100,7 @@ static int32_t vcp_worker(void* context) { while(1) { uint32_t flags = furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - furi_assert((flags & FuriFlagError) == 0); + furi_assert(!(flags & FuriFlagError)); // VCP session opened if(flags & VcpEvtConnect) { @@ -303,7 +300,7 @@ static void vcp_on_cdc_control_line(void* context, uint8_t state) { static void vcp_on_cdc_rx(void* context) { UNUSED(context); uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx); - furi_check((ret & FuriFlagError) == 0); + furi_check(!(ret & FuriFlagError)); } static void vcp_on_cdc_tx_complete(void* context) { diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index a64a3ad0b..1b26ba9fb 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -167,7 +167,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { void crypto_cli_has_key(Cli* cli, FuriString* args) { UNUSED(cli); int key_slot = 0; - uint8_t iv[16]; + uint8_t iv[16] = {0}; do { if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) { @@ -249,7 +249,7 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) { } if(key_slot > 0) { - uint8_t iv[16]; + uint8_t iv[16] = {0}; if(key_slot > 1) { if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) { printf( diff --git a/applications/services/desktop/application.fam b/applications/services/desktop/application.fam index 1c85a2986..6e0b257c2 100644 --- a/applications/services/desktop/application.fam +++ b/applications/services/desktop/application.fam @@ -12,6 +12,6 @@ App( ], provides=["desktop_settings"], conflicts=["updater"], - stack_size=8 * 1024, + stack_size=4 * 1024, order=60, ) diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index de7640a6b..fc145aff7 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -46,6 +46,13 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl return; } + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode > FuriHalRtcHeapTrackModeNone) { + furi_thread_enable_heap_trace(desktop->scene_thread); + } else { + furi_thread_disable_heap_trace(desktop->scene_thread); + } + furi_thread_set_name(desktop->scene_thread, flipper_app->name); furi_thread_set_stack_size(desktop->scene_thread, flipper_app->stack_size); furi_thread_set_callback(desktop->scene_thread, flipper_app->app); @@ -228,8 +235,21 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; break; } + case DesktopMainEventOpenArkanoid: { + LoaderStatus status = loader_start( + desktop->loader, "Applications", EXT_PATH("/apps/Games/Arkanoid.fap")); + consumed = true; + break; + } + case DesktopMainEventOpenHeap: { + LoaderStatus status = loader_start( + desktop->loader, "Applications", EXT_PATH("/apps/Games/Heap_Defence.fap")); + consumed = true; + break; + } case DesktopMainEventOpenSubRemote: { - loader_start(desktop->loader, FLIPPER_APPS[1].name, NULL); + LoaderStatus status = loader_start( + desktop->loader, "Applications", EXT_PATH("/apps/Main/SubGHz_Remote.fap")); consumed = true; break; } diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index c12a35efe..53411acc4 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -19,6 +19,8 @@ typedef enum { DesktopMainEventOpenTetris, DesktopMainEventOpenDOOM, DesktopMainEventOpenDice, + DesktopMainEventOpenArkanoid, + DesktopMainEventOpenHeap, DesktopMainEventOpenSubRemote, DesktopLockedEventUnlocked, diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 449aea5fd..7f9329bd2 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -1,6 +1,7 @@ #include #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 86987b1d6..3034da376 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 @@ -159,7 +160,7 @@ static bool desktop_view_locked_input(InputEvent* event, void* context) { view_commit_model(locked_view->view, is_changed); if(view_state == DesktopViewLockedStateUnlocked) { - return view_state != DesktopViewLockedStateUnlocked; + return false; } else if(view_state == DesktopViewLockedStateLocked && pin_locked) { locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context); } else if( diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index 415005edc..a4b6025e5 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -84,7 +84,8 @@ bool desktop_main_input_callback(InputEvent* event, void* context) { } else if(event->key == InputKeyDown) { main_view->callback(DesktopMainEventOpenFavoriteSecondary, main_view->context); } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenSubRemote, main_view->context); // OPENS SUBGHZ REMOTE + main_view->callback( + DesktopMainEventOpenSubRemote, main_view->context); // OPENS SUBGHZ REMOTE } } } else if(main_view->is_gamemode == true) { @@ -97,7 +98,8 @@ bool desktop_main_input_callback(InputEvent* event, void* context) { // PREFER TO OPEN GAMES MENU main_view->callback(DesktopMainEventOpen2048, main_view->context); // OPENS 2048 } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenClock, main_view->context); // OPENS CLOCK + main_view->callback( + DesktopMainEventOpenTetris, main_view->context); // OPENS TETRIS } } else if(event->type == InputTypeLong) { if(event->key == InputKeyOk) { @@ -105,31 +107,36 @@ bool desktop_main_input_callback(InputEvent* event, void* context) { } else if(event->key == InputKeyUp) { main_view->callback(DesktopMainEventOpenDOOM, main_view->context); // OPENS DOOM } else if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpenZombiez, main_view->context); // OPENS Zombiez + main_view->callback( + DesktopMainEventOpenZombiez, main_view->context); // OPENS Zombiez } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenTetris, main_view->context); // OPENS TETRIS + main_view->callback(DesktopMainEventOpenClock, main_view->context); // OPENS CLOCK } } } else { if(event->type == InputTypeShort) { if(event->key == InputKeyOk) { - main_view->callback(DesktopMainEventOpenDice, main_view->context); // OPENS Dice + main_view->callback(DesktopMainEventOpenSnake, main_view->context); // OPENS SNAKE } else if(event->key == InputKeyUp) { main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); } else if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpen2048, main_view->context); // OPENS 2048 + main_view->callback( + DesktopMainEventOpenTetris, main_view->context); // OPENS Tetris } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenClock, main_view->context); // OPENS CLOCK + main_view->callback( + DesktopMainEventOpenArkanoid, main_view->context); // OPENS Arkanoid } } else if(event->type == InputTypeLong) { if(event->key == InputKeyOk) { main_view->callback(DesktopAnimationEventNewIdleAnimation, main_view->context); } else if(event->key == InputKeyUp) { - main_view->callback(DesktopMainEventOpenSnake, main_view->context); // OPENS SNAKE + main_view->callback(DesktopMainEventOpenDOOM, main_view->context); // OPENS DOOM } else if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpenZombiez, main_view->context); // OPENS Zombiez + main_view->callback( + DesktopMainEventOpenZombiez, main_view->context); // OPENS Zombiez } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenTetris, main_view->context); // OPENS TETRIS + main_view->callback( + DesktopMainEventOpenHeap, main_view->context); // OPENS Heap Defence } } } 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/dolphin/helpers/dolphin_deed.c b/applications/services/dolphin/helpers/dolphin_deed.c index f42a0b3f7..05758d445 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 @@ -35,8 +35,9 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {2, DolphinAppIbutton}, // DolphinDeedIbuttonAdd {3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript - {3, DolphinAppU2f}, // DolphinDeedU2fAuthorized - {1, DolphinAppGpio}, // DolphinDeedGpioUartBridge + {3, DolphinAppPlugin}, // DolphinDeedU2fAuthorized + + {1, DolphinAppPlugin}, // DolphinDeedGpioUartBridge {1, DolphinAppPlugin}, // DolphinDeedPluginStart {1, DolphinAppPlugin}, // DolphinDeedPluginGameStart @@ -50,8 +51,8 @@ static uint8_t dolphin_deed_limits[] = { 100, // DolphinAppIr 100, // DolphinAppIbutton 100, // DolphinAppBadusb - 100, // DolphinAppU2f - 100, // DolphinAppGpio + // 100, // DolphinAppU2f + // 100, // DolphinAppGpio 100, // DolphinAppPlugin }; diff --git a/applications/services/dolphin/helpers/dolphin_deed.h b/applications/services/dolphin/helpers/dolphin_deed.h index 362771b1a..c9cd18f31 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.h +++ b/applications/services/dolphin/helpers/dolphin_deed.h @@ -13,8 +13,6 @@ typedef enum { DolphinAppIr, DolphinAppIbutton, DolphinAppBadusb, - DolphinAppU2f, - DolphinAppGpio, DolphinAppPlugin, DolphinAppMAX, } DolphinApp; @@ -39,8 +37,8 @@ typedef enum { DolphinDeedNfcDetectReader, DolphinDeedNfcEmulate, DolphinDeedNfcMfcAdd, - DolphinDeedNfcMfulError, DolphinDeedNfcAddSave, + DolphinDeedNfcAddEmulate, DolphinDeedIrSend, DolphinDeedIrLearnSuccess, diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam index 76ace7fab..193b9dfd3 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", "canvas_i.h", "view_dispatcher.h", diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 419b8b567..6a2bd3537 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -391,16 +391,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 07ebe8960..756901033 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" { @@ -43,7 +43,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 db080a5a0..fcbcbc21e 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" @@ -113,7 +114,6 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { 2, canvas_width(gui->canvas) - 1, canvas_height(gui->canvas) - 4); - } // Left side @@ -128,7 +128,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { canvas_frame_set( gui->canvas, x - 1, - // SASQUACH SAYS : This is the white box behind the left bar, move it 64 to hide it + // SASQUACH SAYS : This is the white box behind the left bar, move it 64 to hide it GUI_STATUS_BAR_Y + 64, width + 2, GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); @@ -138,8 +138,12 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { canvas_set_color(gui->canvas, ColorBlack); // ViewPort draw canvas_frame_set( - // SASQUACH SAYS : This is where you move the Icons for the left bar, 64 to hide it - gui->canvas, x, GUI_STATUS_BAR_Y + 64, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); + // SASQUACH SAYS : This is where you move the Icons for the left bar, 64 to hide it + gui->canvas, + x, + GUI_STATUS_BAR_Y + 64, + width, + GUI_STATUS_BAR_WORKAREA_HEIGHT); view_port_draw(view_port, gui->canvas); // Recalculate next position left_used += (width + 2); @@ -149,7 +153,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { } // Extra notification if(need_attention) { - width = icon_get_width(&I_Attention_5x8); + width = icon_get_width(&I_Hidden_window_9x8); // Prepare work area background canvas_frame_set( gui->canvas, @@ -163,7 +167,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { // Draw Icon canvas_frame_set( gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); - canvas_draw_icon(gui->canvas, 0, 0, &I_Attention_5x8); + canvas_draw_icon(gui->canvas, 0, 0, &I_Hidden_window_9x8); // Recalculate next position left_used += (width + 2); x += (width + 2); @@ -302,7 +306,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 @@ -413,7 +419,7 @@ void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, vo const CanvasCallbackPair p = {callback, context}; gui_lock(gui); - furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 0); + furi_assert(!CanvasCallbackPairArray_count(gui->canvas_callback_pair, p)); CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p); gui_unlock(gui); 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/file_browser.c b/applications/services/gui/modules/file_browser.c index 60e78b01c..fa119f128 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -5,6 +5,8 @@ #include #include #include "furi_hal_resources.h" +#include "m-string.h" +#include "m-algo.h" #include #include #include @@ -71,13 +73,29 @@ static void BrowserItem_t_clear(BrowserItem_t* obj) { } } -ARRAY_DEF( - items_array, - BrowserItem_t, - (INIT(API_2(BrowserItem_t_init)), - SET(API_6(BrowserItem_t_set)), - INIT_SET(API_6(BrowserItem_t_init_set)), - CLEAR(API_2(BrowserItem_t_clear)))) +static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { + // Back indicator comes before everything, then folders, then all other files. + if((a->type == BrowserItemTypeBack) || + (a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder && + b->type != BrowserItemTypeBack)) { + return -1; + } + + return furi_string_cmp(a->path, b->path); +} + +#define M_OPL_BrowserItem_t() \ + (INIT(API_2(BrowserItem_t_init)), \ + SET(API_6(BrowserItem_t_set)), \ + INIT_SET(API_6(BrowserItem_t_init_set)), \ + CLEAR(API_2(BrowserItem_t_clear)), \ + CMP(API_6(BrowserItem_t_cmp)), \ + SWAP(M_SWAP_DEFAULT), \ + EQUAL(API_6(M_EQUAL_DEFAULT))) + +ARRAY_DEF(items_array, BrowserItem_t) + +ALGO_DEF(items_array, ARRAY_OPLIST(items_array, M_OPL_BrowserItem_t())) struct FileBrowser { View* view; @@ -388,7 +406,13 @@ static void } } else { with_view_model( - browser->view, FileBrowserModel * model, { model->list_loading = false; }, true); + browser->view, + FileBrowserModel * model, + { + items_array_sort(model->items); + model->list_loading = false; + }, + true); } } diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 4bff50c42..e3c38b053 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -363,7 +363,7 @@ static int32_t browser_worker(void* context) { BrowserWorker* file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets) { - BrowserWorker* browser = malloc(sizeof(BrowserWorker)); + BrowserWorker* browser = malloc(sizeof(BrowserWorker)); //-V773 idx_last_array_init(browser->idx_last); @@ -371,11 +371,7 @@ BrowserWorker* browser->skip_assets = skip_assets; browser->path_next = furi_string_alloc_set(path); - browser->thread = furi_thread_alloc(); - furi_thread_set_name(browser->thread, "BrowserWorker"); - furi_thread_set_stack_size(browser->thread, 2048); - furi_thread_set_context(browser->thread, browser); - furi_thread_set_callback(browser->thread, browser_worker); + browser->thread = furi_thread_alloc_ex("BrowserWorker", 2048, browser_worker, browser); furi_thread_start(browser->thread); return browser; 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/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index acd46ee4b..a9b89d63b 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -396,6 +396,10 @@ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_v item->current_value_index = current_value_index; } +void variable_item_set_values_count(VariableItem* item, uint8_t values_count) { + item->values_count = values_count; +} + void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) { furi_string_set(item->current_value_text, current_value_text); } diff --git a/applications/services/gui/modules/variable_item_list.h b/applications/services/gui/modules/variable_item_list.h index 78c7769c5..db2a58993 100644 --- a/applications/services/gui/modules/variable_item_list.h +++ b/applications/services/gui/modules/variable_item_list.h @@ -81,6 +81,13 @@ uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_it */ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index); +/** Set number of values for item + * + * @param item VariableItem* instance + * @param values_count The new values count + */ +void variable_item_set_values_count(VariableItem* item, uint8_t values_count); + /** Set item current selected text * * @param item VariableItem* instance diff --git a/applications/services/gui/modules/widget_elements/widget_element_button.c b/applications/services/gui/modules/widget_elements/widget_element_button.c index be33b1897..e3267058e 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_button.c +++ b/applications/services/gui/modules/widget_elements/widget_element_button.c @@ -60,7 +60,7 @@ WidgetElement* widget_element_button_create( ButtonCallback callback, void* context) { // Allocate and init model - GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); + GuiButtonModel* model = malloc(sizeof(GuiButtonModel)); //-V773 model->button_type = button_type; model->callback = callback; model->context = context; 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..4034cc0b4 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -23,7 +23,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { gui_remove_view_port(view_dispatcher->gui, view_dispatcher->view_port); } // Crash if not all views were freed - furi_assert(ViewDict_size(view_dispatcher->views) == 0); + furi_assert(!ViewDict_size(view_dispatcher->views)); ViewDict_clear(view_dispatcher->views); // Free ViewPort @@ -157,7 +157,7 @@ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_ view_dispatcher->ongoing_input_view = NULL; } // Remove view - ViewDict_erase(view_dispatcher->views, view_id); + furi_check(ViewDict_erase(view_dispatcher->views, view_id)); view_set_update_callback(view, NULL); view_set_update_callback_context(view, NULL); @@ -304,8 +304,7 @@ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32 } // If custom event is not consumed in View, call callback if(!is_consumed && view_dispatcher->custom_event_callback) { - is_consumed = - view_dispatcher->custom_event_callback(view_dispatcher->event_context, event); + view_dispatcher->custom_event_callback(view_dispatcher->event_context, event); } } @@ -331,10 +330,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..ffd01450b 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -7,31 +7,68 @@ // TODO add mutex to view_port ops -static void view_port_rotate_buttons(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"); + +/** 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 +}; + +// 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) { - if(view_port->orientation == ViewPortOrientationHorizontal) { - canvas_set_orientation(canvas, CanvasOrientationHorizontal); - } else if(view_port->orientation == ViewPortOrientationVertical) { + 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() { @@ -122,9 +159,8 @@ 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); + 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 96f2798e2..703e99248 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -16,7 +16,10 @@ typedef struct ViewPort ViewPort; typedef enum { ViewPortOrientationHorizontal, + ViewPortOrientationHorizontalFlip, ViewPortOrientationVertical, + ViewPortOrientationVerticalFlip, + ViewPortOrientationMAX, /**< Special value, don't use it */ } ViewPortOrientation; /** ViewPort Draw callback diff --git a/applications/services/infraredsrv/application.fam b/applications/services/infraredsrv/application.fam new file mode 100644 index 000000000..53168216c --- /dev/null +++ b/applications/services/infraredsrv/application.fam @@ -0,0 +1,7 @@ +App( + appid="infraredsrv", + apptype=FlipperAppType.STARTUP, + entry_point="infrared_on_system_start", + requires=["infrared"], + order=20, +) diff --git a/applications/services/infraredsrv/infrared_brute_force.c b/applications/services/infraredsrv/infrared_brute_force.c new file mode 100644 index 000000000..31bcabd1d --- /dev/null +++ b/applications/services/infraredsrv/infrared_brute_force.c @@ -0,0 +1,158 @@ +#include "infrared_brute_force.h" + +#include +#include +#include + +#include "infrared_signal.h" + +typedef struct { + uint32_t index; + uint32_t count; +} InfraredBruteForceRecord; + +DICT_DEF2( + InfraredBruteForceRecordDict, + FuriString*, + FURI_STRING_OPLIST, + InfraredBruteForceRecord, + M_POD_OPLIST); + +struct InfraredBruteForce { + FlipperFormat* ff; + const char* db_filename; + FuriString* current_record_name; + InfraredSignal* current_signal; + InfraredBruteForceRecordDict_t records; + bool is_started; +}; + +InfraredBruteForce* infrared_brute_force_alloc() { + InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce)); + brute_force->ff = NULL; + brute_force->db_filename = NULL; + brute_force->current_signal = NULL; + brute_force->is_started = false; + brute_force->current_record_name = furi_string_alloc(); + InfraredBruteForceRecordDict_init(brute_force->records); + return brute_force; +} + +void infrared_brute_force_clear_records(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + InfraredBruteForceRecordDict_reset(brute_force->records); +} + +void infrared_brute_force_free(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + InfraredBruteForceRecordDict_clear(brute_force->records); + furi_string_free(brute_force->current_record_name); + free(brute_force); +} + +void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) { + furi_assert(!brute_force->is_started); + brute_force->db_filename = db_filename; +} + +bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + furi_assert(brute_force->db_filename); + bool success = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + success = flipper_format_buffered_file_open_existing(ff, brute_force->db_filename); + if(success) { + FuriString* signal_name; + signal_name = furi_string_alloc(); + while(flipper_format_read_string(ff, "name", signal_name)) { + InfraredBruteForceRecord* record = + InfraredBruteForceRecordDict_get(brute_force->records, signal_name); + if(record) { + ++(record->count); + } + } + furi_string_free(signal_name); + } + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + return success; +} + +bool infrared_brute_force_start( + InfraredBruteForce* brute_force, + uint32_t index, + uint32_t* record_count) { + furi_assert(!brute_force->is_started); + bool success = false; + *record_count = 0; + + InfraredBruteForceRecordDict_it_t it; + for(InfraredBruteForceRecordDict_it(it, brute_force->records); + !InfraredBruteForceRecordDict_end_p(it); + InfraredBruteForceRecordDict_next(it)) { + const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it); + if(record->value.index == index) { + *record_count = record->value.count; + if(*record_count) { + furi_string_set(brute_force->current_record_name, record->key); + } + break; + } + } + + if(*record_count) { + Storage* storage = furi_record_open(RECORD_STORAGE); + brute_force->ff = flipper_format_buffered_file_alloc(storage); + brute_force->current_signal = infrared_signal_alloc(); + brute_force->is_started = true; + success = + flipper_format_buffered_file_open_existing(brute_force->ff, brute_force->db_filename); + if(!success) infrared_brute_force_stop(brute_force); + } + return success; +} + +bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) { + return brute_force->is_started; +} + +void infrared_brute_force_stop(InfraredBruteForce* brute_force) { + furi_assert(brute_force->is_started); + furi_string_reset(brute_force->current_record_name); + infrared_signal_free(brute_force->current_signal); + flipper_format_free(brute_force->ff); + brute_force->current_signal = NULL; + brute_force->ff = NULL; + brute_force->is_started = false; + furi_record_close(RECORD_STORAGE); +} + +bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { + furi_assert(brute_force->is_started); + const bool success = infrared_signal_search_and_read( + brute_force->current_signal, brute_force->ff, brute_force->current_record_name); + if(success) { + infrared_signal_transmit(brute_force->current_signal); + } + return success; +} + +void infrared_brute_force_add_record( + InfraredBruteForce* brute_force, + uint32_t index, + const char* name) { + InfraredBruteForceRecord value = {.index = index, .count = 0}; + FuriString* key; + key = furi_string_alloc_set(name); + InfraredBruteForceRecordDict_set_at(brute_force->records, key, value); + furi_string_free(key); +} + +void infrared_brute_force_reset(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + InfraredBruteForceRecordDict_reset(brute_force->records); +} diff --git a/applications/services/infraredsrv/infrared_brute_force.h b/applications/services/infraredsrv/infrared_brute_force.h new file mode 100644 index 000000000..fff472e79 --- /dev/null +++ b/applications/services/infraredsrv/infrared_brute_force.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +typedef struct InfraredBruteForce InfraredBruteForce; + +InfraredBruteForce* infrared_brute_force_alloc(); +void infrared_brute_force_free(InfraredBruteForce* brute_force); +void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename); +bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); +bool infrared_brute_force_start( + InfraredBruteForce* brute_force, + uint32_t index, + uint32_t* record_count); +bool infrared_brute_force_is_started(InfraredBruteForce* brute_force); +void infrared_brute_force_stop(InfraredBruteForce* brute_force); +bool infrared_brute_force_send_next(InfraredBruteForce* brute_force); +void infrared_brute_force_clear_records(InfraredBruteForce* brute_force); +void infrared_brute_force_add_record( + InfraredBruteForce* brute_force, + uint32_t index, + const char* name); diff --git a/applications/services/infraredsrv/infrared_cli.c b/applications/services/infraredsrv/infrared_cli.c new file mode 100644 index 000000000..7d4c150ef --- /dev/null +++ b/applications/services/infraredsrv/infrared_cli.c @@ -0,0 +1,517 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "infrared_signal.h" +#include "infrared_brute_force.h" + +#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) + +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 const struct { + const char* cmd; + void (*process_function)(Cli* cli, FuriString* args); +} infrared_cli_commands[] = { + {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, + {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, + {.cmd = "decode", .process_function = infrared_cli_process_decode}, + {.cmd = "universal", .process_function = infrared_cli_process_universal}, +}; + +static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { + furi_assert(received_signal); + char buf[100]; + size_t buf_cnt; + Cli* cli = (Cli*)context; + + if(infrared_worker_signal_is_decoded(received_signal)) { + const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); + buf_cnt = snprintf( + buf, + sizeof(buf), + "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", + infrared_get_protocol_name(message->protocol), + ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), + message->address, + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), + message->command, + message->repeat ? " R" : ""); + cli_write(cli, (uint8_t*)buf, buf_cnt); + } else { + const uint32_t* timings; + size_t timings_cnt; + infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); + + buf_cnt = snprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); + cli_write(cli, (uint8_t*)buf, buf_cnt); + for(size_t i = 0; i < timings_cnt; ++i) { + buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]); + cli_write(cli, (uint8_t*)buf, buf_cnt); + } + buf_cnt = snprintf(buf, sizeof(buf), "\r\n"); + cli_write(cli, (uint8_t*)buf, buf_cnt); + } +} + +static void infrared_cli_print_usage(void) { + printf("Usage:\r\n"); + printf("\tir rx [raw]\r\n"); + printf("\tir tx
\r\n"); + printf("\t and
are hex-formatted\r\n"); + printf("\tAvailable protocols:"); + for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) { + printf(" %s", infrared_get_protocol_name((InfraredProtocol)i)); + } + printf("\r\n"); + printf("\tRaw format:\r\n"); + printf("\tir tx RAW F: DC: ...\r\n"); + printf( + "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", + INFRARED_MIN_FREQUENCY, + INFRARED_MAX_FREQUENCY); + printf("\tir decode []\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) { + UNUSED(cli); + + bool enable_decoding = true; + + if(!furi_string_empty(args)) { + if(!furi_string_cmp_str(args, "raw")) { + enable_decoding = false; + } else { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + return; + } + } + + InfraredWorker* worker = infrared_worker_alloc(); + infrared_worker_rx_enable_signal_decoding(worker, enable_decoding); + infrared_worker_rx_start(worker); + infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); + + printf("Receiving %s INFRARED...\r\nPress Ctrl+C to abort\r\n", enable_decoding ? "" : "RAW"); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(50); + } + + infrared_worker_rx_stop(worker); + infrared_worker_free(worker); +} + +static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { + char protocol_name[32]; + InfraredMessage message; + int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message.address, &message.command); + + if(parsed != 3) { + return false; + } + + message.protocol = infrared_get_protocol_by_name(protocol_name); + message.repeat = false; + infrared_signal_set_message(signal, &message); + return infrared_signal_is_valid(signal); +} + +static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { + char frequency_str[INFRARED_CLI_BUF_SIZE]; + char duty_cycle_str[INFRARED_CLI_BUF_SIZE]; + int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); + + if(parsed != 2) { + return false; + } + + uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); + uint32_t frequency = atoi(frequency_str); + float duty_cycle = (float)atoi(duty_cycle_str) / 100; + + str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE; + + size_t timings_size = 0; + while(1) { + while(*str == ' ') { + ++str; + } + + char timing_str[INFRARED_CLI_BUF_SIZE]; + if(sscanf(str, "%9s", timing_str) != 1) { + break; + } + + str += strlen(timing_str); + uint32_t timing = atoi(timing_str); + + if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) { + break; + } + + timings[timings_size] = timing; + ++timings_size; + } + + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); + free(timings); + + return infrared_signal_is_valid(signal); +} + +static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args) { + UNUSED(cli); + const char* str = furi_string_get_cstr(args); + InfraredSignal* signal = infrared_signal_alloc(); + + bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal); + if(success) { + infrared_signal_transmit(signal); + } else { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + } + + infrared_signal_free(signal); +} + +static bool + infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) { + bool ret = infrared_signal_save(signal, file, name); + if(!ret) { + printf("Failed to save signal: \"%s\"\r\n", name); + } + return ret; +} + +static bool infrared_cli_decode_raw_signal( + InfraredRawSignal* raw_signal, + InfraredDecoderHandler* decoder, + FlipperFormat* output_file, + const char* signal_name) { + InfraredSignal* signal = infrared_signal_alloc(); + bool ret = false, level = true, is_decoded = false; + + size_t i; + for(i = 0; i < raw_signal->timings_size; ++i) { + // TODO: Any infrared_check_decoder_ready() magic? + const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]); + + if(message) { + is_decoded = true; + printf( + "Protocol: %s address: 0x%lX command: 0x%lX %s\r\n", + infrared_get_protocol_name(message->protocol), + message->address, + message->command, + (message->repeat ? "R" : "")); + if(output_file && !message->repeat) { + infrared_signal_set_message(signal, message); + if(!infrared_cli_save_signal(signal, output_file, signal_name)) break; + } + } + + level = !level; + } + + if(i == raw_signal->timings_size) { + if(!is_decoded && output_file) { + infrared_signal_set_raw_signal( + signal, + raw_signal->timings, + raw_signal->timings_size, + raw_signal->frequency, + raw_signal->duty_cycle); + ret = infrared_cli_save_signal(signal, output_file, signal_name); + } else { + ret = true; + } + } + + infrared_reset_decoder(decoder); + infrared_signal_free(signal); + return ret; +} + +static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) { + bool ret = false; + + InfraredSignal* signal = infrared_signal_alloc(); + InfraredDecoderHandler* decoder = infrared_alloc_decoder(); + + FuriString* tmp; + tmp = furi_string_alloc(); + + while(infrared_signal_read(signal, input_file, tmp)) { + ret = false; + if(!infrared_signal_is_valid(signal)) { + printf("Invalid signal\r\n"); + break; + } + if(!infrared_signal_is_raw(signal)) { + if(output_file && + !infrared_cli_save_signal(signal, output_file, furi_string_get_cstr(tmp))) { + break; + } else { + printf("Skipping decoded signal\r\n"); + continue; + } + } + InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal); + printf( + "Raw signal: %s, %u samples\r\n", furi_string_get_cstr(tmp), raw_signal->timings_size); + if(!infrared_cli_decode_raw_signal( + raw_signal, decoder, output_file, furi_string_get_cstr(tmp))) + break; + ret = true; + } + + infrared_free_decoder(decoder); + infrared_signal_free(signal); + furi_string_free(tmp); + + return ret; +} + +static void infrared_cli_process_decode(Cli* cli, FuriString* args) { + UNUSED(cli); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage); + FlipperFormat* output_file = NULL; + + uint32_t version; + FuriString *tmp, *header, *input_path, *output_path; + tmp = furi_string_alloc(); + header = furi_string_alloc(); + input_path = furi_string_alloc(); + output_path = furi_string_alloc(); + + do { + if(!args_read_probably_quoted_string_and_trim(args, input_path)) { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + break; + } + args_read_probably_quoted_string_and_trim(args, output_path); + if(!flipper_format_buffered_file_open_existing( + input_file, furi_string_get_cstr(input_path))) { + printf( + "Failed to open file for reading: \"%s\"\r\n", furi_string_get_cstr(input_path)); + break; + } + if(!flipper_format_read_header(input_file, header, &version) || + (!furi_string_start_with_str(header, "IR")) || version != 1) { + printf( + "Invalid or corrupted input file: \"%s\"\r\n", furi_string_get_cstr(input_path)); + break; + } + if(!furi_string_empty(output_path)) { + printf("Writing output to file: \"%s\"\r\n", furi_string_get_cstr(output_path)); + output_file = flipper_format_file_alloc(storage); + } + if(output_file && + !flipper_format_file_open_always(output_file, furi_string_get_cstr(output_path))) { + printf( + "Failed to open file for writing: \"%s\"\r\n", furi_string_get_cstr(output_path)); + break; + } + if(output_file && !flipper_format_write_header(output_file, header, version)) { + printf( + "Failed to write to the output file: \"%s\"\r\n", + furi_string_get_cstr(output_path)); + break; + } + if(!infrared_cli_decode_file(input_file, output_file)) { + break; + } + printf("File successfully decoded.\r\n"); + } while(false); + + furi_string_free(tmp); + furi_string_free(header); + furi_string_free(input_path); + furi_string_free(output_path); + + flipper_format_free(input_file); + if(output_file) flipper_format_free(output_file); + furi_record_close(RECORD_STORAGE); +} + +static void infrared_cli_list_remote_signals(FuriString* remote_name) { + if(furi_string_empty(remote_name)) { + printf("Missing remote name.\r\n"); + return; + } + + 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(); + + 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); + if(v != NULL) { + (*v)++; + max = M_MAX(*v, max); + } else { + 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(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, FuriString* remote_name, FuriString* signal_name) { + InfraredBruteForce* brute_force = infrared_brute_force_alloc(); + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); + + 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)); + + 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; + } + + 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); + + if(cli_cmd_interrupt_received(cli)) break; + + printf("\r%d%% complete.", (int)((float)records_sent++ / (float)record_count * 100)); + fflush(stdout); + } + + infrared_brute_force_stop(brute_force); + } while(false); + + furi_string_free(remote_path); + infrared_brute_force_clear_records(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()) { + printf("INFRARED is busy. Exiting."); + return; + } + + FuriString* command; + command = furi_string_alloc(); + args_read_string_and_trim(args, command); + + size_t i = 0; + for(; i < COUNT_OF(infrared_cli_commands); ++i) { + size_t cmd_len = strlen(infrared_cli_commands[i].cmd); + if(!strncmp(furi_string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) { + break; + } + } + + if(i < COUNT_OF(infrared_cli_commands)) { + infrared_cli_commands[i].process_function(cli, args); + } else { + infrared_cli_print_usage(); + } + + furi_string_free(command); +} +void infrared_on_system_start() { +#ifdef SRV_CLI + Cli* cli = (Cli*)furi_record_open(RECORD_CLI); + cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); + furi_record_close(RECORD_CLI); +#else + UNUSED(infrared_cli_start_ir); +#endif +} diff --git a/applications/services/infraredsrv/infrared_signal.c b/applications/services/infraredsrv/infrared_signal.c new file mode 100644 index 000000000..d399b9587 --- /dev/null +++ b/applications/services/infraredsrv/infrared_signal.c @@ -0,0 +1,300 @@ +#include "infrared_signal.h" + +#include +#include +#include +#include +#include + +#define TAG "InfraredSignal" + +struct InfraredSignal { + bool is_raw; + union { + InfraredMessage message; + InfraredRawSignal raw; + } payload; +}; + +static void infrared_signal_clear_timings(InfraredSignal* signal) { + if(signal->is_raw) { + free(signal->payload.raw.timings); + signal->payload.raw.timings_size = 0; + signal->payload.raw.timings = NULL; + } +} + +static bool infrared_signal_is_message_valid(InfraredMessage* message) { + if(!infrared_is_protocol_valid(message->protocol)) { + FURI_LOG_E(TAG, "Unknown protocol"); + return false; + } + + uint32_t address_length = infrared_get_protocol_address_length(message->protocol); + uint32_t address_mask = (1UL << address_length) - 1; + + if(message->address != (message->address & address_mask)) { + FURI_LOG_E( + TAG, + "Address is out of range (mask 0x%08lX): 0x%lX\r\n", + address_mask, + message->address); + return false; + } + + uint32_t command_length = infrared_get_protocol_command_length(message->protocol); + uint32_t command_mask = (1UL << command_length) - 1; + + if(message->command != (message->command & command_mask)) { + FURI_LOG_E( + TAG, + "Command is out of range (mask 0x%08lX): 0x%lX\r\n", + command_mask, + message->command); + return false; + } + + return true; +} + +static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) { + if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) { + FURI_LOG_E( + TAG, + "Frequency is out of range (%X - %X): %lX", + INFRARED_MIN_FREQUENCY, + INFRARED_MAX_FREQUENCY, + raw->frequency); + return false; + + } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) { + FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle); + return false; + + } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) { + FURI_LOG_E( + TAG, + "Timings amount is out of range (0 - %X): %X", + MAX_TIMINGS_AMOUNT, + raw->timings_size); + return false; + } + + return true; +} + +static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) { + const char* protocol_name = infrared_get_protocol_name(message->protocol); + return flipper_format_write_string_cstr(ff, "type", "parsed") && + flipper_format_write_string_cstr(ff, "protocol", protocol_name) && + flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) && + flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); +} + +static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) { + furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); + return flipper_format_write_string_cstr(ff, "type", "raw") && + flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && + flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) && + flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size); +} + +static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { + FuriString* buf; + buf = furi_string_alloc(); + bool success = false; + + do { + if(!flipper_format_read_string(ff, "protocol", buf)) break; + + InfraredMessage message; + message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf)); + + success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) && + flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) && + infrared_signal_is_message_valid(&message); + + if(!success) break; + + infrared_signal_set_message(signal, &message); + } while(0); + + furi_string_free(buf); + return success; +} + +static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { + uint32_t timings_size, frequency; + float duty_cycle; + + bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) && + flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) && + flipper_format_get_value_count(ff, "data", &timings_size); + + if(!success || timings_size > MAX_TIMINGS_AMOUNT) { + return false; + } + + uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); + success = flipper_format_read_uint32(ff, "data", timings, timings_size); + + if(success) { + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); + } + + free(timings); + return success; +} + +static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { + FuriString* tmp = furi_string_alloc(); + + bool success = false; + + do { + if(!flipper_format_read_string(ff, "type", tmp)) break; + if(furi_string_equal(tmp, "raw")) { + success = infrared_signal_read_raw(signal, ff); + } else if(furi_string_equal(tmp, "parsed")) { + success = infrared_signal_read_message(signal, ff); + } else { + FURI_LOG_E(TAG, "Unknown signal type"); + } + } while(false); + + furi_string_free(tmp); + return success; +} + +InfraredSignal* infrared_signal_alloc() { + InfraredSignal* signal = malloc(sizeof(InfraredSignal)); + + signal->is_raw = false; + signal->payload.message.protocol = InfraredProtocolUnknown; + + return signal; +} + +void infrared_signal_free(InfraredSignal* signal) { + infrared_signal_clear_timings(signal); + free(signal); +} + +bool infrared_signal_is_raw(InfraredSignal* signal) { + return signal->is_raw; +} + +bool infrared_signal_is_valid(InfraredSignal* signal) { + return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) : + infrared_signal_is_message_valid(&signal->payload.message); +} + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) { + if(other->is_raw) { + const InfraredRawSignal* raw = &other->payload.raw; + infrared_signal_set_raw_signal( + signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); + } else { + const InfraredMessage* message = &other->payload.message; + infrared_signal_set_message(signal, message); + } +} + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle) { + infrared_signal_clear_timings(signal); + + signal->is_raw = true; + + signal->payload.raw.timings_size = timings_size; + signal->payload.raw.frequency = frequency; + signal->payload.raw.duty_cycle = duty_cycle; + + signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t)); + memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t)); +} + +InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) { + furi_assert(signal->is_raw); + return &signal->payload.raw; +} + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) { + infrared_signal_clear_timings(signal); + + signal->is_raw = false; + signal->payload.message = *message; +} + +InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) { + furi_assert(!signal->is_raw); + return &signal->payload.message; +} + +bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) { + if(!flipper_format_write_comment_cstr(ff, "") || + !flipper_format_write_string_cstr(ff, "name", name)) { + return false; + } else if(signal->is_raw) { + return infrared_signal_save_raw(&signal->payload.raw, ff); + } else { + return infrared_signal_save_message(&signal->payload.message, ff); + } +} + +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { + FuriString* tmp = furi_string_alloc(); + + bool success = false; + + do { + if(!flipper_format_read_string(ff, "name", tmp)) break; + furi_string_set(name, tmp); + if(!infrared_signal_read_body(signal, ff)) break; + success = true; + } while(0); + + furi_string_free(tmp); + return success; +} + +bool infrared_signal_search_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const FuriString* name) { + bool success = false; + FuriString* tmp = furi_string_alloc(); + + do { + bool is_name_found = false; + while(flipper_format_read_string(ff, "name", tmp)) { + is_name_found = furi_string_equal(name, tmp); + if(is_name_found) break; + } + if(!is_name_found) break; + if(!infrared_signal_read_body(signal, ff)) break; + success = true; + } while(false); + + furi_string_free(tmp); + return success; +} + +void infrared_signal_transmit(InfraredSignal* signal) { + if(signal->is_raw) { + InfraredRawSignal* raw_signal = &signal->payload.raw; + infrared_send_raw_ext( + raw_signal->timings, + raw_signal->timings_size, + true, + raw_signal->frequency, + raw_signal->duty_cycle); + } else { + InfraredMessage* message = &signal->payload.message; + infrared_send(message, 1); + } +} diff --git a/applications/services/infraredsrv/infrared_signal.h b/applications/services/infraredsrv/infrared_signal.h new file mode 100644 index 000000000..29c661938 --- /dev/null +++ b/applications/services/infraredsrv/infrared_signal.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include +#include + +typedef struct InfraredSignal InfraredSignal; + +typedef struct { + size_t timings_size; + uint32_t* timings; + uint32_t frequency; + float duty_cycle; +} InfraredRawSignal; + +InfraredSignal* infrared_signal_alloc(); +void infrared_signal_free(InfraredSignal* signal); + +bool infrared_signal_is_raw(InfraredSignal* signal); +bool infrared_signal_is_valid(InfraredSignal* signal); + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other); + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle); +InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal); + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message); +InfraredMessage* infrared_signal_get_message(InfraredSignal* signal); + +bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name); +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); +bool infrared_signal_search_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const FuriString* name); + +void infrared_signal_transmit(InfraredSignal* signal); diff --git a/applications/services/input/input.c b/applications/services/input/input.c index 7b8433aef..1d02df1e5 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -1,5 +1,7 @@ #include "input_i.h" +// #define INPUT_DEBUG + #define GPIO_Read(input_pin) (furi_hal_gpio_read(input_pin.pin->gpio) ^ (input_pin.pin->inverted)) static Input* input = NULL; @@ -60,8 +62,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) { @@ -71,6 +74,10 @@ int32_t input_srv(void* p) { input->event_pubsub = furi_pubsub_alloc(); furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub); +#if INPUT_DEBUG + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); +#endif + #ifdef SRV_CLI input->cli = furi_record_open(RECORD_CLI); if(input->cli) { @@ -94,10 +101,16 @@ int32_t input_srv(void* p) { bool is_changing = false; for(size_t i = 0; i < input_pins_count; i++) { bool state = GPIO_Read(input->pin_states[i]); + if(state) { + if(input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) + input->pin_states[i].debounce += 1; + } else { + if(input->pin_states[i].debounce > 0) input->pin_states[i].debounce -= 1; + } + if(input->pin_states[i].debounce > 0 && input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { is_changing = true; - input->pin_states[i].debounce += (state ? 1 : -1); } else if(input->pin_states[i].state != state) { input->pin_states[i].state = state; @@ -128,8 +141,14 @@ int32_t input_srv(void* p) { } if(is_changing) { +#if INPUT_DEBUG + furi_hal_gpio_write(&gpio_ext_pa4, 1); +#endif furi_delay_tick(1); } else { +#if INPUT_DEBUG + furi_hal_gpio_write(&gpio_ext_pa4, 0); +#endif furi_thread_flags_wait(INPUT_THREAD_FLAG_ISR, FuriFlagWaitAny, FuriWaitForever); } } 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/applications/services/lfrfidsrv/application.fam b/applications/services/lfrfidsrv/application.fam new file mode 100644 index 000000000..9a6044d00 --- /dev/null +++ b/applications/services/lfrfidsrv/application.fam @@ -0,0 +1,7 @@ +App( + appid="lfrfidsrv", + apptype=FlipperAppType.STARTUP, + entry_point="lfrfid_on_system_start", + requires=["lfrfid"], + order=50, +) diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/services/lfrfidsrv/lfrfid_cli.c similarity index 99% rename from applications/main/lfrfid/lfrfid_cli.c rename to applications/services/lfrfidsrv/lfrfid_cli.c index 640274529..655c2648c 100644 --- a/applications/main/lfrfid/lfrfid_cli.c +++ b/applications/services/lfrfidsrv/lfrfid_cli.c @@ -29,6 +29,7 @@ static void lfrfid_cli_print_usage() { printf("rfid \r\n"); printf("rfid raw_read \r\n"); printf("rfid raw_emulate \r\n"); + printf("rfid raw_analyze \r\n"); }; typedef struct { diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 2e0c54448..b41f4284f 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -22,6 +22,13 @@ static bool FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode > FuriHalRtcHeapTrackModeNone) { + furi_thread_enable_heap_trace(loader_instance->application_thread); + } else { + furi_thread_disable_heap_trace(loader_instance->application_thread); + } + furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name); furi_thread_set_stack_size( loader_instance->application_thread, loader_instance->application->stack_size); @@ -280,22 +287,18 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con event.type = LoaderEventTypeApplicationStarted; furi_pubsub_publish(loader_instance->pubsub, &event); - if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { + if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_enter(); } } else if(thread_state == FuriThreadStateStopped) { - FURI_LOG_I( - TAG, - "Application thread stopped. Free heap: %d. Thread allocation balance: %d.", - memmgr_get_free_heap(), - furi_thread_get_heap_size(instance->application_thread)); + FURI_LOG_I(TAG, "Application stopped. Free heap: %d", memmgr_get_free_heap()); if(loader_instance->application_arguments) { free(loader_instance->application_arguments); loader_instance->application_arguments = NULL; } - if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { + if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_exit(); } loader_unlock(instance); @@ -321,7 +324,7 @@ static Loader* loader_alloc() { Loader* instance = malloc(sizeof(Loader)); instance->application_thread = furi_thread_alloc(); - furi_thread_enable_heap_trace(instance->application_thread); + furi_thread_set_state_context(instance->application_thread, instance); furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback); diff --git a/applications/services/namechangersrv/namechangersrv.c b/applications/services/namechangersrv/namechangersrv.c index 9316e8428..d05c2cdc5 100644 --- a/applications/services/namechangersrv/namechangersrv.c +++ b/applications/services/namechangersrv/namechangersrv.c @@ -108,7 +108,7 @@ void namechanger_on_system_start() { furi_string_free(name); } else { furi_string_trim(data); - FURI_LOG_I(TAG, "data: %s", data); + FURI_LOG_I(TAG, "data: %s", furi_string_get_cstr(data)); if(!furi_string_size(data)) { //Empty file - get default name and write to file. diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 640bd7d71..6091f0aa7 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -22,7 +22,7 @@ static const uint8_t reset_blink_mask = 1 << 6; void notification_vibro_on(); void notification_vibro_off(); -void notification_sound_on(float pwm, float freq); +void notification_sound_on(float freq, float volume); void notification_sound_off(); uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 303afa8a0..ec1f2e8aa 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -12,10 +12,8 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_draw_icon(canvas, 0, 0, &I_Battery_26x8); if(power->info.gauge_is_ok) { - char batteryPercentile[5]; + char batteryPercentile[4]; snprintf(batteryPercentile, sizeof(batteryPercentile), "%d", power->info.charge); - strcat(batteryPercentile, "%"); - if((power->displayBatteryPercentage == 1) && (power->state != PowerStateCharging)) { //if display battery percentage, black background white text @@ -23,14 +21,14 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 1, 1, 22, 6); canvas_set_color(canvas, ColorWhite); - canvas_draw_str_aligned(canvas, 12, 4, AlignCenter, AlignCenter, batteryPercentile); + canvas_draw_str_aligned(canvas, 11, 4, AlignCenter, AlignCenter, batteryPercentile); } else if( (power->displayBatteryPercentage == 2) && (power->state != PowerStateCharging)) { //if display inverted percentage, white background black text canvas_set_font(canvas, FontBatteryPercent); canvas_set_color(canvas, ColorBlack); - canvas_draw_str_aligned(canvas, 12, 4, AlignCenter, AlignCenter, batteryPercentile); + canvas_draw_str_aligned(canvas, 11, 4, AlignCenter, AlignCenter, batteryPercentile); } else if( (power->displayBatteryPercentage == 3) && (power->state != PowerStateCharging)) { //Retro style segmented display, 3 parts @@ -66,11 +64,31 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { } if(power->state == PowerStateCharging) { canvas_set_bitmap_mode(canvas, 1); - canvas_set_color(canvas, ColorWhite); // TODO: replace -1 magic for uint8_t with re-framing - canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_mask_9x10); - canvas_set_color(canvas, ColorBlack); - canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_9x10); + if(power->displayBatteryPercentage == 1) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 1, 1, 22, 6); + canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10); + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_mask_9x10); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned( + canvas, 16, 4, AlignCenter, AlignCenter, batteryPercentile); + } else if(power->displayBatteryPercentage == 2) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 1, 1, 22, 6); + canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_mask_9x10); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned( + canvas, 16, 4, AlignCenter, AlignCenter, batteryPercentile); + } else { + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_mask_9x10); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_9x10); + } canvas_set_bitmap_mode(canvas, 0); } } else { @@ -94,6 +112,10 @@ static void power_stop_auto_shutdown_timer(Power* power) { furi_timer_stop(power->auto_shutdown_timer); } +static uint32_t power_is_running_auto_shutdown_timer(Power* power) { + return furi_timer_is_running(power->auto_shutdown_timer); +} + static void power_input_event_callback(const void* value, void* context) { furi_assert(value); furi_assert(context); @@ -106,8 +128,10 @@ static void power_input_event_callback(const void* value, void* context) { static void power_auto_shutdown_arm(Power* power) { if(power->shutdown_idle_delay_ms) { - power->input_events_subscription = - furi_pubsub_subscribe(power->input_events_pubsub, power_input_event_callback, power); + if(power->input_events_subscription == NULL) { + power->input_events_subscription = furi_pubsub_subscribe( + power->input_events_pubsub, power_input_event_callback, power); + } power_start_auto_shutdown_timer(power); } } @@ -127,18 +151,14 @@ static void power_auto_shutdown_timer_callback(void* context) { power_off(power); } -static void auto_shutdown_update(Power* power) { - uint32_t old_time = power->shutdown_idle_delay_ms; - LOAD_POWER_SETTINGS(&power->shutdown_idle_delay_ms); +static void power_shutdown_time_changed_callback(const void* event, void* context) { + furi_assert(event); + furi_assert(context); + Power* power = context; + power->shutdown_idle_delay_ms = *(uint32_t*)event; if(power->shutdown_idle_delay_ms) { - if(power->shutdown_idle_delay_ms != old_time) { - if(old_time) { - power_start_auto_shutdown_timer(power); - } else { - power_auto_shutdown_arm(power); - } - } - } else if(old_time) { + power_auto_shutdown_arm(power); + } else if(power_is_running_auto_shutdown_timer(power)) { power_auto_shutdown_inhibit(power); } } @@ -151,6 +171,10 @@ Power* power_alloc() { power->gui = furi_record_open(RECORD_GUI); // Pubsub power->event_pubsub = furi_pubsub_alloc(); + power->settings_events = furi_pubsub_alloc(); + furi_pubsub_subscribe(power->settings_events, power_shutdown_time_changed_callback, power); + power->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); + power->input_events_subscription = NULL; power->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); power->input_events_subscription = NULL; @@ -201,6 +225,7 @@ void power_free(Power* power) { // FuriPubSub furi_pubsub_free(power->event_pubsub); + furi_pubsub_free(power->settings_events); furi_pubsub_free(power->input_events_pubsub); if(power->input_events_subscription) { @@ -208,6 +233,9 @@ void power_free(Power* power) { power->input_events_subscription = NULL; } + //Auto shutdown timer + furi_timer_free(power->auto_shutdown_timer); + // Records furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_GUI); @@ -338,9 +366,6 @@ int32_t power_srv(void* p) { free(settings); while(1) { - //Check current setting for automatic shutdown - auto_shutdown_update(power); - // Update data from gauge and charger bool need_refresh = power_update_info(power); diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index bdf5fa529..3144a46e4 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -80,6 +80,14 @@ void power_get_info(Power* power, PowerInfo* info); */ FuriPubSub* power_get_pubsub(Power* power); +/** Get power settings events pubsub handler + * + * @param power Power instance + * + * @return FuriPubSub instance + */ +FuriPubSub* power_get_settings_events_pubsub(Power* power); + /** Check battery health * * @return true if battery is healthy diff --git a/applications/services/power/power_service/power_api.c b/applications/services/power/power_service/power_api.c index 8185b7cd5..ef25ca019 100644 --- a/applications/services/power/power_service/power_api.c +++ b/applications/services/power/power_service/power_api.c @@ -38,6 +38,11 @@ FuriPubSub* power_get_pubsub(Power* power) { return power->event_pubsub; } +FuriPubSub* power_get_settings_events_pubsub(Power* power) { + furi_assert(power); + return power->settings_events; +} + bool power_is_battery_healthy(Power* power) { furi_assert(power); bool is_healthy = false; diff --git a/applications/services/power/power_service/power_i.h b/applications/services/power/power_service/power_i.h index 2b5682213..e92663535 100644 --- a/applications/services/power/power_service/power_i.h +++ b/applications/services/power/power_service/power_i.h @@ -5,10 +5,11 @@ #include #include #include +#include #include #include "views/power_off.h" -#include "applications/settings/power_settings_app/power_settings.h" +#include #include "views/power_unplug_usb.h" #include @@ -30,6 +31,7 @@ struct Power { Gui* gui; NotificationApp* notification; FuriPubSub* event_pubsub; + FuriPubSub* settings_events; FuriPubSub* input_events_pubsub; FuriPubSubSubscription* input_events_subscription; PowerEvent event; 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/power/power_settings.h b/applications/services/power/power_settings.h new file mode 100644 index 000000000..0f2afa295 --- /dev/null +++ b/applications/services/power/power_settings.h @@ -0,0 +1,16 @@ +#include +#include +#include "power_settings_filename.h" + +#define POWER_SETTINGS_VER (1) + +#define POWER_SETTINGS_PATH INT_PATH(POWER_SETTINGS_FILE_NAME) +#define POWER_SETTINGS_MAGIC (0x21) + +#define SAVE_POWER_SETTINGS(x) \ + saved_struct_save( \ + POWER_SETTINGS_PATH, (x), sizeof(uint32_t), POWER_SETTINGS_MAGIC, POWER_SETTINGS_VER) + +#define LOAD_POWER_SETTINGS(x) \ + saved_struct_load( \ + POWER_SETTINGS_PATH, (x), sizeof(uint32_t), POWER_SETTINGS_MAGIC, POWER_SETTINGS_VER) diff --git a/applications/services/power/power_settings_filename.h b/applications/services/power/power_settings_filename.h new file mode 100644 index 000000000..6a3be534c --- /dev/null +++ b/applications/services/power/power_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define POWER_SETTINGS_FILE_NAME ".power.settings" \ No newline at end of file diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 06c05173c..ebf015722 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -148,7 +148,8 @@ size_t rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) { furi_assert(session); furi_assert(encoded_bytes); - furi_assert(size > 0); + + if(!size) return 0; size_t bytes_sent = furi_stream_buffer_send(session->stream, encoded_bytes, size, timeout); @@ -369,11 +370,7 @@ RpcSession* rpc_session_open(Rpc* rpc) { }; rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler); - session->thread = furi_thread_alloc(); - furi_thread_set_name(session->thread, "RpcSessionWorker"); - furi_thread_set_stack_size(session->thread, 2048); - furi_thread_set_context(session->thread, session); - furi_thread_set_callback(session->thread, rpc_session_worker); + session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); furi_thread_set_state_context(session->thread, session); furi_thread_set_state_callback(session->thread, rpc_session_free_callback); diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index 029ed0106..e66553d51 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -88,12 +88,8 @@ static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, v malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(framebuffer_size)); rpc_gui->transmit_frame->content.gui_screen_frame.data->size = framebuffer_size; // Transmission thread for async TX - rpc_gui->transmit_thread = furi_thread_alloc(); - furi_thread_set_name(rpc_gui->transmit_thread, "GuiRpcWorker"); - furi_thread_set_callback( - rpc_gui->transmit_thread, rpc_system_gui_screen_stream_frame_transmit_thread); - furi_thread_set_context(rpc_gui->transmit_thread, rpc_gui); - furi_thread_set_stack_size(rpc_gui->transmit_thread, 1024); + rpc_gui->transmit_thread = furi_thread_alloc_ex( + "GuiRpcWorker", 1024, rpc_system_gui_screen_stream_frame_transmit_thread, rpc_gui); furi_thread_start(rpc_gui->transmit_thread); // GUI framebuffer callback gui_add_framebuffer_callback( diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 1b545b414..16e343fce 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -138,6 +138,41 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex furi_record_close(RECORD_STORAGE); } +static void rpc_system_storage_timestamp_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_storage_timestamp_request_tag); + + FURI_LOG_D(TAG, "Timestamp"); + + RpcStorageSystem* rpc_storage = context; + RpcSession* session = rpc_storage->session; + furi_assert(session); + + rpc_system_storage_reset_state(rpc_storage, session, true); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + + const char* path = request->content.storage_timestamp_request.path; + uint32_t timestamp = 0; + FS_Error error = storage_common_timestamp(fs_api, path, ×tamp); + + response->command_status = rpc_system_storage_get_error(error); + response->which_content = PB_Main_empty_tag; + + if(error == FSE_OK) { + response->which_content = PB_Main_storage_timestamp_response_tag; + response->content.storage_timestamp_response.timestamp = timestamp; + } + + rpc_send_and_release(session, response); + free(response); + furi_record_close(RECORD_STORAGE); +} + static void rpc_system_storage_stat_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); @@ -405,6 +440,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) { @@ -668,6 +707,9 @@ void* rpc_system_storage_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_storage_info_process; rpc_add_handler(session, PB_Main_storage_info_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_storage_timestamp_process; + rpc_add_handler(session, PB_Main_storage_timestamp_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_storage_stat_process; rpc_add_handler(session, PB_Main_storage_stat_request_tag, &rpc_handler); diff --git a/applications/services/storage/storage.c b/applications/services/storage/storage.c index 9079a95ed..1816bf921 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 @@ -38,6 +39,7 @@ Storage* storage_app_alloc() { for(uint8_t i = 0; i < STORAGE_COUNT; i++) { storage_data_init(&app->storage[i]); + storage_data_timestamp(&app->storage[i]); } #ifndef FURI_RAM_EXEC diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index 968b69048..9c133e9be 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -177,6 +177,16 @@ bool storage_dir_rewind(File* file); /******************* Common Functions *******************/ +/** Retrieves unix timestamp of last access + * + * @param storage The storage instance + * @param path path to file/directory + * @param timestamp the timestamp pointer + * + * @return FS_Error operation result + */ +FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp); + /** Retrieves information about a file/directory * @param app pointer to the api * @param path path to file/directory diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 880fb9700..c83f16499 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -32,6 +32,7 @@ static void storage_cli_print_usage() { printf("\tmkdir\t - creates a new directory\r\n"); printf("\tmd5\t - md5 hash of the file\r\n"); printf("\tstat\t - info about file or dir\r\n"); + printf("\ttimestamp\t - last modification timestamp\r\n"); }; static void storage_cli_print_error(FS_Error error) { @@ -274,7 +275,7 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args uint32_t buffer_size; int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - if(parsed_count == EOF || parsed_count != 1) { + if(parsed_count != 1) { storage_cli_print_usage(); } else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t file_size = storage_file_size(file); @@ -314,7 +315,7 @@ static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args uint32_t buffer_size; int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - if(parsed_count == EOF || parsed_count != 1) { + if(parsed_count != 1) { storage_cli_print_usage(); } else { if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { @@ -386,6 +387,22 @@ static void storage_cli_stat(Cli* cli, FuriString* path) { furi_record_close(RECORD_STORAGE); } +static void storage_cli_timestamp(Cli* cli, FuriString* path) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + + uint32_t timestamp = 0; + FS_Error error = storage_common_timestamp(api, furi_string_get_cstr(path), ×tamp); + + if(error != FSE_OK) { + printf("Invalid arguments\r\n"); + } else { + printf("Timestamp %lu\r\n", timestamp); + } + + furi_record_close(RECORD_STORAGE); +} + static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) { UNUSED(cli); Storage* api = furi_record_open(RECORD_STORAGE); @@ -578,6 +595,11 @@ void storage_cli(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "timestamp") == 0) { + storage_cli_timestamp(cli, path); + break; + } + storage_cli_print_usage(); } while(false); diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index c0c730fb7..2c3a7bfc9 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -354,6 +354,16 @@ bool storage_dir_rewind(File* file) { /****************** COMMON ******************/ +FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp) { + S_API_PROLOGUE; + + SAData data = {.ctimestamp = {.path = path, .timestamp = timestamp}}; + + S_API_MESSAGE(StorageCommandCommonTimestamp); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) { S_API_PROLOGUE; @@ -535,8 +545,8 @@ static FS_Error FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) { FS_Error error; - const char* new_path_tmp; - FuriString* new_path_next; + const char* new_path_tmp = NULL; + FuriString* new_path_next = NULL; new_path_next = furi_string_alloc(); FileInfo fileinfo; diff --git a/applications/services/storage/storage_glue.c b/applications/services/storage/storage_glue.c index c5682f67b..c6ff08bdc 100644 --- a/applications/services/storage/storage_glue.c +++ b/applications/services/storage/storage_glue.c @@ -82,6 +82,14 @@ const char* storage_data_status_text(StorageData* storage) { return result; } +void storage_data_timestamp(StorageData* storage) { + storage->timestamp = furi_hal_rtc_get_timestamp(); +} + +uint32_t storage_data_get_timestamp(StorageData* storage) { + return storage->timestamp; +} + /****************** storage glue ******************/ bool storage_has_file(const File* file, StorageData* storage_data) { diff --git a/applications/services/storage/storage_glue.h b/applications/services/storage/storage_glue.h index 53fa0de19..6fdc70099 100644 --- a/applications/services/storage/storage_glue.h +++ b/applications/services/storage/storage_glue.h @@ -42,6 +42,8 @@ bool storage_data_lock(StorageData* storage); bool storage_data_unlock(StorageData* storage); StorageStatus storage_data_status(StorageData* storage); const char* storage_data_status_text(StorageData* storage); +void storage_data_timestamp(StorageData* storage); +uint32_t storage_data_get_timestamp(StorageData* storage); LIST_DEF( StorageFileList, @@ -58,6 +60,7 @@ struct StorageData { FuriMutex* mutex; StorageStatus status; StorageFileList_t files; + uint32_t timestamp; }; bool storage_has_file(const File* file, StorageData* storage_data); diff --git a/applications/services/storage/storage_i.h b/applications/services/storage/storage_i.h index 5c836ccd2..406fc921e 100644 --- a/applications/services/storage/storage_i.h +++ b/applications/services/storage/storage_i.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include "storage_glue.h" #include "storage_sd_api.h" diff --git a/applications/services/storage/storage_message.h b/applications/services/storage/storage_message.h index 78cd1e03c..987268017 100644 --- a/applications/services/storage/storage_message.h +++ b/applications/services/storage/storage_message.h @@ -42,6 +42,11 @@ typedef struct { uint16_t name_length; } SADataDRead; +typedef struct { + const char* path; + uint32_t* timestamp; +} SADataCTimestamp; + typedef struct { const char* path; FileInfo* fileinfo; @@ -78,6 +83,7 @@ typedef union { SADataDOpen dopen; SADataDRead dread; + SADataCTimestamp ctimestamp; SADataCStat cstat; SADataCFSInfo cfsinfo; @@ -112,6 +118,7 @@ typedef enum { StorageCommandDirClose, StorageCommandDirRead, StorageCommandDirRewind, + StorageCommandCommonTimestamp, StorageCommandCommonStat, StorageCommandCommonRemove, StorageCommandCommonMkDir, diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 8643e974e..795a5d11c 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -114,6 +114,9 @@ bool storage_process_file_open( if(storage_path_already_open(real_path, storage->files)) { file->error_id = FSE_ALREADY_OPEN; } else { + if(access_mode & FSAM_WRITE) { + storage_data_timestamp(storage); + } storage_push_storage_file(file, real_path, type, storage); FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode)); } @@ -166,6 +169,7 @@ static uint16_t storage_process_file_write( if(storage == NULL) { file->error_id = FSE_INVALID_PARAMETER; } else { + storage_data_timestamp(storage); FS_CALL(storage, file.write(storage, file, buff, bytes_to_write)); } @@ -209,6 +213,7 @@ static bool storage_process_file_truncate(Storage* app, File* file) { if(storage == NULL) { file->error_id = FSE_INVALID_PARAMETER; } else { + storage_data_timestamp(storage); FS_CALL(storage, file.truncate(storage, file)); } @@ -222,6 +227,7 @@ static bool storage_process_file_sync(Storage* app, File* file) { if(storage == NULL) { file->error_id = FSE_INVALID_PARAMETER; } else { + storage_data_timestamp(storage); FS_CALL(storage, file.sync(storage, file)); } @@ -332,6 +338,21 @@ bool storage_process_dir_rewind(Storage* app, File* file) { /******************* Common FS Functions *******************/ +static FS_Error + storage_process_common_timestamp(Storage* app, const char* path, uint32_t* timestamp) { + FS_Error ret = FSE_OK; + StorageType type = storage_get_type_by_path(app, path); + + if(storage_type_is_not_valid(type)) { + ret = FSE_INVALID_NAME; + } else { + StorageData* storage = storage_get_storage_by_type(app, type); + *timestamp = storage_data_get_timestamp(storage); + } + + return ret; +} + static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) { FS_Error ret = FSE_OK; StorageType type = storage_get_type_by_path(app, path); @@ -366,6 +387,7 @@ static FS_Error storage_process_common_remove(Storage* app, const char* path) { break; } + storage_data_timestamp(storage); FS_CALL(storage, common.remove(storage, remove_vfs(path))); } while(false); @@ -382,6 +404,7 @@ static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { ret = FSE_INVALID_NAME; } else { StorageData* storage = storage_get_storage_by_type(app, type); + storage_data_timestamp(storage); FS_CALL(storage, common.mkdir(storage, remove_vfs(path))); } @@ -417,6 +440,7 @@ static FS_Error storage_process_sd_format(Storage* app) { ret = FSE_NOT_READY; } else { ret = sd_format_card(&app->storage[ST_EXT]); + storage_data_timestamp(&app->storage[ST_EXT]); } return ret; @@ -429,6 +453,7 @@ static FS_Error storage_process_sd_unmount(Storage* app) { ret = FSE_NOT_READY; } else { sd_unmount_card(&app->storage[ST_EXT]); + storage_data_timestamp(&app->storage[ST_EXT]); } return ret; @@ -541,6 +566,10 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { message->return_data->bool_value = storage_process_dir_rewind(app, message->data->file.file); break; + case StorageCommandCommonTimestamp: + message->return_data->error_value = storage_process_common_timestamp( + app, message->data->ctimestamp.path, message->data->ctimestamp.timestamp); + break; case StorageCommandCommonStat: message->return_data->error_value = storage_process_common_stat( app, message->data->cstat.path, message->data->cstat.fileinfo); diff --git a/applications/services/storage/storages/storage_ext.c b/applications/services/storage/storages/storage_ext.c index 7341a6ec8..0c81a0006 100644 --- a/applications/services/storage/storages/storage_ext.c +++ b/applications/services/storage/storages/storage_ext.c @@ -90,6 +90,7 @@ static bool sd_mount_card(StorageData* storage, bool notify) { } } + storage_data_timestamp(storage); storage_data_unlock(storage); return result; 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/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index 07ba9925f..bdd9589ed 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -11,9 +11,16 @@ static bool favorite_fap_selector_item_callback( uint8_t** icon_ptr, FuriString* item_name) { UNUSED(context); +#ifdef APP_FAP_LOADER Storage* storage = furi_record_open(RECORD_STORAGE); bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); furi_record_close(RECORD_STORAGE); +#else + UNUSED(file_path); + UNUSED(icon_ptr); + UNUSED(item_name); + bool success = false; +#endif return success; } diff --git a/applications/settings/power_settings_app/power_settings_app.c b/applications/settings/power_settings_app/power_settings_app.c index ec010ee71..016439c0d 100644 --- a/applications/settings/power_settings_app/power_settings_app.c +++ b/applications/settings/power_settings_app/power_settings_app.c @@ -25,6 +25,9 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { app->gui = furi_record_open(RECORD_GUI); app->power = furi_record_open(RECORD_POWER); + //PubSub + app->settings_events = power_get_settings_events_pubsub(app->power); + // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&power_settings_scene_handlers, app); @@ -66,18 +69,24 @@ void power_settings_app_free(PowerSettingsApp* app) { // Views view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo); battery_info_free(app->batery_info); + view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); submenu_free(app->submenu); - variable_item_list_free(app->variable_item_list); + view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewDialog); dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewVariableItemList); + variable_item_list_free(app->variable_item_list); + // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); + // Records furi_record_close(RECORD_POWER); furi_record_close(RECORD_GUI); + free(app); } diff --git a/applications/settings/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h index 383163ac3..d30cbaf43 100644 --- a/applications/settings/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -6,13 +6,14 @@ #include #include #include +#include #include "views/battery_info.h" #include #include #include -#include "power_settings.h" +#include #include "scenes/power_settings_scene.h" typedef struct { @@ -26,6 +27,7 @@ typedef struct { PowerInfo info; VariableItemList* variable_item_list; uint32_t shutdown_idle_delay_ms; + FuriPubSub* settings_events; } PowerSettingsApp; typedef enum { diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_shutdown_idle.c b/applications/settings/power_settings_app/scenes/power_settings_scene_shutdown_idle.c index b6c36a890..7b0bea340 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_shutdown_idle.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_shutdown_idle.c @@ -1,14 +1,14 @@ #include "../power_settings_app.h" #include -#define SHUTDOWN_IDLE_DELAY_COUNT 8 +#define SHUTDOWN_IDLE_DELAY_COUNT 9 #define SCENE_EVENT_SELECT_SHUTDOWN_IDLE_DELAY 0 const char* const shutdown_idle_delay_text[SHUTDOWN_IDLE_DELAY_COUNT] = - {"OFF", "15min", "30min", "1h", "6h", "12h", "24h", "48h"}; + {"OFF", "15min", "30min", "1h", "2h", "6h", "12h", "24h", "48h"}; const uint32_t shutdown_idle_delay_value[SHUTDOWN_IDLE_DELAY_COUNT] = - {0, 900000, 1800000, 3600000, 21600000, 43200000, 86400000, 172800000}; + {0, 900000, 1800000, 3600000, 7200000, 21600000, 43200000, 86400000, 172800000}; static void power_settings_scene_shutodwn_idle_callback(void* context, uint32_t index) { PowerSettingsApp* app = context; @@ -63,5 +63,6 @@ bool power_settings_scene_shutdown_idle_on_event(void* context, SceneManagerEven void power_settings_scene_shutdown_idle_on_exit(void* context) { PowerSettingsApp* app = context; SAVE_POWER_SETTINGS(&app->shutdown_idle_delay_ms); + furi_pubsub_publish(app->settings_events, &app->shutdown_idle_delay_ms); variable_item_list_reset(app->variable_item_list); } diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 1a8bc71ec..bbb0acb9a 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -1,6 +1,10 @@ #include "battery_info.h" #include #include +#include + +#define LOW_CHARGE_THRESHOLD 10 +#define HIGH_DRAIN_CURRENT_THRESHOLD 100 struct BatteryInfo { View* view; @@ -27,9 +31,9 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); if(charge_current > 0) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); - } else if(drain_current > 100) { + } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); - } else if(data->charge < 10) { + } else if(data->charge < LOW_CHARGE_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); } else { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); @@ -50,11 +54,19 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { (uint32_t)(data->vbus_voltage * 10) % 10, charge_current); } else if(drain_current > 0) { - snprintf(emote, sizeof(emote), "%s", drain_current > 100 ? "Oh no!" : "Om-nom-nom!"); + snprintf( + emote, + sizeof(emote), + "%s", + drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); snprintf(header, sizeof(header), "%s", "Consumption is"); snprintf( - value, sizeof(value), "%ld %s", drain_current, drain_current > 100 ? "mA!" : "mA"); - } else if(charge_current != 0 || drain_current != 0) { + value, + sizeof(value), + "%ld %s", + drain_current, + drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(drain_current != 0) { snprintf(header, 20, "..."); } else { snprintf(header, sizeof(header), "Charged!"); 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, 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/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 7661413d7..dfce11a22 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -45,6 +45,31 @@ static void debug_changed(VariableItem* item) { loader_update_menu(); } +const char* const heap_trace_mode_text[] = { + "None", + "Main", +#if FURI_DEBUG + "Tree", + "All", +#endif +}; + +const uint32_t heap_trace_mode_value[] = { + FuriHalRtcHeapTrackModeNone, + FuriHalRtcHeapTrackModeMain, +#if FURI_DEBUG + FuriHalRtcHeapTrackModeTree, + FuriHalRtcHeapTrackModeAll, +#endif +}; + +static void heap_trace_mode_changed(VariableItem* item) { + // SystemSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, heap_trace_mode_text[index]); + furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); +} + static uint32_t system_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -79,6 +104,18 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, debug_text[value_index]); + item = variable_item_list_add( + app->var_item_list, + "Heap Trace", + COUNT_OF(heap_trace_mode_text), + heap_trace_mode_changed, + app); + value_index = value_index_uint32( + furi_hal_rtc_get_heap_track_mode(), heap_trace_mode_value, COUNT_OF(heap_trace_mode_text)); + furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[value_index]); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, heap_trace_mode_text[value_index]); + view_set_previous_callback( variable_item_list_get_view(app->var_item_list), system_settings_exit); view_dispatcher_add_view( diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index c3cdbb5f7..f8e21ca3e 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -110,11 +110,8 @@ static void updater_start_app() { * inside loader process, at startup. * So, accessing its record would cause a deadlock */ - FuriThread* thread = furi_thread_alloc(); - - furi_thread_set_name(thread, "UpdateAppSpawner"); - furi_thread_set_stack_size(thread, 768); - furi_thread_set_callback(thread, updater_spawner_thread_worker); + FuriThread* thread = + furi_thread_alloc_ex("UpdateAppSpawner", 768, updater_spawner_thread_worker, NULL); furi_thread_set_state_callback(thread, updater_spawner_thread_cleanup); furi_thread_set_state_context(thread, thread); furi_thread_start(thread); diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c index de172dd49..b43a6df16 100644 --- a/applications/system/updater/util/update_task.c +++ b/applications/system/updater/util/update_task.c @@ -216,11 +216,8 @@ UpdateTask* update_task_alloc() { update_task->boot_mode = furi_hal_rtc_get_boot_mode(); update_task->update_path = furi_string_alloc(); - FuriThread* thread = update_task->thread = furi_thread_alloc(); - - furi_thread_set_name(thread, "UpdateWorker"); - furi_thread_set_stack_size(thread, 5120); - furi_thread_set_context(thread, update_task); + FuriThread* thread = update_task->thread = + furi_thread_alloc_ex("UpdateWorker", 5120, NULL, update_task); furi_thread_set_state_callback(thread, update_task_worker_thread_cb); furi_thread_set_state_context(thread, update_task); 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/assets/.gitignore b/assets/.gitignore index 9bc0bdc0c..2ecf4ed66 100644 --- a/assets/.gitignore +++ b/assets/.gitignore @@ -1,3 +1,3 @@ /core2_firmware /resources/Manifest -/resources/apps/* \ No newline at end of file +/resources/apps/* diff --git a/assets/SConscript b/assets/SConscript index e1bf546cc..b078e0f41 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}", @@ -94,4 +96,4 @@ if assetsenv["IS_BASE_FIRMWARE"]: env["FW_RESOURCES"] = resources assetsenv.Alias("resources", resources) -Return("assetslib") +Return("assetslib") \ No newline at end of file diff --git a/assets/dolphin/ReadMe.md b/assets/dolphin/ReadMe.md index 6b59d231f..643086c26 100644 --- a/assets/dolphin/ReadMe.md +++ b/assets/dolphin/ReadMe.md @@ -52,7 +52,7 @@ Version: 1 - `Active cooldown` - amount of seconds (after passive mode) to pass before entering next active mode. - `Bubble slots` - amount of bubble sequences. -- Any bubble sequence plays whole sequence during active mode. There can be many bubble sequences and bubbles inside it. Bubbles in 1 bubble sequence have to reside in 1 slot. Bubbles order in 1 bubble sequence is determined by occurance in file. As soon as frame index goes out of EndFrame index of bubble - next animation bubble is choosen. There can also be free of bubbles frames between 2 bubbles. +- Any bubble sequence plays whole sequence during active mode. There can be many bubble sequences and bubbles inside it. Bubbles in 1 bubble sequence have to reside in 1 slot. Bubbles order in 1 bubble sequence is determined by occurrence in file. As soon as frame index goes out of EndFrame index of bubble - next animation bubble is chosen. There can also be free of bubbles frames between 2 bubbles. - `Slot` - number to unite bubbles for same sequence. - `X`, `Y` - are coordinates of left top corner of bubble. diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_0.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_0.png new file mode 100644 index 000000000..229a40c8f Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_0.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_1.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_1.png new file mode 100644 index 000000000..a715f970b Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_1.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_10.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_10.png new file mode 100644 index 000000000..0beebf5b1 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_10.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_11.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_11.png new file mode 100644 index 000000000..197e199c7 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_11.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_12.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_12.png new file mode 100644 index 000000000..685121ab8 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_12.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_13.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_13.png new file mode 100644 index 000000000..92e50da56 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_13.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_14.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_14.png new file mode 100644 index 000000000..b3d46b0f9 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_14.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_15.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_15.png new file mode 100644 index 000000000..1b7a1ec6a Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_15.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_16.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_16.png new file mode 100644 index 000000000..53037d1eb Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_16.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_17.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_17.png new file mode 100644 index 000000000..15f4d80d3 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_17.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_18.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_18.png new file mode 100644 index 000000000..7a0fade55 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_18.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_2.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_2.png new file mode 100644 index 000000000..1ce573813 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_2.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_3.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_3.png new file mode 100644 index 000000000..51187a21d Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_3.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_4.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_4.png new file mode 100644 index 000000000..507fb5467 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_4.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_5.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_5.png new file mode 100644 index 000000000..286f05d67 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_5.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_6.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_6.png new file mode 100644 index 000000000..c00a10a94 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_6.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_7.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_7.png new file mode 100644 index 000000000..2aae7a752 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_7.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_8.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_8.png new file mode 100644 index 000000000..b0ef16af0 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_8.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_9.png b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_9.png new file mode 100644 index 000000000..b71982261 Binary files /dev/null and b/assets/dolphin/external/Kuronons_RMCFW_128x64/frame_9.png differ diff --git a/assets/dolphin/external/Kuronons_RMCFW_128x64/meta.txt b/assets/dolphin/external/Kuronons_RMCFW_128x64/meta.txt new file mode 100644 index 000000000..aa9584f03 --- /dev/null +++ b/assets/dolphin/external/Kuronons_RMCFW_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 10 +Active frames: 42 +Frames order: 11 11 12 13 14 15 14 13 12 11 0 1 1 1 2 2 3 4 4 5 6 7 7 8 8 9 10 10 10 11 11 11 11 11 11 12 12 13 13 14 14 15 15 15 15 15 16 16 17 18 18 18 +Active cycles: 1 +Frame rate: 6 +Duration: 3600 +Active cooldown: 4 + +Bubble slots: 0 diff --git a/assets/dolphin/external/L1_Halloween_128x64/frame_0.png b/assets/dolphin/external/L1_Halloween_128x64/frame_0.png new file mode 100644 index 000000000..f69a43f5e Binary files /dev/null and b/assets/dolphin/external/L1_Halloween_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Halloween_128x64/frame_1.png b/assets/dolphin/external/L1_Halloween_128x64/frame_1.png new file mode 100644 index 000000000..d2f2bc4b2 Binary files /dev/null and b/assets/dolphin/external/L1_Halloween_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Halloween_128x64/frame_2.png b/assets/dolphin/external/L1_Halloween_128x64/frame_2.png new file mode 100644 index 000000000..5d7c9ed8b Binary files /dev/null and b/assets/dolphin/external/L1_Halloween_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Halloween_128x64/frame_3.png b/assets/dolphin/external/L1_Halloween_128x64/frame_3.png new file mode 100644 index 000000000..be199f1df Binary files /dev/null and b/assets/dolphin/external/L1_Halloween_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Halloween_128x64/meta.txt b/assets/dolphin/external/L1_Halloween_128x64/meta.txt new file mode 100644 index 000000000..9762d4caa --- /dev/null +++ b/assets/dolphin/external/L1_Halloween_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 4 +Active frames: 0 +Frames order: 0 1 2 3 +Active cycles: 0 +Frame rate: 3 +Duration: 3600 +Active cooldown: 0 + +Bubble slots: 0 diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_0.png b/assets/dolphin/external/L1_Mods_128x64/frame_0.png new file mode 100644 index 000000000..220908495 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_1.png b/assets/dolphin/external/L1_Mods_128x64/frame_1.png new file mode 100644 index 000000000..9123906fb Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_10.png b/assets/dolphin/external/L1_Mods_128x64/frame_10.png new file mode 100644 index 000000000..e90ad5e90 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_11.png b/assets/dolphin/external/L1_Mods_128x64/frame_11.png new file mode 100644 index 000000000..031c0ad81 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_12.png b/assets/dolphin/external/L1_Mods_128x64/frame_12.png new file mode 100644 index 000000000..856e068fd Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_13.png b/assets/dolphin/external/L1_Mods_128x64/frame_13.png new file mode 100644 index 000000000..a0366b2cf Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_14.png b/assets/dolphin/external/L1_Mods_128x64/frame_14.png new file mode 100644 index 000000000..24fd557ab Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_15.png b/assets/dolphin/external/L1_Mods_128x64/frame_15.png new file mode 100644 index 000000000..3bf1d3ed2 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_16.png b/assets/dolphin/external/L1_Mods_128x64/frame_16.png new file mode 100644 index 000000000..f0b44898f Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_17.png b/assets/dolphin/external/L1_Mods_128x64/frame_17.png new file mode 100644 index 000000000..c98c70c91 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_18.png b/assets/dolphin/external/L1_Mods_128x64/frame_18.png new file mode 100644 index 000000000..4f7b7ae82 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_19.png b/assets/dolphin/external/L1_Mods_128x64/frame_19.png new file mode 100644 index 000000000..b3ad6700c Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_2.png b/assets/dolphin/external/L1_Mods_128x64/frame_2.png new file mode 100644 index 000000000..c4aac4b91 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_20.png b/assets/dolphin/external/L1_Mods_128x64/frame_20.png new file mode 100644 index 000000000..ea2eae4d7 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/L1_Mods_128x64/frame_21.png new file mode 100644 index 000000000..900cc7d12 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_22.png b/assets/dolphin/external/L1_Mods_128x64/frame_22.png new file mode 100644 index 000000000..de6c511e4 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_23.png b/assets/dolphin/external/L1_Mods_128x64/frame_23.png new file mode 100644 index 000000000..4f82f63bc Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/L1_Mods_128x64/frame_24.png new file mode 100644 index 000000000..d7c614902 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/L1_Mods_128x64/frame_25.png new file mode 100644 index 000000000..768030b3c Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_26.png b/assets/dolphin/external/L1_Mods_128x64/frame_26.png new file mode 100644 index 000000000..12f22abdb Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/L1_Mods_128x64/frame_27.png new file mode 100644 index 000000000..9fca976de Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_28.png b/assets/dolphin/external/L1_Mods_128x64/frame_28.png new file mode 100644 index 000000000..4b2ab5863 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_29.png b/assets/dolphin/external/L1_Mods_128x64/frame_29.png new file mode 100644 index 000000000..69c709adc Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_3.png b/assets/dolphin/external/L1_Mods_128x64/frame_3.png new file mode 100644 index 000000000..1b0e77426 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_30.png b/assets/dolphin/external/L1_Mods_128x64/frame_30.png new file mode 100644 index 000000000..13caae7ca Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_31.png b/assets/dolphin/external/L1_Mods_128x64/frame_31.png new file mode 100644 index 000000000..b1d1e8bfe Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_32.png b/assets/dolphin/external/L1_Mods_128x64/frame_32.png new file mode 100644 index 000000000..acf000827 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/L1_Mods_128x64/frame_33.png new file mode 100644 index 000000000..b6c6fbb19 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_34.png b/assets/dolphin/external/L1_Mods_128x64/frame_34.png new file mode 100644 index 000000000..7d2dcda5d Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/L1_Mods_128x64/frame_35.png new file mode 100644 index 000000000..461270ba4 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/L1_Mods_128x64/frame_36.png new file mode 100644 index 000000000..b018a94c1 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_37.png b/assets/dolphin/external/L1_Mods_128x64/frame_37.png new file mode 100644 index 000000000..fa2b303cc Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_38.png b/assets/dolphin/external/L1_Mods_128x64/frame_38.png new file mode 100644 index 000000000..ed38122f5 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_39.png b/assets/dolphin/external/L1_Mods_128x64/frame_39.png new file mode 100644 index 000000000..38610bb4b Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_4.png b/assets/dolphin/external/L1_Mods_128x64/frame_4.png new file mode 100644 index 000000000..45e47de12 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/L1_Mods_128x64/frame_40.png new file mode 100644 index 000000000..7f6b4b29a Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_5.png b/assets/dolphin/external/L1_Mods_128x64/frame_5.png new file mode 100644 index 000000000..7c293b485 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_6.png b/assets/dolphin/external/L1_Mods_128x64/frame_6.png new file mode 100644 index 000000000..e72e7a30e Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_7.png b/assets/dolphin/external/L1_Mods_128x64/frame_7.png new file mode 100644 index 000000000..5c840d6f6 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_8.png b/assets/dolphin/external/L1_Mods_128x64/frame_8.png new file mode 100644 index 000000000..f689f190c Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_9.png b/assets/dolphin/external/L1_Mods_128x64/frame_9.png new file mode 100644 index 000000000..628394e57 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/meta.txt b/assets/dolphin/external/L1_Mods_128x64/meta.txt new file mode 100644 index 000000000..0225c7e55 --- /dev/null +++ b/assets/dolphin/external/L1_Mods_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 23 +Active frames: 18 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 0 \ No newline at end of file diff --git a/assets/dolphin/external/L1_Painting_128x64/meta.txt b/assets/dolphin/external/L1_Painting_128x64/meta.txt index 6964b479b..46528d598 100644 --- a/assets/dolphin/external/L1_Painting_128x64/meta.txt +++ b/assets/dolphin/external/L1_Painting_128x64/meta.txt @@ -11,12 +11,12 @@ Frame rate: 2 Duration: 3600 Active cooldown: 7 -Bubble slots: 1 +Bubble slots: 2 Slot: 0 X: 57 Y: 24 -Text: No mistakes, +Text: $20 to put this AlignH: Left AlignV: Center StartFrame: 11 @@ -25,8 +25,26 @@ EndFrame: 14 Slot: 0 X: 57 Y: 21 -Text: only happy\n accidents +Text: in my blowhole AlignH: Left AlignV: Center StartFrame: 15 -EndFrame: 18 \ No newline at end of file +EndFrame: 18 + +Slot: 1 +X: 57 +Y: 24 +Text: How many dicks +AlignH: Left +AlignV: Center +StartFrame: 11 +EndFrame: 14 + +Slot: 0 +X: 57 +Y: 21 +Text: we drawing today? +AlignH: Left +AlignV: Center +StartFrame: 15 +EndFrame: 18 diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_0.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_0.png new file mode 100644 index 000000000..f10393867 Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_1.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_1.png new file mode 100644 index 000000000..96fd173b4 Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_10.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_10.png new file mode 100644 index 000000000..cd4c7442c Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_2.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_2.png new file mode 100644 index 000000000..81d28fcf8 Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_3.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_3.png new file mode 100644 index 000000000..cc3670d9a Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_4.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_4.png new file mode 100644 index 000000000..a0cc88f8e Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_5.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_5.png new file mode 100644 index 000000000..25213cde0 Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_6.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_6.png new file mode 100644 index 000000000..ea9099759 Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_7.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_7.png new file mode 100644 index 000000000..54d1543a7 Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_8.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_8.png new file mode 100644 index 000000000..f1d240ced Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/frame_9.png b/assets/dolphin/external/L1_Purple_rain_128x64/frame_9.png new file mode 100644 index 000000000..b5abae076 Binary files /dev/null and b/assets/dolphin/external/L1_Purple_rain_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Purple_rain_128x64/meta.txt b/assets/dolphin/external/L1_Purple_rain_128x64/meta.txt new file mode 100644 index 000000000..c60148050 --- /dev/null +++ b/assets/dolphin/external/L1_Purple_rain_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 11 +Active frames: 0 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 +Active cycles: 0 +Frame rate: 4 +Duration: 3600 +Active cooldown: 0 + +Bubble slots: 0 diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_0.png b/assets/dolphin/external/L1_Read_books_128x64/frame_0.png deleted file mode 100644 index fbf265286..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_1.png b/assets/dolphin/external/L1_Read_books_128x64/frame_1.png deleted file mode 100644 index be9c5dc15..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_2.png b/assets/dolphin/external/L1_Read_books_128x64/frame_2.png deleted file mode 100644 index eb8075ecb..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_3.png b/assets/dolphin/external/L1_Read_books_128x64/frame_3.png deleted file mode 100644 index 0f1fd5c52..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_4.png b/assets/dolphin/external/L1_Read_books_128x64/frame_4.png deleted file mode 100644 index dd2d918af..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_5.png b/assets/dolphin/external/L1_Read_books_128x64/frame_5.png deleted file mode 100644 index bfcc7bcd2..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_5.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_6.png b/assets/dolphin/external/L1_Read_books_128x64/frame_6.png deleted file mode 100644 index eabe9def3..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_6.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_7.png b/assets/dolphin/external/L1_Read_books_128x64/frame_7.png deleted file mode 100644 index 3ff0aee0c..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_7.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_8.png b/assets/dolphin/external/L1_Read_books_128x64/frame_8.png deleted file mode 100644 index ce663f75f..000000000 Binary files a/assets/dolphin/external/L1_Read_books_128x64/frame_8.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Read_books_128x64/meta.txt b/assets/dolphin/external/L1_Read_books_128x64/meta.txt deleted file mode 100644 index 7432507ce..000000000 --- a/assets/dolphin/external/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/dolphin/external/L1_Waves_128x50/frame_0.png b/assets/dolphin/external/L1_Waves_128x50/frame_0.png deleted file mode 100644 index adad4f413..000000000 Binary files a/assets/dolphin/external/L1_Waves_128x50/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_1.png b/assets/dolphin/external/L1_Waves_128x50/frame_1.png deleted file mode 100644 index 462824be2..000000000 Binary files a/assets/dolphin/external/L1_Waves_128x50/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_2.png b/assets/dolphin/external/L1_Waves_128x50/frame_2.png deleted file mode 100644 index a5a728849..000000000 Binary files a/assets/dolphin/external/L1_Waves_128x50/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_3.png b/assets/dolphin/external/L1_Waves_128x50/frame_3.png deleted file mode 100644 index 4f454a743..000000000 Binary files a/assets/dolphin/external/L1_Waves_128x50/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L1_Waves_128x50/meta.txt b/assets/dolphin/external/L1_Waves_128x50/meta.txt deleted file mode 100644 index c2f63b4c7..000000000 --- a/assets/dolphin/external/L1_Waves_128x50/meta.txt +++ /dev/null @@ -1,50 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 50 -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: 3 - -Slot: 0 -X: 1 -Y: 17 -Text: I am happy,\nmy friend! -AlignH: Right -AlignV: Bottom -StartFrame: 3 -EndFrame: 9 - -Slot: 1 -X: 1 -Y: 17 -Text: So long and\nthanks for\nall the fish! -AlignH: Right -AlignV: Center -StartFrame: 3 -EndFrame: 9 - -Slot: 2 -X: 1 -Y: 25 -Text: I wish I could -AlignH: Right -AlignV: Bottom -StartFrame: 3 -EndFrame: 5 - -Slot: 2 -X: 1 -Y: 25 -Text: swim all day -AlignH: Right -AlignV: Bottom -StartFrame: 6 -EndFrame: 9 \ No newline at end of file diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_0.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_0.png index 1b4de2fcd..e8a8fcd06 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_0.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_1.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_1.png index 5c05ab5e1..82fa5a8cc 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_1.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_2.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_2.png index 2065ab2bf..1120d8539 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_2.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_3.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_3.png index 3a04ec159..e78ff10dd 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_3.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_4.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_4.png index 67eb6274a..59cb890f8 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_4.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_5.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_5.png index c35307497..da89112ca 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_5.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_6.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_6.png index fa85be7cc..49f040bb5 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_6.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_7.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_7.png index 4adaf63a8..de0efa639 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_7.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_8.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_8.png index c1f7c02f5..5d9841fe2 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_8.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L2_FlipperCity_128x64/frame_9.png b/assets/dolphin/external/L2_FlipperCity_128x64/frame_9.png index 64df2cb16..1c6f7c903 100644 Binary files a/assets/dolphin/external/L2_FlipperCity_128x64/frame_9.png and b/assets/dolphin/external/L2_FlipperCity_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png deleted file mode 100644 index d303aef08..000000000 Binary files a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png deleted file mode 100644 index 45aeb57b5..000000000 Binary files a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png deleted file mode 100644 index a4508f14b..000000000 Binary files a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png deleted file mode 100644 index 350002167..000000000 Binary files a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png deleted file mode 100644 index 7addbf932..000000000 Binary files a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt b/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt deleted file mode 100644 index 8ad8d42a3..000000000 --- a/assets/dolphin/external/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/dolphin/external/L2_Soldering_128x64/frame_0.png b/assets/dolphin/external/L2_Soldering_128x64/frame_0.png deleted file mode 100644 index bd20ae843..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_1.png b/assets/dolphin/external/L2_Soldering_128x64/frame_1.png deleted file mode 100644 index b9453d056..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_10.png b/assets/dolphin/external/L2_Soldering_128x64/frame_10.png deleted file mode 100644 index 0519e851d..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_10.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_2.png b/assets/dolphin/external/L2_Soldering_128x64/frame_2.png deleted file mode 100644 index 514241266..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_3.png b/assets/dolphin/external/L2_Soldering_128x64/frame_3.png deleted file mode 100644 index ba7ad99ae..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_4.png b/assets/dolphin/external/L2_Soldering_128x64/frame_4.png deleted file mode 100644 index ca696e4cf..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_5.png b/assets/dolphin/external/L2_Soldering_128x64/frame_5.png deleted file mode 100644 index aa7fc8ea0..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_5.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_6.png b/assets/dolphin/external/L2_Soldering_128x64/frame_6.png deleted file mode 100644 index 85d1e30c9..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_6.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_7.png b/assets/dolphin/external/L2_Soldering_128x64/frame_7.png deleted file mode 100644 index 5ef6820f9..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_7.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_8.png b/assets/dolphin/external/L2_Soldering_128x64/frame_8.png deleted file mode 100644 index 12bed1b4a..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_8.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_9.png b/assets/dolphin/external/L2_Soldering_128x64/frame_9.png deleted file mode 100644 index 46f9fd378..000000000 Binary files a/assets/dolphin/external/L2_Soldering_128x64/frame_9.png and /dev/null differ diff --git a/assets/dolphin/external/L2_Soldering_128x64/meta.txt b/assets/dolphin/external/L2_Soldering_128x64/meta.txt deleted file mode 100644 index b705bf623..000000000 --- a/assets/dolphin/external/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/dolphin/external/L3_FlipperMustache_128x64/frame_0.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_0.png new file mode 100644 index 000000000..6f058ed55 Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_1.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_1.png new file mode 100644 index 000000000..ab34e0fa9 Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_10.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_10.png new file mode 100644 index 000000000..382e161fa Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_11.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_11.png new file mode 100644 index 000000000..5638a1a27 Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_2.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_2.png new file mode 100644 index 000000000..0f39e356a Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_3.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_3.png new file mode 100644 index 000000000..fc5f2d2c4 Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_4.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_4.png new file mode 100644 index 000000000..bbab8543d Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_5.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_5.png new file mode 100644 index 000000000..dabadecab Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_6.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_6.png new file mode 100644 index 000000000..098c2f528 Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_7.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_7.png new file mode 100644 index 000000000..c3b82a23e Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_8.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_8.png new file mode 100644 index 000000000..8067df06e Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/frame_9.png b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_9.png new file mode 100644 index 000000000..03875fb30 Binary files /dev/null and b/assets/dolphin/external/L3_FlipperMustache_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L3_FlipperMustache_128x64/meta.txt b/assets/dolphin/external/L3_FlipperMustache_128x64/meta.txt new file mode 100644 index 000000000..8440c67d5 --- /dev/null +++ b/assets/dolphin/external/L3_FlipperMustache_128x64/meta.txt @@ -0,0 +1,34 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 10 +Active frames: 2 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 +Active cycles: 8 +Frame rate: 3 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 60 +Y: 30 +Text: Some hack\nwe made +AlignH: Left +AlignV: Center +StartFrame: 10 +EndFrame: 17 + +Slot: 0 +X: 60 +Y: 30 +Text: It's a firmware\nupgrade! +AlignH: Left +AlignV: Center +StartFrame: 18 +EndFrame: 25 + + diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png deleted file mode 100644 index 6fb7de66c..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png deleted file mode 100644 index eaf39cff4..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png deleted file mode 100644 index 1e3890a7b..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png deleted file mode 100644 index fc8aef13a..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png deleted file mode 100644 index 8174af0d1..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png deleted file mode 100644 index b5e75921a..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png deleted file mode 100644 index 6a239a1b6..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png deleted file mode 100644 index 8cec74395..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png deleted file mode 100644 index d034b0a53..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png deleted file mode 100644 index 472241e56..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png deleted file mode 100644 index db53a0bb4..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png deleted file mode 100644 index f2015a77d..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png deleted file mode 100644 index 39b8d5f8f..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png deleted file mode 100644 index 8b5e6f5ee..000000000 Binary files a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt b/assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt deleted file mode 100644 index 1d415b4b8..000000000 --- a/assets/dolphin/external/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/dolphin/external/L3_Lab_research_128x54/frame_0.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_0.png deleted file mode 100644 index c85bb77c4..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_0.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png deleted file mode 100644 index dedff6024..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png deleted file mode 100644 index 1a53ab75d..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png deleted file mode 100644 index 1768d50dd..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png deleted file mode 100644 index 2beb237d9..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png deleted file mode 100644 index c46dc3bab..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png deleted file mode 100644 index 01f07c68d..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png deleted file mode 100644 index 148fe4640..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png deleted file mode 100644 index 0d402b0db..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png deleted file mode 100644 index 6853e1d61..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png deleted file mode 100644 index 9a92ba8d8..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png deleted file mode 100644 index 910ae5852..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png deleted file mode 100644 index 0f681e6ab..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png deleted file mode 100644 index 0f391b75e..000000000 Binary files a/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png and /dev/null differ diff --git a/assets/dolphin/external/L3_Lab_research_128x54/meta.txt b/assets/dolphin/external/L3_Lab_research_128x54/meta.txt deleted file mode 100644 index e83a8b436..000000000 --- a/assets/dolphin/external/L3_Lab_research_128x54/meta.txt +++ /dev/null @@ -1,59 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 54 -Passive frames: 6 -Active frames: 8 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 71 -Y: 23 -Text: 7em!g!7j!\nVyP5?T- -AlignH: Left -AlignV: Center -StartFrame: 8 -EndFrame: 8 - -Slot: 0 -X: 71 -Y: 23 -Text: aUqF7sz!\n%9.mP5H -AlignH: Left -AlignV: Center -StartFrame: 9 -EndFrame: 9 - -Slot: 0 -X: 71 -Y: 23 -Text: 2%Kx2mV\nL8EyA84 -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 10 - -Slot: 0 -X: 71 -Y: 23 -Text: U7%cRXr\nvbBa!_W1 -AlignH: Left -AlignV: Center -StartFrame: 11 -EndFrame: 11 - -Slot: 0 -X: 71 -Y: 23 -Text: 5rm_[K%\n!!(U9d$tE -AlignH: Left -AlignV: Center -StartFrame: 12 -EndFrame: 12 diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_0.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_0.png new file mode 100644 index 000000000..9a494e199 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_0.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_1.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_1.png new file mode 100644 index 000000000..197f91281 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_1.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_10.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_10.png new file mode 100644 index 000000000..4467b9c9a Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_10.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_11.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_11.png new file mode 100644 index 000000000..f22773f20 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_11.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_12.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_12.png new file mode 100644 index 000000000..6dd18469b Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_12.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_13.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_13.png new file mode 100644 index 000000000..842bec2ed Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_13.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_14.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_14.png new file mode 100644 index 000000000..706fbe717 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_14.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_15.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_15.png new file mode 100644 index 000000000..6fa5a2ada Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_15.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_16.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_16.png new file mode 100644 index 000000000..380298283 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_16.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_17.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_17.png new file mode 100644 index 000000000..a936578fd Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_17.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_18.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_18.png new file mode 100644 index 000000000..23bfd352f Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_18.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_19.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_19.png new file mode 100644 index 000000000..7aa3762f0 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_19.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_2.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_2.png new file mode 100644 index 000000000..bb80ea33e Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_2.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_20.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_20.png new file mode 100644 index 000000000..bf7e09ec1 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_20.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_21.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_21.png new file mode 100644 index 000000000..eb657d3c6 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_21.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_22.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_22.png new file mode 100644 index 000000000..ac75c38f6 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_22.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_23.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_23.png new file mode 100644 index 000000000..8111a5d8a Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_23.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_24.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_24.png new file mode 100644 index 000000000..1774bd5c7 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_24.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_25.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_25.png new file mode 100644 index 000000000..11e24b271 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_25.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_26.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_26.png new file mode 100644 index 000000000..2b9857523 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_26.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_27.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_27.png new file mode 100644 index 000000000..46f4685a7 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_27.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_28.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_28.png new file mode 100644 index 000000000..43c39460d Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_28.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_29.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_29.png new file mode 100644 index 000000000..6645f1ffe Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_29.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_3.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_3.png new file mode 100644 index 000000000..2ce685140 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_3.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_30.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_30.png new file mode 100644 index 000000000..61a1151b0 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_30.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_31.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_31.png new file mode 100644 index 000000000..2f66d4885 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_31.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_32.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_32.png new file mode 100644 index 000000000..fc15964dd Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_32.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_33.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_33.png new file mode 100644 index 000000000..d0392432b Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_33.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_34.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_34.png new file mode 100644 index 000000000..31c3582d3 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_34.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_35.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_35.png new file mode 100644 index 000000000..3bc9219c0 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_35.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_36.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_36.png new file mode 100644 index 000000000..87402cafd Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_36.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_37.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_37.png new file mode 100644 index 000000000..7332421b1 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_37.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_38.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_38.png new file mode 100644 index 000000000..2c7181f77 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_38.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_39.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_39.png new file mode 100644 index 000000000..44cd04e16 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_39.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_4.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_4.png new file mode 100644 index 000000000..b2e4a2d8c Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_4.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_40.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_40.png new file mode 100644 index 000000000..acdda1376 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_40.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_41.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_41.png new file mode 100644 index 000000000..b946e9c79 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_41.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_42.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_42.png new file mode 100644 index 000000000..f001cfb44 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_42.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_43.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_43.png new file mode 100644 index 000000000..a81dd6549 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_43.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_44.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_44.png new file mode 100644 index 000000000..802967665 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_44.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_45.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_45.png new file mode 100644 index 000000000..7193364ee Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_45.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_46.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_46.png new file mode 100644 index 000000000..42e44c79f Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_46.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_47.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_47.png new file mode 100644 index 000000000..cd43b8dd0 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_47.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_48.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_48.png new file mode 100644 index 000000000..25c9ada3c Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_48.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_49.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_49.png new file mode 100644 index 000000000..353ea2dd3 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_49.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_5.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_5.png new file mode 100644 index 000000000..397c1af52 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_5.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_6.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_6.png new file mode 100644 index 000000000..5b46eca2c Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_6.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_7.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_7.png new file mode 100644 index 000000000..e0ff86f75 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_7.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_8.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_8.png new file mode 100644 index 000000000..365707573 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_8.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/frame_9.png b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_9.png new file mode 100644 index 000000000..9e9c4068c Binary files /dev/null and b/assets/dolphin/external/Sasquach_Blaster_128x64/frame_9.png differ diff --git a/assets/dolphin/external/Sasquach_Blaster_128x64/meta.txt b/assets/dolphin/external/Sasquach_Blaster_128x64/meta.txt new file mode 100644 index 000000000..63d6e4750 --- /dev/null +++ b/assets/dolphin/external/Sasquach_Blaster_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 10 +Active frames: 40 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 +Active cycles: 1 +Frame rate: 6 +Duration: 3600 +Active cooldown: 4 + +Bubble slots: 0 diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_0.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_0.png new file mode 100644 index 000000000..cdec5596d Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_0.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_1.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_1.png new file mode 100644 index 000000000..01818b6a6 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_1.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_10.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_10.png new file mode 100644 index 000000000..0e15ba631 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_10.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_11.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_11.png new file mode 100644 index 000000000..b3ad788c0 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_11.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_12.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_12.png new file mode 100644 index 000000000..6adc432a7 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_12.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_13.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_13.png new file mode 100644 index 000000000..930327f3f Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_13.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_14.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_14.png new file mode 100644 index 000000000..0bda5f482 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_14.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_15.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_15.png new file mode 100644 index 000000000..f736e4bdc Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_15.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_16.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_16.png new file mode 100644 index 000000000..30594b5f2 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_16.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_17.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_17.png new file mode 100644 index 000000000..568893834 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_17.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_18.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_18.png new file mode 100644 index 000000000..5a9cbd9f8 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_18.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_19.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_19.png new file mode 100644 index 000000000..3c4f222f6 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_19.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_2.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_2.png new file mode 100644 index 000000000..e5b5cac21 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_2.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_20.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_20.png new file mode 100644 index 000000000..fd3759ed4 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_20.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_21.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_21.png new file mode 100644 index 000000000..191eb20ae Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_21.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_22.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_22.png new file mode 100644 index 000000000..21dff779e Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_22.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_23.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_23.png new file mode 100644 index 000000000..9ff2aae69 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_23.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_24.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_24.png new file mode 100644 index 000000000..da7fea8d1 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_24.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_25.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_25.png new file mode 100644 index 000000000..0e3a314c9 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_25.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_26.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_26.png new file mode 100644 index 000000000..f8890e09d Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_26.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_27.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_27.png new file mode 100644 index 000000000..611012fea Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_27.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_28.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_28.png new file mode 100644 index 000000000..83a0faa0c Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_28.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_29.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_29.png new file mode 100644 index 000000000..aabb57287 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_29.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_3.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_3.png new file mode 100644 index 000000000..485f1ea5b Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_3.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_30.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_30.png new file mode 100644 index 000000000..6fe6ad15d Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_30.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_31.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_31.png new file mode 100644 index 000000000..1f4958e35 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_31.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_32.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_32.png new file mode 100644 index 000000000..878689820 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_32.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_33.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_33.png new file mode 100644 index 000000000..608957f77 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_33.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_34.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_34.png new file mode 100644 index 000000000..9e0e5ce7e Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_34.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_35.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_35.png new file mode 100644 index 000000000..d3eed2554 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_35.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_36.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_36.png new file mode 100644 index 000000000..9e841b0f5 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_36.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_37.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_37.png new file mode 100644 index 000000000..649f0d405 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_37.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_38.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_38.png new file mode 100644 index 000000000..5f67c3653 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_38.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_39.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_39.png new file mode 100644 index 000000000..6bc64a134 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_39.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_4.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_4.png new file mode 100644 index 000000000..0d5178db7 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_4.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_40.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_40.png new file mode 100644 index 000000000..8eda64c64 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_40.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_41.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_41.png new file mode 100644 index 000000000..f145f9d80 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_41.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_42.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_42.png new file mode 100644 index 000000000..5d7ad776d Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_42.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_43.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_43.png new file mode 100644 index 000000000..5ec728639 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_43.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_44.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_44.png new file mode 100644 index 000000000..cd8e8139c Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_44.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_45.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_45.png new file mode 100644 index 000000000..0ea0af39c Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_45.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_46.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_46.png new file mode 100644 index 000000000..5f10dbbfc Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_46.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_5.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_5.png new file mode 100644 index 000000000..ecc510827 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_5.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_6.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_6.png new file mode 100644 index 000000000..65b403718 Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_6.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_7.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_7.png new file mode 100644 index 000000000..8eeed972c Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_7.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_8.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_8.png new file mode 100644 index 000000000..c73723c2c Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_8.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_9.png b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_9.png new file mode 100644 index 000000000..677ef566f Binary files /dev/null and b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/frame_9.png differ diff --git a/assets/dolphin/external/Sasquach_CloudG0ku_128x64/meta.txt b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/meta.txt new file mode 100644 index 000000000..e780d7e95 --- /dev/null +++ b/assets/dolphin/external/Sasquach_CloudG0ku_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 10 +Active frames: 38 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 46 +Active cycles: 1 +Frame rate: 5 +Duration: 3600 +Active cooldown: 1 + +Bubble slots: 0 diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_0.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_0.png new file mode 100644 index 000000000..5273ad9f4 Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_0.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_1.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_1.png new file mode 100644 index 000000000..57649b861 Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_1.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_10.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_10.png new file mode 100644 index 000000000..37d18fd5a Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_10.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_2.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_2.png new file mode 100644 index 000000000..df0ee8058 Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_2.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_3.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_3.png new file mode 100644 index 000000000..5a2badb34 Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_3.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_4.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_4.png new file mode 100644 index 000000000..86b3020a0 Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_4.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_5.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_5.png new file mode 100644 index 000000000..57124a3fd Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_5.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_6.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_6.png new file mode 100644 index 000000000..91926bb94 Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_6.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_7.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_7.png new file mode 100644 index 000000000..a1146ced6 Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_7.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_8.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_8.png new file mode 100644 index 000000000..1af0ce93b Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_8.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_9.png b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_9.png new file mode 100644 index 000000000..b8ff0e6fa Binary files /dev/null and b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/frame_9.png differ diff --git a/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/meta.txt b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/meta.txt new file mode 100644 index 000000000..b8a080c97 --- /dev/null +++ b/assets/dolphin/external/Sasquach_D1g1talRa1n_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 11 +Active frames: 0 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 +Active cycles: 0 +Frame rate: 7 +Duration: 3600 +Active cooldown: 0 + +Bubble slots: 0 diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_0.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_0.png new file mode 100644 index 000000000..18f38e267 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_0.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_1.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_1.png new file mode 100644 index 000000000..c47cadc0f Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_1.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_10.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_10.png new file mode 100644 index 000000000..a645b9cdd Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_10.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_11.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_11.png new file mode 100644 index 000000000..d0a69418c Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_11.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_12.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_12.png new file mode 100644 index 000000000..4983b12cb Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_12.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_13.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_13.png new file mode 100644 index 000000000..f3135f3e0 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_13.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_14.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_14.png new file mode 100644 index 000000000..bb52a89d7 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_14.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_15.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_15.png new file mode 100644 index 000000000..04420b51d Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_15.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_16.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_16.png new file mode 100644 index 000000000..56facb9a2 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_16.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_17.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_17.png new file mode 100644 index 000000000..2d486ecba Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_17.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_18.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_18.png new file mode 100644 index 000000000..6aef0a443 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_18.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_19.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_19.png new file mode 100644 index 000000000..2b8cab0d3 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_19.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_2.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_2.png new file mode 100644 index 000000000..0e6ddacad Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_2.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_20.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_20.png new file mode 100644 index 000000000..b5fa71646 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_20.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_21.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_21.png new file mode 100644 index 000000000..da14ff61d Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_21.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_22.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_22.png new file mode 100644 index 000000000..eb1820ab3 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_22.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_23.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_23.png new file mode 100644 index 000000000..5f8ce6aa7 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_23.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_24.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_24.png new file mode 100644 index 000000000..34104e566 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_24.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_25.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_25.png new file mode 100644 index 000000000..ff7175f1c Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_25.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_26.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_26.png new file mode 100644 index 000000000..449195e55 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_26.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_27.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_27.png new file mode 100644 index 000000000..34931ccac Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_27.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_28.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_28.png new file mode 100644 index 000000000..a483ca498 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_28.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_29.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_29.png new file mode 100644 index 000000000..42b4c3c93 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_29.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_3.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_3.png new file mode 100644 index 000000000..a417ee476 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_3.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_4.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_4.png new file mode 100644 index 000000000..99fae827b Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_4.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_5.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_5.png new file mode 100644 index 000000000..f1631a8c4 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_5.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_6.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_6.png new file mode 100644 index 000000000..1bc4bd9d7 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_6.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_7.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_7.png new file mode 100644 index 000000000..db3c8d50e Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_7.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_8.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_8.png new file mode 100644 index 000000000..23f0b56d2 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_8.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/frame_9.png b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_9.png new file mode 100644 index 000000000..d84ab6684 Binary files /dev/null and b/assets/dolphin/external/Sasquach_Narut0_128x64/frame_9.png differ diff --git a/assets/dolphin/external/Sasquach_Narut0_128x64/meta.txt b/assets/dolphin/external/Sasquach_Narut0_128x64/meta.txt new file mode 100644 index 000000000..1c4b1c117 --- /dev/null +++ b/assets/dolphin/external/Sasquach_Narut0_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 10 +Active frames: 20 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 +Active cycles: 1 +Frame rate: 6 +Duration: 3600 +Active cooldown: 4 + +Bubble slots: 0 diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_0.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_0.png new file mode 100644 index 000000000..aa66a9921 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_0.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_1.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_1.png new file mode 100644 index 000000000..c65535685 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_1.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_10.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_10.png new file mode 100644 index 000000000..dd27449f5 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_10.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_11.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_11.png new file mode 100644 index 000000000..a2f2136a2 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_11.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_12.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_12.png new file mode 100644 index 000000000..b5dbb2279 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_12.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_13.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_13.png new file mode 100644 index 000000000..cb93f7af9 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_13.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_14.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_14.png new file mode 100644 index 000000000..e937dc6ea Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_14.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_15.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_15.png new file mode 100644 index 000000000..9e7849137 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_15.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_16.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_16.png new file mode 100644 index 000000000..cc68643c4 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_16.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_17.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_17.png new file mode 100644 index 000000000..ae46a473f Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_17.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_18.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_18.png new file mode 100644 index 000000000..1e0428cb4 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_18.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_19.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_19.png new file mode 100644 index 000000000..bb9f3471a Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_19.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_2.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_2.png new file mode 100644 index 000000000..61682d0d3 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_2.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_20.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_20.png new file mode 100644 index 000000000..a7f598223 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_20.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_21.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_21.png new file mode 100644 index 000000000..cf9d8f547 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_21.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_22.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_22.png new file mode 100644 index 000000000..c9f412e39 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_22.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_23.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_23.png new file mode 100644 index 000000000..cf9d8f547 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_23.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_24.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_24.png new file mode 100644 index 000000000..403188095 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_24.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_25.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_25.png new file mode 100644 index 000000000..3622e59b8 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_25.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_26.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_26.png new file mode 100644 index 000000000..593d08d1f Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_26.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_27.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_27.png new file mode 100644 index 000000000..1a8c9376f Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_27.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_28.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_28.png new file mode 100644 index 000000000..9c97ced6a Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_28.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_29.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_29.png new file mode 100644 index 000000000..4f754fcdb Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_29.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_3.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_3.png new file mode 100644 index 000000000..4d881bc21 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_3.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_30.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_30.png new file mode 100644 index 000000000..a5b1b40f9 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_30.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_31.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_31.png new file mode 100644 index 000000000..0263ea8fc Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_31.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_32.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_32.png new file mode 100644 index 000000000..036bf96bd Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_32.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_33.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_33.png new file mode 100644 index 000000000..f477dc3bc Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_33.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_34.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_34.png new file mode 100644 index 000000000..1320d8be9 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_34.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_35.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_35.png new file mode 100644 index 000000000..fa998160b Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_35.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_36.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_36.png new file mode 100644 index 000000000..b19194efa Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_36.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_37.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_37.png new file mode 100644 index 000000000..249142655 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_37.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_38.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_38.png new file mode 100644 index 000000000..49705dc80 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_38.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_4.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_4.png new file mode 100644 index 000000000..05042411a Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_4.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_5.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_5.png new file mode 100644 index 000000000..91c2cc2ae Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_5.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_6.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_6.png new file mode 100644 index 000000000..2a8df7f1b Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_6.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_7.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_7.png new file mode 100644 index 000000000..95b53497b Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_7.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_8.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_8.png new file mode 100644 index 000000000..787af7d73 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_8.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/frame_9.png b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_9.png new file mode 100644 index 000000000..f56e685b9 Binary files /dev/null and b/assets/dolphin/external/Sasquach_RMCF_128x64/frame_9.png differ diff --git a/assets/dolphin/external/Sasquach_RMCF_128x64/meta.txt b/assets/dolphin/external/Sasquach_RMCF_128x64/meta.txt new file mode 100644 index 000000000..ffc01409d --- /dev/null +++ b/assets/dolphin/external/Sasquach_RMCF_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 9 +Active frames: 99 +Frames order: 0 1 2 3 4 3 2 1 0 1 2 3 4 5 6 7 8 9 10 11 11 11 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 13 14 15 16 17 18 17 18 17 18 19 20 19 20 19 20 19 20 19 20 21 22 21 22 22 22 23 24 25 26 27 28 29 30 31 32 33 0 34 35 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 37 38 +Active cycles: 1 +Frame rate: 4 +Duration: 3600 +Active cooldown: 4 + +Bubble slots: 1 + +Slot: 0 +X: 33 +Y: 35 +Text: I Know \nKung Fu +AlignH: Left +AlignV: Top +StartFrame: 14 +EndFrame: 20 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 35b9a7912..245c109d2 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -1,65 +1,93 @@ Filetype: Flipper Animation Manifest Version: 1 -Name: L1_Waves_128x50 +Name: Kuronons_RMCFW_128x64 Min butthurt: 0 -Max butthurt: 5 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: L1_Read_books_128x64 -Min butthurt: 0 -Max butthurt: 8 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: L2_Hacking_pc_128x64 -Min butthurt: 0 -Max butthurt: 8 +Max butthurt: 14 Min level: 2 Max level: 30 -Weight: 3 +Weight: 7 -Name: L1_Painting_128x64 +Name: Sasquach_Blaster_128x64 Min butthurt: 0 -Max butthurt: 7 +Max butthurt: 14 Min level: 1 -Max level: 3 -Weight: 6 - -Name: L3_Hijack_radio_128x64 -Min butthurt: 0 -Max butthurt: 8 -Min level: 3 Max level: 30 -Weight: 3 +Weight: 7 -Name: L3_Lab_research_128x54 +Name: Sasquach_CloudG0ku_128x64 Min butthurt: 0 -Max butthurt: 10 -Min level: 3 +Max butthurt: 14 +Min level: 1 Max level: 30 -Weight: 3 +Weight: 7 -Name: L3_Fireplace_128x64 +Name: Sasquach_D1g1talRa1n_128x64 Min butthurt: 0 -Max butthurt: 13 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Sasquach_Narut0_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Sasquach_RMCF_128x64 +Min butthurt: 0 +Max butthurt: 14 Min level: 2 Max level: 30 -Weight: 3 +Weight: 7 + +Name: L1_Purple_rain_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 Name: L2_FlipperCity_128x64 Min butthurt: 0 -Max butthurt: 13 -Min level: 2 -Max level: 3 -Weight: 3 - -Name: L2_Soldering_128x64 -Min butthurt: 0 -Max butthurt: 10 +Max butthurt: 14 Min level: 2 Max level: 30 -Weight: 3 +Weight: 7 + +Name: L3_FlipperMustache_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 3 +Max level: 30 +Weight: 7 + +Name: L3_Fireplace_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 3 +Max level: 30 +Weight: 7 + +Name: L1_Mods_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: L1_Painting_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: L1_Halloween_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 5 diff --git a/assets/dolphin/internal/manifest.txt b/assets/dolphin/internal/manifest.txt index 11b3a8a18..b17ad7577 100644 --- a/assets/dolphin/internal/manifest.txt +++ b/assets/dolphin/internal/manifest.txt @@ -7,7 +7,7 @@ Min butthurt: 0 Max butthurt: 14 Min level: 1 Max level: 30 -Weight: 3 +Weight: 1 # Animation 2 Name: L1_BadBattery_128x47 diff --git a/assets/icons/Infrared/Rotate_25x27.png b/assets/icons/Infrared/Rotate_25x27.png index 0c62e9dc1..648634a09 100644 Binary files a/assets/icons/Infrared/Rotate_25x27.png and b/assets/icons/Infrared/Rotate_25x27.png differ diff --git a/assets/icons/Infrared/Rotate_hvr_25x27.png b/assets/icons/Infrared/Rotate_hvr_25x27.png index d2ab0f3fc..a2b5cf93d 100644 Binary files a/assets/icons/Infrared/Rotate_hvr_25x27.png and b/assets/icons/Infrared/Rotate_hvr_25x27.png differ diff --git a/assets/icons/Infrared/Timer_25x27.png b/assets/icons/Infrared/Timer_25x27.png index 5ce468198..2f1853a34 100644 Binary files a/assets/icons/Infrared/Timer_25x27.png and b/assets/icons/Infrared/Timer_25x27.png differ diff --git a/assets/icons/Infrared/Timer_hvr_25x27.png b/assets/icons/Infrared/Timer_hvr_25x27.png index f6959c244..d4dffa544 100644 Binary files a/assets/icons/Infrared/Timer_hvr_25x27.png and b/assets/icons/Infrared/Timer_hvr_25x27.png differ diff --git a/assets/icons/StatusBar/Alert_9x8.png b/assets/icons/StatusBar/Alert_9x8.png new file mode 100644 index 000000000..d03f107ef Binary files /dev/null and b/assets/icons/StatusBar/Alert_9x8.png differ diff --git a/assets/icons/StatusBar/Hidden_window_9x8.png b/assets/icons/StatusBar/Hidden_window_9x8.png new file mode 100644 index 000000000..d6fc2b326 Binary files /dev/null and b/assets/icons/StatusBar/Hidden_window_9x8.png differ diff --git a/assets/icons/SubGhz/Dynamic_9x7.png b/assets/icons/SubGhz/Dynamic_9x7.png new file mode 100644 index 000000000..50922d4fb Binary files /dev/null and b/assets/icons/SubGhz/Dynamic_9x7.png differ diff --git a/assets/icons/SubGhz/Raw_9x7.png b/assets/icons/SubGhz/Raw_9x7.png new file mode 100644 index 000000000..39a6d0386 Binary files /dev/null and b/assets/icons/SubGhz/Raw_9x7.png differ diff --git a/assets/icons/SubGhz/Static_9x7.png b/assets/icons/SubGhz/Static_9x7.png new file mode 100644 index 000000000..dad4833e3 Binary files /dev/null and b/assets/icons/SubGhz/Static_9x7.png differ diff --git a/assets/protobuf b/assets/protobuf index 6727eaf28..e5af96e08 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 6727eaf287db077dcd28719cd764f5804712223e +Subproject commit e5af96e08fea8351898f7b8c6d1e34ce5fd6cdef diff --git a/assets/resources/dolphin/Allen_128x64/frame_0.bm b/assets/resources/dolphin/Allen_128x64/frame_0.bm deleted file mode 100644 index bacc1c0e9..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_1.bm b/assets/resources/dolphin/Allen_128x64/frame_1.bm deleted file mode 100644 index fbabe2d5f..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_10.bm b/assets/resources/dolphin/Allen_128x64/frame_10.bm deleted file mode 100644 index 2a47ecc21..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_11.bm b/assets/resources/dolphin/Allen_128x64/frame_11.bm deleted file mode 100644 index 52531dce0..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_12.bm b/assets/resources/dolphin/Allen_128x64/frame_12.bm deleted file mode 100644 index 61a4fc883..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_13.bm b/assets/resources/dolphin/Allen_128x64/frame_13.bm deleted file mode 100644 index 48b008f4f..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_14.bm b/assets/resources/dolphin/Allen_128x64/frame_14.bm deleted file mode 100644 index 5c2b56e60..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_15.bm b/assets/resources/dolphin/Allen_128x64/frame_15.bm deleted file mode 100644 index f6482d292..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_16.bm b/assets/resources/dolphin/Allen_128x64/frame_16.bm deleted file mode 100644 index 313cb0a77..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_17.bm b/assets/resources/dolphin/Allen_128x64/frame_17.bm deleted file mode 100644 index eaff0733e..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_2.bm b/assets/resources/dolphin/Allen_128x64/frame_2.bm deleted file mode 100644 index 6037c884d..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_3.bm b/assets/resources/dolphin/Allen_128x64/frame_3.bm deleted file mode 100644 index a5695d6fc..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_4.bm b/assets/resources/dolphin/Allen_128x64/frame_4.bm deleted file mode 100644 index d955b9dc5..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_5.bm b/assets/resources/dolphin/Allen_128x64/frame_5.bm deleted file mode 100644 index 1c67d5808..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_6.bm b/assets/resources/dolphin/Allen_128x64/frame_6.bm deleted file mode 100644 index b92226915..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_7.bm b/assets/resources/dolphin/Allen_128x64/frame_7.bm deleted file mode 100644 index 1d98a0b02..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_8.bm b/assets/resources/dolphin/Allen_128x64/frame_8.bm deleted file mode 100644 index 71da3e1cf..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/frame_9.bm b/assets/resources/dolphin/Allen_128x64/frame_9.bm deleted file mode 100644 index 90b01a2b2..000000000 Binary files a/assets/resources/dolphin/Allen_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Allen_128x64/meta.txt b/assets/resources/dolphin/Allen_128x64/meta.txt deleted file mode 100644 index debd9565a..000000000 --- a/assets/resources/dolphin/Allen_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 17 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -Active cycles: 1 -Frame rate: 5 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/frame_0.bm b/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/frame_0.bm deleted file mode 100644 index a598fba6e..000000000 Binary files a/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/frame_1.bm b/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/frame_1.bm deleted file mode 100644 index 57d4b126d..000000000 Binary files a/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/meta.txt b/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/meta.txt deleted file mode 100644 index 77cf60869..000000000 --- a/assets/resources/dolphin/FelixTwoTone_Veemon_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 5 -Active frames: 5 -Frames order: 0 1 0 1 0 1 0 1 0 1 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_0.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_0.bm deleted file mode 100644 index 0d81c6251..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_1.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_1.bm deleted file mode 100644 index 9f96adcd7..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_10.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_10.bm deleted file mode 100644 index f29d83372..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_11.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_11.bm deleted file mode 100644 index a74c5958d..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_12.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_12.bm deleted file mode 100644 index 7332247b0..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_13.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_13.bm deleted file mode 100644 index 16a39fc12..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_14.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_14.bm deleted file mode 100644 index 56b0d9550..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_15.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_15.bm deleted file mode 100644 index 90ded0204..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_16.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_16.bm deleted file mode 100644 index c90e0ae23..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_17.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_17.bm deleted file mode 100644 index cf008485d..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_18.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_18.bm deleted file mode 100644 index 1a8308040..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_19.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_19.bm deleted file mode 100644 index 9dc687ad9..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_2.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_2.bm deleted file mode 100644 index 396f007c7..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_20.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_20.bm deleted file mode 100644 index 60a0ad88b..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_21.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_21.bm deleted file mode 100644 index 4c1fe7f5c..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_22.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_22.bm deleted file mode 100644 index 2e9624360..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_23.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_23.bm deleted file mode 100644 index cbbdbca3f..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_24.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_24.bm deleted file mode 100644 index 3113dfeff..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_25.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_25.bm deleted file mode 100644 index ac6da448b..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_26.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_26.bm deleted file mode 100644 index c1e21a3d0..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_27.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_27.bm deleted file mode 100644 index 31f8bff01..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_28.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_28.bm deleted file mode 100644 index 8bc7073ef..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_29.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_29.bm deleted file mode 100644 index abf1fc6dd..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_3.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_3.bm deleted file mode 100644 index 354a3074b..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_30.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_30.bm deleted file mode 100644 index a1b228314..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_31.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_31.bm deleted file mode 100644 index 9a1303eea..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_32.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_32.bm deleted file mode 100644 index bd57f58e5..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_33.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_33.bm deleted file mode 100644 index 3ec25295d..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_34.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_34.bm deleted file mode 100644 index 49173f4a0..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_35.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_35.bm deleted file mode 100644 index 07bc6bab8..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_36.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_36.bm deleted file mode 100644 index 043690c42..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_37.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_37.bm deleted file mode 100644 index 057d47548..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_38.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_38.bm deleted file mode 100644 index 304e139c2..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_39.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_39.bm deleted file mode 100644 index f0331a19a..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_4.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_4.bm deleted file mode 100644 index 31447f6c2..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_5.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_5.bm deleted file mode 100644 index 402783063..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_6.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_6.bm deleted file mode 100644 index 98a30f722..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_7.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_7.bm deleted file mode 100644 index bc79ab225..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_8.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_8.bm deleted file mode 100644 index 1b63784c5..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/frame_9.bm b/assets/resources/dolphin/Haseo_80s_128x64/frame_9.bm deleted file mode 100644 index 1ac58cd97..000000000 Binary files a/assets/resources/dolphin/Haseo_80s_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_80s_128x64/meta.txt b/assets/resources/dolphin/Haseo_80s_128x64/meta.txt deleted file mode 100644 index ffd3b34f2..000000000 --- a/assets/resources/dolphin/Haseo_80s_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 39 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_0.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_0.bm deleted file mode 100644 index 3bce1a327..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_1.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_1.bm deleted file mode 100644 index f89b879dc..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_10.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_10.bm deleted file mode 100644 index f5227438d..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_11.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_11.bm deleted file mode 100644 index d29ca5a03..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_12.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_12.bm deleted file mode 100644 index c91141f76..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_13.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_13.bm deleted file mode 100644 index 6155277db..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_14.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_14.bm deleted file mode 100644 index 9d4b56b9a..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_15.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_15.bm deleted file mode 100644 index 1522e6e4f..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_16.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_16.bm deleted file mode 100644 index 694e2db9d..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_17.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_17.bm deleted file mode 100644 index 20dbb1468..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_18.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_18.bm deleted file mode 100644 index 9120d336c..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_19.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_19.bm deleted file mode 100644 index 336041e8c..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_2.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_2.bm deleted file mode 100644 index 42538f31f..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_20.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_20.bm deleted file mode 100644 index d2291872e..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_21.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_21.bm deleted file mode 100644 index e6bd4bcf5..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_22.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_22.bm deleted file mode 100644 index 6f692ac01..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_23.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_23.bm deleted file mode 100644 index 54be972e0..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_24.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_24.bm deleted file mode 100644 index a1219847c..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_25.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_25.bm deleted file mode 100644 index 0a4f09c05..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_26.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_26.bm deleted file mode 100644 index 2a7296df7..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_27.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_27.bm deleted file mode 100644 index 3c3f5e768..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_28.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_28.bm deleted file mode 100644 index a5280b881..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_29.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_29.bm deleted file mode 100644 index 337b652e5..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_3.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_3.bm deleted file mode 100644 index c87d99960..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_30.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_30.bm deleted file mode 100644 index d4e752b63..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_31.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_31.bm deleted file mode 100644 index 917e1857e..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_32.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_32.bm deleted file mode 100644 index 4bb44e21e..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_33.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_33.bm deleted file mode 100644 index d8d6dbb64..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_34.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_34.bm deleted file mode 100644 index a0c1be273..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_35.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_35.bm deleted file mode 100644 index b41e55f62..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_36.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_36.bm deleted file mode 100644 index 9904957f1..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_37.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_37.bm deleted file mode 100644 index aa762921e..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_38.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_38.bm deleted file mode 100644 index cba93a149..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_39.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_39.bm deleted file mode 100644 index 59f1b2960..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_4.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_4.bm deleted file mode 100644 index 60535f4f8..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_40.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_40.bm deleted file mode 100644 index 78f3f9d07..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_41.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_41.bm deleted file mode 100644 index 2fa08a16f..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_42.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_42.bm deleted file mode 100644 index 7149ad306..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_43.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_43.bm deleted file mode 100644 index 50d85d7b2..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_44.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_44.bm deleted file mode 100644 index 80c34c57d..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_45.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_45.bm deleted file mode 100644 index 66ffd6770..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_46.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_46.bm deleted file mode 100644 index 13410bc72..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_47.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_47.bm deleted file mode 100644 index ec2a19f19..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_48.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_48.bm deleted file mode 100644 index 280cb78b4..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_49.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_49.bm deleted file mode 100644 index 0b075b9a4..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_5.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_5.bm deleted file mode 100644 index 7450fad9e..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_50.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_50.bm deleted file mode 100644 index e8733e93c..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_50.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_51.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_51.bm deleted file mode 100644 index d911b2ff9..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_51.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_52.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_52.bm deleted file mode 100644 index 01bf91190..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_52.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_53.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_53.bm deleted file mode 100644 index aeed2aa4d..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_53.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_54.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_54.bm deleted file mode 100644 index d98234092..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_54.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_55.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_55.bm deleted file mode 100644 index c1ed87000..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_55.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_56.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_56.bm deleted file mode 100644 index 4faa26ae0..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_56.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_57.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_57.bm deleted file mode 100644 index 723a16a4b..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_57.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_58.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_58.bm deleted file mode 100644 index ab6c4a12e..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_58.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_6.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_6.bm deleted file mode 100644 index b5873cb55..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_7.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_7.bm deleted file mode 100644 index 1f35b645f..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_8.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_8.bm deleted file mode 100644 index e5b868e18..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_9.bm b/assets/resources/dolphin/Haseo_G0ku_128x64/frame_9.bm deleted file mode 100644 index 1432b15f3..000000000 Binary files a/assets/resources/dolphin/Haseo_G0ku_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_G0ku_128x64/meta.txt b/assets/resources/dolphin/Haseo_G0ku_128x64/meta.txt deleted file mode 100644 index 62053bbf2..000000000 --- a/assets/resources/dolphin/Haseo_G0ku_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 58 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_0.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_0.bm deleted file mode 100644 index d69074438..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_1.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_1.bm deleted file mode 100644 index 5236fcf9b..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_10.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_10.bm deleted file mode 100644 index ce5d06566..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_11.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_11.bm deleted file mode 100644 index 5ed66fcd9..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_12.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_12.bm deleted file mode 100644 index 8629f13ef..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_13.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_13.bm deleted file mode 100644 index 064dc354e..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_14.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_14.bm deleted file mode 100644 index 9a4e5cf3d..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_15.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_15.bm deleted file mode 100644 index 0ac50256e..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_16.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_16.bm deleted file mode 100644 index b0ef3907b..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_17.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_17.bm deleted file mode 100644 index 2b904f404..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_18.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_18.bm deleted file mode 100644 index 120461485..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_19.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_19.bm deleted file mode 100644 index abc62ae49..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_2.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_2.bm deleted file mode 100644 index ff5f1cd78..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_20.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_20.bm deleted file mode 100644 index 82be0ae28..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_3.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_3.bm deleted file mode 100644 index a37535e80..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_4.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_4.bm deleted file mode 100644 index caba9cee0..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_5.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_5.bm deleted file mode 100644 index f52042511..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_6.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_6.bm deleted file mode 100644 index cc09f33d3..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_7.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_7.bm deleted file mode 100644 index c6b5d78e9..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_8.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_8.bm deleted file mode 100644 index 7f2cdd8f7..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_9.bm b/assets/resources/dolphin/Haseo_Lufy_128x64/frame_9.bm deleted file mode 100644 index 7f1de4863..000000000 Binary files a/assets/resources/dolphin/Haseo_Lufy_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Lufy_128x64/meta.txt b/assets/resources/dolphin/Haseo_Lufy_128x64/meta.txt deleted file mode 100644 index 805fc8ec9..000000000 --- a/assets/resources/dolphin/Haseo_Lufy_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 20 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_0.bm b/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_0.bm deleted file mode 100644 index 42451b09e..000000000 Binary files a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_1.bm b/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_1.bm deleted file mode 100644 index 865ace8d5..000000000 Binary files a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_2.bm b/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_2.bm deleted file mode 100644 index 0f179477f..000000000 Binary files a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_3.bm b/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_3.bm deleted file mode 100644 index 42451b09e..000000000 Binary files a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_4.bm b/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_4.bm deleted file mode 100644 index 865ace8d5..000000000 Binary files a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_5.bm b/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_5.bm deleted file mode 100644 index 0f179477f..000000000 Binary files a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_6.bm b/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_6.bm deleted file mode 100644 index 865ace8d5..000000000 Binary files a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_7.bm b/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_7.bm deleted file mode 100644 index 42451b09e..000000000 Binary files a/assets/resources/dolphin/Haseo_Mug1wara_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Haseo_Mug1wara_128x64/meta.txt b/assets/resources/dolphin/Haseo_Mug1wara_128x64/meta.txt deleted file mode 100644 index 471aaf9df..000000000 --- a/assets/resources/dolphin/Haseo_Mug1wara_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 7 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/Kuronons_RMCFW_128x64/meta.txt b/assets/resources/dolphin/Kuronons_RMCFW_128x64/meta.txt index 3154d0aef..aa9584f03 100644 --- a/assets/resources/dolphin/Kuronons_RMCFW_128x64/meta.txt +++ b/assets/resources/dolphin/Kuronons_RMCFW_128x64/meta.txt @@ -3,12 +3,12 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 54 -Active frames: 0 -Frames order: 0 0 0 0 1 1 1 2 2 3 4 4 5 6 7 7 8 8 9 10 10 10 11 11 11 11 11 11 12 12 13 13 14 14 15 15 15 15 15 16 16 17 18 18 18 18 18 18 18 18 18 18 18 18 -Active cycles: 0 +Passive frames: 10 +Active frames: 42 +Frames order: 11 11 12 13 14 15 14 13 12 11 0 1 1 1 2 2 3 4 4 5 6 7 7 8 8 9 10 10 10 11 11 11 11 11 11 12 12 13 13 14 14 15 15 15 15 15 16 16 17 18 18 18 +Active cycles: 1 Frame rate: 6 Duration: 3600 -Active cooldown: 0 +Active cooldown: 4 Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_0.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_0.bm deleted file mode 100644 index 0e53bdb87..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_1.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_1.bm deleted file mode 100644 index 380295fbf..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_10.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_10.bm deleted file mode 100644 index ebf194a0b..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_11.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_11.bm deleted file mode 100644 index 80f91e7d0..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_12.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_12.bm deleted file mode 100644 index 56cff026d..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_13.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_13.bm deleted file mode 100644 index 748784bd8..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_14.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_14.bm deleted file mode 100644 index 3d1d871bd..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_15.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_15.bm deleted file mode 100644 index 7215adb8b..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_16.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_16.bm deleted file mode 100644 index 9e0a1896e..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_17.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_17.bm deleted file mode 100644 index 8f54b880b..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_18.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_18.bm deleted file mode 100644 index 3f1b3f341..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_19.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_19.bm deleted file mode 100644 index 3b92d6430..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_2.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_2.bm deleted file mode 100644 index 9e569e4cb..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_20.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_20.bm deleted file mode 100644 index e24ca55d5..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_21.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_21.bm deleted file mode 100644 index 954fda807..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_22.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_22.bm deleted file mode 100644 index 4d95fa315..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_23.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_23.bm deleted file mode 100644 index 584a34d9f..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_3.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_3.bm deleted file mode 100644 index 55384862e..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_4.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_4.bm deleted file mode 100644 index 2312ffb82..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_5.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_5.bm deleted file mode 100644 index a7e8f49c8..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_6.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_6.bm deleted file mode 100644 index 387cc70c2..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_7.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_7.bm deleted file mode 100644 index f24983118..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_8.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_8.bm deleted file mode 100644 index 4bd019c32..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/frame_9.bm b/assets/resources/dolphin/L1_Agumon_128x64/frame_9.bm deleted file mode 100644 index db91ad28c..000000000 Binary files a/assets/resources/dolphin/L1_Agumon_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Agumon_128x64/meta.txt b/assets/resources/dolphin/L1_Agumon_128x64/meta.txt deleted file mode 100644 index cff50095e..000000000 --- a/assets/resources/dolphin/L1_Agumon_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 23 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_0.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_0.bm deleted file mode 100644 index 493420a35..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_1.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_1.bm deleted file mode 100644 index f02c0d6c7..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_10.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_10.bm deleted file mode 100644 index c21e8ea23..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_11.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_11.bm deleted file mode 100644 index f74b3b203..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_12.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_12.bm deleted file mode 100644 index ae71406ed..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_13.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_13.bm deleted file mode 100644 index a62d1230b..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_14.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_14.bm deleted file mode 100644 index 6590f21a2..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_15.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_15.bm deleted file mode 100644 index f068cd379..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_16.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_16.bm deleted file mode 100644 index 29d7ad511..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_17.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_17.bm deleted file mode 100644 index 5ebdba8fa..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_18.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_18.bm deleted file mode 100644 index 8d903d729..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_19.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_19.bm deleted file mode 100644 index 5f28f3b62..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_2.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_2.bm deleted file mode 100644 index c08a3b370..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_20.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_20.bm deleted file mode 100644 index 2811be219..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_21.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_21.bm deleted file mode 100644 index 04d3c9eef..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_22.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_22.bm deleted file mode 100644 index 759eb953b..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_23.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_23.bm deleted file mode 100644 index 03594fad5..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_24.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_24.bm deleted file mode 100644 index 39f89e109..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_25.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_25.bm deleted file mode 100644 index 22c356b04..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_26.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_26.bm deleted file mode 100644 index 7b66b0363..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_27.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_27.bm deleted file mode 100644 index dcd3d143c..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_28.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_28.bm deleted file mode 100644 index 6213ffcbd..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_29.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_29.bm deleted file mode 100644 index ddbabb519..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_3.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_3.bm deleted file mode 100644 index f28a8732c..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_30.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_30.bm deleted file mode 100644 index 4c596c623..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_31.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_31.bm deleted file mode 100644 index ddbabb519..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_32.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_32.bm deleted file mode 100644 index 5822edf21..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_33.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_33.bm deleted file mode 100644 index e2442aa5e..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_34.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_34.bm deleted file mode 100644 index 5f28f3b62..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_35.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_35.bm deleted file mode 100644 index 2811be219..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_36.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_36.bm deleted file mode 100644 index 04d3c9eef..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_37.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_37.bm deleted file mode 100644 index ae921ef05..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_4.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_4.bm deleted file mode 100644 index 48d969037..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_5.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_5.bm deleted file mode 100644 index 2be602142..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_6.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_6.bm deleted file mode 100644 index c2823e0a1..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_7.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_7.bm deleted file mode 100644 index e9579da0e..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_8.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_8.bm deleted file mode 100644 index 7b4498dc8..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_9.bm b/assets/resources/dolphin/L1_B0ws3r_128x64/frame_9.bm deleted file mode 100644 index 87653a5c1..000000000 Binary files a/assets/resources/dolphin/L1_B0ws3r_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_B0ws3r_128x64/meta.txt b/assets/resources/dolphin/L1_B0ws3r_128x64/meta.txt deleted file mode 100644 index 579843efa..000000000 --- a/assets/resources/dolphin/L1_B0ws3r_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 37 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_0.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_0.bm deleted file mode 100644 index 13487fb8a..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_1.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_1.bm deleted file mode 100644 index e9126d4c4..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_10.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_10.bm deleted file mode 100644 index 6d3665e35..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_11.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_11.bm deleted file mode 100644 index 8ae70cd61..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_2.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_2.bm deleted file mode 100644 index ca686f54c..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_3.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_3.bm deleted file mode 100644 index 59ca26a7d..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_4.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_4.bm deleted file mode 100644 index 7e8d7663c..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_5.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_5.bm deleted file mode 100644 index 482dc2e11..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_6.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_6.bm deleted file mode 100644 index bf3bf429c..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_7.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_7.bm deleted file mode 100644 index 8871e0b38..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_8.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_8.bm deleted file mode 100644 index 8fa504d9d..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/frame_9.bm b/assets/resources/dolphin/L1_DJ_128x64/frame_9.bm deleted file mode 100644 index 941c1d7e1..000000000 Binary files a/assets/resources/dolphin/L1_DJ_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_DJ_128x64/meta.txt b/assets/resources/dolphin/L1_DJ_128x64/meta.txt deleted file mode 100644 index 430ee72e9..000000000 --- a/assets/resources/dolphin/L1_DJ_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 34 -Active frames: 2 -Frames order: 0 1 0 1 0 1 0 1 2 3 2 3 2 3 4 8 4 5 4 8 9 5 9 5 9 8 6 7 6 7 6 7 6 7 10 11 -Active cycles: 3 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 1 - -Slot: 0 -X: 75 -Y: 17 -Text: Let's drop! -AlignH: Left -AlignV: Top -StartFrame: 35 -EndFrame: 39 diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_0.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_0.bm deleted file mode 100644 index 372e1e7b6..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_1.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_1.bm deleted file mode 100644 index 2193db19e..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_10.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_10.bm deleted file mode 100644 index 6710810c8..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_11.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_11.bm deleted file mode 100644 index 3f2456080..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_12.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_12.bm deleted file mode 100644 index 31d6df6fa..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_13.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_13.bm deleted file mode 100644 index 2ebdb58da..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_14.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_14.bm deleted file mode 100644 index ab37b0f4b..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_15.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_15.bm deleted file mode 100644 index 904a40ec5..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_16.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_16.bm deleted file mode 100644 index cbdd3b67b..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_17.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_17.bm deleted file mode 100644 index 6162b5c92..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_18.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_18.bm deleted file mode 100644 index 5581c6bc1..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_19.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_19.bm deleted file mode 100644 index f185d07d2..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_2.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_2.bm deleted file mode 100644 index 009fba67f..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_20.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_20.bm deleted file mode 100644 index 8209510c6..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_21.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_21.bm deleted file mode 100644 index 81c861082..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_22.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_22.bm deleted file mode 100644 index df8d316a5..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_23.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_23.bm deleted file mode 100644 index 8d1b4d70f..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_24.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_24.bm deleted file mode 100644 index e4f3cf240..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_25.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_25.bm deleted file mode 100644 index cf5bbf152..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_26.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_26.bm deleted file mode 100644 index 0300cfeb4..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_27.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_27.bm deleted file mode 100644 index c923ebe2b..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_28.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_28.bm deleted file mode 100644 index 23ae369ec..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_29.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_29.bm deleted file mode 100644 index 58cc84436..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_3.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_3.bm deleted file mode 100644 index 9d36e4d2c..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_30.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_30.bm deleted file mode 100644 index f3095f881..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_31.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_31.bm deleted file mode 100644 index 50533c7c2..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_32.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_32.bm deleted file mode 100644 index f4672da8b..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_33.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_33.bm deleted file mode 100644 index aaa0ec46e..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_34.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_34.bm deleted file mode 100644 index 95dffb717..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_35.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_35.bm deleted file mode 100644 index b234d2f61..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_36.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_36.bm deleted file mode 100644 index 839424d6b..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_37.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_37.bm deleted file mode 100644 index 2d6569ca1..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_38.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_38.bm deleted file mode 100644 index 4cc4af146..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_39.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_39.bm deleted file mode 100644 index 34b6fe14f..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_4.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_4.bm deleted file mode 100644 index 704a19ec1..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_40.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_40.bm deleted file mode 100644 index b80650bc3..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_41.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_41.bm deleted file mode 100644 index ea5adfc18..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_42.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_42.bm deleted file mode 100644 index 5f704d94e..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_43.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_43.bm deleted file mode 100644 index c19886276..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_44.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_44.bm deleted file mode 100644 index 19fc10aa3..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_45.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_45.bm deleted file mode 100644 index 4b387ff37..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_46.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_46.bm deleted file mode 100644 index a3d4cbf2a..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_47.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_47.bm deleted file mode 100644 index 577341e38..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_48.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_48.bm deleted file mode 100644 index 3739f9ad5..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_49.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_49.bm deleted file mode 100644 index dcc1eed86..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_5.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_5.bm deleted file mode 100644 index 5024cbddc..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_50.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_50.bm deleted file mode 100644 index 189290e84..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_50.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_51.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_51.bm deleted file mode 100644 index 17f4c43d5..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_51.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_52.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_52.bm deleted file mode 100644 index 894551c22..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_52.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_53.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_53.bm deleted file mode 100644 index 23b63bbe8..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_53.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_54.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_54.bm deleted file mode 100644 index 6c7d1e464..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_54.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_55.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_55.bm deleted file mode 100644 index 930d33cd0..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_55.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_56.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_56.bm deleted file mode 100644 index eebe4f29f..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_56.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_57.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_57.bm deleted file mode 100644 index 61c10c4a3..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_57.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_58.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_58.bm deleted file mode 100644 index b65a6cec2..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_58.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_59.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_59.bm deleted file mode 100644 index 88a2a4b04..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_59.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_6.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_6.bm deleted file mode 100644 index 6ab3ed60e..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_60.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_60.bm deleted file mode 100644 index 1978d382b..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_60.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_61.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_61.bm deleted file mode 100644 index 75bb4e73f..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_61.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_62.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_62.bm deleted file mode 100644 index cf0ad7c28..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_62.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_63.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_63.bm deleted file mode 100644 index 590c58bf9..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_63.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_64.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_64.bm deleted file mode 100644 index 01d334326..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_64.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_65.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_65.bm deleted file mode 100644 index 0878eaa4b..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_65.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_66.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_66.bm deleted file mode 100644 index cc116b685..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_66.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_67.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_67.bm deleted file mode 100644 index 8184e75e3..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_67.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_68.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_68.bm deleted file mode 100644 index f9d4f38b1..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_68.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_69.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_69.bm deleted file mode 100644 index 24ba85474..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_69.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_7.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_7.bm deleted file mode 100644 index 0e7386434..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_70.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_70.bm deleted file mode 100644 index 74d809e89..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_70.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_71.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_71.bm deleted file mode 100644 index 65ef94ad1..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_71.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_72.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_72.bm deleted file mode 100644 index 9898c35ba..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_72.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_8.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_8.bm deleted file mode 100644 index 638160cbd..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_9.bm b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_9.bm deleted file mode 100644 index dcbb311d4..000000000 Binary files a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/meta.txt b/assets/resources/dolphin/L1_Earth_Arcadia_128x64/meta.txt deleted file mode 100644 index d2288c4e6..000000000 --- a/assets/resources/dolphin/L1_Earth_Arcadia_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 24 -Active frames: 72 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0 1 2 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 4 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_0.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_0.bm deleted file mode 100644 index 7d733bcff..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_1.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_1.bm deleted file mode 100644 index f4bdb499a..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_10.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_10.bm deleted file mode 100644 index d3aca319e..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_11.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_11.bm deleted file mode 100644 index 1b0910a8d..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_12.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_12.bm deleted file mode 100644 index 7d733bcff..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_13.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_13.bm deleted file mode 100644 index f4bdb499a..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_14.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_14.bm deleted file mode 100644 index 7ba28ca0d..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_15.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_15.bm deleted file mode 100644 index 18bc34642..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_16.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_16.bm deleted file mode 100644 index 34bca9244..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_17.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_17.bm deleted file mode 100644 index 357522607..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_18.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_18.bm deleted file mode 100644 index 9cdfeb102..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_19.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_19.bm deleted file mode 100644 index 3a63fbcef..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_2.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_2.bm deleted file mode 100644 index 7ba28ca0d..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_20.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_20.bm deleted file mode 100644 index afb5e23bb..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_21.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_21.bm deleted file mode 100644 index 1ae586f6b..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_22.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_22.bm deleted file mode 100644 index d3aca319e..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_23.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_23.bm deleted file mode 100644 index 1b0910a8d..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_3.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_3.bm deleted file mode 100644 index 18bc34642..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_4.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_4.bm deleted file mode 100644 index 22b538211..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_5.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_5.bm deleted file mode 100644 index 357522607..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_6.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_6.bm deleted file mode 100644 index aa2173363..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_7.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_7.bm deleted file mode 100644 index 291de706d..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_8.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_8.bm deleted file mode 100644 index afb5e23bb..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/frame_9.bm b/assets/resources/dolphin/L1_GITS_128x64/frame_9.bm deleted file mode 100644 index e083c5f22..000000000 Binary files a/assets/resources/dolphin/L1_GITS_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_GITS_128x64/meta.txt b/assets/resources/dolphin/L1_GITS_128x64/meta.txt deleted file mode 100644 index ed6fb940e..000000000 --- a/assets/resources/dolphin/L1_GITS_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 16 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Halloween_128x64/frame_0.bm b/assets/resources/dolphin/L1_Halloween_128x64/frame_0.bm new file mode 100644 index 000000000..2d7dbac04 Binary files /dev/null and b/assets/resources/dolphin/L1_Halloween_128x64/frame_0.bm differ diff --git a/assets/resources/dolphin/L1_Halloween_128x64/frame_1.bm b/assets/resources/dolphin/L1_Halloween_128x64/frame_1.bm new file mode 100644 index 000000000..495c3cc8a Binary files /dev/null and b/assets/resources/dolphin/L1_Halloween_128x64/frame_1.bm differ diff --git a/assets/resources/dolphin/L1_Halloween_128x64/frame_2.bm b/assets/resources/dolphin/L1_Halloween_128x64/frame_2.bm new file mode 100644 index 000000000..91f2de20e Binary files /dev/null and b/assets/resources/dolphin/L1_Halloween_128x64/frame_2.bm differ diff --git a/assets/resources/dolphin/L1_Halloween_128x64/frame_3.bm b/assets/resources/dolphin/L1_Halloween_128x64/frame_3.bm new file mode 100644 index 000000000..4a914a294 Binary files /dev/null and b/assets/resources/dolphin/L1_Halloween_128x64/frame_3.bm differ diff --git a/assets/resources/dolphin/L1_Halloween_128x64/meta.txt b/assets/resources/dolphin/L1_Halloween_128x64/meta.txt new file mode 100644 index 000000000..6ff707d12 --- /dev/null +++ b/assets/resources/dolphin/L1_Halloween_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 4 +Active frames: 0 +Frames order: 0 1 2 3 +Active cycles: 0 +Frame rate: 3 +Duration: 3600 +Active cooldown: 0 + +Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_0.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_0.bm deleted file mode 100644 index 39be14c40..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_1.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_1.bm deleted file mode 100644 index 3ff70f416..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_10.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_10.bm deleted file mode 100644 index 70b3894aa..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_11.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_11.bm deleted file mode 100644 index b2a8aa2e0..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_12.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_12.bm deleted file mode 100644 index 479f50390..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_13.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_13.bm deleted file mode 100644 index d4bae7e8a..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_14.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_14.bm deleted file mode 100644 index 21f1a0ced..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_15.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_15.bm deleted file mode 100644 index 3c76ad196..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_16.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_16.bm deleted file mode 100644 index c969c0024..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_17.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_17.bm deleted file mode 100644 index 7f8c26b22..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_18.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_18.bm deleted file mode 100644 index 88371322b..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_19.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_19.bm deleted file mode 100644 index 774f837a4..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_2.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_2.bm deleted file mode 100644 index 69fe31fe6..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_20.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_20.bm deleted file mode 100644 index aab27d437..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_21.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_21.bm deleted file mode 100644 index aab27d437..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_22.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_22.bm deleted file mode 100644 index aab27d437..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_23.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_23.bm deleted file mode 100644 index 955c9dfbe..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_24.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_24.bm deleted file mode 100644 index 5888f8bdb..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_25.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_25.bm deleted file mode 100644 index 9f25cff58..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_26.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_26.bm deleted file mode 100644 index c1d11b434..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_27.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_27.bm deleted file mode 100644 index f4586201f..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_28.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_28.bm deleted file mode 100644 index 3385f6e0d..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_29.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_29.bm deleted file mode 100644 index 8eb87e43e..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_3.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_3.bm deleted file mode 100644 index 8bb687b4e..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_30.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_30.bm deleted file mode 100644 index c36b9d4e6..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_31.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_31.bm deleted file mode 100644 index de8ecab6e..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_32.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_32.bm deleted file mode 100644 index c243fc4a3..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_33.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_33.bm deleted file mode 100644 index 607cde72d..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_34.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_34.bm deleted file mode 100644 index 2f257cf42..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_35.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_35.bm deleted file mode 100644 index 0e196b5ee..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_36.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_36.bm deleted file mode 100644 index db4c4f37a..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_37.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_37.bm deleted file mode 100644 index 848bcd8e9..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_38.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_38.bm deleted file mode 100644 index b7e5195cf..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_39.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_39.bm deleted file mode 100644 index f434702b1..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_4.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_4.bm deleted file mode 100644 index 3e8b3288a..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_40.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_40.bm deleted file mode 100644 index 233cfd34f..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_41.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_41.bm deleted file mode 100644 index cde7338b3..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_42.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_42.bm deleted file mode 100644 index 36aab9d4e..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_43.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_43.bm deleted file mode 100644 index 7b73949db..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_44.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_44.bm deleted file mode 100644 index f295afabd..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_5.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_5.bm deleted file mode 100644 index d8b0e9818..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_6.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_6.bm deleted file mode 100644 index b09ab40e2..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_7.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_7.bm deleted file mode 100644 index 8765f014a..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_8.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_8.bm deleted file mode 100644 index 5a419a032..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/frame_9.bm b/assets/resources/dolphin/L1_Mario_128x64/frame_9.bm deleted file mode 100644 index ad4fc08e1..000000000 Binary files a/assets/resources/dolphin/L1_Mario_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mario_128x64/meta.txt b/assets/resources/dolphin/L1_Mario_128x64/meta.txt deleted file mode 100644 index 2c14d5e08..000000000 --- a/assets/resources/dolphin/L1_Mario_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 44 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_0.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_0.bm deleted file mode 100644 index 72625ddc0..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_1.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_1.bm deleted file mode 100644 index 65dc9fd27..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_10.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_10.bm deleted file mode 100644 index 4ea80afa0..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_2.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_2.bm deleted file mode 100644 index 14b7839c7..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_3.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_3.bm deleted file mode 100644 index 54f768ef2..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_4.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_4.bm deleted file mode 100644 index 254f4abd0..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_5.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_5.bm deleted file mode 100644 index f56c78b18..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_6.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_6.bm deleted file mode 100644 index 468c4476c..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_7.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_7.bm deleted file mode 100644 index 1d6d86ec7..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_8.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_8.bm deleted file mode 100644 index 0c8cacfef..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/frame_9.bm b/assets/resources/dolphin/L1_Mew_128x64/frame_9.bm deleted file mode 100644 index 248ca0940..000000000 Binary files a/assets/resources/dolphin/L1_Mew_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mew_128x64/meta.txt b/assets/resources/dolphin/L1_Mew_128x64/meta.txt deleted file mode 100644 index 1d99bfd34..000000000 --- a/assets/resources/dolphin/L1_Mew_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 4 -Active frames: 9 -Frames order: 0 1 2 1 3 4 5 6 7 8 9 10 3 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_0.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_0.bm new file mode 100644 index 000000000..eb0435241 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_0.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_1.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_1.bm new file mode 100644 index 000000000..9988e18a0 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_1.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_10.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_10.bm new file mode 100644 index 000000000..fdf7d1665 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_10.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_11.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_11.bm new file mode 100644 index 000000000..645703a17 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_11.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_12.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_12.bm new file mode 100644 index 000000000..7ee0db1f8 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_12.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_13.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_13.bm new file mode 100644 index 000000000..2055cd974 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_13.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_14.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_14.bm new file mode 100644 index 000000000..f1d511ba6 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_14.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_15.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_15.bm new file mode 100644 index 000000000..f108180b8 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_15.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_16.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_16.bm new file mode 100644 index 000000000..9ed1f5520 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_16.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_17.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_17.bm new file mode 100644 index 000000000..2b19f5f25 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_17.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_18.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_18.bm new file mode 100644 index 000000000..43c8c42a0 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_18.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_19.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_19.bm new file mode 100644 index 000000000..554d4cca8 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_19.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_2.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_2.bm new file mode 100644 index 000000000..19c045034 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_2.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_20.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_20.bm new file mode 100644 index 000000000..9af692ea6 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_20.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_21.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_21.bm new file mode 100644 index 000000000..a3a91bc35 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_21.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_22.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_22.bm new file mode 100644 index 000000000..6699b7525 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_22.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_23.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_23.bm new file mode 100644 index 000000000..8dc4812d4 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_23.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_24.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_24.bm new file mode 100644 index 000000000..3ef5e556e Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_24.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_25.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_25.bm new file mode 100644 index 000000000..2d48abcda Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_25.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_26.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_26.bm new file mode 100644 index 000000000..2e491d991 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_26.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_27.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_27.bm new file mode 100644 index 000000000..63cf443bc Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_27.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_28.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_28.bm new file mode 100644 index 000000000..23e28db93 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_28.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_29.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_29.bm new file mode 100644 index 000000000..db54d6e42 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_29.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_3.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_3.bm new file mode 100644 index 000000000..4c7f7b7de Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_3.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_30.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_30.bm new file mode 100644 index 000000000..afda830df Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_30.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_31.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_31.bm new file mode 100644 index 000000000..ec9b2c0d0 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_31.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_32.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_32.bm new file mode 100644 index 000000000..0757af4f1 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_32.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_33.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_33.bm new file mode 100644 index 000000000..b1411659c Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_33.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_34.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_34.bm new file mode 100644 index 000000000..0f4dd9c27 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_34.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_35.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_35.bm new file mode 100644 index 000000000..3298ab216 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_35.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_36.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_36.bm new file mode 100644 index 000000000..58be4e0d8 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_36.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_37.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_37.bm new file mode 100644 index 000000000..cfbe16d18 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_37.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_38.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_38.bm new file mode 100644 index 000000000..14c557d06 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_38.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_39.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_39.bm new file mode 100644 index 000000000..789a82339 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_39.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_4.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_4.bm new file mode 100644 index 000000000..42dd423a8 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_4.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_40.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_40.bm new file mode 100644 index 000000000..376b1b8b2 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_40.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_5.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_5.bm new file mode 100644 index 000000000..4fdfdf8ab Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_5.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_6.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_6.bm new file mode 100644 index 000000000..a27687b5d Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_6.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_7.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_7.bm new file mode 100644 index 000000000..248dabbc6 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_7.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_8.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_8.bm new file mode 100644 index 000000000..5a46b96dc Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_8.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/frame_9.bm b/assets/resources/dolphin/L1_Mods_128x64/frame_9.bm new file mode 100644 index 000000000..4fe07c9b1 Binary files /dev/null and b/assets/resources/dolphin/L1_Mods_128x64/frame_9.bm differ diff --git a/assets/resources/dolphin/L1_Mods_128x64/meta.txt b/assets/resources/dolphin/L1_Mods_128x64/meta.txt new file mode 100644 index 000000000..c9c35ee41 --- /dev/null +++ b/assets/resources/dolphin/L1_Mods_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 23 +Active frames: 18 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_0.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_0.bm deleted file mode 100644 index 458f6b9cf..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_1.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_1.bm deleted file mode 100644 index 47ce02aed..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_10.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_10.bm deleted file mode 100644 index 0db7ad5ad..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_11.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_11.bm deleted file mode 100644 index 43a60d034..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_2.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_2.bm deleted file mode 100644 index 9cfc882f9..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_3.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_3.bm deleted file mode 100644 index 36d3ac7df..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_4.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_4.bm deleted file mode 100644 index 714f37d3d..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_5.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_5.bm deleted file mode 100644 index 0802b652a..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_6.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_6.bm deleted file mode 100644 index bd1e1cfd9..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_7.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_7.bm deleted file mode 100644 index a7173c456..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_8.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_8.bm deleted file mode 100644 index 4f0a80a29..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/frame_9.bm b/assets/resources/dolphin/L1_NyanCat_128x64/frame_9.bm deleted file mode 100644 index 75dad3171..000000000 Binary files a/assets/resources/dolphin/L1_NyanCat_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_NyanCat_128x64/meta.txt b/assets/resources/dolphin/L1_NyanCat_128x64/meta.txt deleted file mode 100644 index d4c7d5c48..000000000 --- a/assets/resources/dolphin/L1_NyanCat_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 11 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 -Active cycles: 5 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_0.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_0.bm deleted file mode 100644 index 06a758ad7..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_1.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_1.bm deleted file mode 100644 index be051403f..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_10.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_10.bm deleted file mode 100644 index 77d07f992..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_11.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_11.bm deleted file mode 100644 index bf527562c..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_2.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_2.bm deleted file mode 100644 index 182f52c0f..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_3.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_3.bm deleted file mode 100644 index ef03dc988..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_4.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_4.bm deleted file mode 100644 index 76d57b483..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_5.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_5.bm deleted file mode 100644 index efd785f08..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_6.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_6.bm deleted file mode 100644 index 6dbeeb252..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_7.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_7.bm deleted file mode 100644 index 77d07f992..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_8.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_8.bm deleted file mode 100644 index bf527562c..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_9.bm b/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_9.bm deleted file mode 100644 index 6dbeeb252..000000000 Binary files a/assets/resources/dolphin/L1_P0liwhirl_128x51/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_P0liwhirl_128x51/meta.txt b/assets/resources/dolphin/L1_P0liwhirl_128x51/meta.txt deleted file mode 100644 index de37d5b2e..000000000 --- a/assets/resources/dolphin/L1_P0liwhirl_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_Painting_128x64/meta.txt b/assets/resources/dolphin/L1_Painting_128x64/meta.txt index e5f5fc0a6..46528d598 100644 --- a/assets/resources/dolphin/L1_Painting_128x64/meta.txt +++ b/assets/resources/dolphin/L1_Painting_128x64/meta.txt @@ -11,12 +11,12 @@ Frame rate: 2 Duration: 3600 Active cooldown: 7 -Bubble slots: 1 +Bubble slots: 2 Slot: 0 X: 57 Y: 24 -Text: No mistakes, +Text: $20 to put this AlignH: Left AlignV: Center StartFrame: 11 @@ -25,7 +25,25 @@ EndFrame: 14 Slot: 0 X: 57 Y: 21 -Text: only happy\n accidents +Text: in my blowhole +AlignH: Left +AlignV: Center +StartFrame: 15 +EndFrame: 18 + +Slot: 1 +X: 57 +Y: 24 +Text: How many dicks +AlignH: Left +AlignV: Center +StartFrame: 11 +EndFrame: 14 + +Slot: 0 +X: 57 +Y: 21 +Text: we drawing today? AlignH: Left AlignV: Center StartFrame: 15 diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_0.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_0.bm new file mode 100644 index 000000000..37ebe0e15 Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_0.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_1.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_1.bm new file mode 100644 index 000000000..d2f20b34d Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_1.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_10.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_10.bm new file mode 100644 index 000000000..54878228b Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_10.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_2.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_2.bm new file mode 100644 index 000000000..0914affad Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_2.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_3.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_3.bm new file mode 100644 index 000000000..d3fd071e7 Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_3.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_4.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_4.bm new file mode 100644 index 000000000..59f3a6715 Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_4.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_5.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_5.bm new file mode 100644 index 000000000..2018eac7b Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_5.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_6.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_6.bm new file mode 100644 index 000000000..74070c881 Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_6.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_7.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_7.bm new file mode 100644 index 000000000..f66bdefcf Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_7.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_8.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_8.bm new file mode 100644 index 000000000..c7dd073f6 Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_8.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/frame_9.bm b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_9.bm new file mode 100644 index 000000000..7c8e1357a Binary files /dev/null and b/assets/resources/dolphin/L1_Purple_rain_128x64/frame_9.bm differ diff --git a/assets/resources/dolphin/L1_Purple_rain_128x64/meta.txt b/assets/resources/dolphin/L1_Purple_rain_128x64/meta.txt new file mode 100644 index 000000000..c60148050 --- /dev/null +++ b/assets/resources/dolphin/L1_Purple_rain_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 11 +Active frames: 0 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 +Active cycles: 0 +Frame rate: 4 +Duration: 3600 +Active cooldown: 0 + +Bubble slots: 0 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 1169e42d6..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm and /dev/null differ 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 80e2f39bb..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm and /dev/null differ 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 959b02556..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm deleted file mode 100644 index 8c106d906..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm and /dev/null differ 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 389d2a8ef..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm deleted file mode 100644 index 4b65b6d9b..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm and /dev/null differ 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 451c80a26..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm deleted file mode 100644 index 417809078..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm deleted file mode 100644 index 4370393dc..000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm and /dev/null differ 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_Rickroll_128x64/frame_0.bm b/assets/resources/dolphin/L1_Rickroll_128x64/frame_0.bm deleted file mode 100644 index 88bc186d5..000000000 Binary files a/assets/resources/dolphin/L1_Rickroll_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Rickroll_128x64/frame_1.bm b/assets/resources/dolphin/L1_Rickroll_128x64/frame_1.bm deleted file mode 100644 index a014dc74e..000000000 Binary files a/assets/resources/dolphin/L1_Rickroll_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Rickroll_128x64/frame_2.bm b/assets/resources/dolphin/L1_Rickroll_128x64/frame_2.bm deleted file mode 100644 index 0a465f0e6..000000000 Binary files a/assets/resources/dolphin/L1_Rickroll_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Rickroll_128x64/frame_3.bm b/assets/resources/dolphin/L1_Rickroll_128x64/frame_3.bm deleted file mode 100644 index 88bc186d5..000000000 Binary files a/assets/resources/dolphin/L1_Rickroll_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Rickroll_128x64/meta.txt b/assets/resources/dolphin/L1_Rickroll_128x64/meta.txt deleted file mode 100644 index 8d76f69d4..000000000 --- a/assets/resources/dolphin/L1_Rickroll_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 2 -Active frames: 8 -Frames order: 0 1 0 2 1 2 0 2 1 2 -Active cycles: 1 -Frame rate: 1 -Duration: 3600 -Active cooldown: 1 - -Bubble slots: 1 - -Slot: 0 -X: 1 -Y: 17 -Text: Never gonna give you up -AlignH: Right -AlignV: Bottom -StartFrame: 2 -EndFrame: 4 - -Slot: 0 -X: 1 -Y: 17 -Text: Never gonna let you down\nNever gonna run around and -AlignH: Right -AlignV: Center -StartFrame: 5 -EndFrame: 7 - -Slot: 0 -X: 1 -Y: 17 -Text: DESERT YOU! -AlignH: Left -AlignV: Center -StartFrame: 8 -EndFrame: 10 diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_0.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_0.bm deleted file mode 100644 index bc81630af..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_1.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_1.bm deleted file mode 100644 index bc81630af..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_10.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_10.bm deleted file mode 100644 index 88b4e7558..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_11.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_11.bm deleted file mode 100644 index a96459a51..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_12.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_12.bm deleted file mode 100644 index ad6bfeb83..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_13.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_13.bm deleted file mode 100644 index c9f5d70f6..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_14.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_14.bm deleted file mode 100644 index 07003f8a9..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_15.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_15.bm deleted file mode 100644 index 938d85b80..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_16.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_16.bm deleted file mode 100644 index 807d2ef26..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_17.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_17.bm deleted file mode 100644 index 230646e9b..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_18.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_18.bm deleted file mode 100644 index 2dfd0a7e3..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_19.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_19.bm deleted file mode 100644 index 710a570ff..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_2.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_2.bm deleted file mode 100644 index 3047afb07..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_20.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_20.bm deleted file mode 100644 index eb213c7d8..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_21.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_21.bm deleted file mode 100644 index dff24ca3d..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_22.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_22.bm deleted file mode 100644 index 07ae5af5a..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_23.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_23.bm deleted file mode 100644 index d4ad76af1..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_24.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_24.bm deleted file mode 100644 index 566a594fc..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_25.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_25.bm deleted file mode 100644 index 22d8869d9..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_26.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_26.bm deleted file mode 100644 index 2293745e1..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_27.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_27.bm deleted file mode 100644 index c343cfea2..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_28.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_28.bm deleted file mode 100644 index 9bc83841c..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_29.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_29.bm deleted file mode 100644 index 1b597ee82..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_3.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_3.bm deleted file mode 100644 index 486bb2fb4..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_30.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_30.bm deleted file mode 100644 index cd5bfca43..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_31.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_31.bm deleted file mode 100644 index a4dd63308..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_32.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_32.bm deleted file mode 100644 index 9689ad2d6..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_33.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_33.bm deleted file mode 100644 index de396e4bc..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_34.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_34.bm deleted file mode 100644 index cb8e9283b..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_35.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_35.bm deleted file mode 100644 index 7b36915d7..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_36.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_36.bm deleted file mode 100644 index b83dac82c..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_37.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_37.bm deleted file mode 100644 index 20dbf96bf..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_38.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_38.bm deleted file mode 100644 index 349d954b8..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_39.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_39.bm deleted file mode 100644 index a8ba71239..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_4.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_4.bm deleted file mode 100644 index 0cc775cb2..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_40.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_40.bm deleted file mode 100644 index b6376e199..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_41.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_41.bm deleted file mode 100644 index c7129770c..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_42.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_42.bm deleted file mode 100644 index abaa8f01a..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_43.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_43.bm deleted file mode 100644 index 62cc86e57..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_44.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_44.bm deleted file mode 100644 index 248fc1bcf..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_45.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_45.bm deleted file mode 100644 index 692148eef..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_46.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_46.bm deleted file mode 100644 index e3157f493..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_47.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_47.bm deleted file mode 100644 index 521d2d1f4..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_48.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_48.bm deleted file mode 100644 index b29813138..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_49.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_49.bm deleted file mode 100644 index 222516fcd..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_5.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_5.bm deleted file mode 100644 index 57a3d2008..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_50.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_50.bm deleted file mode 100644 index a59e0d855..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_50.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_51.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_51.bm deleted file mode 100644 index 78aaef024..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_51.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_52.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_52.bm deleted file mode 100644 index c7f21af53..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_52.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_53.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_53.bm deleted file mode 100644 index d573967b6..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_53.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_54.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_54.bm deleted file mode 100644 index 0c8a58261..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_54.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_55.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_55.bm deleted file mode 100644 index b5984ae73..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_55.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_56.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_56.bm deleted file mode 100644 index 48dd45d98..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_56.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_57.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_57.bm deleted file mode 100644 index 8e572103b..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_57.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_58.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_58.bm deleted file mode 100644 index 1dcf3da63..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_58.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_59.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_59.bm deleted file mode 100644 index c5c3ce21e..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_59.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_6.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_6.bm deleted file mode 100644 index 11c9869ae..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_60.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_60.bm deleted file mode 100644 index 6aa25dc39..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_60.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_61.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_61.bm deleted file mode 100644 index 5890a6177..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_61.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_62.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_62.bm deleted file mode 100644 index d55ccde6c..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_62.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_63.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_63.bm deleted file mode 100644 index eb1130071..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_63.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_64.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_64.bm deleted file mode 100644 index c3668a45d..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_64.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_65.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_65.bm deleted file mode 100644 index 24f5e3d0b..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_65.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_66.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_66.bm deleted file mode 100644 index 7b27125cc..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_66.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_67.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_67.bm deleted file mode 100644 index df42b0b21..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_67.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_68.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_68.bm deleted file mode 100644 index cbf06399a..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_68.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_69.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_69.bm deleted file mode 100644 index 4195412d3..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_69.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_7.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_7.bm deleted file mode 100644 index 38462e0e4..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_70.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_70.bm deleted file mode 100644 index 210aa528b..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_70.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_8.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_8.bm deleted file mode 100644 index 7e5631591..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/frame_9.bm b/assets/resources/dolphin/L1_Slayers_128x64/frame_9.bm deleted file mode 100644 index 8201557b1..000000000 Binary files a/assets/resources/dolphin/L1_Slayers_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Slayers_128x64/meta.txt b/assets/resources/dolphin/L1_Slayers_128x64/meta.txt deleted file mode 100644 index 60a43c1e0..000000000 --- a/assets/resources/dolphin/L1_Slayers_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 71 -Active frames: 6 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 7 8 9 10 9 8 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 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 aa7454666..000000000 Binary files a/assets/resources/dolphin/L1_Waves_128x50/frame_0.bm and /dev/null differ 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 a23d250b9..000000000 Binary files a/assets/resources/dolphin/L1_Waves_128x50/frame_1.bm and /dev/null differ 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 cd39b17ec..000000000 Binary files a/assets/resources/dolphin/L1_Waves_128x50/frame_2.bm and /dev/null differ 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 2d5452d7c..000000000 Binary files a/assets/resources/dolphin/L1_Waves_128x50/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Waves_128x50/meta.txt b/assets/resources/dolphin/L1_Waves_128x50/meta.txt deleted file mode 100644 index 376447af7..000000000 --- a/assets/resources/dolphin/L1_Waves_128x50/meta.txt +++ /dev/null @@ -1,50 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 50 -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: 3 - -Slot: 0 -X: 1 -Y: 17 -Text: I am happy,\nmy friend! -AlignH: Right -AlignV: Bottom -StartFrame: 3 -EndFrame: 9 - -Slot: 1 -X: 1 -Y: 17 -Text: So long and\nthanks for\nall the fish! -AlignH: Right -AlignV: Center -StartFrame: 3 -EndFrame: 9 - -Slot: 2 -X: 1 -Y: 25 -Text: I wish I could -AlignH: Right -AlignV: Bottom -StartFrame: 3 -EndFrame: 5 - -Slot: 2 -X: 1 -Y: 25 -Text: swim all day -AlignH: Right -AlignV: Bottom -StartFrame: 6 -EndFrame: 9 diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_0.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_0.bm index 436bc602f..8da6b45f0 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_0.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_0.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_1.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_1.bm index 33228b8e1..e9597309a 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_1.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_1.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_2.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_2.bm index d21335363..ebd22d637 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_2.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_2.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_3.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_3.bm index 8c0430da1..4ac97ced6 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_3.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_3.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_4.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_4.bm index 78a5862a1..41bcf9e29 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_4.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_4.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_5.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_5.bm index 7702099d0..d394375f6 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_5.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_5.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_6.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_6.bm index cda2e4d0d..082961555 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_6.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_6.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_7.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_7.bm index 8c7910c83..e5780c17c 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_7.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_7.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_8.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_8.bm index 9cb483006..112c38f2f 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_8.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_8.bm differ diff --git a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_9.bm b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_9.bm index 538c4edb6..3d12a0d0d 100644 Binary files a/assets/resources/dolphin/L2_FlipperCity_128x64/frame_9.bm and b/assets/resources/dolphin/L2_FlipperCity_128x64/frame_9.bm differ 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 3ff70a916..000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm and /dev/null differ 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 ed11583f8..000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm and /dev/null differ 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 41850505b..000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm deleted file mode 100644 index d4b47960a..000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm deleted file mode 100644 index ee3a5f188..000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm and /dev/null differ 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 3fc364406..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_1.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_1.bm deleted file mode 100644 index 60a2e700a..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_1.bm and /dev/null differ 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 f80819358..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_2.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_2.bm deleted file mode 100644 index 1d981e7d5..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_3.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_3.bm deleted file mode 100644 index 48d0aa85d..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_3.bm and /dev/null differ 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 a961f4c0a..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_5.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_5.bm deleted file mode 100644 index 81b569339..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_5.bm and /dev/null differ 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 2f030833a..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm and /dev/null differ 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 4519819ea..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_8.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_8.bm deleted file mode 100644 index 4bb3b7983..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_8.bm and /dev/null differ 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 1339c607e..000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm and /dev/null differ 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_FlipperMustache_128x64/frame_0.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_0.bm new file mode 100644 index 000000000..fdbf0c922 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_0.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_1.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_1.bm new file mode 100644 index 000000000..159fd5f1b Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_1.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_10.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_10.bm new file mode 100644 index 000000000..465759184 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_10.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_11.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_11.bm new file mode 100644 index 000000000..f89baa157 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_11.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_2.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_2.bm new file mode 100644 index 000000000..8c59fc403 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_2.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_3.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_3.bm new file mode 100644 index 000000000..2753b8abb Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_3.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_4.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_4.bm new file mode 100644 index 000000000..25471df39 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_4.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_5.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_5.bm new file mode 100644 index 000000000..f4982cafa Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_5.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_6.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_6.bm new file mode 100644 index 000000000..d98632563 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_6.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_7.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_7.bm new file mode 100644 index 000000000..d93803b89 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_7.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_8.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_8.bm new file mode 100644 index 000000000..5c0b2ae22 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_8.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_9.bm b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_9.bm new file mode 100644 index 000000000..5d0862439 Binary files /dev/null and b/assets/resources/dolphin/L3_FlipperMustache_128x64/frame_9.bm differ diff --git a/assets/resources/dolphin/L3_FlipperMustache_128x64/meta.txt b/assets/resources/dolphin/L3_FlipperMustache_128x64/meta.txt new file mode 100644 index 000000000..64de3a3df --- /dev/null +++ b/assets/resources/dolphin/L3_FlipperMustache_128x64/meta.txt @@ -0,0 +1,32 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 10 +Active frames: 2 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 +Active cycles: 8 +Frame rate: 3 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 60 +Y: 30 +Text: Some hack\nwe made +AlignH: Left +AlignV: Center +StartFrame: 10 +EndFrame: 17 + +Slot: 0 +X: 60 +Y: 30 +Text: It's a firmware\nupgrade! +AlignH: Left +AlignV: Center +StartFrame: 18 +EndFrame: 25 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 cf2120ff4..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_1.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_1.bm deleted file mode 100644 index 24a492132..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_1.bm and /dev/null differ 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 1354c78f2..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm and /dev/null differ 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 c15289b5e..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_12.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_12.bm deleted file mode 100644 index ac9f08334..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_13.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_13.bm deleted file mode 100644 index 9ad7b9cf3..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_2.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_2.bm deleted file mode 100644 index 30c4bedcd..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_2.bm and /dev/null differ 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 dc0fb9b79..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm and /dev/null differ 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 025477a7a..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm and /dev/null differ 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 89a4cd6ac..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_6.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_6.bm deleted file mode 100644 index c93ff6acd..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_6.bm and /dev/null differ 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 fb6d9bd29..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm and /dev/null differ 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 a0377f635..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_9.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_9.bm deleted file mode 100644 index 06f66ab3a..000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_9.bm and /dev/null differ 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 db283e81f..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm and /dev/null differ 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 c60074370..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm and /dev/null differ 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 694302a9d..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm and /dev/null differ 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 246b955cf..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm and /dev/null differ 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 b6fb1130b..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_13.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_13.bm deleted file mode 100644 index 561335413..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_13.bm and /dev/null differ 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 1025137e4..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm and /dev/null differ 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 e623a1c0f..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_4.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_4.bm deleted file mode 100644 index 654a68e87..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_5.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_5.bm deleted file mode 100644 index 14eae4c1c..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_6.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_6.bm deleted file mode 100644 index 561335413..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_6.bm and /dev/null differ 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 c9b99a014..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_8.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_8.bm deleted file mode 100644 index 812db0d46..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_9.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_9.bm deleted file mode 100644 index 0cad9cc26..000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/meta.txt b/assets/resources/dolphin/L3_Lab_research_128x54/meta.txt deleted file mode 100644 index e83a8b436..000000000 --- a/assets/resources/dolphin/L3_Lab_research_128x54/meta.txt +++ /dev/null @@ -1,59 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 54 -Passive frames: 6 -Active frames: 8 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 71 -Y: 23 -Text: 7em!g!7j!\nVyP5?T- -AlignH: Left -AlignV: Center -StartFrame: 8 -EndFrame: 8 - -Slot: 0 -X: 71 -Y: 23 -Text: aUqF7sz!\n%9.mP5H -AlignH: Left -AlignV: Center -StartFrame: 9 -EndFrame: 9 - -Slot: 0 -X: 71 -Y: 23 -Text: 2%Kx2mV\nL8EyA84 -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 10 - -Slot: 0 -X: 71 -Y: 23 -Text: U7%cRXr\nvbBa!_W1 -AlignH: Left -AlignV: Center -StartFrame: 11 -EndFrame: 11 - -Slot: 0 -X: 71 -Y: 23 -Text: 5rm_[K%\n!!(U9d$tE -AlignH: Left -AlignV: Center -StartFrame: 12 -EndFrame: 12 diff --git a/assets/resources/dolphin/Maha_128x64/frame_0.bm b/assets/resources/dolphin/Maha_128x64/frame_0.bm deleted file mode 100644 index 2a0f7d6f9..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_1.bm b/assets/resources/dolphin/Maha_128x64/frame_1.bm deleted file mode 100644 index d2f0e2fe8..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_10.bm b/assets/resources/dolphin/Maha_128x64/frame_10.bm deleted file mode 100644 index 9c19717c4..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_11.bm b/assets/resources/dolphin/Maha_128x64/frame_11.bm deleted file mode 100644 index 6c73db8ef..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_12.bm b/assets/resources/dolphin/Maha_128x64/frame_12.bm deleted file mode 100644 index 099ebccba..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_13.bm b/assets/resources/dolphin/Maha_128x64/frame_13.bm deleted file mode 100644 index 2a2dcfebc..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_14.bm b/assets/resources/dolphin/Maha_128x64/frame_14.bm deleted file mode 100644 index f4265b3d3..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_15.bm b/assets/resources/dolphin/Maha_128x64/frame_15.bm deleted file mode 100644 index 111446b1a..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_16.bm b/assets/resources/dolphin/Maha_128x64/frame_16.bm deleted file mode 100644 index e7fa5433b..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_17.bm b/assets/resources/dolphin/Maha_128x64/frame_17.bm deleted file mode 100644 index 3dad1643c..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_18.bm b/assets/resources/dolphin/Maha_128x64/frame_18.bm deleted file mode 100644 index 870cf2720..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_19.bm b/assets/resources/dolphin/Maha_128x64/frame_19.bm deleted file mode 100644 index 1007320dd..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_2.bm b/assets/resources/dolphin/Maha_128x64/frame_2.bm deleted file mode 100644 index dc53f130d..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_20.bm b/assets/resources/dolphin/Maha_128x64/frame_20.bm deleted file mode 100644 index 07b5cb85a..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_21.bm b/assets/resources/dolphin/Maha_128x64/frame_21.bm deleted file mode 100644 index 362abc4b1..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_22.bm b/assets/resources/dolphin/Maha_128x64/frame_22.bm deleted file mode 100644 index 24b1adf83..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_23.bm b/assets/resources/dolphin/Maha_128x64/frame_23.bm deleted file mode 100644 index 5c5792da8..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_24.bm b/assets/resources/dolphin/Maha_128x64/frame_24.bm deleted file mode 100644 index b0d5596a0..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_25.bm b/assets/resources/dolphin/Maha_128x64/frame_25.bm deleted file mode 100644 index 19550b7be..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_26.bm b/assets/resources/dolphin/Maha_128x64/frame_26.bm deleted file mode 100644 index 22b3a8d40..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_27.bm b/assets/resources/dolphin/Maha_128x64/frame_27.bm deleted file mode 100644 index 6f633460e..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_28.bm b/assets/resources/dolphin/Maha_128x64/frame_28.bm deleted file mode 100644 index 274a50d10..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_29.bm b/assets/resources/dolphin/Maha_128x64/frame_29.bm deleted file mode 100644 index 64f7722e2..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_3.bm b/assets/resources/dolphin/Maha_128x64/frame_3.bm deleted file mode 100644 index 2103dec40..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_30.bm b/assets/resources/dolphin/Maha_128x64/frame_30.bm deleted file mode 100644 index 6a2b2e949..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_31.bm b/assets/resources/dolphin/Maha_128x64/frame_31.bm deleted file mode 100644 index 7019a66e6..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_32.bm b/assets/resources/dolphin/Maha_128x64/frame_32.bm deleted file mode 100644 index 6303c6eda..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_33.bm b/assets/resources/dolphin/Maha_128x64/frame_33.bm deleted file mode 100644 index 565851e13..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_34.bm b/assets/resources/dolphin/Maha_128x64/frame_34.bm deleted file mode 100644 index a5669d9f8..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_35.bm b/assets/resources/dolphin/Maha_128x64/frame_35.bm deleted file mode 100644 index bf3529122..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_36.bm b/assets/resources/dolphin/Maha_128x64/frame_36.bm deleted file mode 100644 index dff30f047..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_37.bm b/assets/resources/dolphin/Maha_128x64/frame_37.bm deleted file mode 100644 index 9873d2bab..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_38.bm b/assets/resources/dolphin/Maha_128x64/frame_38.bm deleted file mode 100644 index 2843ac81a..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_39.bm b/assets/resources/dolphin/Maha_128x64/frame_39.bm deleted file mode 100644 index 8ab45bbb6..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_4.bm b/assets/resources/dolphin/Maha_128x64/frame_4.bm deleted file mode 100644 index e8e69d262..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_5.bm b/assets/resources/dolphin/Maha_128x64/frame_5.bm deleted file mode 100644 index e1f8ea7c6..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_6.bm b/assets/resources/dolphin/Maha_128x64/frame_6.bm deleted file mode 100644 index b2accb8bb..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_7.bm b/assets/resources/dolphin/Maha_128x64/frame_7.bm deleted file mode 100644 index b1c95c3f7..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_8.bm b/assets/resources/dolphin/Maha_128x64/frame_8.bm deleted file mode 100644 index ac569f806..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/frame_9.bm b/assets/resources/dolphin/Maha_128x64/frame_9.bm deleted file mode 100644 index 3db1f4b80..000000000 Binary files a/assets/resources/dolphin/Maha_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Maha_128x64/meta.txt b/assets/resources/dolphin/Maha_128x64/meta.txt deleted file mode 100644 index 975e6b95b..000000000 --- a/assets/resources/dolphin/Maha_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 39 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 -Active cycles: 1 -Frame rate: 5 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_0.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_0.bm deleted file mode 100644 index 9998a6402..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_1.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_1.bm deleted file mode 100644 index 803dc9d2c..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_10.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_10.bm deleted file mode 100644 index f65c8e836..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_11.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_11.bm deleted file mode 100644 index 94ba56a6e..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_12.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_12.bm deleted file mode 100644 index ad88a4d5e..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_13.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_13.bm deleted file mode 100644 index 3bd7ed7f0..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_14.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_14.bm deleted file mode 100644 index 2faab4640..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_15.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_15.bm deleted file mode 100644 index 18dcfda02..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_16.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_16.bm deleted file mode 100644 index d20af7833..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_17.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_17.bm deleted file mode 100644 index 0ed14fee9..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_18.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_18.bm deleted file mode 100644 index 14ef195a7..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_19.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_19.bm deleted file mode 100644 index 44ef3c354..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_2.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_2.bm deleted file mode 100644 index 5e78ddd17..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_20.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_20.bm deleted file mode 100644 index 5d15f8542..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_21.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_21.bm deleted file mode 100644 index f4a2f73c5..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_22.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_22.bm deleted file mode 100644 index a121198fa..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_23.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_23.bm deleted file mode 100644 index 67c7d00d8..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_3.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_3.bm deleted file mode 100644 index 76a13e552..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_4.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_4.bm deleted file mode 100644 index 20025a627..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_5.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_5.bm deleted file mode 100644 index 31a2b731b..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_6.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_6.bm deleted file mode 100644 index cc181a093..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_7.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_7.bm deleted file mode 100644 index 0d5426cf0..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_8.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_8.bm deleted file mode 100644 index a40d4f30a..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/frame_9.bm b/assets/resources/dolphin/MjK_Akira_128x64/frame_9.bm deleted file mode 100644 index 2714d4819..000000000 Binary files a/assets/resources/dolphin/MjK_Akira_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Akira_128x64/meta.txt b/assets/resources/dolphin/MjK_Akira_128x64/meta.txt deleted file mode 100644 index bc4bf4a18..000000000 --- a/assets/resources/dolphin/MjK_Akira_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 24 -Active frames: 2 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 13 19 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 - -Slot: 0 -X: 1 -Y: 17 -Text: I am Mad Scientist -AlignH: Right -AlignV: Bottom -StartFrame: 2 -EndFrame: 9 - -Slot: 0 -X: 1 -Y: 17 -Text: It's so COOOOL -AlignH: Right -AlignV: Center -StartFrame: 10 -EndFrame: 15 - -Slot: 0 -X: 1 -Y: 17 -Text: Sonovabitch -AlignH: Left -AlignV: Center -StartFrame: 16 -EndFrame: 23 diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_0.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_0.bm deleted file mode 100644 index 1045b25fc..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_1.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_1.bm deleted file mode 100644 index 7beea2e41..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_10.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_10.bm deleted file mode 100644 index 2e7c814cc..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_100.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_100.bm deleted file mode 100644 index 0c622f49e..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_100.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_101.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_101.bm deleted file mode 100644 index f4a4e8a40..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_101.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_102.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_102.bm deleted file mode 100644 index 0dc474469..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_102.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_103.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_103.bm deleted file mode 100644 index 769b08064..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_103.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_104.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_104.bm deleted file mode 100644 index 82d81eeb1..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_104.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_105.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_105.bm deleted file mode 100644 index d2eccb533..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_105.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_106.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_106.bm deleted file mode 100644 index 0f0f98dcf..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_106.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_107.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_107.bm deleted file mode 100644 index 4b1dfd8f4..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_107.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_108.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_108.bm deleted file mode 100644 index 2ede18e32..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_108.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_109.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_109.bm deleted file mode 100644 index 368321c31..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_109.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_11.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_11.bm deleted file mode 100644 index 30dc184b4..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_110.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_110.bm deleted file mode 100644 index 165d8f532..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_110.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_111.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_111.bm deleted file mode 100644 index 01a26f3f5..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_111.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_112.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_112.bm deleted file mode 100644 index 0a66540a1..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_112.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_113.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_113.bm deleted file mode 100644 index 55acace83..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_113.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_114.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_114.bm deleted file mode 100644 index a373a76c6..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_114.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_115.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_115.bm deleted file mode 100644 index 552708195..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_115.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_116.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_116.bm deleted file mode 100644 index 9dbb45b4d..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_116.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_117.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_117.bm deleted file mode 100644 index 581b4c897..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_117.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_118.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_118.bm deleted file mode 100644 index c9d12829a..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_118.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_119.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_119.bm deleted file mode 100644 index abd659aed..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_119.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_12.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_12.bm deleted file mode 100644 index 5979d01e2..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_120.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_120.bm deleted file mode 100644 index 6fdcf8c6b..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_120.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_121.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_121.bm deleted file mode 100644 index 89cc03c38..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_121.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_122.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_122.bm deleted file mode 100644 index e6eb9ca62..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_122.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_123.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_123.bm deleted file mode 100644 index b03af204f..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_123.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_124.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_124.bm deleted file mode 100644 index e64b3fd0c..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_124.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_125.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_125.bm deleted file mode 100644 index 66ace148a..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_125.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_126.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_126.bm deleted file mode 100644 index 60bbe2cac..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_126.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_127.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_127.bm deleted file mode 100644 index 948884205..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_127.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_128.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_128.bm deleted file mode 100644 index 2ea1daff7..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_128.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_129.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_129.bm deleted file mode 100644 index f8a7953ce..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_129.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_13.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_13.bm deleted file mode 100644 index eedcaedd2..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_130.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_130.bm deleted file mode 100644 index 96feb87ea..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_130.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_131.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_131.bm deleted file mode 100644 index a60937ae3..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_131.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_132.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_132.bm deleted file mode 100644 index ddc58470f..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_132.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_133.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_133.bm deleted file mode 100644 index 8d7679621..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_133.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_134.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_134.bm deleted file mode 100644 index 5623adc09..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_134.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_135.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_135.bm deleted file mode 100644 index a28e9e796..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_135.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_136.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_136.bm deleted file mode 100644 index bccda2b79..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_136.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_137.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_137.bm deleted file mode 100644 index 5f7395bdd..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_137.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_138.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_138.bm deleted file mode 100644 index acb5a472f..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_138.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_139.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_139.bm deleted file mode 100644 index 32cb62247..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_139.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_14.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_14.bm deleted file mode 100644 index 14aed51b0..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_140.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_140.bm deleted file mode 100644 index e0e668bef..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_140.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_141.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_141.bm deleted file mode 100644 index dfa1eb043..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_141.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_142.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_142.bm deleted file mode 100644 index a038e75ac..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_142.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_143.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_143.bm deleted file mode 100644 index 1c5765cce..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_143.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_144.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_144.bm deleted file mode 100644 index 214abd62e..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_144.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_145.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_145.bm deleted file mode 100644 index 3b61d72b4..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_145.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_146.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_146.bm deleted file mode 100644 index e40684692..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_146.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_147.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_147.bm deleted file mode 100644 index 532aff7eb..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_147.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_148.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_148.bm deleted file mode 100644 index cfb7fb409..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_148.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_149.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_149.bm deleted file mode 100644 index de547c779..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_149.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_15.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_15.bm deleted file mode 100644 index 200d44e68..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_150.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_150.bm deleted file mode 100644 index 82c803405..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_150.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_151.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_151.bm deleted file mode 100644 index 40d442467..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_151.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_152.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_152.bm deleted file mode 100644 index cf1b40366..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_152.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_153.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_153.bm deleted file mode 100644 index 0c622f49e..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_153.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_154.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_154.bm deleted file mode 100644 index f4a4e8a40..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_154.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_155.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_155.bm deleted file mode 100644 index 0dc474469..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_155.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_156.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_156.bm deleted file mode 100644 index 769b08064..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_156.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_157.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_157.bm deleted file mode 100644 index 82d81eeb1..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_157.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_158.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_158.bm deleted file mode 100644 index d2eccb533..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_158.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_159.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_159.bm deleted file mode 100644 index 0f0f98dcf..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_159.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_16.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_16.bm deleted file mode 100644 index 6a4a63e5f..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_160.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_160.bm deleted file mode 100644 index 4b1dfd8f4..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_160.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_161.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_161.bm deleted file mode 100644 index 2ede18e32..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_161.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_162.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_162.bm deleted file mode 100644 index 368321c31..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_162.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_163.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_163.bm deleted file mode 100644 index 165d8f532..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_163.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_164.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_164.bm deleted file mode 100644 index 01a26f3f5..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_164.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_165.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_165.bm deleted file mode 100644 index 0a66540a1..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_165.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_166.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_166.bm deleted file mode 100644 index 55acace83..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_166.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_167.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_167.bm deleted file mode 100644 index a373a76c6..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_167.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_168.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_168.bm deleted file mode 100644 index 552708195..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_168.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_169.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_169.bm deleted file mode 100644 index 9dbb45b4d..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_169.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_17.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_17.bm deleted file mode 100644 index b6862fc53..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_170.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_170.bm deleted file mode 100644 index 581b4c897..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_170.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_171.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_171.bm deleted file mode 100644 index c9d12829a..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_171.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_172.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_172.bm deleted file mode 100644 index d06a247e7..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_172.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_173.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_173.bm deleted file mode 100644 index c2a354782..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_173.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_174.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_174.bm deleted file mode 100644 index d093fada3..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_174.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_175.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_175.bm deleted file mode 100644 index 2e61e5f1b..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_175.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_176.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_176.bm deleted file mode 100644 index 5fbdb25e4..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_176.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_177.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_177.bm deleted file mode 100644 index b02701c3d..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_177.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_178.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_178.bm deleted file mode 100644 index 23587fba4..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_178.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_179.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_179.bm deleted file mode 100644 index 7244f95c8..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_179.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_18.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_18.bm deleted file mode 100644 index 3050a4991..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_180.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_180.bm deleted file mode 100644 index 2183e6edb..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_180.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_181.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_181.bm deleted file mode 100644 index 67e6806db..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_181.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_182.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_182.bm deleted file mode 100644 index aa2a0dcde..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_182.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_183.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_183.bm deleted file mode 100644 index 90cfd92d8..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_183.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_184.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_184.bm deleted file mode 100644 index 6c3439529..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_184.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_185.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_185.bm deleted file mode 100644 index 2fcb515eb..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_185.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_186.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_186.bm deleted file mode 100644 index 8a00b1ef6..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_186.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_187.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_187.bm deleted file mode 100644 index 82466b08c..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_187.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_188.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_188.bm deleted file mode 100644 index 00c07e38a..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_188.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_19.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_19.bm deleted file mode 100644 index 2136e0958..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_2.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_2.bm deleted file mode 100644 index 94f256a63..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_20.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_20.bm deleted file mode 100644 index da8f6d52b..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_21.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_21.bm deleted file mode 100644 index 913433498..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_22.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_22.bm deleted file mode 100644 index f461e1a32..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_23.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_23.bm deleted file mode 100644 index c75d64a41..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_24.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_24.bm deleted file mode 100644 index 74aa37d7b..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_25.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_25.bm deleted file mode 100644 index 0e8595a4f..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_26.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_26.bm deleted file mode 100644 index 8b5e4dca4..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_27.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_27.bm deleted file mode 100644 index 115d04ea0..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_28.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_28.bm deleted file mode 100644 index de7da1a59..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_29.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_29.bm deleted file mode 100644 index 4bf1aa072..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_3.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_3.bm deleted file mode 100644 index 2a27d44ed..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_30.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_30.bm deleted file mode 100644 index 496159243..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_31.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_31.bm deleted file mode 100644 index 1e1f25b4d..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_32.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_32.bm deleted file mode 100644 index fbc98bafe..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_33.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_33.bm deleted file mode 100644 index 07f8a526b..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_34.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_34.bm deleted file mode 100644 index e89a4c514..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_35.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_35.bm deleted file mode 100644 index 57c769c7e..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_36.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_36.bm deleted file mode 100644 index 85341c2f3..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_37.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_37.bm deleted file mode 100644 index bbd96de8d..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_38.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_38.bm deleted file mode 100644 index 1cbb70ea6..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_39.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_39.bm deleted file mode 100644 index dcc7c2718..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_4.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_4.bm deleted file mode 100644 index 3b6839bdb..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_40.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_40.bm deleted file mode 100644 index 08e511083..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_41.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_41.bm deleted file mode 100644 index 0b6698b12..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_42.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_42.bm deleted file mode 100644 index 137da5fa9..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_43.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_43.bm deleted file mode 100644 index e11a910a2..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_44.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_44.bm deleted file mode 100644 index 6d7021a2c..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_45.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_45.bm deleted file mode 100644 index dab6f91c3..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_46.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_46.bm deleted file mode 100644 index e785fb8d2..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_47.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_47.bm deleted file mode 100644 index 595ef5ded..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_48.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_48.bm deleted file mode 100644 index 7c9c2c4aa..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_49.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_49.bm deleted file mode 100644 index b7d504cde..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_5.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_5.bm deleted file mode 100644 index 9d8abf995..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_50.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_50.bm deleted file mode 100644 index 0b0391a53..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_50.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_51.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_51.bm deleted file mode 100644 index d23411bd0..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_51.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_52.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_52.bm deleted file mode 100644 index f7543d237..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_52.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_53.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_53.bm deleted file mode 100644 index e9d738edc..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_53.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_54.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_54.bm deleted file mode 100644 index 9e4401cfc..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_54.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_55.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_55.bm deleted file mode 100644 index 6206ad32e..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_55.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_56.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_56.bm deleted file mode 100644 index d9c4c7949..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_56.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_57.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_57.bm deleted file mode 100644 index 7ca7ba1ac..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_57.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_58.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_58.bm deleted file mode 100644 index 395ae0347..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_58.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_59.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_59.bm deleted file mode 100644 index 4b7b1c898..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_59.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_6.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_6.bm deleted file mode 100644 index 36f70e1f1..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_60.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_60.bm deleted file mode 100644 index ab411d7f3..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_60.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_61.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_61.bm deleted file mode 100644 index 6b330766f..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_61.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_62.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_62.bm deleted file mode 100644 index 55f962910..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_62.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_63.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_63.bm deleted file mode 100644 index 42fdb65db..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_63.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_64.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_64.bm deleted file mode 100644 index 0d1204cd7..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_64.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_65.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_65.bm deleted file mode 100644 index 0e421fdf1..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_65.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_66.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_66.bm deleted file mode 100644 index 19eec925c..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_66.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_67.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_67.bm deleted file mode 100644 index 167280a64..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_67.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_68.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_68.bm deleted file mode 100644 index eb5d45acb..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_68.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_69.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_69.bm deleted file mode 100644 index 45c31a557..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_69.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_7.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_7.bm deleted file mode 100644 index 9e5ee49ad..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_70.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_70.bm deleted file mode 100644 index 109187e81..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_70.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_71.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_71.bm deleted file mode 100644 index 38564336e..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_71.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_72.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_72.bm deleted file mode 100644 index 0d1292dc5..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_72.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_73.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_73.bm deleted file mode 100644 index 1de0079c1..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_73.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_74.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_74.bm deleted file mode 100644 index bcb02ea45..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_74.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_75.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_75.bm deleted file mode 100644 index 625ec9be7..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_75.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_76.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_76.bm deleted file mode 100644 index fa8bad9f1..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_76.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_77.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_77.bm deleted file mode 100644 index 949060b3c..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_77.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_78.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_78.bm deleted file mode 100644 index 308f09ae4..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_78.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_79.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_79.bm deleted file mode 100644 index 99f57c103..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_79.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_8.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_8.bm deleted file mode 100644 index cba208c4b..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_80.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_80.bm deleted file mode 100644 index 449cd7e34..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_80.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_81.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_81.bm deleted file mode 100644 index a5af03359..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_81.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_82.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_82.bm deleted file mode 100644 index cbb01ac57..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_82.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_83.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_83.bm deleted file mode 100644 index 6a377e449..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_83.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_84.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_84.bm deleted file mode 100644 index 199edcbf6..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_84.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_85.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_85.bm deleted file mode 100644 index f552f3e10..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_85.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_86.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_86.bm deleted file mode 100644 index 3df44063b..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_86.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_87.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_87.bm deleted file mode 100644 index 8dda9304a..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_87.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_88.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_88.bm deleted file mode 100644 index 8833c7c33..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_88.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_89.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_89.bm deleted file mode 100644 index 076f7895f..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_89.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_9.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_9.bm deleted file mode 100644 index 100edf9e0..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_90.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_90.bm deleted file mode 100644 index 5c28ee595..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_90.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_91.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_91.bm deleted file mode 100644 index c88ac2309..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_91.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_92.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_92.bm deleted file mode 100644 index f6f5f8136..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_92.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_93.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_93.bm deleted file mode 100644 index 690715665..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_93.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_94.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_94.bm deleted file mode 100644 index 39cbce55f..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_94.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_95.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_95.bm deleted file mode 100644 index 6148004f9..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_95.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_96.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_96.bm deleted file mode 100644 index de547c779..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_96.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_97.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_97.bm deleted file mode 100644 index 82c803405..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_97.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_98.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_98.bm deleted file mode 100644 index 40d442467..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_98.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_99.bm b/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_99.bm deleted file mode 100644 index cf1b40366..000000000 Binary files a/assets/resources/dolphin/MjK_LionsRoar_128x64/frame_99.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_LionsRoar_128x64/meta.txt b/assets/resources/dolphin/MjK_LionsRoar_128x64/meta.txt deleted file mode 100644 index 684075f48..000000000 --- a/assets/resources/dolphin/MjK_LionsRoar_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 118 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 34 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 -Active cycles: 1 -Frame rate: 8 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_0.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_0.bm deleted file mode 100644 index e29c2707e..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_1.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_1.bm deleted file mode 100644 index 86cb085bd..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_10.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_10.bm deleted file mode 100644 index 29f789693..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_11.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_11.bm deleted file mode 100644 index 9efa1afda..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_12.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_12.bm deleted file mode 100644 index bb02d425a..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_13.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_13.bm deleted file mode 100644 index a13d3a0b9..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_14.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_14.bm deleted file mode 100644 index eabf51625..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_15.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_15.bm deleted file mode 100644 index a343d2832..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_16.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_16.bm deleted file mode 100644 index 7e696b1ea..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_17.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_17.bm deleted file mode 100644 index f0450536d..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_18.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_18.bm deleted file mode 100644 index ac07f2627..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_19.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_19.bm deleted file mode 100644 index 20add4b8a..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_2.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_2.bm deleted file mode 100644 index fdc463e5a..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_20.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_20.bm deleted file mode 100644 index e75346588..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_21.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_21.bm deleted file mode 100644 index 5133e6000..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_22.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_22.bm deleted file mode 100644 index b387a07cf..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_23.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_23.bm deleted file mode 100644 index aa1a09a37..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_3.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_3.bm deleted file mode 100644 index 75c40ce24..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_4.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_4.bm deleted file mode 100644 index 997c608e5..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_5.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_5.bm deleted file mode 100644 index 8b8a78607..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_6.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_6.bm deleted file mode 100644 index 0b05bc216..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_7.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_7.bm deleted file mode 100644 index 5ac81b426..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_8.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_8.bm deleted file mode 100644 index d04d22573..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_9.bm b/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_9.bm deleted file mode 100644 index 5282e190e..000000000 Binary files a/assets/resources/dolphin/MjK_Mad_Sci_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Mad_Sci_128x64/meta.txt b/assets/resources/dolphin/MjK_Mad_Sci_128x64/meta.txt deleted file mode 100644 index 971cac3ba..000000000 --- a/assets/resources/dolphin/MjK_Mad_Sci_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 24 -Active frames: 2 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 13 19 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 1 -Y: 17 -Text: I am Mad Scientist -AlignH: Right -AlignV: Bottom -StartFrame: 2 -EndFrame: 9 - -Slot: 0 -X: 1 -Y: 17 -Text: It's so COOOOL -AlignH: Right -AlignV: Center -StartFrame: 10 -EndFrame: 15 - -Slot: 0 -X: 1 -Y: 17 -Text: Sonovabitch -AlignH: Left -AlignV: Center -StartFrame: 16 -EndFrame: 23 diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_0.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_0.bm deleted file mode 100644 index f79633a73..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_1.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_1.bm deleted file mode 100644 index 31d4cc7de..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_10.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_10.bm deleted file mode 100644 index 302982e9e..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_11.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_11.bm deleted file mode 100644 index 4562c70f2..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_12.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_12.bm deleted file mode 100644 index a0a8f62e1..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_13.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_13.bm deleted file mode 100644 index c51d5cbba..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_14.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_14.bm deleted file mode 100644 index 6df055ae4..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_15.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_15.bm deleted file mode 100644 index 20d4d6b04..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_16.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_16.bm deleted file mode 100644 index 25ee71522..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_17.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_17.bm deleted file mode 100644 index 25ee71522..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_18.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_18.bm deleted file mode 100644 index 632f8353b..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_19.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_19.bm deleted file mode 100644 index 6237859b3..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_2.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_2.bm deleted file mode 100644 index c30695d39..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_20.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_20.bm deleted file mode 100644 index 900e3e8f3..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_21.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_21.bm deleted file mode 100644 index 35d270b04..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_22.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_22.bm deleted file mode 100644 index d90b2608e..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_23.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_23.bm deleted file mode 100644 index 7f268fbe1..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_24.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_24.bm deleted file mode 100644 index 198d91cfb..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_25.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_25.bm deleted file mode 100644 index e24352d49..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_26.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_26.bm deleted file mode 100644 index 72501cb58..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_27.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_27.bm deleted file mode 100644 index d157e16b0..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_28.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_28.bm deleted file mode 100644 index 99ddd7d57..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_29.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_29.bm deleted file mode 100644 index b69c1142a..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_3.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_3.bm deleted file mode 100644 index 061694be2..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_30.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_30.bm deleted file mode 100644 index 4e21f3d3f..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_31.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_31.bm deleted file mode 100644 index fe58ea3cf..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_32.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_32.bm deleted file mode 100644 index 09b3279db..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_33.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_33.bm deleted file mode 100644 index 091e4425c..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_34.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_34.bm deleted file mode 100644 index 8112b9772..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_35.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_35.bm deleted file mode 100644 index a4c61822e..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_36.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_36.bm deleted file mode 100644 index d7d8b2105..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_37.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_37.bm deleted file mode 100644 index 347f249cb..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_38.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_38.bm deleted file mode 100644 index 22da71d89..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_39.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_39.bm deleted file mode 100644 index feceae113..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_4.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_4.bm deleted file mode 100644 index ac11ead8c..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_40.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_40.bm deleted file mode 100644 index 8b6e3167b..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_41.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_41.bm deleted file mode 100644 index 94fda77a5..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_42.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_42.bm deleted file mode 100644 index acff9f16f..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_43.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_43.bm deleted file mode 100644 index 551e4aa8b..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_44.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_44.bm deleted file mode 100644 index 556d430d9..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_45.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_45.bm deleted file mode 100644 index 62ce08f97..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_46.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_46.bm deleted file mode 100644 index c1e09700e..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_47.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_47.bm deleted file mode 100644 index 8e480efff..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_48.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_48.bm deleted file mode 100644 index 5ad400bfa..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_49.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_49.bm deleted file mode 100644 index ee4c990f1..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_5.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_5.bm deleted file mode 100644 index 87215bc82..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_6.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_6.bm deleted file mode 100644 index 71b23b25c..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_7.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_7.bm deleted file mode 100644 index 11c2b7b4d..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_8.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_8.bm deleted file mode 100644 index a2f424fe4..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/frame_9.bm b/assets/resources/dolphin/MjK_Starfield_128x64/frame_9.bm deleted file mode 100644 index 2b89d95d4..000000000 Binary files a/assets/resources/dolphin/MjK_Starfield_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/MjK_Starfield_128x64/meta.txt b/assets/resources/dolphin/MjK_Starfield_128x64/meta.txt deleted file mode 100644 index 5d2124cd7..000000000 --- a/assets/resources/dolphin/MjK_Starfield_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 50 -Active frames: 2 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_0.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_0.bm deleted file mode 100644 index 84e1670ad..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_1.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_1.bm deleted file mode 100644 index 93b3a9712..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_10.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_10.bm deleted file mode 100644 index 479dd97d7..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_11.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_11.bm deleted file mode 100644 index 4bb759e3b..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_12.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_12.bm deleted file mode 100644 index 3d52b98dd..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_13.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_13.bm deleted file mode 100644 index 8f8a50598..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_14.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_14.bm deleted file mode 100644 index 9f0499d70..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_15.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_15.bm deleted file mode 100644 index 6dfdb4027..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_16.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_16.bm deleted file mode 100644 index de78df900..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_17.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_17.bm deleted file mode 100644 index 7f6703c81..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_18.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_18.bm deleted file mode 100644 index 8f8b6acad..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_19.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_19.bm deleted file mode 100644 index 84382df7f..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_2.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_2.bm deleted file mode 100644 index 82ecfe260..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_20.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_20.bm deleted file mode 100644 index 9c2e7bcf4..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_21.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_21.bm deleted file mode 100644 index bb5eee3d7..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_22.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_22.bm deleted file mode 100644 index c2db660ac..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_23.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_23.bm deleted file mode 100644 index 46c3b0b98..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_24.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_24.bm deleted file mode 100644 index 7595e4b1a..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_25.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_25.bm deleted file mode 100644 index 837f30dd9..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_26.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_26.bm deleted file mode 100644 index 84592381c..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_27.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_27.bm deleted file mode 100644 index 72e0c36dd..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_28.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_28.bm deleted file mode 100644 index 8dce66410..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_29.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_29.bm deleted file mode 100644 index 78657425c..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_3.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_3.bm deleted file mode 100644 index bd3b529f1..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_30.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_30.bm deleted file mode 100644 index 14ecec149..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_31.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_31.bm deleted file mode 100644 index 505e51ce2..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_32.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_32.bm deleted file mode 100644 index 5b1a69f42..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_33.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_33.bm deleted file mode 100644 index 7a9c9ccd4..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_34.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_34.bm deleted file mode 100644 index 944583997..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_35.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_35.bm deleted file mode 100644 index d5e0f24b8..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_36.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_36.bm deleted file mode 100644 index 4b2ef2e80..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_37.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_37.bm deleted file mode 100644 index b5858b066..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_38.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_38.bm deleted file mode 100644 index 15c69ce06..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_39.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_39.bm deleted file mode 100644 index c82fa1960..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_4.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_4.bm deleted file mode 100644 index 387529ae5..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_40.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_40.bm deleted file mode 100644 index 9808aeab5..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_41.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_41.bm deleted file mode 100644 index 7669e5ebc..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_42.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_42.bm deleted file mode 100644 index ff9751d1d..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_43.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_43.bm deleted file mode 100644 index fa797004a..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_44.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_44.bm deleted file mode 100644 index b300ad011..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_45.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_45.bm deleted file mode 100644 index f02100b1b..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_46.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_46.bm deleted file mode 100644 index 2fe2da4a0..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_47.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_47.bm deleted file mode 100644 index e8aa854e6..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_48.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_48.bm deleted file mode 100644 index 3d68cba9f..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_49.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_49.bm deleted file mode 100644 index e9199c2d3..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_5.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_5.bm deleted file mode 100644 index 5bcacd783..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_50.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_50.bm deleted file mode 100644 index 03126db98..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_50.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_51.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_51.bm deleted file mode 100644 index 488caf5fa..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_51.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_52.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_52.bm deleted file mode 100644 index ee482facf..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_52.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_53.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_53.bm deleted file mode 100644 index 348ad45a3..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_53.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_54.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_54.bm deleted file mode 100644 index d7579a00c..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_54.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_55.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_55.bm deleted file mode 100644 index f0334cb2a..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_55.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_56.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_56.bm deleted file mode 100644 index de9e741d6..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_56.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_6.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_6.bm deleted file mode 100644 index 7944ea4b4..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_7.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_7.bm deleted file mode 100644 index 9135b3322..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_8.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_8.bm deleted file mode 100644 index 227cd1005..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_9.bm b/assets/resources/dolphin/NeonK_Jiji_Milk/frame_9.bm deleted file mode 100644 index 9c5f5dd9c..000000000 Binary files a/assets/resources/dolphin/NeonK_Jiji_Milk/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/NeonK_Jiji_Milk/meta.txt b/assets/resources/dolphin/NeonK_Jiji_Milk/meta.txt deleted file mode 100644 index f069f47ee..000000000 --- a/assets/resources/dolphin/NeonK_Jiji_Milk/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 57 -Active frames: 4 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 46 48 49 50 51 52 53 54 55 56 3 2 1 0 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_0.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_0.bm deleted file mode 100644 index 4efaa7dbb..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_1.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_1.bm deleted file mode 100644 index cbec3f8cb..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_10.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_10.bm deleted file mode 100644 index b0d64bd30..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_11.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_11.bm deleted file mode 100644 index 8d4fa5079..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_12.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_12.bm deleted file mode 100644 index 6ea3e040b..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_13.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_13.bm deleted file mode 100644 index 30359119d..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_14.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_14.bm deleted file mode 100644 index 29cd4b089..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_15.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_15.bm deleted file mode 100644 index 9e78ae2a7..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_16.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_16.bm deleted file mode 100644 index 764c5dbe1..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_17.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_17.bm deleted file mode 100644 index 6df34b380..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_18.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_18.bm deleted file mode 100644 index 3a159b6c6..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_19.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_19.bm deleted file mode 100644 index f2833aace..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_2.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_2.bm deleted file mode 100644 index 2303e1dbe..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_20.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_20.bm deleted file mode 100644 index 6bdb697f5..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_21.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_21.bm deleted file mode 100644 index 87da502f8..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_22.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_22.bm deleted file mode 100644 index 03f3da30e..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_23.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_23.bm deleted file mode 100644 index f6f789d99..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_24.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_24.bm deleted file mode 100644 index c3d31809f..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_25.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_25.bm deleted file mode 100644 index 4a7a76d78..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_26.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_26.bm deleted file mode 100644 index 6265a9402..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_27.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_27.bm deleted file mode 100644 index de2186bf9..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_28.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_28.bm deleted file mode 100644 index 908b6da98..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_29.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_29.bm deleted file mode 100644 index 37cc88691..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_3.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_3.bm deleted file mode 100644 index 77f76b242..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_30.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_30.bm deleted file mode 100644 index b4485e6e8..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_31.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_31.bm deleted file mode 100644 index 50b934e7d..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_32.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_32.bm deleted file mode 100644 index c23f983ee..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_33.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_33.bm deleted file mode 100644 index 7118f5ec7..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_34.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_34.bm deleted file mode 100644 index 41b4bc895..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_35.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_35.bm deleted file mode 100644 index df5339d80..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_36.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_36.bm deleted file mode 100644 index 8f3259a24..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_37.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_37.bm deleted file mode 100644 index f6c3f231e..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_38.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_38.bm deleted file mode 100644 index b5db27354..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_39.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_39.bm deleted file mode 100644 index 2c54a4c6f..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_4.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_4.bm deleted file mode 100644 index 30dff1d56..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_40.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_40.bm deleted file mode 100644 index d00d3a4b6..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_41.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_41.bm deleted file mode 100644 index 443d66c96..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_42.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_42.bm deleted file mode 100644 index 959501ed6..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_43.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_43.bm deleted file mode 100644 index 3f2760efb..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_44.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_44.bm deleted file mode 100644 index a36afeaaa..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_45.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_45.bm deleted file mode 100644 index 3463d4d65..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_46.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_46.bm deleted file mode 100644 index 793262cce..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_47.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_47.bm deleted file mode 100644 index 049053936..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_5.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_5.bm deleted file mode 100644 index ec94cf7a2..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_6.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_6.bm deleted file mode 100644 index 87b31d67b..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_7.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_7.bm deleted file mode 100644 index 28328fe35..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_8.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_8.bm deleted file mode 100644 index d655901ff..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_9.bm b/assets/resources/dolphin/On3_Pi3ce_128x64/frame_9.bm deleted file mode 100644 index d010f53b8..000000000 Binary files a/assets/resources/dolphin/On3_Pi3ce_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/On3_Pi3ce_128x64/meta.txt b/assets/resources/dolphin/On3_Pi3ce_128x64/meta.txt deleted file mode 100644 index c04e72fcc..000000000 --- a/assets/resources/dolphin/On3_Pi3ce_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 47 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/README.md b/assets/resources/dolphin/README.md index 6deeef23b..632933385 100644 --- a/assets/resources/dolphin/README.md +++ b/assets/resources/dolphin/README.md @@ -1,5 +1,8 @@ -- Not all of these animations are here, some are [here](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin) +- Animations: Trimmed out the Flipper animations. `/ext/dolphin` folder on your Flipper should now be managed by you! [Copy this folder (RM Select)](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin-RMselect) or [this folder (RM minimal)](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin-minimal) if you don't want to do the work but want more animations. +- - Rename the [minimal animation file](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/assets/resources/dolphin/manifest.txt.exampleMin) to see it, or [RM select](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/assets/resources/dolphin/manifest.txt.exampleRM) if you have copied over [the RM select](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin-RMselect) animations. + +- Not all of these animations are here, some are [here](https://github.com/RogueMaster/awesome-flipperzero-withModules/tree/rogue_main/dolphin-all) - Assets: Includes New Dolphin Animations: [Rick Roll, Matrix & Swim animations (By qqMajiKpp)], [Rukamon 2x and Agumon animations (By Syrius)], [Mew (By Arkaivos)], [Eye of the Flipper (By Kuronons)], [Shodan (By qqMajiKpp)], [Sirene & The Witch (By Haseo)], [Dino & A New Hope (By Haseo)], [Earth Arcadia (By Kuronons)], [Kuronons Black Flags Collection (21 Animations)(By Kuronons)], [D.Va (By Haseo)], [GITS (By Haseo)], [Slayers (By qqMajiKpp)], [P0liwhirl (By Panzer00Z)], [RogueMaster CFW Animation (By Kuronons)], [Nyan Cat (By Haseo)], [L (By Kuronons)], [Laughing Man (By Kuronons)], [B0ws3r (By Haseo)], [Kuronons Black Flags Collection VOL 2 (9 Animations)(By Kuronons)], [Thanks for all the fish (By qqMajiKpp)], [OCP, Skynet and Weyland (By Kuronons)], [Mario (By Haseo)], [Umbrella (By Haseo)], [Trioptimum (By qqMajiKpp)], [Tyrell & Cyberdyne (By Kuronons)], [Allen & Maha (By Haseo)], [Starfield (By qqMajiKpp)], [DJ (By ut1s)], [Fireplace (By Friend of xMasterX)], [Akira & Mad Scientist (By qqMajiKpp)], [Shar!ngan (By Haseo)], [Lions Roar (By qqMajiKpp)], [0ne Pi3ce (By Haseo)], [Calcifier & Last Unicorn (By NeonKodama)], [Jiji Milk (By NeonKodama)], [Halloween, Hexadecimal, Init D Water, Kam3Ham3Ha, Kirbs, Kirbs Confused, M3gamanZ3r0 Battle, OP G3ar 4, Z3lda 0h & Zelda R3ady (By RogueMaster)], [Rekall (By Kuronons)], [Stick Fight (By Sasquach)], [G0ku, Lufy & Mug1wara (By Haseo)], [Tardi5 (By RogueMaster)] & [80s (By Haseo)] - Assets: Includes New Dolphin Animations: [School Days, Whistper of the Heart and The Legend of Zelda (By stop_oxy] - Assets: Includes New Dolphin Animations: [Rogue Master Custom Firmware (By Sasquach)] @@ -12,12 +15,17 @@ - Assets: Includes New Dolphin Animations: [Narut0 (By Sasquach)] - Assets: Includes New Dolphin Animations: [Blaster (By Sasquach)] - Assets: Includes New Dolphin Animations: [Flipper City (By Svaarich)] +- Unleashed PR [feature[animation]: purple rain animation #111 (By wotori)](https://github.com/DarkFlippers/unleashed-firmware/pull/111) +- Assets: Includes New Dolphin Animations: [D1g1talRa1n (By Sasquach)] ## REPOSITORIES FOR OTHER ANIMATION CREATORS: ## [Kuronons Flipper Animations](https://github.com/Kuronons/FZ_graphics) ## [Haseo Flipper Animations](https://github.com/Haseosama/FZ_Animations) ## [Talking-Sasquach Flipper Animations](https://github.com/skizzophrenic/Talking-Sasquach) +## [stopoxy Flipper Animations](https://github.com/stopoxy/FZAnimations) +## [HexxedBitHeadz Flipper Animations](https://github.com/HexxedBitHeadz/FlipperZeroWallpaper) +## [CharlesTheGreat77 Flipper Animations](https://github.com/CharlesTheGreat77/FlipperZeroAnimation) ## Some Previews Provided By Creators: diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_0.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_0.bm deleted file mode 100644 index a49b5c532..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_1.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_1.bm deleted file mode 100644 index 58e86d72f..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_10.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_10.bm deleted file mode 100644 index ff9ed6ed9..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_11.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_11.bm deleted file mode 100644 index 6e9b07bbf..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_12.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_12.bm deleted file mode 100644 index 4bec42dfb..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_13.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_13.bm deleted file mode 100644 index 5e4669829..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_14.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_14.bm deleted file mode 100644 index 44eba5e7f..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_15.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_15.bm deleted file mode 100644 index 872d04b74..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_16.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_16.bm deleted file mode 100644 index 791892663..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_17.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_17.bm deleted file mode 100644 index 62ebc2ce8..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_18.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_18.bm deleted file mode 100644 index 14018ef53..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_19.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_19.bm deleted file mode 100644 index 6e8bad545..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_2.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_2.bm deleted file mode 100644 index c781331b1..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_20.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_20.bm deleted file mode 100644 index cade3fe88..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_21.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_21.bm deleted file mode 100644 index 757ea719f..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_22.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_22.bm deleted file mode 100644 index 2a44726bb..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_23.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_23.bm deleted file mode 100644 index 5ec69a96a..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_24.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_24.bm deleted file mode 100644 index ec8b0807e..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_25.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_25.bm deleted file mode 100644 index 429f06d96..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_26.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_26.bm deleted file mode 100644 index 592187cd5..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_27.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_27.bm deleted file mode 100644 index 93f429333..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_28.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_28.bm deleted file mode 100644 index 7992419ca..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_29.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_29.bm deleted file mode 100644 index c12c1d262..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_3.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_3.bm deleted file mode 100644 index dcf7b5e34..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_30.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_30.bm deleted file mode 100644 index 4fd230621..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_31.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_31.bm deleted file mode 100644 index 9543cb627..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_32.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_32.bm deleted file mode 100644 index 30fce7f86..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_33.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_33.bm deleted file mode 100644 index d203c1401..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_34.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_34.bm deleted file mode 100644 index 250418561..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_35.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_35.bm deleted file mode 100644 index c51e715db..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_36.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_36.bm deleted file mode 100644 index a6da0eeab..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_37.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_37.bm deleted file mode 100644 index 1774cee24..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_4.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_4.bm deleted file mode 100644 index c083be687..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_5.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_5.bm deleted file mode 100644 index a4abac97a..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_6.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_6.bm deleted file mode 100644 index 63df2b655..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_7.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_7.bm deleted file mode 100644 index 7e632ac9d..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_8.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_8.bm deleted file mode 100644 index da0837d10..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_9.bm b/assets/resources/dolphin/RM_AikaZer0_128x64/frame_9.bm deleted file mode 100644 index b082792d7..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_128x64/meta.txt b/assets/resources/dolphin/RM_AikaZer0_128x64/meta.txt deleted file mode 100644 index 8a8ce6a99..000000000 --- a/assets/resources/dolphin/RM_AikaZer0_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 7 -Active frames: 31 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_0.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_0.bm deleted file mode 100644 index 5693d4ca8..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_1.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_1.bm deleted file mode 100644 index 9de69d6d0..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_10.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_10.bm deleted file mode 100644 index 8397f3a17..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_11.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_11.bm deleted file mode 100644 index 658334a16..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_12.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_12.bm deleted file mode 100644 index d7e090205..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_13.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_13.bm deleted file mode 100644 index 45b6a6fa2..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_14.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_14.bm deleted file mode 100644 index 45798e3d3..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_15.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_15.bm deleted file mode 100644 index d56890c28..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_16.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_16.bm deleted file mode 100644 index d5163914a..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_17.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_17.bm deleted file mode 100644 index c2b938844..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_18.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_18.bm deleted file mode 100644 index 79f5b926e..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_19.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_19.bm deleted file mode 100644 index 0de486cd7..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_2.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_2.bm deleted file mode 100644 index b7eaa4c6e..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_20.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_20.bm deleted file mode 100644 index c88ae8839..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_3.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_3.bm deleted file mode 100644 index c82f54755..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_4.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_4.bm deleted file mode 100644 index ae57494c7..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_5.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_5.bm deleted file mode 100644 index 661cc1779..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_6.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_6.bm deleted file mode 100644 index b60d4aedf..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_7.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_7.bm deleted file mode 100644 index 389c9647f..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_8.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_8.bm deleted file mode 100644 index 53150bd2e..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_9.bm b/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_9.bm deleted file mode 100644 index 759943409..000000000 Binary files a/assets/resources/dolphin/RM_AikaZer0_2_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_AikaZer0_2_128x64/meta.txt b/assets/resources/dolphin/RM_AikaZer0_2_128x64/meta.txt deleted file mode 100644 index 68279da9c..000000000 --- a/assets/resources/dolphin/RM_AikaZer0_2_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 7 -Active frames: 14 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_0.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_0.bm deleted file mode 100644 index e6da26eea..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_1.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_1.bm deleted file mode 100644 index 42356b39f..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_10.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_10.bm deleted file mode 100644 index cce6cc01a..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_11.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_11.bm deleted file mode 100644 index 27e178bdf..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_12.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_12.bm deleted file mode 100644 index e40ca653f..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_13.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_13.bm deleted file mode 100644 index 9ceb32041..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_14.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_14.bm deleted file mode 100644 index 2d8f09fe9..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_2.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_2.bm deleted file mode 100644 index e2505feb2..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_3.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_3.bm deleted file mode 100644 index daa5627b6..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_4.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_4.bm deleted file mode 100644 index 7aca85815..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_5.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_5.bm deleted file mode 100644 index 6a02bd3ed..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_6.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_6.bm deleted file mode 100644 index 972a04712..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_7.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_7.bm deleted file mode 100644 index 0c53e2a5c..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_8.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_8.bm deleted file mode 100644 index 972a04712..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_9.bm b/assets/resources/dolphin/RM_BulmFlash_128x64/frame_9.bm deleted file mode 100644 index 6a02bd3ed..000000000 Binary files a/assets/resources/dolphin/RM_BulmFlash_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_BulmFlash_128x64/meta.txt b/assets/resources/dolphin/RM_BulmFlash_128x64/meta.txt deleted file mode 100644 index 902a33c20..000000000 --- a/assets/resources/dolphin/RM_BulmFlash_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 2 -Active frames: 13 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -Active cycles: 1 -Frame rate: 3 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_0.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_0.bm deleted file mode 100644 index 9468148ab..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_1.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_1.bm deleted file mode 100644 index 642d773d5..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_10.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_10.bm deleted file mode 100644 index 61a6ffda3..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_11.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_11.bm deleted file mode 100644 index f292f6efb..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_2.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_2.bm deleted file mode 100644 index 505f792d0..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_3.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_3.bm deleted file mode 100644 index e364c0072..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_4.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_4.bm deleted file mode 100644 index 28a969371..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_5.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_5.bm deleted file mode 100644 index f2dfdff4c..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_6.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_6.bm deleted file mode 100644 index 262f7b67b..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_7.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_7.bm deleted file mode 100644 index d28a81661..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_8.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_8.bm deleted file mode 100644 index a2f0cdd99..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/frame_9.bm b/assets/resources/dolphin/RM_ChiChi_128x64/frame_9.bm deleted file mode 100644 index 059ac8089..000000000 Binary files a/assets/resources/dolphin/RM_ChiChi_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_ChiChi_128x64/meta.txt b/assets/resources/dolphin/RM_ChiChi_128x64/meta.txt deleted file mode 100644 index b18e1c3dc..000000000 --- a/assets/resources/dolphin/RM_ChiChi_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 5 -Active frames: 7 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 -Active cycles: 1 -Frame rate: 3 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_0.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_0.bm deleted file mode 100644 index df209e98e..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_1.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_1.bm deleted file mode 100644 index 4022aa392..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_10.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_10.bm deleted file mode 100644 index 6e9d562b8..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_11.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_11.bm deleted file mode 100644 index 9e863772a..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_12.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_12.bm deleted file mode 100644 index a16308460..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_13.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_13.bm deleted file mode 100644 index e58524693..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_14.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_14.bm deleted file mode 100644 index 9e5be8485..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_15.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_15.bm deleted file mode 100644 index 4ca8ae573..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_16.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_16.bm deleted file mode 100644 index 0264af835..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_17.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_17.bm deleted file mode 100644 index dda06f478..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_18.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_18.bm deleted file mode 100644 index 371a862e7..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_2.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_2.bm deleted file mode 100644 index 049b97d69..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_3.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_3.bm deleted file mode 100644 index 8911d00f4..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_4.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_4.bm deleted file mode 100644 index d888ef64d..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_5.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_5.bm deleted file mode 100644 index b957f52cd..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_6.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_6.bm deleted file mode 100644 index e015c02d1..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_7.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_7.bm deleted file mode 100644 index cf5d4cdad..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_8.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_8.bm deleted file mode 100644 index 8df6f460f..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_9.bm b/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_9.bm deleted file mode 100644 index c2c6c55db..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPatPat_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPatPat_128x64/meta.txt b/assets/resources/dolphin/RM_G0kuPatPat_128x64/meta.txt deleted file mode 100644 index 4800ad1bb..000000000 --- a/assets/resources/dolphin/RM_G0kuPatPat_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 4 -Active frames: 15 -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: 3 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_0.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_0.bm deleted file mode 100644 index 8e4af96b4..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_1.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_1.bm deleted file mode 100644 index 9dcda2e40..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_10.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_10.bm deleted file mode 100644 index 123a13f63..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_11.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_11.bm deleted file mode 100644 index 8434ba8fb..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_12.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_12.bm deleted file mode 100644 index 80a2ac0ae..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_2.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_2.bm deleted file mode 100644 index 711a43c80..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_3.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_3.bm deleted file mode 100644 index 69f3bbab4..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_4.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_4.bm deleted file mode 100644 index ec77813d4..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_5.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_5.bm deleted file mode 100644 index df9a866a0..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_6.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_6.bm deleted file mode 100644 index e925864a3..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_7.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_7.bm deleted file mode 100644 index 7d777cfad..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_8.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_8.bm deleted file mode 100644 index 94303db8b..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_9.bm b/assets/resources/dolphin/RM_G0kuPew_128x64/frame_9.bm deleted file mode 100644 index 2b3605ff9..000000000 Binary files a/assets/resources/dolphin/RM_G0kuPew_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_G0kuPew_128x64/meta.txt b/assets/resources/dolphin/RM_G0kuPew_128x64/meta.txt deleted file mode 100644 index 629e44217..000000000 --- a/assets/resources/dolphin/RM_G0kuPew_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 4 -Active frames: 9 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 -Active cycles: 1 -Frame rate: 3 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_0.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_0.bm deleted file mode 100644 index 24b21a05d..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_1.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_1.bm deleted file mode 100644 index 34f807017..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_10.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_10.bm deleted file mode 100644 index 8f44658d0..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_11.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_11.bm deleted file mode 100644 index f7faf7045..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_12.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_12.bm deleted file mode 100644 index a1bd442b3..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_13.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_13.bm deleted file mode 100644 index f96e9b73d..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_14.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_14.bm deleted file mode 100644 index 081a3c57f..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_15.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_15.bm deleted file mode 100644 index 8931b0919..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_16.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_16.bm deleted file mode 100644 index c1a2b731f..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_17.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_17.bm deleted file mode 100644 index ced5f008b..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_18.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_18.bm deleted file mode 100644 index b840304ce..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_19.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_19.bm deleted file mode 100644 index f977a37df..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_2.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_2.bm deleted file mode 100644 index a0c0dfb28..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_20.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_20.bm deleted file mode 100644 index fd571c8fd..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_21.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_21.bm deleted file mode 100644 index db17988bf..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_22.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_22.bm deleted file mode 100644 index 7b0d845e8..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_23.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_23.bm deleted file mode 100644 index ef75519c4..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_24.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_24.bm deleted file mode 100644 index 94222e4bc..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_25.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_25.bm deleted file mode 100644 index 1681cdfbd..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_26.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_26.bm deleted file mode 100644 index 7ef098ef7..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_27.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_27.bm deleted file mode 100644 index 8bf5ab15a..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_28.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_28.bm deleted file mode 100644 index 5de103a4c..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_29.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_29.bm deleted file mode 100644 index fe9ebba4c..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_3.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_3.bm deleted file mode 100644 index ed186a89c..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_30.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_30.bm deleted file mode 100644 index 226c58ad0..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_31.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_31.bm deleted file mode 100644 index 758aa709c..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_32.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_32.bm deleted file mode 100644 index 74b6b2a26..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_33.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_33.bm deleted file mode 100644 index 842f647b5..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_34.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_34.bm deleted file mode 100644 index 2c33cbf85..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_35.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_35.bm deleted file mode 100644 index 48ebf3a63..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_36.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_36.bm deleted file mode 100644 index 70541295b..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_37.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_37.bm deleted file mode 100644 index 578efdf8b..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_38.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_38.bm deleted file mode 100644 index b7d17da4e..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_39.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_39.bm deleted file mode 100644 index 5e6114f62..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_4.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_4.bm deleted file mode 100644 index bed1b4c25..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_40.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_40.bm deleted file mode 100644 index 59f1847e9..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_41.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_41.bm deleted file mode 100644 index f514146c5..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_42.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_42.bm deleted file mode 100644 index 198b046c0..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_43.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_43.bm deleted file mode 100644 index 5a1dd8931..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_44.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_44.bm deleted file mode 100644 index e6cf69877..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_45.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_45.bm deleted file mode 100644 index 77de4b4cd..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_46.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_46.bm deleted file mode 100644 index 6b5bc13ce..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_47.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_47.bm deleted file mode 100644 index d17726e30..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_48.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_48.bm deleted file mode 100644 index 050507575..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_49.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_49.bm deleted file mode 100644 index 110f8d9b1..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_5.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_5.bm deleted file mode 100644 index 9413496cb..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_50.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_50.bm deleted file mode 100644 index ce161d758..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_50.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_51.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_51.bm deleted file mode 100644 index 480b5fadd..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_51.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_52.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_52.bm deleted file mode 100644 index 147440b5f..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_52.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_53.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_53.bm deleted file mode 100644 index 1aaf51435..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_53.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_54.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_54.bm deleted file mode 100644 index dd2d7ae5c..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_54.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_55.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_55.bm deleted file mode 100644 index 20e698964..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_55.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_56.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_56.bm deleted file mode 100644 index 29ad436e8..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_56.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_57.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_57.bm deleted file mode 100644 index 061686695..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_57.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_58.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_58.bm deleted file mode 100644 index 3a2486606..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_58.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_59.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_59.bm deleted file mode 100644 index be25f488a..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_59.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_6.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_6.bm deleted file mode 100644 index 8f39a33d8..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_60.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_60.bm deleted file mode 100644 index 4948508c9..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_60.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_61.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_61.bm deleted file mode 100644 index 5e55e29c7..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_61.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_62.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_62.bm deleted file mode 100644 index 0cb877596..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_62.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_63.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_63.bm deleted file mode 100644 index 5467cfa10..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_63.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_64.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_64.bm deleted file mode 100644 index 90b820f3e..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_64.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_65.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_65.bm deleted file mode 100644 index d0cee220b..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_65.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_66.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_66.bm deleted file mode 100644 index 551ede9ff..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_66.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_67.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_67.bm deleted file mode 100644 index fdf09455a..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_67.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_68.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_68.bm deleted file mode 100644 index 62599d1ae..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_68.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_69.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_69.bm deleted file mode 100644 index 2a1beb28a..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_69.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_7.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_7.bm deleted file mode 100644 index 1f329bfd6..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_70.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_70.bm deleted file mode 100644 index 45d4120e1..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_70.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_71.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_71.bm deleted file mode 100644 index 1d96741b4..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_71.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_72.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_72.bm deleted file mode 100644 index 877e571a1..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_72.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_73.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_73.bm deleted file mode 100644 index e5e6dde58..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_73.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_74.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_74.bm deleted file mode 100644 index 9046de517..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_74.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_75.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_75.bm deleted file mode 100644 index 4807d803c..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_75.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_76.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_76.bm deleted file mode 100644 index bc41046eb..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_76.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_77.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_77.bm deleted file mode 100644 index 84c399ad9..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_77.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_78.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_78.bm deleted file mode 100644 index 57b79ceed..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_78.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_79.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_79.bm deleted file mode 100644 index 8ff998939..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_79.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_8.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_8.bm deleted file mode 100644 index d3c626329..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_80.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_80.bm deleted file mode 100644 index 74a9ce2db..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_80.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_81.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_81.bm deleted file mode 100644 index 01036886a..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_81.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_82.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_82.bm deleted file mode 100644 index 59eb7931f..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_82.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_83.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_83.bm deleted file mode 100644 index 327e972c1..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_83.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_84.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_84.bm deleted file mode 100644 index dd8b4428f..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_84.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_85.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_85.bm deleted file mode 100644 index 8b48f7bf0..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_85.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_86.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_86.bm deleted file mode 100644 index 4a7053337..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_86.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_87.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_87.bm deleted file mode 100644 index 234c44033..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_87.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_88.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_88.bm deleted file mode 100644 index 776c4ec8b..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_88.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_89.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_89.bm deleted file mode 100644 index 0f8647ac3..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_89.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_9.bm b/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_9.bm deleted file mode 100644 index c3ff4cc68..000000000 Binary files a/assets/resources/dolphin/RM_Hexadecimal_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Hexadecimal_128x64/meta.txt b/assets/resources/dolphin/RM_Hexadecimal_128x64/meta.txt deleted file mode 100644 index beb7951fa..000000000 --- a/assets/resources/dolphin/RM_Hexadecimal_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 5 -Active frames: 85 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_0.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_0.bm deleted file mode 100644 index d83a4ac3d..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_1.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_1.bm deleted file mode 100644 index 29216fc1f..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_10.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_10.bm deleted file mode 100644 index 085a862ce..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_11.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_11.bm deleted file mode 100644 index d01ce1d77..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_12.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_12.bm deleted file mode 100644 index 7b70cb7bf..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_13.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_13.bm deleted file mode 100644 index 43231b27d..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_14.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_14.bm deleted file mode 100644 index e3a5d2418..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_15.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_15.bm deleted file mode 100644 index 389da7cad..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_16.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_16.bm deleted file mode 100644 index 33e9fe9ab..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_17.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_17.bm deleted file mode 100644 index 3e205414f..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_18.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_18.bm deleted file mode 100644 index 396a9ecbb..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_19.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_19.bm deleted file mode 100644 index e669e6cfe..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_2.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_2.bm deleted file mode 100644 index 3625e9688..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_20.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_20.bm deleted file mode 100644 index 08409fa4f..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_21.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_21.bm deleted file mode 100644 index 3bac10d30..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_22.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_22.bm deleted file mode 100644 index daeea3987..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_23.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_23.bm deleted file mode 100644 index 859a4434d..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_24.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_24.bm deleted file mode 100644 index 76ad71a92..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_25.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_25.bm deleted file mode 100644 index 59fb9118f..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_26.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_26.bm deleted file mode 100644 index bf1e4adbf..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_27.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_27.bm deleted file mode 100644 index 105011960..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_28.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_28.bm deleted file mode 100644 index 573b29582..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_29.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_29.bm deleted file mode 100644 index 99620c989..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_3.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_3.bm deleted file mode 100644 index 8f83593e1..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_30.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_30.bm deleted file mode 100644 index ba0f430f3..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_31.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_31.bm deleted file mode 100644 index 81e68b320..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_32.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_32.bm deleted file mode 100644 index 68930a818..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_33.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_33.bm deleted file mode 100644 index e2e385a22..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_34.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_34.bm deleted file mode 100644 index 440e64de5..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_35.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_35.bm deleted file mode 100644 index f0609f2f5..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_36.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_36.bm deleted file mode 100644 index b8b13f15c..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_4.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_4.bm deleted file mode 100644 index 89a74dd37..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_5.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_5.bm deleted file mode 100644 index df318ea66..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_6.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_6.bm deleted file mode 100644 index b62070b27..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_7.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_7.bm deleted file mode 100644 index adc214c6f..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_8.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_8.bm deleted file mode 100644 index 3cf69f329..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_9.bm b/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_9.bm deleted file mode 100644 index e7d02161d..000000000 Binary files a/assets/resources/dolphin/RM_Init_D_Water_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Init_D_Water_128x64/meta.txt b/assets/resources/dolphin/RM_Init_D_Water_128x64/meta.txt deleted file mode 100644 index 214b76f88..000000000 --- a/assets/resources/dolphin/RM_Init_D_Water_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 5 -Active frames: 32 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 -Active cycles: 1 -Frame rate: 8 -Duration: 3600 -Active cooldown: 2 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_0.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_0.bm deleted file mode 100644 index 4fae36cf2..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_1.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_1.bm deleted file mode 100644 index 30dcb8c42..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_10.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_10.bm deleted file mode 100644 index 43a85a7f2..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_11.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_11.bm deleted file mode 100644 index 9500e8724..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_12.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_12.bm deleted file mode 100644 index 01d1f1b76..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_13.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_13.bm deleted file mode 100644 index 4cc4415eb..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_14.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_14.bm deleted file mode 100644 index 252bb2244..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_15.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_15.bm deleted file mode 100644 index a8f2143bb..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_16.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_16.bm deleted file mode 100644 index 04838b86d..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_17.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_17.bm deleted file mode 100644 index f3a1d1ab9..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_18.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_18.bm deleted file mode 100644 index e2c1218a7..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_19.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_19.bm deleted file mode 100644 index 66848679b..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_2.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_2.bm deleted file mode 100644 index 40432d744..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_20.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_20.bm deleted file mode 100644 index 9e2caca2a..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_21.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_21.bm deleted file mode 100644 index 22f468546..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_22.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_22.bm deleted file mode 100644 index 0d145ba04..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_23.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_23.bm deleted file mode 100644 index f1cbe8111..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_24.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_24.bm deleted file mode 100644 index a2cc3fbcf..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_25.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_25.bm deleted file mode 100644 index 93f0fa0de..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_26.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_26.bm deleted file mode 100644 index 09a7d2da0..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_27.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_27.bm deleted file mode 100644 index d17ef43da..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_28.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_28.bm deleted file mode 100644 index 84e4be89f..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_29.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_29.bm deleted file mode 100644 index 24d0d61ea..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_3.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_3.bm deleted file mode 100644 index b1272df10..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_30.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_30.bm deleted file mode 100644 index 0b52ba4a3..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_31.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_31.bm deleted file mode 100644 index a4d36a26d..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_4.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_4.bm deleted file mode 100644 index 1f38f3d30..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_5.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_5.bm deleted file mode 100644 index 2a54a13e5..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_6.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_6.bm deleted file mode 100644 index af78d3443..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_7.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_7.bm deleted file mode 100644 index 6b1b43673..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_8.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_8.bm deleted file mode 100644 index 88b769ed2..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_9.bm b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_9.bm deleted file mode 100644 index 1543d4d60..000000000 Binary files a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/meta.txt b/assets/resources/dolphin/RM_Kam3ham3ha_128x64/meta.txt deleted file mode 100644 index 1b2c52406..000000000 --- a/assets/resources/dolphin/RM_Kam3ham3ha_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 6 -Active frames: 48 -Frames order: 0 0 0 1 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 21 22 23 24 25 26 27 28 29 30 30 30 30 30 30 30 30 30 30 30 30 31 31 31 31 31 31 31 -Active cycles: 1 -Frame rate: 9 -Duration: 3600 -Active cooldown: 1 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_0.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_0.bm deleted file mode 100644 index 47c35f4c0..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_1.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_1.bm deleted file mode 100644 index c725757dc..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_10.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_10.bm deleted file mode 100644 index 70ccde6b3..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_11.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_11.bm deleted file mode 100644 index 3260eb780..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_12.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_12.bm deleted file mode 100644 index 77eead7d0..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_13.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_13.bm deleted file mode 100644 index 18694587b..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_14.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_14.bm deleted file mode 100644 index 71d54910d..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_15.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_15.bm deleted file mode 100644 index c4ac53439..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_16.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_16.bm deleted file mode 100644 index 22f4a4168..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_17.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_17.bm deleted file mode 100644 index 7b340a2ed..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_2.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_2.bm deleted file mode 100644 index c7116e3da..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_3.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_3.bm deleted file mode 100644 index e685c1e67..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_4.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_4.bm deleted file mode 100644 index 39ec4d354..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_5.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_5.bm deleted file mode 100644 index b88636d7d..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_6.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_6.bm deleted file mode 100644 index 2e423093e..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_7.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_7.bm deleted file mode 100644 index 9ff1f6405..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_8.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_8.bm deleted file mode 100644 index d1b76ce36..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_9.bm b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_9.bm deleted file mode 100644 index db847ce83..000000000 Binary files a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/meta.txt b/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/meta.txt deleted file mode 100644 index f5e056341..000000000 --- a/assets/resources/dolphin/RM_Kaz0ku_Haha_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 3 -Active frames: 15 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -Active cycles: 1 -Frame rate: 3 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_0.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_0.bm deleted file mode 100644 index 81b040179..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_1.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_1.bm deleted file mode 100644 index cd77a0dfc..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_10.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_10.bm deleted file mode 100644 index 43ae9c96c..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_11.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_11.bm deleted file mode 100644 index 5ce566ae5..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_12.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_12.bm deleted file mode 100644 index 57ca14edc..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_13.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_13.bm deleted file mode 100644 index e2d0d32c8..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_14.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_14.bm deleted file mode 100644 index 12575a52d..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_15.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_15.bm deleted file mode 100644 index 81b3d3dd1..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_16.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_16.bm deleted file mode 100644 index 41226c5e7..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_17.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_17.bm deleted file mode 100644 index 5d4099adb..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_18.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_18.bm deleted file mode 100644 index 5b7f330f0..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_19.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_19.bm deleted file mode 100644 index 553a4432b..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_2.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_2.bm deleted file mode 100644 index 9af798d0c..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_20.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_20.bm deleted file mode 100644 index 15615248e..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_21.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_21.bm deleted file mode 100644 index cddc95649..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_22.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_22.bm deleted file mode 100644 index 9a0d98356..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_23.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_23.bm deleted file mode 100644 index a5733c9e3..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_24.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_24.bm deleted file mode 100644 index a010f8ba7..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_25.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_25.bm deleted file mode 100644 index 98e22c6da..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_26.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_26.bm deleted file mode 100644 index b1b8cee29..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_27.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_27.bm deleted file mode 100644 index 9d7a6a467..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_28.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_28.bm deleted file mode 100644 index df59ace19..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_29.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_29.bm deleted file mode 100644 index 7aeac43d6..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_3.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_3.bm deleted file mode 100644 index 3eea683c3..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_30.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_30.bm deleted file mode 100644 index b8b1a5b41..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_31.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_31.bm deleted file mode 100644 index 220efd5d5..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_32.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_32.bm deleted file mode 100644 index a1049232f..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_33.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_33.bm deleted file mode 100644 index 1a32c60c5..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_34.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_34.bm deleted file mode 100644 index eaacbf6f0..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_4.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_4.bm deleted file mode 100644 index f081ecf4f..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_5.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_5.bm deleted file mode 100644 index d8e3e27d3..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_6.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_6.bm deleted file mode 100644 index 615186881..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_7.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_7.bm deleted file mode 100644 index 651867b14..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_8.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_8.bm deleted file mode 100644 index 87ddb3a2a..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/frame_9.bm b/assets/resources/dolphin/RM_Kirbs_128x64/frame_9.bm deleted file mode 100644 index af5693cb2..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_128x64/meta.txt b/assets/resources/dolphin/RM_Kirbs_128x64/meta.txt deleted file mode 100644 index 718a4191b..000000000 --- a/assets/resources/dolphin/RM_Kirbs_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 34 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 -Active cycles: 1 -Frame rate: 5 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_0.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_0.bm deleted file mode 100644 index 5e91ef15a..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_1.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_1.bm deleted file mode 100644 index 3604d84f8..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_10.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_10.bm deleted file mode 100644 index 6d2e1bf24..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_11.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_11.bm deleted file mode 100644 index 18855f99e..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_12.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_12.bm deleted file mode 100644 index c3a851af9..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_13.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_13.bm deleted file mode 100644 index 83e830cdd..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_14.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_14.bm deleted file mode 100644 index c5cd3da76..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_15.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_15.bm deleted file mode 100644 index e49fdc9ab..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_16.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_16.bm deleted file mode 100644 index 2b3a47384..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_17.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_17.bm deleted file mode 100644 index 0dcca119b..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_18.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_18.bm deleted file mode 100644 index 80b25c446..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_19.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_19.bm deleted file mode 100644 index 02368d1dc..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_2.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_2.bm deleted file mode 100644 index 606f14b4a..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_3.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_3.bm deleted file mode 100644 index 201c277b5..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_4.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_4.bm deleted file mode 100644 index c20f911d7..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_5.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_5.bm deleted file mode 100644 index ca58139db..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_6.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_6.bm deleted file mode 100644 index 6d3df34c6..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_7.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_7.bm deleted file mode 100644 index 6f5534c4d..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_8.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_8.bm deleted file mode 100644 index f15e99ba6..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_9.bm b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_9.bm deleted file mode 100644 index 2a52368f3..000000000 Binary files a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/meta.txt b/assets/resources/dolphin/RM_Kirbs_Confused_128x64/meta.txt deleted file mode 100644 index 64adc896a..000000000 --- a/assets/resources/dolphin/RM_Kirbs_Confused_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_0.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_0.bm deleted file mode 100644 index 8e5706249..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_1.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_1.bm deleted file mode 100644 index 30a107954..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_10.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_10.bm deleted file mode 100644 index cf06646df..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_11.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_11.bm deleted file mode 100644 index f985ced04..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_12.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_12.bm deleted file mode 100644 index 5d0be31df..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_13.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_13.bm deleted file mode 100644 index ddef7fc97..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_14.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_14.bm deleted file mode 100644 index 79a4b6284..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_15.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_15.bm deleted file mode 100644 index e362b849e..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_16.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_16.bm deleted file mode 100644 index 6f47e25da..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_17.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_17.bm deleted file mode 100644 index b004291ea..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_18.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_18.bm deleted file mode 100644 index 997800ee6..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_19.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_19.bm deleted file mode 100644 index 460aac3cb..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_2.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_2.bm deleted file mode 100644 index ed4d9b3ee..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_20.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_20.bm deleted file mode 100644 index ead7224df..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_21.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_21.bm deleted file mode 100644 index d845223ed..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_22.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_22.bm deleted file mode 100644 index 71b441403..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_23.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_23.bm deleted file mode 100644 index a750cfae1..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_24.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_24.bm deleted file mode 100644 index 544cbb8e3..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_25.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_25.bm deleted file mode 100644 index 7b56dc5a6..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_26.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_26.bm deleted file mode 100644 index b8a322a45..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_27.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_27.bm deleted file mode 100644 index b13389b21..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_28.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_28.bm deleted file mode 100644 index 67514ae1b..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_29.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_29.bm deleted file mode 100644 index d4166b4a7..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_3.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_3.bm deleted file mode 100644 index f77c4175f..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_30.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_30.bm deleted file mode 100644 index a5903dcd2..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_31.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_31.bm deleted file mode 100644 index 5259d07cf..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_32.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_32.bm deleted file mode 100644 index 6b6fdbee4..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_33.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_33.bm deleted file mode 100644 index 73eeaa126..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_34.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_34.bm deleted file mode 100644 index 7d5561c30..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_35.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_35.bm deleted file mode 100644 index 40eaaa0c6..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_36.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_36.bm deleted file mode 100644 index 7fa46d7ed..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_37.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_37.bm deleted file mode 100644 index 7733ab6c9..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_38.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_38.bm deleted file mode 100644 index 5b229c9f2..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_39.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_39.bm deleted file mode 100644 index 0b759e985..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_4.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_4.bm deleted file mode 100644 index 5675e83bf..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_40.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_40.bm deleted file mode 100644 index d54568696..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_41.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_41.bm deleted file mode 100644 index 210bd6f87..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_42.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_42.bm deleted file mode 100644 index 9438587e1..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_43.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_43.bm deleted file mode 100644 index f5509a97b..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_44.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_44.bm deleted file mode 100644 index 6cc316772..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_45.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_45.bm deleted file mode 100644 index 4241ddea4..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_46.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_46.bm deleted file mode 100644 index ed97d8014..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_47.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_47.bm deleted file mode 100644 index db3a1585e..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_48.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_48.bm deleted file mode 100644 index 21b490ed9..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_49.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_49.bm deleted file mode 100644 index 988c7749b..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_5.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_5.bm deleted file mode 100644 index 7e0b606f3..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_50.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_50.bm deleted file mode 100644 index 6e9d3df88..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_50.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_51.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_51.bm deleted file mode 100644 index 9d37449e7..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_51.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_52.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_52.bm deleted file mode 100644 index 38480b89c..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_52.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_53.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_53.bm deleted file mode 100644 index d0f18edad..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_53.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_54.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_54.bm deleted file mode 100644 index 4049ad9b9..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_54.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_55.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_55.bm deleted file mode 100644 index dd0cfd4e4..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_55.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_56.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_56.bm deleted file mode 100644 index cd6ff193b..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_56.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_57.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_57.bm deleted file mode 100644 index 23061bb7d..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_57.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_58.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_58.bm deleted file mode 100644 index 3a5f07546..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_58.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_59.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_59.bm deleted file mode 100644 index 182d86a5b..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_59.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_6.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_6.bm deleted file mode 100644 index 7ed41a724..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_60.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_60.bm deleted file mode 100644 index e2ffd7bd4..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_60.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_61.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_61.bm deleted file mode 100644 index 3b65df7a6..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_61.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_62.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_62.bm deleted file mode 100644 index a48a7bf4e..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_62.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_63.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_63.bm deleted file mode 100644 index a41f91ed2..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_63.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_64.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_64.bm deleted file mode 100644 index 6e66f178a..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_64.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_65.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_65.bm deleted file mode 100644 index f767245c4..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_65.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_66.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_66.bm deleted file mode 100644 index 5f5d66761..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_66.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_67.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_67.bm deleted file mode 100644 index b803ac46f..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_67.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_68.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_68.bm deleted file mode 100644 index 6f33b793d..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_68.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_69.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_69.bm deleted file mode 100644 index fa67d862d..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_69.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_7.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_7.bm deleted file mode 100644 index 5ee25d78f..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_70.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_70.bm deleted file mode 100644 index 0ffc5bb59..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_70.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_71.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_71.bm deleted file mode 100644 index 83ae5da53..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_71.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_72.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_72.bm deleted file mode 100644 index d18254a45..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_72.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_73.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_73.bm deleted file mode 100644 index db88791a1..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_73.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_74.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_74.bm deleted file mode 100644 index 8bf57b7a5..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_74.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_75.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_75.bm deleted file mode 100644 index 59a59fd1f..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_75.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_76.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_76.bm deleted file mode 100644 index dbc92add2..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_76.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_77.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_77.bm deleted file mode 100644 index a32ddf289..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_77.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_78.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_78.bm deleted file mode 100644 index a224dd012..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_78.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_79.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_79.bm deleted file mode 100644 index 19d5c6587..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_79.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_8.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_8.bm deleted file mode 100644 index 95edf3443..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_80.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_80.bm deleted file mode 100644 index 2992a4300..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_80.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_81.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_81.bm deleted file mode 100644 index 3bcec91e0..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_81.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_82.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_82.bm deleted file mode 100644 index 5c44aefad..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_82.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_83.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_83.bm deleted file mode 100644 index 9b223d6dc..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_83.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_84.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_84.bm deleted file mode 100644 index e1c57fdfb..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_84.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_85.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_85.bm deleted file mode 100644 index e77e52767..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_85.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_86.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_86.bm deleted file mode 100644 index 8b3a3d1af..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_86.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_87.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_87.bm deleted file mode 100644 index d68a62ebe..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_87.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_88.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_88.bm deleted file mode 100644 index 9e2702ae0..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_88.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_89.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_89.bm deleted file mode 100644 index 2ff961b91..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_89.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_9.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_9.bm deleted file mode 100644 index 531df5e52..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_90.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_90.bm deleted file mode 100644 index 011b979e7..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_90.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_91.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_91.bm deleted file mode 100644 index 2b2b17566..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_91.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_92.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_92.bm deleted file mode 100644 index 3a875655b..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_92.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_93.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_93.bm deleted file mode 100644 index a3b7a7545..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_93.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_94.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_94.bm deleted file mode 100644 index ca9432ac8..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_94.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_95.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_95.bm deleted file mode 100644 index e69b97b52..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_95.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_96.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_96.bm deleted file mode 100644 index a1d1978b5..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_96.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_97.bm b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_97.bm deleted file mode 100644 index 92e7cfb31..000000000 Binary files a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/frame_97.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/meta.txt b/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/meta.txt deleted file mode 100644 index 8a91a1fec..000000000 --- a/assets/resources/dolphin/RM_M3gamanZ3r0_Battle_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 94 -Active frames: 4 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_0.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_0.bm deleted file mode 100644 index 6e82d1d51..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_1.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_1.bm deleted file mode 100644 index 34e3660d8..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_10.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_10.bm deleted file mode 100644 index 24ca64e22..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_11.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_11.bm deleted file mode 100644 index 996d3f1c8..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_12.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_12.bm deleted file mode 100644 index 04dc5f245..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_13.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_13.bm deleted file mode 100644 index f8bb732c2..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_14.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_14.bm deleted file mode 100644 index 7a0807f73..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_15.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_15.bm deleted file mode 100644 index 44e3a3406..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_16.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_16.bm deleted file mode 100644 index b6a141ff5..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_17.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_17.bm deleted file mode 100644 index 2a19ff35c..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_18.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_18.bm deleted file mode 100644 index a6f9fd327..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_19.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_19.bm deleted file mode 100644 index e5695b0a9..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_2.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_2.bm deleted file mode 100644 index f2bc04715..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_20.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_20.bm deleted file mode 100644 index 4aabbba49..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_21.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_21.bm deleted file mode 100644 index cac84e4a4..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_22.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_22.bm deleted file mode 100644 index 67c41e9d4..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_23.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_23.bm deleted file mode 100644 index e5baee3db..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_24.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_24.bm deleted file mode 100644 index 51f9b4713..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_25.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_25.bm deleted file mode 100644 index 94e714a5e..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_26.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_26.bm deleted file mode 100644 index 1ad047882..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_27.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_27.bm deleted file mode 100644 index 1ea1d39e3..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_3.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_3.bm deleted file mode 100644 index 9dde96f6f..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_4.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_4.bm deleted file mode 100644 index d9202a728..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_5.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_5.bm deleted file mode 100644 index a2ed66764..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_6.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_6.bm deleted file mode 100644 index 881c419a0..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_7.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_7.bm deleted file mode 100644 index 59e80a486..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_8.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_8.bm deleted file mode 100644 index daf744aa1..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_9.bm b/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_9.bm deleted file mode 100644 index f21d94587..000000000 Binary files a/assets/resources/dolphin/RM_OP_G3ar4_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_OP_G3ar4_128x64/meta.txt b/assets/resources/dolphin/RM_OP_G3ar4_128x64/meta.txt deleted file mode 100644 index 91710c56d..000000000 --- a/assets/resources/dolphin/RM_OP_G3ar4_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 6 -Active frames: 52 -Frames order: 0 0 0 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 4 4 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 21 22 23 24 25 25 25 25 25 25 25 25 26 26 26 26 27 27 27 27 -Active cycles: 1 -Frame rate: 8 -Duration: 3600 -Active cooldown: 1 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_0.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_0.bm deleted file mode 100644 index 5b8f1c17e..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_1.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_1.bm deleted file mode 100644 index e6c216a60..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_10.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_10.bm deleted file mode 100644 index 422edea7a..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_11.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_11.bm deleted file mode 100644 index 45542d4f2..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_12.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_12.bm deleted file mode 100644 index 95815b6c7..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_13.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_13.bm deleted file mode 100644 index 7b713a98b..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_14.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_14.bm deleted file mode 100644 index f81a46173..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_15.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_15.bm deleted file mode 100644 index 67dd28955..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_16.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_16.bm deleted file mode 100644 index 7d99ef7c6..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_17.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_17.bm deleted file mode 100644 index a6e7c34d9..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_18.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_18.bm deleted file mode 100644 index 028d6e342..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_19.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_19.bm deleted file mode 100644 index 1a65b28f0..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_2.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_2.bm deleted file mode 100644 index 8c04072b3..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_20.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_20.bm deleted file mode 100644 index f76af6900..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_21.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_21.bm deleted file mode 100644 index 3da0e8961..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_22.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_22.bm deleted file mode 100644 index 7a79d596b..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_3.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_3.bm deleted file mode 100644 index 8e61d4c54..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_4.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_4.bm deleted file mode 100644 index 0355beb61..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_5.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_5.bm deleted file mode 100644 index 9871aa6e0..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_6.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_6.bm deleted file mode 100644 index 8dae3d823..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_7.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_7.bm deleted file mode 100644 index 894f2f246..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_8.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_8.bm deleted file mode 100644 index 8e230b675..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/frame_9.bm b/assets/resources/dolphin/RM_P3achRun_128x64/frame_9.bm deleted file mode 100644 index b569be285..000000000 Binary files a/assets/resources/dolphin/RM_P3achRun_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_P3achRun_128x64/meta.txt b/assets/resources/dolphin/RM_P3achRun_128x64/meta.txt deleted file mode 100644 index f407288c8..000000000 --- a/assets/resources/dolphin/RM_P3achRun_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 14 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -Active cycles: 1 -Frame rate: 4 -Duration: 3600 -Active cooldown: 1 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_0.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_0.bm deleted file mode 100644 index 94185f84c..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_1.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_1.bm deleted file mode 100644 index 492b55fac..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_10.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_10.bm deleted file mode 100644 index 1a884d1b3..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_11.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_11.bm deleted file mode 100644 index 850343b9b..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_12.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_12.bm deleted file mode 100644 index 97de0d189..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_13.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_13.bm deleted file mode 100644 index dd5ee3cc6..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_14.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_14.bm deleted file mode 100644 index 17feec024..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_15.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_15.bm deleted file mode 100644 index 8a4ec66cb..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_16.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_16.bm deleted file mode 100644 index 987cab85b..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_17.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_17.bm deleted file mode 100644 index a7ada2119..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_2.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_2.bm deleted file mode 100644 index e6bd3617f..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_3.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_3.bm deleted file mode 100644 index 57b543fd1..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_4.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_4.bm deleted file mode 100644 index 3a6701236..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_5.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_5.bm deleted file mode 100644 index 470c4f7d4..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_6.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_6.bm deleted file mode 100644 index 325cb3610..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_7.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_7.bm deleted file mode 100644 index 6d7f3f49c..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_8.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_8.bm deleted file mode 100644 index 4bcd64bb0..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/frame_9.bm b/assets/resources/dolphin/RM_R0shi_128x64/frame_9.bm deleted file mode 100644 index 90b83b215..000000000 Binary files a/assets/resources/dolphin/RM_R0shi_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_R0shi_128x64/meta.txt b/assets/resources/dolphin/RM_R0shi_128x64/meta.txt deleted file mode 100644 index f5e056341..000000000 --- a/assets/resources/dolphin/RM_R0shi_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 3 -Active frames: 15 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -Active cycles: 1 -Frame rate: 3 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_0.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_0.bm deleted file mode 100644 index 84bffbf6d..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_1.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_1.bm deleted file mode 100644 index c3e67b1af..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_2.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_2.bm deleted file mode 100644 index 8166c5d9d..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_3.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_3.bm deleted file mode 100644 index b65066fd2..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_4.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_4.bm deleted file mode 100644 index eb45ab517..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_5.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_5.bm deleted file mode 100644 index 336757579..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_6.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_6.bm deleted file mode 100644 index 077abb9fd..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_7.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_7.bm deleted file mode 100644 index 1f80b169c..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_8.bm b/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_8.bm deleted file mode 100644 index b0a080f0a..000000000 Binary files a/assets/resources/dolphin/RM_Sail0rM00n_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Sail0rM00n_128x64/meta.txt b/assets/resources/dolphin/RM_Sail0rM00n_128x64/meta.txt deleted file mode 100644 index 62579690a..000000000 --- a/assets/resources/dolphin/RM_Sail0rM00n_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 3 -Active frames: 6 -Frames order: 0 1 2 3 4 5 6 7 8 -Active cycles: 1 -Frame rate: 3 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_0.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_0.bm deleted file mode 100644 index c2beb5813..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_1.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_1.bm deleted file mode 100644 index 1f8400d53..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_10.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_10.bm deleted file mode 100644 index ed73ffa1d..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_11.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_11.bm deleted file mode 100644 index 664b700a8..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_12.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_12.bm deleted file mode 100644 index e0ad9b953..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_13.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_13.bm deleted file mode 100644 index 56c1ceb3c..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_14.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_14.bm deleted file mode 100644 index e02a2ac48..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_15.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_15.bm deleted file mode 100644 index 1edb73d33..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_16.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_16.bm deleted file mode 100644 index a3ec20804..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_17.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_17.bm deleted file mode 100644 index 32679e92d..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_18.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_18.bm deleted file mode 100644 index d0c653dc4..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_19.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_19.bm deleted file mode 100644 index f8bc63292..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_2.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_2.bm deleted file mode 100644 index 51d13b75a..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_20.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_20.bm deleted file mode 100644 index 7dfb223ee..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_21.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_21.bm deleted file mode 100644 index 5962ff034..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_22.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_22.bm deleted file mode 100644 index 55b3bf127..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_23.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_23.bm deleted file mode 100644 index 188af9777..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_24.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_24.bm deleted file mode 100644 index 2df3dfeac..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_25.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_25.bm deleted file mode 100644 index e35957de3..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_26.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_26.bm deleted file mode 100644 index 7e75263f3..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_27.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_27.bm deleted file mode 100644 index afdc97c19..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_28.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_28.bm deleted file mode 100644 index fd79c02a2..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_29.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_29.bm deleted file mode 100644 index f64cc1004..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_3.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_3.bm deleted file mode 100644 index 5537e215b..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_30.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_30.bm deleted file mode 100644 index 7c2a74728..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_31.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_31.bm deleted file mode 100644 index 103091984..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_32.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_32.bm deleted file mode 100644 index 536b98f4a..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_33.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_33.bm deleted file mode 100644 index 1b5561442..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_34.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_34.bm deleted file mode 100644 index 1314149f8..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_35.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_35.bm deleted file mode 100644 index a43437cab..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_36.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_36.bm deleted file mode 100644 index 6b7e007d7..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_37.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_37.bm deleted file mode 100644 index 802a048c3..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_38.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_38.bm deleted file mode 100644 index e9d264b9e..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_39.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_39.bm deleted file mode 100644 index 7e4bafceb..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_4.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_4.bm deleted file mode 100644 index d2a66946e..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_40.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_40.bm deleted file mode 100644 index de2d24721..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_41.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_41.bm deleted file mode 100644 index 0b1fe9f2d..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_42.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_42.bm deleted file mode 100644 index a06ce636e..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_43.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_43.bm deleted file mode 100644 index b7fae4bc8..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_44.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_44.bm deleted file mode 100644 index 964d592a1..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_45.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_45.bm deleted file mode 100644 index 4b240b1e1..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_46.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_46.bm deleted file mode 100644 index 40cd103fa..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_47.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_47.bm deleted file mode 100644 index 353a484f6..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_48.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_48.bm deleted file mode 100644 index d03c8ccb0..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_5.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_5.bm deleted file mode 100644 index ba1c0ff64..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_6.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_6.bm deleted file mode 100644 index 81eb6d7ac..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_7.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_7.bm deleted file mode 100644 index 7fdbef669..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_8.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_8.bm deleted file mode 100644 index a8162e7e2..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/frame_9.bm b/assets/resources/dolphin/RM_Tardi5_128x64/frame_9.bm deleted file mode 100644 index 5178b9825..000000000 Binary files a/assets/resources/dolphin/RM_Tardi5_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Tardi5_128x64/meta.txt b/assets/resources/dolphin/RM_Tardi5_128x64/meta.txt deleted file mode 100644 index 115464ef1..000000000 --- a/assets/resources/dolphin/RM_Tardi5_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 12 -Active frames: 37 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_0.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_0.bm deleted file mode 100644 index 8ec0cce47..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_1.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_1.bm deleted file mode 100644 index ac22824c4..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_10.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_10.bm deleted file mode 100644 index 830f50f88..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_11.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_11.bm deleted file mode 100644 index c178b4e67..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_12.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_12.bm deleted file mode 100644 index 1865aa560..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_13.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_13.bm deleted file mode 100644 index 390d2e014..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_14.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_14.bm deleted file mode 100644 index db1d5de6d..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_15.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_15.bm deleted file mode 100644 index 4e8069102..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_16.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_16.bm deleted file mode 100644 index adece4019..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_17.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_17.bm deleted file mode 100644 index 2479c87fb..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_18.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_18.bm deleted file mode 100644 index 28ec4244e..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_19.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_19.bm deleted file mode 100644 index ba84a98da..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_2.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_2.bm deleted file mode 100644 index af813932e..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_20.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_20.bm deleted file mode 100644 index 66ed9ea19..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_21.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_21.bm deleted file mode 100644 index ab318558d..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_22.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_22.bm deleted file mode 100644 index e60eccddb..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_23.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_23.bm deleted file mode 100644 index 2fdf37311..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_24.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_24.bm deleted file mode 100644 index b6734bb2d..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_25.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_25.bm deleted file mode 100644 index 5375efcc5..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_26.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_26.bm deleted file mode 100644 index 5ddc4cc51..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_27.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_27.bm deleted file mode 100644 index 80e616d8f..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_28.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_28.bm deleted file mode 100644 index b29b1624e..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_29.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_29.bm deleted file mode 100644 index 42fa85da9..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_3.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_3.bm deleted file mode 100644 index 95e324bab..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_30.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_30.bm deleted file mode 100644 index 89825c7b1..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_31.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_31.bm deleted file mode 100644 index 05a8c5d50..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_32.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_32.bm deleted file mode 100644 index b0974e5dc..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_33.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_33.bm deleted file mode 100644 index fd271399b..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_34.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_34.bm deleted file mode 100644 index 25ebb534e..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_35.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_35.bm deleted file mode 100644 index 94139ceca..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_36.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_36.bm deleted file mode 100644 index 1bd3e2f52..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_37.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_37.bm deleted file mode 100644 index 253a7a935..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_38.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_38.bm deleted file mode 100644 index 029eeeda5..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_39.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_39.bm deleted file mode 100644 index 9b5f2cd92..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_4.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_4.bm deleted file mode 100644 index d9762c2c9..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_40.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_40.bm deleted file mode 100644 index 6ebb18715..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_41.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_41.bm deleted file mode 100644 index 722fd3c4a..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_42.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_42.bm deleted file mode 100644 index 7db469f07..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_43.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_43.bm deleted file mode 100644 index b693def02..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_44.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_44.bm deleted file mode 100644 index 77e90984e..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_45.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_45.bm deleted file mode 100644 index aa4d40b05..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_46.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_46.bm deleted file mode 100644 index 66a294563..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_47.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_47.bm deleted file mode 100644 index b1356dac0..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_5.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_5.bm deleted file mode 100644 index 447d19656..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_6.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_6.bm deleted file mode 100644 index e07d298de..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_7.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_7.bm deleted file mode 100644 index a88d7c2cb..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_8.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_8.bm deleted file mode 100644 index f330b11b0..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_9.bm b/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_9.bm deleted file mode 100644 index a594ddf6d..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_0h_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_0h_128x64/meta.txt b/assets/resources/dolphin/RM_Z3lda_0h_128x64/meta.txt deleted file mode 100644 index ce44ad8b5..000000000 --- a/assets/resources/dolphin/RM_Z3lda_0h_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 3 -Active frames: 45 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 -Active cycles: 1 -Frame rate: 5 -Duration: 3600 -Active cooldown: 2 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_0.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_0.bm deleted file mode 100644 index 26a7b0000..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_1.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_1.bm deleted file mode 100644 index abe158f32..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_10.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_10.bm deleted file mode 100644 index ede0d7c41..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_11.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_11.bm deleted file mode 100644 index ccd4a5b10..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_12.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_12.bm deleted file mode 100644 index cc4276df5..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_13.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_13.bm deleted file mode 100644 index 5f42c40bc..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_14.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_14.bm deleted file mode 100644 index 0e441fff6..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_15.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_15.bm deleted file mode 100644 index ec85dd3d2..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_16.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_16.bm deleted file mode 100644 index c0f6b581a..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_17.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_17.bm deleted file mode 100644 index d6548942b..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_18.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_18.bm deleted file mode 100644 index 08d604542..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_19.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_19.bm deleted file mode 100644 index fa21439f8..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_2.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_2.bm deleted file mode 100644 index 8ba7b6f90..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_20.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_20.bm deleted file mode 100644 index e01becfe0..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_21.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_21.bm deleted file mode 100644 index 90a6887b6..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_22.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_22.bm deleted file mode 100644 index f51032ff4..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_23.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_23.bm deleted file mode 100644 index 51c303196..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_24.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_24.bm deleted file mode 100644 index 9169759b5..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_25.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_25.bm deleted file mode 100644 index 047385dc6..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_26.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_26.bm deleted file mode 100644 index 872ec84fc..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_27.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_27.bm deleted file mode 100644 index 103138563..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_28.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_28.bm deleted file mode 100644 index 14ca1ace0..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_29.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_29.bm deleted file mode 100644 index 14d919a63..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_3.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_3.bm deleted file mode 100644 index 8eac2e4a7..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_4.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_4.bm deleted file mode 100644 index f411fb2f1..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_5.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_5.bm deleted file mode 100644 index e9d95fc7f..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_6.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_6.bm deleted file mode 100644 index 3fee7d45b..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_7.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_7.bm deleted file mode 100644 index 2704a8194..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_8.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_8.bm deleted file mode 100644 index f196f50fb..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_9.bm b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_9.bm deleted file mode 100644 index 2b1cfe88d..000000000 Binary files a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/meta.txt b/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/meta.txt deleted file mode 100644 index 18e71a3f9..000000000 --- a/assets/resources/dolphin/RM_Z3lda_R3ady_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 6 -Active frames: 29 -Frames order: 0 0 0 1 1 1 2 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 -Active cycles: 1 -Frame rate: 3 -Duration: 3600 -Active cooldown: 4 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_0.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_0.bm deleted file mode 100644 index c36a5d389..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_1.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_1.bm deleted file mode 100644 index e2f42f713..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_10.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_10.bm deleted file mode 100644 index 11a6b9e53..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_11.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_11.bm deleted file mode 100644 index c33baf79e..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_12.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_12.bm deleted file mode 100644 index c17673d20..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_13.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_13.bm deleted file mode 100644 index 505fbf131..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_14.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_14.bm deleted file mode 100644 index d1c6d454e..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_15.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_15.bm deleted file mode 100644 index e52f37256..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_16.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_16.bm deleted file mode 100644 index a0952625e..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_17.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_17.bm deleted file mode 100644 index 6939e3f79..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_18.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_18.bm deleted file mode 100644 index c13fa7d51..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_19.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_19.bm deleted file mode 100644 index 8914e1505..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_2.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_2.bm deleted file mode 100644 index a0a2c5037..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_20.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_20.bm deleted file mode 100644 index 6c4d8a4e8..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_21.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_21.bm deleted file mode 100644 index ec98e9ba5..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_22.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_22.bm deleted file mode 100644 index 3542a4703..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_23.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_23.bm deleted file mode 100644 index 28f5ad832..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_24.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_24.bm deleted file mode 100644 index 2c9b2211c..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_25.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_25.bm deleted file mode 100644 index 4520a08de..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_26.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_26.bm deleted file mode 100644 index ba8e1e51d..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_27.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_27.bm deleted file mode 100644 index e67f1d5fd..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_28.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_28.bm deleted file mode 100644 index 67c3ab34c..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_29.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_29.bm deleted file mode 100644 index 71e2c643d..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_3.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_3.bm deleted file mode 100644 index a344e48cd..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_30.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_30.bm deleted file mode 100644 index bcc9a1a45..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_31.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_31.bm deleted file mode 100644 index 2abebeaaf..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_32.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_32.bm deleted file mode 100644 index 0a554166b..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_33.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_33.bm deleted file mode 100644 index 8706b522c..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_34.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_34.bm deleted file mode 100644 index 1af0c7475..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_35.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_35.bm deleted file mode 100644 index 2bff5ed97..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_36.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_36.bm deleted file mode 100644 index 00321c889..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_37.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_37.bm deleted file mode 100644 index 419140895..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_38.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_38.bm deleted file mode 100644 index c5137064d..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_39.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_39.bm deleted file mode 100644 index 1722e49e5..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_4.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_4.bm deleted file mode 100644 index 0d17df9b9..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_40.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_40.bm deleted file mode 100644 index 08e4a0dd0..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_41.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_41.bm deleted file mode 100644 index 9da0ac4cd..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_42.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_42.bm deleted file mode 100644 index 4a7321946..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_43.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_43.bm deleted file mode 100644 index a48722c01..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_5.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_5.bm deleted file mode 100644 index 4e611cd84..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_6.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_6.bm deleted file mode 100644 index 90531cf2a..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_7.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_7.bm deleted file mode 100644 index 5ddf65e8e..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_8.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_8.bm deleted file mode 100644 index 618ca702e..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_9.bm b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_9.bm deleted file mode 100644 index f8b42b603..000000000 Binary files a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/meta.txt b/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/meta.txt deleted file mode 100644 index 7b7c34575..000000000 --- a/assets/resources/dolphin/STOPOXY_SCHOOL_DAYS_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 43 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 -Active cycles: 1 -Frame rate: 12 -Duration: 3600 -Active cooldown: 4 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_0.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_0.bm deleted file mode 100644 index 82a031271..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_1.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_1.bm deleted file mode 100644 index 2ebb2b9f2..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_10.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_10.bm deleted file mode 100644 index 127ebb195..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_11.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_11.bm deleted file mode 100644 index 02676b381..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_12.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_12.bm deleted file mode 100644 index b8c7a72ce..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_13.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_13.bm deleted file mode 100644 index f7aeac8e4..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_14.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_14.bm deleted file mode 100644 index a7c7a7f67..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_15.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_15.bm deleted file mode 100644 index a46e172ce..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_16.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_16.bm deleted file mode 100644 index d713c438b..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_17.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_17.bm deleted file mode 100644 index 4e991bdc3..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_18.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_18.bm deleted file mode 100644 index 798ffb6ac..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_19.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_19.bm deleted file mode 100644 index a9df07a7a..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_2.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_2.bm deleted file mode 100644 index b3aa3610e..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_20.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_20.bm deleted file mode 100644 index d647312b1..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_21.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_21.bm deleted file mode 100644 index 82a031271..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_22.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_22.bm deleted file mode 100644 index 4f91d5a17..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_23.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_23.bm deleted file mode 100644 index 841399ccf..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_24.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_24.bm deleted file mode 100644 index 3ace307c8..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_25.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_25.bm deleted file mode 100644 index 271aa5a22..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_26.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_26.bm deleted file mode 100644 index 84f362888..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_27.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_27.bm deleted file mode 100644 index 798ffb6ac..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_28.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_28.bm deleted file mode 100644 index 528ccfa3d..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_29.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_29.bm deleted file mode 100644 index f3d9cb954..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_3.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_3.bm deleted file mode 100644 index 798ffb6ac..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_30.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_30.bm deleted file mode 100644 index 2057bf256..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_31.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_31.bm deleted file mode 100644 index a7c7a7f67..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_32.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_32.bm deleted file mode 100644 index 13f024c27..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_4.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_4.bm deleted file mode 100644 index f3d9cb954..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_5.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_5.bm deleted file mode 100644 index 2057bf256..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_6.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_6.bm deleted file mode 100644 index a7c7a7f67..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_7.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_7.bm deleted file mode 100644 index 13f024c27..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_8.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_8.bm deleted file mode 100644 index 05a12aac8..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_9.bm b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_9.bm deleted file mode 100644 index 87ac614c9..000000000 Binary files a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/meta.txt b/assets/resources/dolphin/STOPOXY_TLOZ_128x64/meta.txt deleted file mode 100644 index 3a87f4de7..000000000 --- a/assets/resources/dolphin/STOPOXY_TLOZ_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 15 -Active frames: 18 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 -Active cycles: 1 -Frame rate: 8 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_0.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_0.bm deleted file mode 100644 index 7565b7793..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_1.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_1.bm deleted file mode 100644 index 22e380fae..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_10.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_10.bm deleted file mode 100644 index 6a5b2cf4d..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_11.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_11.bm deleted file mode 100644 index 0d84491bb..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_12.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_12.bm deleted file mode 100644 index 303e1bcba..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_13.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_13.bm deleted file mode 100644 index a59fd680d..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_14.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_14.bm deleted file mode 100644 index 17cf5bb9d..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_15.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_15.bm deleted file mode 100644 index 009f82de0..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_16.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_16.bm deleted file mode 100644 index 3bd7962ad..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_17.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_17.bm deleted file mode 100644 index 08efbbd28..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_18.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_18.bm deleted file mode 100644 index 26c7cb11c..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_19.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_19.bm deleted file mode 100644 index 7ece564a1..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_2.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_2.bm deleted file mode 100644 index e896571cf..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_20.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_20.bm deleted file mode 100644 index 524d8de7f..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_21.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_21.bm deleted file mode 100644 index bf9702eda..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_22.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_22.bm deleted file mode 100644 index 3c52d3d69..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_23.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_23.bm deleted file mode 100644 index f23b6ba30..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_24.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_24.bm deleted file mode 100644 index 66ba570f9..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_25.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_25.bm deleted file mode 100644 index df798ae8a..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_3.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_3.bm deleted file mode 100644 index 0f2299874..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_4.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_4.bm deleted file mode 100644 index b91dbfd79..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_5.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_5.bm deleted file mode 100644 index 22df0f090..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_6.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_6.bm deleted file mode 100644 index 3fd12ffe0..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_7.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_7.bm deleted file mode 100644 index 4978e55bc..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_8.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_8.bm deleted file mode 100644 index 319d0c404..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_9.bm b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_9.bm deleted file mode 100644 index 4b64661da..000000000 Binary files a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/meta.txt b/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/meta.txt deleted file mode 100644 index 953bc5a74..000000000 --- a/assets/resources/dolphin/STOPOXY_WOT_HEART_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 13 -Active frames: 13 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 -Active cycles: 1 -Frame rate: 8 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 67 -Y: 24 -Text: ... -AlignH: Left -AlignV: Center -StartFrame: 15 -EndFrame: 19 diff --git a/assets/resources/dolphin/Sasquach_Blaster_128x64/meta.txt b/assets/resources/dolphin/Sasquach_Blaster_128x64/meta.txt index 4d5cb4433..63d6e4750 100644 --- a/assets/resources/dolphin/Sasquach_Blaster_128x64/meta.txt +++ b/assets/resources/dolphin/Sasquach_Blaster_128x64/meta.txt @@ -3,12 +3,12 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 50 -Active frames: 0 +Passive frames: 10 +Active frames: 40 Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 -Active cycles: 0 +Active cycles: 1 Frame rate: 6 Duration: 3600 -Active cooldown: 0 +Active cooldown: 4 Bubble slots: 0 diff --git a/assets/resources/dolphin/Sasquach_CloudG0ku_128x64/meta.txt b/assets/resources/dolphin/Sasquach_CloudG0ku_128x64/meta.txt index dcbf70702..e780d7e95 100644 --- a/assets/resources/dolphin/Sasquach_CloudG0ku_128x64/meta.txt +++ b/assets/resources/dolphin/Sasquach_CloudG0ku_128x64/meta.txt @@ -3,9 +3,9 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 47 -Active frames: 0 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 +Passive frames: 10 +Active frames: 38 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 46 Active cycles: 1 Frame rate: 5 Duration: 3600 diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_0.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_0.bm new file mode 100644 index 000000000..2e5b64eb2 Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_0.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_1.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_1.bm new file mode 100644 index 000000000..94700ae24 Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_1.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_10.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_10.bm new file mode 100644 index 000000000..5a996b6ab Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_10.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_2.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_2.bm new file mode 100644 index 000000000..c19c790b9 Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_2.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_3.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_3.bm new file mode 100644 index 000000000..a2d137710 Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_3.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_4.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_4.bm new file mode 100644 index 000000000..5f8366db1 Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_4.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_5.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_5.bm new file mode 100644 index 000000000..6733bc76d Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_5.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_6.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_6.bm new file mode 100644 index 000000000..ff222c72a Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_6.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_7.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_7.bm new file mode 100644 index 000000000..39dac6d40 Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_7.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_8.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_8.bm new file mode 100644 index 000000000..8b70b31d1 Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_8.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_9.bm b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_9.bm new file mode 100644 index 000000000..b709421f6 Binary files /dev/null and b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/frame_9.bm differ diff --git a/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/meta.txt b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/meta.txt new file mode 100644 index 000000000..b8a080c97 --- /dev/null +++ b/assets/resources/dolphin/Sasquach_D1g1talRa1n_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 11 +Active frames: 0 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 +Active cycles: 0 +Frame rate: 7 +Duration: 3600 +Active cooldown: 0 + +Bubble slots: 0 diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_0.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_0.bm deleted file mode 100644 index eae8784bf..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_1.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_1.bm deleted file mode 100644 index 9687644bb..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_10.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_10.bm deleted file mode 100644 index 6dbadabfb..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_11.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_11.bm deleted file mode 100644 index 7b11df87c..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_12.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_12.bm deleted file mode 100644 index 486eb77b8..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_13.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_13.bm deleted file mode 100644 index aa8f9f66d..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_14.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_14.bm deleted file mode 100644 index 55934346f..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_15.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_15.bm deleted file mode 100644 index e74a406f7..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_16.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_16.bm deleted file mode 100644 index 9a0e9b6d0..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_17.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_17.bm deleted file mode 100644 index 47d06da4b..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_18.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_18.bm deleted file mode 100644 index 306f35771..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_19.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_19.bm deleted file mode 100644 index 3f2222928..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_2.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_2.bm deleted file mode 100644 index ff74b61be..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_20.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_20.bm deleted file mode 100644 index d8c722fc5..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_21.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_21.bm deleted file mode 100644 index d6976fd79..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_22.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_22.bm deleted file mode 100644 index f3bf402cc..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_23.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_23.bm deleted file mode 100644 index 3f30dfb34..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_24.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_24.bm deleted file mode 100644 index a8e9d6812..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_25.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_25.bm deleted file mode 100644 index b3d576255..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_26.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_26.bm deleted file mode 100644 index 34203e815..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_27.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_27.bm deleted file mode 100644 index 2308c1212..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_28.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_28.bm deleted file mode 100644 index 1d64b958f..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_29.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_29.bm deleted file mode 100644 index e9278f76c..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_3.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_3.bm deleted file mode 100644 index 8f89f5b05..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_30.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_30.bm deleted file mode 100644 index ed11a733f..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_31.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_31.bm deleted file mode 100644 index d44d9d94e..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_32.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_32.bm deleted file mode 100644 index 740c8b04f..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_33.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_33.bm deleted file mode 100644 index 2153f2a9c..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_34.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_34.bm deleted file mode 100644 index 7031ead25..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_35.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_35.bm deleted file mode 100644 index 68eaeb5fa..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_36.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_36.bm deleted file mode 100644 index 0ac8e2f25..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_37.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_37.bm deleted file mode 100644 index dc6d12bd3..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_4.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_4.bm deleted file mode 100644 index 71a72b450..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_5.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_5.bm deleted file mode 100644 index b7104a6f0..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_6.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_6.bm deleted file mode 100644 index daf6ac55a..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_7.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_7.bm deleted file mode 100644 index c0dd048d9..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_8.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_8.bm deleted file mode 100644 index f2f2910a8..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_9.bm b/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_9.bm deleted file mode 100644 index c48955b1e..000000000 Binary files a/assets/resources/dolphin/Sasquach_G0ku_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_G0ku_128x64/meta.txt b/assets/resources/dolphin/Sasquach_G0ku_128x64/meta.txt deleted file mode 100644 index 1e70864f4..000000000 --- a/assets/resources/dolphin/Sasquach_G0ku_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 58 -Active frames: 0 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 -Active cycles: 1 -Frame rate: 7 -Duration: 3600 -Active cooldown: 1 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/Sasquach_Narut0_128x64/meta.txt b/assets/resources/dolphin/Sasquach_Narut0_128x64/meta.txt index 7e7e55b26..1c4b1c117 100644 --- a/assets/resources/dolphin/Sasquach_Narut0_128x64/meta.txt +++ b/assets/resources/dolphin/Sasquach_Narut0_128x64/meta.txt @@ -3,12 +3,12 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 30 -Active frames: 0 +Passive frames: 10 +Active frames: 20 Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 -Active cycles: 0 +Active cycles: 1 Frame rate: 6 Duration: 3600 -Active cooldown: 0 +Active cooldown: 4 Bubble slots: 0 diff --git a/assets/resources/dolphin/Sasquach_RMCF_128x64/meta.txt b/assets/resources/dolphin/Sasquach_RMCF_128x64/meta.txt index b6b3fdf3e..ffc01409d 100644 --- a/assets/resources/dolphin/Sasquach_RMCF_128x64/meta.txt +++ b/assets/resources/dolphin/Sasquach_RMCF_128x64/meta.txt @@ -3,13 +3,13 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 100 -Active frames: 0 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 11 11 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 13 14 15 16 17 18 17 18 17 18 19 20 19 20 19 20 19 20 19 20 21 22 21 22 22 22 23 24 25 26 27 28 29 30 31 32 33 0 34 35 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 37 38 -Active cycles: 0 +Passive frames: 9 +Active frames: 99 +Frames order: 0 1 2 3 4 3 2 1 0 1 2 3 4 5 6 7 8 9 10 11 11 11 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 13 14 15 16 17 18 17 18 17 18 19 20 19 20 19 20 19 20 19 20 21 22 21 22 22 22 23 24 25 26 27 28 29 30 31 32 33 0 34 35 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 37 38 +Active cycles: 1 Frame rate: 4 Duration: 3600 -Active cooldown: 0 +Active cooldown: 4 Bubble slots: 1 diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_0.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_0.bm deleted file mode 100644 index ec013a8c8..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_1.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_1.bm deleted file mode 100644 index 811c426e9..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_10.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_10.bm deleted file mode 100644 index adda31bbb..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_11.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_11.bm deleted file mode 100644 index cefe6ba03..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_12.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_12.bm deleted file mode 100644 index 1df2ab2e9..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_13.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_13.bm deleted file mode 100644 index eabd707af..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_14.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_14.bm deleted file mode 100644 index 694735982..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_15.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_15.bm deleted file mode 100644 index 96d4e9a8b..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_16.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_16.bm deleted file mode 100644 index 0b798c110..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_17.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_17.bm deleted file mode 100644 index 044c3effa..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_18.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_18.bm deleted file mode 100644 index f2763e29b..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_19.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_19.bm deleted file mode 100644 index 94c38fd0a..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_2.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_2.bm deleted file mode 100644 index f5ffa2d44..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_20.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_20.bm deleted file mode 100644 index 4baf3a670..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_21.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_21.bm deleted file mode 100644 index 25a87e423..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_22.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_22.bm deleted file mode 100644 index b68fa0f16..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_23.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_23.bm deleted file mode 100644 index 1184423a2..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_24.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_24.bm deleted file mode 100644 index 06a1cc2d8..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_25.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_25.bm deleted file mode 100644 index f930f4e05..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_26.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_26.bm deleted file mode 100644 index 03ea91425..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_27.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_27.bm deleted file mode 100644 index 24fc7994c..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_28.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_28.bm deleted file mode 100644 index 1c9c07d8b..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_29.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_29.bm deleted file mode 100644 index 7fbce08ed..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_3.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_3.bm deleted file mode 100644 index ab1f5f0cb..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_30.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_30.bm deleted file mode 100644 index e56ea814e..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_31.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_31.bm deleted file mode 100644 index e793213c2..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_32.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_32.bm deleted file mode 100644 index aaf76b11d..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_33.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_33.bm deleted file mode 100644 index e67b26422..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_34.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_34.bm deleted file mode 100644 index 0fd4c1fa7..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_35.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_35.bm deleted file mode 100644 index bcd6b88e6..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_36.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_36.bm deleted file mode 100644 index 5b073ac8a..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_37.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_37.bm deleted file mode 100644 index f3a0dd1a9..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_38.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_38.bm deleted file mode 100644 index f1d00659d..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_39.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_39.bm deleted file mode 100644 index 0bce65677..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_4.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_4.bm deleted file mode 100644 index cbacdfe0c..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_40.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_40.bm deleted file mode 100644 index e0d6f3c0e..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_41.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_41.bm deleted file mode 100644 index b290a3523..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_42.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_42.bm deleted file mode 100644 index d22cd2901..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_43.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_43.bm deleted file mode 100644 index 9faa771c5..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_44.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_44.bm deleted file mode 100644 index fe1989b15..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_45.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_45.bm deleted file mode 100644 index 632d4bbd3..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_5.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_5.bm deleted file mode 100644 index a9111f41c..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_6.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_6.bm deleted file mode 100644 index 0821ede65..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_7.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_7.bm deleted file mode 100644 index 81c9a1826..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_8.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_8.bm deleted file mode 100644 index 9783fdd6e..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_9.bm b/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_9.bm deleted file mode 100644 index 39c3cd79b..000000000 Binary files a/assets/resources/dolphin/Sasquach_StickFight_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sasquach_StickFight_128x64/meta.txt b/assets/resources/dolphin/Sasquach_StickFight_128x64/meta.txt deleted file mode 100644 index b745b90c0..000000000 --- a/assets/resources/dolphin/Sasquach_StickFight_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 46 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 0 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 1 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_0.bm b/assets/resources/dolphin/Sharingan_128x64/frame_0.bm deleted file mode 100644 index 3a40432c8..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_1.bm b/assets/resources/dolphin/Sharingan_128x64/frame_1.bm deleted file mode 100644 index 151740a3c..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_10.bm b/assets/resources/dolphin/Sharingan_128x64/frame_10.bm deleted file mode 100644 index 0f61b9deb..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_100.bm b/assets/resources/dolphin/Sharingan_128x64/frame_100.bm deleted file mode 100644 index 232bc4cd4..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_100.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_101.bm b/assets/resources/dolphin/Sharingan_128x64/frame_101.bm deleted file mode 100644 index 9e74f1f83..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_101.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_102.bm b/assets/resources/dolphin/Sharingan_128x64/frame_102.bm deleted file mode 100644 index ff15c648c..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_102.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_103.bm b/assets/resources/dolphin/Sharingan_128x64/frame_103.bm deleted file mode 100644 index f432c264b..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_103.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_104.bm b/assets/resources/dolphin/Sharingan_128x64/frame_104.bm deleted file mode 100644 index 833cf97c2..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_104.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_105.bm b/assets/resources/dolphin/Sharingan_128x64/frame_105.bm deleted file mode 100644 index 79adbdecf..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_105.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_11.bm b/assets/resources/dolphin/Sharingan_128x64/frame_11.bm deleted file mode 100644 index 90ee21551..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_12.bm b/assets/resources/dolphin/Sharingan_128x64/frame_12.bm deleted file mode 100644 index 73cb240fa..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_13.bm b/assets/resources/dolphin/Sharingan_128x64/frame_13.bm deleted file mode 100644 index d58b23663..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_14.bm b/assets/resources/dolphin/Sharingan_128x64/frame_14.bm deleted file mode 100644 index d27bfe91e..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_15.bm b/assets/resources/dolphin/Sharingan_128x64/frame_15.bm deleted file mode 100644 index 2e3c071f2..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_16.bm b/assets/resources/dolphin/Sharingan_128x64/frame_16.bm deleted file mode 100644 index 18c41ce8d..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_17.bm b/assets/resources/dolphin/Sharingan_128x64/frame_17.bm deleted file mode 100644 index 9412a8b4d..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_18.bm b/assets/resources/dolphin/Sharingan_128x64/frame_18.bm deleted file mode 100644 index d1e33f519..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_19.bm b/assets/resources/dolphin/Sharingan_128x64/frame_19.bm deleted file mode 100644 index 4428bb7ef..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_2.bm b/assets/resources/dolphin/Sharingan_128x64/frame_2.bm deleted file mode 100644 index d616ec150..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_20.bm b/assets/resources/dolphin/Sharingan_128x64/frame_20.bm deleted file mode 100644 index c1590ff82..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_21.bm b/assets/resources/dolphin/Sharingan_128x64/frame_21.bm deleted file mode 100644 index c6d39afb8..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_22.bm b/assets/resources/dolphin/Sharingan_128x64/frame_22.bm deleted file mode 100644 index df1a359be..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_23.bm b/assets/resources/dolphin/Sharingan_128x64/frame_23.bm deleted file mode 100644 index f8e3b59a3..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_24.bm b/assets/resources/dolphin/Sharingan_128x64/frame_24.bm deleted file mode 100644 index e6575334e..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_25.bm b/assets/resources/dolphin/Sharingan_128x64/frame_25.bm deleted file mode 100644 index 118ff6b21..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_26.bm b/assets/resources/dolphin/Sharingan_128x64/frame_26.bm deleted file mode 100644 index b32aaa1d4..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_27.bm b/assets/resources/dolphin/Sharingan_128x64/frame_27.bm deleted file mode 100644 index 6f9229b83..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_28.bm b/assets/resources/dolphin/Sharingan_128x64/frame_28.bm deleted file mode 100644 index 1e6c92b6b..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_29.bm b/assets/resources/dolphin/Sharingan_128x64/frame_29.bm deleted file mode 100644 index d68550068..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_3.bm b/assets/resources/dolphin/Sharingan_128x64/frame_3.bm deleted file mode 100644 index ad588193f..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_30.bm b/assets/resources/dolphin/Sharingan_128x64/frame_30.bm deleted file mode 100644 index 6b5def7ca..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_31.bm b/assets/resources/dolphin/Sharingan_128x64/frame_31.bm deleted file mode 100644 index 7e9e37a6a..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_32.bm b/assets/resources/dolphin/Sharingan_128x64/frame_32.bm deleted file mode 100644 index c42ac834d..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_33.bm b/assets/resources/dolphin/Sharingan_128x64/frame_33.bm deleted file mode 100644 index f2437b47a..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_34.bm b/assets/resources/dolphin/Sharingan_128x64/frame_34.bm deleted file mode 100644 index ba0e6b617..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_35.bm b/assets/resources/dolphin/Sharingan_128x64/frame_35.bm deleted file mode 100644 index 370049d8e..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_35.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_36.bm b/assets/resources/dolphin/Sharingan_128x64/frame_36.bm deleted file mode 100644 index 9c5258273..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_36.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_37.bm b/assets/resources/dolphin/Sharingan_128x64/frame_37.bm deleted file mode 100644 index 64da841c2..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_37.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_38.bm b/assets/resources/dolphin/Sharingan_128x64/frame_38.bm deleted file mode 100644 index bc6215f51..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_38.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_39.bm b/assets/resources/dolphin/Sharingan_128x64/frame_39.bm deleted file mode 100644 index 779fd8887..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_39.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_4.bm b/assets/resources/dolphin/Sharingan_128x64/frame_4.bm deleted file mode 100644 index 2da212c06..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_40.bm b/assets/resources/dolphin/Sharingan_128x64/frame_40.bm deleted file mode 100644 index ce77ac2a7..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_40.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_41.bm b/assets/resources/dolphin/Sharingan_128x64/frame_41.bm deleted file mode 100644 index 901a3cca7..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_41.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_42.bm b/assets/resources/dolphin/Sharingan_128x64/frame_42.bm deleted file mode 100644 index 1a39aa5a7..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_42.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_43.bm b/assets/resources/dolphin/Sharingan_128x64/frame_43.bm deleted file mode 100644 index 4a5f61bee..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_43.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_44.bm b/assets/resources/dolphin/Sharingan_128x64/frame_44.bm deleted file mode 100644 index 23fd42489..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_44.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_45.bm b/assets/resources/dolphin/Sharingan_128x64/frame_45.bm deleted file mode 100644 index 72fb341bf..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_45.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_46.bm b/assets/resources/dolphin/Sharingan_128x64/frame_46.bm deleted file mode 100644 index 15e13be14..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_46.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_47.bm b/assets/resources/dolphin/Sharingan_128x64/frame_47.bm deleted file mode 100644 index ce092ae4f..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_47.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_48.bm b/assets/resources/dolphin/Sharingan_128x64/frame_48.bm deleted file mode 100644 index 7f18dd447..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_48.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_49.bm b/assets/resources/dolphin/Sharingan_128x64/frame_49.bm deleted file mode 100644 index 38ac7a1dd..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_49.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_5.bm b/assets/resources/dolphin/Sharingan_128x64/frame_5.bm deleted file mode 100644 index ed7ae748c..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_50.bm b/assets/resources/dolphin/Sharingan_128x64/frame_50.bm deleted file mode 100644 index 207d4edab..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_50.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_51.bm b/assets/resources/dolphin/Sharingan_128x64/frame_51.bm deleted file mode 100644 index e347387d8..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_51.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_52.bm b/assets/resources/dolphin/Sharingan_128x64/frame_52.bm deleted file mode 100644 index df7618c4e..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_52.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_53.bm b/assets/resources/dolphin/Sharingan_128x64/frame_53.bm deleted file mode 100644 index d47a1cc11..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_53.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_54.bm b/assets/resources/dolphin/Sharingan_128x64/frame_54.bm deleted file mode 100644 index 6f5d4879a..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_54.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_55.bm b/assets/resources/dolphin/Sharingan_128x64/frame_55.bm deleted file mode 100644 index 298c6cf0c..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_55.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_56.bm b/assets/resources/dolphin/Sharingan_128x64/frame_56.bm deleted file mode 100644 index 8e355474e..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_56.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_57.bm b/assets/resources/dolphin/Sharingan_128x64/frame_57.bm deleted file mode 100644 index 56c86b227..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_57.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_58.bm b/assets/resources/dolphin/Sharingan_128x64/frame_58.bm deleted file mode 100644 index 80e16f64d..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_58.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_59.bm b/assets/resources/dolphin/Sharingan_128x64/frame_59.bm deleted file mode 100644 index 357b70b1d..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_59.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_6.bm b/assets/resources/dolphin/Sharingan_128x64/frame_6.bm deleted file mode 100644 index d409adcbc..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_60.bm b/assets/resources/dolphin/Sharingan_128x64/frame_60.bm deleted file mode 100644 index 9e35aad48..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_60.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_61.bm b/assets/resources/dolphin/Sharingan_128x64/frame_61.bm deleted file mode 100644 index 085ff5633..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_61.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_62.bm b/assets/resources/dolphin/Sharingan_128x64/frame_62.bm deleted file mode 100644 index a41311c82..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_62.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_63.bm b/assets/resources/dolphin/Sharingan_128x64/frame_63.bm deleted file mode 100644 index af1030a89..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_63.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_64.bm b/assets/resources/dolphin/Sharingan_128x64/frame_64.bm deleted file mode 100644 index 5224de3fb..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_64.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_65.bm b/assets/resources/dolphin/Sharingan_128x64/frame_65.bm deleted file mode 100644 index 7721600e1..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_65.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_66.bm b/assets/resources/dolphin/Sharingan_128x64/frame_66.bm deleted file mode 100644 index c2dd48fe9..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_66.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_67.bm b/assets/resources/dolphin/Sharingan_128x64/frame_67.bm deleted file mode 100644 index e244bf628..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_67.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_68.bm b/assets/resources/dolphin/Sharingan_128x64/frame_68.bm deleted file mode 100644 index c64617c4a..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_68.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_69.bm b/assets/resources/dolphin/Sharingan_128x64/frame_69.bm deleted file mode 100644 index 06d6c2eff..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_69.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_7.bm b/assets/resources/dolphin/Sharingan_128x64/frame_7.bm deleted file mode 100644 index a0ab3bf54..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_70.bm b/assets/resources/dolphin/Sharingan_128x64/frame_70.bm deleted file mode 100644 index c41e7d914..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_70.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_71.bm b/assets/resources/dolphin/Sharingan_128x64/frame_71.bm deleted file mode 100644 index 75803c19e..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_71.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_72.bm b/assets/resources/dolphin/Sharingan_128x64/frame_72.bm deleted file mode 100644 index 83d63f9f1..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_72.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_73.bm b/assets/resources/dolphin/Sharingan_128x64/frame_73.bm deleted file mode 100644 index dd531505b..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_73.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_74.bm b/assets/resources/dolphin/Sharingan_128x64/frame_74.bm deleted file mode 100644 index c5783544a..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_74.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_75.bm b/assets/resources/dolphin/Sharingan_128x64/frame_75.bm deleted file mode 100644 index 4e3e0f222..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_75.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_76.bm b/assets/resources/dolphin/Sharingan_128x64/frame_76.bm deleted file mode 100644 index bc6c7132d..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_76.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_77.bm b/assets/resources/dolphin/Sharingan_128x64/frame_77.bm deleted file mode 100644 index 7a5ce1553..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_77.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_78.bm b/assets/resources/dolphin/Sharingan_128x64/frame_78.bm deleted file mode 100644 index 196f29a74..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_78.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_79.bm b/assets/resources/dolphin/Sharingan_128x64/frame_79.bm deleted file mode 100644 index cb73a9d63..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_79.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_8.bm b/assets/resources/dolphin/Sharingan_128x64/frame_8.bm deleted file mode 100644 index b62579cd2..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_80.bm b/assets/resources/dolphin/Sharingan_128x64/frame_80.bm deleted file mode 100644 index 83473075d..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_80.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_81.bm b/assets/resources/dolphin/Sharingan_128x64/frame_81.bm deleted file mode 100644 index 13c593445..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_81.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_82.bm b/assets/resources/dolphin/Sharingan_128x64/frame_82.bm deleted file mode 100644 index e46b602f8..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_82.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_83.bm b/assets/resources/dolphin/Sharingan_128x64/frame_83.bm deleted file mode 100644 index edaa66a28..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_83.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_84.bm b/assets/resources/dolphin/Sharingan_128x64/frame_84.bm deleted file mode 100644 index 444e8ffd5..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_84.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_85.bm b/assets/resources/dolphin/Sharingan_128x64/frame_85.bm deleted file mode 100644 index 9c3e1a231..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_85.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_86.bm b/assets/resources/dolphin/Sharingan_128x64/frame_86.bm deleted file mode 100644 index d0020aa83..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_86.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_87.bm b/assets/resources/dolphin/Sharingan_128x64/frame_87.bm deleted file mode 100644 index 2b382e52e..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_87.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_88.bm b/assets/resources/dolphin/Sharingan_128x64/frame_88.bm deleted file mode 100644 index 4516a1805..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_88.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_89.bm b/assets/resources/dolphin/Sharingan_128x64/frame_89.bm deleted file mode 100644 index 6b75196d3..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_89.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_9.bm b/assets/resources/dolphin/Sharingan_128x64/frame_9.bm deleted file mode 100644 index f72375239..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_90.bm b/assets/resources/dolphin/Sharingan_128x64/frame_90.bm deleted file mode 100644 index c4bf2a670..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_90.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_91.bm b/assets/resources/dolphin/Sharingan_128x64/frame_91.bm deleted file mode 100644 index ca0595523..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_91.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_92.bm b/assets/resources/dolphin/Sharingan_128x64/frame_92.bm deleted file mode 100644 index 3d90293eb..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_92.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_93.bm b/assets/resources/dolphin/Sharingan_128x64/frame_93.bm deleted file mode 100644 index 4d347fc46..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_93.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_94.bm b/assets/resources/dolphin/Sharingan_128x64/frame_94.bm deleted file mode 100644 index 760936a84..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_94.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_95.bm b/assets/resources/dolphin/Sharingan_128x64/frame_95.bm deleted file mode 100644 index 933757d25..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_95.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_96.bm b/assets/resources/dolphin/Sharingan_128x64/frame_96.bm deleted file mode 100644 index 0a8196410..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_96.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_97.bm b/assets/resources/dolphin/Sharingan_128x64/frame_97.bm deleted file mode 100644 index 87712b6d1..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_97.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_98.bm b/assets/resources/dolphin/Sharingan_128x64/frame_98.bm deleted file mode 100644 index 613f6e1b9..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_98.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/frame_99.bm b/assets/resources/dolphin/Sharingan_128x64/frame_99.bm deleted file mode 100644 index c6aea2ed7..000000000 Binary files a/assets/resources/dolphin/Sharingan_128x64/frame_99.bm and /dev/null differ diff --git a/assets/resources/dolphin/Sharingan_128x64/meta.txt b/assets/resources/dolphin/Sharingan_128x64/meta.txt deleted file mode 100644 index d4c5000e3..000000000 --- a/assets/resources/dolphin/Sharingan_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 105 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 -Active cycles: 1 -Frame rate: 6 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_0.bm b/assets/resources/dolphin/Umbrella_128x64/frame_0.bm deleted file mode 100644 index 1d8d99f0f..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_1.bm b/assets/resources/dolphin/Umbrella_128x64/frame_1.bm deleted file mode 100644 index 7bb3bda87..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_10.bm b/assets/resources/dolphin/Umbrella_128x64/frame_10.bm deleted file mode 100644 index dd412a3f8..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_11.bm b/assets/resources/dolphin/Umbrella_128x64/frame_11.bm deleted file mode 100644 index a128df474..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_12.bm b/assets/resources/dolphin/Umbrella_128x64/frame_12.bm deleted file mode 100644 index dc8c0910c..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_13.bm b/assets/resources/dolphin/Umbrella_128x64/frame_13.bm deleted file mode 100644 index d85c59407..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_14.bm b/assets/resources/dolphin/Umbrella_128x64/frame_14.bm deleted file mode 100644 index 922fec8e6..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_15.bm b/assets/resources/dolphin/Umbrella_128x64/frame_15.bm deleted file mode 100644 index af5fdbe4c..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_16.bm b/assets/resources/dolphin/Umbrella_128x64/frame_16.bm deleted file mode 100644 index deb46af0e..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_17.bm b/assets/resources/dolphin/Umbrella_128x64/frame_17.bm deleted file mode 100644 index 96fdd1bf3..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_18.bm b/assets/resources/dolphin/Umbrella_128x64/frame_18.bm deleted file mode 100644 index eaff0733e..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_19.bm b/assets/resources/dolphin/Umbrella_128x64/frame_19.bm deleted file mode 100644 index 05d1084b1..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_19.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_2.bm b/assets/resources/dolphin/Umbrella_128x64/frame_2.bm deleted file mode 100644 index 2a59d18c5..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_20.bm b/assets/resources/dolphin/Umbrella_128x64/frame_20.bm deleted file mode 100644 index ceb3b9ee2..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_20.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_21.bm b/assets/resources/dolphin/Umbrella_128x64/frame_21.bm deleted file mode 100644 index 86e0bf7a1..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_21.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_22.bm b/assets/resources/dolphin/Umbrella_128x64/frame_22.bm deleted file mode 100644 index 02d3660b3..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_22.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_23.bm b/assets/resources/dolphin/Umbrella_128x64/frame_23.bm deleted file mode 100644 index 54976f638..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_23.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_24.bm b/assets/resources/dolphin/Umbrella_128x64/frame_24.bm deleted file mode 100644 index 1ddfe4b43..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_24.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_25.bm b/assets/resources/dolphin/Umbrella_128x64/frame_25.bm deleted file mode 100644 index 4473311ee..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_25.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_26.bm b/assets/resources/dolphin/Umbrella_128x64/frame_26.bm deleted file mode 100644 index 72528553b..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_26.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_27.bm b/assets/resources/dolphin/Umbrella_128x64/frame_27.bm deleted file mode 100644 index ab065282c..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_27.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_28.bm b/assets/resources/dolphin/Umbrella_128x64/frame_28.bm deleted file mode 100644 index cbe0c75d4..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_28.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_29.bm b/assets/resources/dolphin/Umbrella_128x64/frame_29.bm deleted file mode 100644 index 510c36089..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_29.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_3.bm b/assets/resources/dolphin/Umbrella_128x64/frame_3.bm deleted file mode 100644 index e31dc17bf..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_30.bm b/assets/resources/dolphin/Umbrella_128x64/frame_30.bm deleted file mode 100644 index 89549e7c3..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_30.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_31.bm b/assets/resources/dolphin/Umbrella_128x64/frame_31.bm deleted file mode 100644 index 4f5a3f5d7..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_31.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_32.bm b/assets/resources/dolphin/Umbrella_128x64/frame_32.bm deleted file mode 100644 index e23e06788..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_32.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_33.bm b/assets/resources/dolphin/Umbrella_128x64/frame_33.bm deleted file mode 100644 index c9296ce3f..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_33.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_34.bm b/assets/resources/dolphin/Umbrella_128x64/frame_34.bm deleted file mode 100644 index 8a1e09293..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_34.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_4.bm b/assets/resources/dolphin/Umbrella_128x64/frame_4.bm deleted file mode 100644 index c4317183a..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_5.bm b/assets/resources/dolphin/Umbrella_128x64/frame_5.bm deleted file mode 100644 index 24b5d6c1f..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_6.bm b/assets/resources/dolphin/Umbrella_128x64/frame_6.bm deleted file mode 100644 index 95e4483f3..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_7.bm b/assets/resources/dolphin/Umbrella_128x64/frame_7.bm deleted file mode 100644 index 8808e7683..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_8.bm b/assets/resources/dolphin/Umbrella_128x64/frame_8.bm deleted file mode 100644 index 3c0283ed7..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/frame_9.bm b/assets/resources/dolphin/Umbrella_128x64/frame_9.bm deleted file mode 100644 index 0edd71ecd..000000000 Binary files a/assets/resources/dolphin/Umbrella_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/Umbrella_128x64/meta.txt b/assets/resources/dolphin/Umbrella_128x64/meta.txt deleted file mode 100644 index 718a4191b..000000000 --- a/assets/resources/dolphin/Umbrella_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 34 -Active frames: 1 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 -Active cycles: 1 -Frame rate: 5 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/manifest.txt b/assets/resources/dolphin/manifest.txt index a2cd013b3..245c109d2 100644 --- a/assets/resources/dolphin/manifest.txt +++ b/assets/resources/dolphin/manifest.txt @@ -1,68 +1,12 @@ Filetype: Flipper Animation Manifest Version: 1 -Name: RM_G0kuPatPat_128x64 +Name: Kuronons_RMCFW_128x64 Min butthurt: 0 Max butthurt: 14 -Min level: 18 +Min level: 2 Max level: 30 -Weight: 0 - -Name: RM_Sail0rM00n_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 18 -Max level: 30 -Weight: 0 - -Name: RM_BulmFlash_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 18 -Max level: 30 -Weight: 0 - -Name: RM_Kaz0ku_Haha_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 18 -Max level: 30 -Weight: 0 - -Name: RM_P3achRun_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 18 -Max level: 30 -Weight: 0 - -Name: RM_AikaZer0_2_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 18 -Max level: 30 -Weight: 0 - -Name: RM_AikaZer0_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 18 -Max level: 30 -Weight: 0 - -Name: RM_Z3lda_R3ady_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 18 -Max level: 30 -Weight: 0 - -Name: RM_Z3lda_0h_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 18 -Max level: 30 -Weight: 0 +Weight: 7 Name: Sasquach_Blaster_128x64 Min butthurt: 0 @@ -71,136 +15,24 @@ Min level: 1 Max level: 30 Weight: 7 -Name: Sasquach_Naruto_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_G0kuPew_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_ChiChi_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_R0shi_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_Tardi5_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: Haseo_Lufy_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: Haseo_G0ku_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: Haseo_Mug1wara_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_Init_D_Water_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_Hexadecimal_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_Kirbs_Confused_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_Kirbs_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_OP_G3ar4_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_M3gamanZ3r0_Battle_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: RM_Kam3ham3ha_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: On3_Pi3ce_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: Sharingan_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: Sasquach_G0ku_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 2 -Max level: 30 -Weight: 7 - Name: Sasquach_CloudG0ku_128x64 Min butthurt: 0 Max butthurt: 14 -Min level: 2 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Sasquach_D1g1talRa1n_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Sasquach_Narut0_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 Max level: 30 Weight: 7 @@ -211,17 +43,10 @@ Min level: 2 Max level: 30 Weight: 7 -Name: Kuronons_RMCFW_128x64 +Name: L1_Purple_rain_128x64 Min butthurt: 0 Max butthurt: 14 -Min level: 2 -Max level: 30 -Weight: 7 - -Name: L3_Fireplace_128x64 -Min butthurt: 0 -Max butthurt: 13 -Min level: 10 +Min level: 1 Max level: 30 Weight: 7 @@ -232,219 +57,37 @@ Min level: 2 Max level: 30 Weight: 7 -Name: L1_B0ws3r_128x64 +Name: L3_FlipperMustache_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 3 +Max level: 30 +Weight: 7 + +Name: L3_Fireplace_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 3 +Max level: 30 +Weight: 7 + +Name: L1_Mods_128x64 Min butthurt: 0 Max butthurt: 14 Min level: 1 Max level: 30 Weight: 7 -Name: L1_Mario_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: Haseo_80s_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 7 - -Name: FelixTwoTone_Veemon_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 3 -Max level: 30 -Weight: 5 - -Name: L1_NyanCat_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 5 - -Name: Sasquach_StickFight_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: MjK_Starfield_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: STOPOXY_SCHOOL_DAYS_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 5 - -Name: STOPOXY_TLOZ_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 5 - -Name: STOPOXY_WOT_HEART_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 5 - -Name: L1_P0liwhirl_128x51 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 4 - -Name: L1_Slayers_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 4 - -Name: L1_DJ_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: L1_Agumon_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: L1_Mew_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: MjK_Akira_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: MjK_LionsRoar_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: L1_Earth_Arcadia_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: L1_GITS_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: Allen_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: Maha_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: Umbrella_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: NeonK_Jiji_Milk -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 2 - -Name: L1_Rickroll_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 2 - -Name: L2_Hacking_pc_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 2 -Max level: 30 -Weight: 4 - -Name: L3_Hijack_radio_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 3 -Max level: 30 -Weight: 4 - -Name: L3_Lab_research_128x54 -Min butthurt: 0 -Max butthurt: 14 -Min level: 3 -Max level: 30 -Weight: 4 - -Name: L2_Soldering_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 2 -Max level: 30 -Weight: 4 - -Name: L1_Waves_128x50 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - -Name: L1_Read_books_128x64 -Min butthurt: 0 -Max butthurt: 14 -Min level: 1 -Max level: 30 -Weight: 3 - Name: L1_Painting_128x64 Min butthurt: 0 Max butthurt: 14 Min level: 1 Max level: 30 -Weight: 3 +Weight: 7 + +Name: L1_Halloween_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 5 diff --git a/assets/resources/dolphin/manifest.txt.exampleRM b/assets/resources/dolphin/manifest.txt.exampleRM new file mode 100644 index 000000000..3cb306e3c --- /dev/null +++ b/assets/resources/dolphin/manifest.txt.exampleRM @@ -0,0 +1,366 @@ +Filetype: Flipper Animation Manifest +Version: 1 + +Name: RM_Sail0rM00n_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 18 +Max level: 30 +Weight: 0 + +Name: RM_BulmFlash_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 18 +Max level: 30 +Weight: 0 + +Name: RM_Kaz0ku_Haha_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 18 +Max level: 30 +Weight: 0 + +Name: RM_P3achRun_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 18 +Max level: 30 +Weight: 0 + +Name: RM_AikaZer0_2_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 18 +Max level: 30 +Weight: 0 + +Name: RM_AikaZer0_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 18 +Max level: 30 +Weight: 0 + +Name: RM_Z3lda_0h_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 18 +Max level: 30 +Weight: 0 + +Name: Haseo_Sharingan_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Sasquach_Blaster_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Sasquach_Narut0_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_R0shi_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_Tardi5_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Haseo_Lufy_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Haseo_G0ku_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Haseo_Mug1wara_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_Init_D_Water_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_Hexadecimal_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_Kirbs_Confused_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_Kirbs_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_OP_G3ar4_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_M3gamanZ3r0_Battle_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: RM_Kam3ham3ha_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: On3_Pi3ce_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Sasquach_G0ku_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 2 +Max level: 30 +Weight: 7 + +Name: Sasquach_CloudG0ku_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 2 +Max level: 30 +Weight: 7 + +Name: Sasquach_RMCF_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 2 +Max level: 30 +Weight: 7 + +Name: Kuronons_RMCFW_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 2 +Max level: 30 +Weight: 7 + +Name: L3_Fireplace_128x64 +Min butthurt: 0 +Max butthurt: 13 +Min level: 10 +Max level: 30 +Weight: 7 + +Name: L2_FlipperCity_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 2 +Max level: 30 +Weight: 7 + +Name: Haseo_80s_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: Sasquach_D1g1talRa1n_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: L1_Purple_rain_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 7 + +Name: FelixTwoTone_Veemon_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 3 +Max level: 30 +Weight: 5 + +Name: L1_NyanCat_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 5 + +Name: Sasquach_StickFight_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 3 + +Name: MjK_Starfield_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 3 + +Name: STOPOXY_TLOZ_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 5 + +Name: STOPOXY_WOT_HEART_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 5 + +Name: L1_P0liwhirl_128x51 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 4 + +Name: L1_DJ_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 3 + +Name: L1_Agumon_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 3 + +Name: L1_Mew_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 3 + +Name: MjK_Akira_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 3 + +Name: L1_Rickroll_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 2 + +Name: L2_Hacking_pc_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 2 +Max level: 30 +Weight: 4 + +Name: L3_Hijack_radio_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 3 +Max level: 30 +Weight: 4 + +Name: L3_Lab_research_128x54 +Min butthurt: 0 +Max butthurt: 14 +Min level: 3 +Max level: 30 +Weight: 4 + +Name: L2_Soldering_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 2 +Max level: 30 +Weight: 4 + +Name: L1_Waves_128x50 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 3 + +Name: L1_Read_books_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 3 + +Name: L1_Painting_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 30 +Weight: 5 + +Name: L1_Halloween_128x64 +Min butthurt: 0 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 8 diff --git a/assets/resources/dolphin/name.txt b/assets/resources/dolphin/name.txt.example similarity index 100% rename from assets/resources/dolphin/name.txt rename to assets/resources/dolphin/name.txt.example diff --git a/assets/resources/ibtnfuzzer/example_uids_cyfral.txt b/assets/resources/ibtnfuzzer/example_uids_cyfral.txt new file mode 100644 index 000000000..497d2211a --- /dev/null +++ b/assets/resources/ibtnfuzzer/example_uids_cyfral.txt @@ -0,0 +1,8 @@ +# Example file, P.S. keep empty line at the end! +0000 +F000 +FE00 +CAFE +00CA +FF00 +FFFF diff --git a/assets/resources/ibtnfuzzer/example_uids_ds1990.txt b/assets/resources/ibtnfuzzer/example_uids_ds1990.txt new file mode 100644 index 000000000..6828bb423 --- /dev/null +++ b/assets/resources/ibtnfuzzer/example_uids_ds1990.txt @@ -0,0 +1,11 @@ +# Example file, P.S. keep empty line at the end! +0000000000000000 +FE00000000000000 +CAFE000000000000 +00CAFE0000000000 +0000CAFE00000000 +000000CAFE000000 +00000000CA000000 +0000000000A00000 +00000000000123FF +FFFFFFFFFFFFFFFF diff --git a/assets/resources/ibtnfuzzer/example_uids_metakom.txt b/assets/resources/ibtnfuzzer/example_uids_metakom.txt new file mode 100644 index 000000000..911ea73b2 --- /dev/null +++ b/assets/resources/ibtnfuzzer/example_uids_metakom.txt @@ -0,0 +1,9 @@ +# Example file, P.S. keep empty line at the end! +00000000 +F0000000 +E0000000 +FE000000 +CAFE0000 +00CAFE00 +0000CA00 +FFFFFFFF diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index fe8992907..8cc02f355 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,6 +1,97 @@ Filetype: IR library file Version: 1 -# Last Updated 9th Oct, 2022 +# Last Updated 14th Nov, 2022 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3480 1701 468 1248 467 1250 465 407 460 411 467 405 462 1255 460 412 466 405 462 1255 460 1257 468 404 463 1253 462 410 468 404 463 1253 462 1255 470 402 465 1252 463 1254 461 411 467 405 462 1254 461 411 467 405 462 1254 461 411 467 405 462 409 469 403 464 407 460 411 467 405 462 409 469 403 464 407 461 411 467 405 462 409 469 403 464 407 460 411 467 404 463 1254 461 410 468 404 463 1254 461 410 468 404 463 408 460 412 466 406 462 1255 460 412 466 406 461 410 468 404 463 1253 462 1255 470 1247 468 404 463 409 469 402 465 406 461 411 467 1250 465 407 460 1256 469 1248 467 1250 465 1252 463 410 468 403 464 408 460 412 466 406 461 410 468 404 463 408 460 412 466 406 461 410 468 404 463 408 459 412 466 405 462 409 469 403 464 407 460 411 467 405 462 409 469 403 464 407 460 411 467 405 462 409 469 402 465 407 460 411 467 404 463 1253 462 1255 470 402 465 406 461 1256 469 402 465 1252 463 408 470 1248 467 1250 465 407 460 1256 469 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3477 1704 465 1251 463 1254 460 411 467 405 462 410 468 1249 465 406 461 410 468 1249 465 1252 462 409 469 1248 466 406 461 410 468 1249 465 1252 462 409 469 1248 466 1251 463 408 470 402 465 1252 462 409 469 403 464 1253 461 410 468 404 463 408 470 402 465 407 460 411 467 405 462 409 469 403 464 407 460 412 466 405 462 410 468 404 463 408 459 412 465 406 461 1255 470 402 465 407 460 1256 469 403 464 407 460 411 467 405 462 410 468 1249 465 406 461 410 468 404 463 408 470 1248 466 1250 464 1252 462 410 468 1249 465 407 460 411 467 405 462 1255 459 412 466 1251 463 1254 460 1256 469 1248 466 406 461 410 468 404 463 409 469 403 464 407 460 411 467 405 462 409 469 403 464 407 460 412 466 405 462 410 468 404 463 408 459 412 466 406 461 410 468 404 463 408 470 402 465 406 461 411 467 404 463 409 469 402 465 407 460 411 467 405 462 1254 460 1256 469 403 464 407 460 1257 468 404 463 1254 460 411 467 405 462 409 469 1249 465 1251 463 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3477 1703 466 1251 463 1253 461 411 467 404 463 409 469 1248 466 405 462 409 469 1249 465 1251 463 408 470 1248 466 405 462 409 469 1248 466 1251 463 408 459 1258 467 1250 464 407 460 411 467 1251 463 408 459 412 466 1251 463 408 470 402 465 406 461 411 467 405 462 409 469 403 464 407 460 411 467 405 462 409 469 403 464 407 460 411 467 405 462 409 469 403 464 1252 462 409 469 403 464 1253 461 410 468 404 463 408 470 402 465 407 460 1256 469 402 465 407 460 411 467 405 462 1254 460 1257 468 1249 465 406 461 411 467 1250 464 407 460 412 466 1251 463 408 470 1247 467 1250 464 1252 462 1255 470 402 465 406 461 411 467 405 462 409 469 403 464 407 460 411 467 405 462 409 469 403 464 408 459 412 466 406 461 410 468 404 463 408 459 412 466 406 461 410 468 404 463 408 470 402 465 406 461 410 468 404 463 409 469 402 465 407 460 411 467 1250 464 1252 462 409 469 403 464 1253 461 410 468 1250 464 407 460 1256 469 403 464 1253 461 1256 469 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3484 1696 462 1254 460 1257 468 404 463 408 470 402 465 1252 462 410 468 404 463 1253 461 1256 469 403 464 1253 461 410 468 404 463 1254 460 1257 468 403 464 1253 461 1256 469 403 464 407 460 1257 468 404 463 408 470 1248 466 405 462 409 469 403 464 408 459 412 466 406 461 410 468 404 463 409 469 403 464 407 460 411 467 405 462 410 468 404 463 408 459 412 465 1251 463 408 470 402 465 1252 462 409 469 403 464 1253 461 1256 469 403 464 407 460 411 466 406 461 410 468 404 463 409 469 1248 466 405 462 1254 460 412 465 406 461 410 468 404 463 1254 460 411 467 1250 464 1253 461 1256 469 1248 466 405 462 410 468 404 463 409 469 403 464 407 460 411 467 405 462 410 468 403 464 408 459 412 466 406 461 410 468 404 463 408 459 412 466 406 461 411 467 405 462 409 469 403 464 407 460 411 466 405 462 410 468 404 463 408 459 412 466 406 461 1255 470 1247 467 405 462 409 469 1249 465 1251 463 409 469 402 465 1252 462 1255 470 402 465 1252 462 +# +# POWER_ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9048 4430 705 501 705 501 704 1603 729 1579 728 479 726 480 726 505 701 506 701 505 700 505 701 505 701 1606 701 505 701 505 701 506 700 505 701 505 701 506 700 506 700 505 701 506 700 1607 701 1607 701 505 701 505 701 505 700 506 700 506 700 1608 700 505 701 1607 700 505 701 506 701 1607 700 505 700 19917 724 505 701 1607 700 505 701 505 700 1607 700 506 700 1607 701 505 700 506 700 1607 701 506 700 506 700 506 700 506 700 1607 700 505 701 505 701 506 700 505 702 506 700 505 700 506 700 506 700 505 701 505 700 506 700 506 700 505 701 1608 700 1607 700 1608 700 506 700 39927 9074 4408 726 481 725 505 700 1606 700 1607 701 505 701 505 701 505 700 506 700 506 700 505 701 505 701 1607 700 506 700 505 701 506 701 505 701 505 701 505 701 505 701 505 701 505 700 1607 701 1607 701 505 701 505 701 505 701 505 701 505 701 1608 700 1607 701 1607 700 506 700 506 700 1607 700 506 701 19938 700 505 701 505 700 505 701 505 700 505 701 505 701 505 701 506 700 506 700 505 701 506 700 505 701 505 701 505 700 506 699 506 700 505 700 505 700 506 700 506 700 506 699 504 701 506 700 505 701 506 700 505 701 505 701 506 700 506 699 1607 700 1607 700 1608 700 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9105 4400 730 500 731 1552 755 1576 730 1577 729 477 728 478 727 479 727 479 727 479 727 479 727 479 728 1581 726 479 727 479 726 479 727 479 727 479 727 479 727 479 726 479 727 479 728 1580 728 1581 726 479 727 479 727 480 726 479 727 480 727 1580 727 479 727 1581 726 479 727 479 727 1581 727 479 727 19915 727 479 727 479 727 479 727 479 727 479 727 479 727 479 727 479 726 479 727 1581 726 480 726 480 726 480 726 480 726 1582 726 480 726 480 726 480 726 481 725 481 724 481 725 481 725 481 725 481 725 481 725 481 725 481 725 481 725 482 724 482 724 1583 724 482 724 39924 9074 4407 727 478 728 1580 728 1580 727 1580 728 477 728 478 728 478 728 479 728 478 728 479 727 479 727 1580 727 479 727 479 727 479 727 479 727 479 728 478 728 478 727 479 727 478 728 1581 727 1580 727 479 727 479 726 479 727 479 727 478 727 1581 726 1581 727 1580 727 479 727 479 727 1581 726 479 728 19915 727 479 727 480 726 480 726 481 725 505 700 481 725 480 725 504 701 505 701 480 726 505 702 482 724 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 700 505 700 505 701 506 700 506 701 505 701 505 701 506 701 505 701 505 701 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9049 4430 705 501 705 1602 705 1602 730 1578 728 1580 727 479 726 1583 724 505 701 1607 700 506 700 505 700 1607 700 506 700 506 700 506 700 506 700 506 700 505 701 506 700 505 701 506 701 1607 702 1607 700 506 700 505 701 506 700 505 700 506 700 1607 700 506 701 1607 700 506 700 506 700 1607 700 506 700 19940 700 1607 701 505 700 505 700 505 700 1607 700 505 700 506 700 506 700 506 701 1606 701 506 700 506 700 506 700 506 700 1608 699 506 699 506 700 506 700 506 700 505 701 505 701 506 700 506 699 506 700 506 700 506 700 506 700 505 700 506 700 1608 700 1608 700 506 700 39927 9072 4409 727 479 726 1606 701 1607 701 1607 700 1606 701 505 701 1606 701 505 700 1608 700 505 700 505 701 1607 701 505 701 506 700 505 700 506 700 506 700 505 701 505 700 505 700 506 701 1607 700 1607 700 506 700 506 700 505 701 505 701 506 700 1607 701 1608 700 1608 699 505 700 506 700 1607 700 506 700 19940 700 505 701 506 700 505 701 506 700 505 701 505 700 505 701 505 701 505 701 505 701 505 701 506 700 505 701 506 699 506 700 506 700 506 700 506 700 505 701 506 700 1607 700 506 700 506 700 506 700 506 700 506 701 506 700 506 700 506 700 1606 700 506 701 506 700 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9052 4428 705 500 706 1601 705 1602 730 1579 729 1579 729 478 727 1580 727 479 727 479 727 479 727 479 727 1581 726 480 726 479 727 480 726 480 726 479 727 479 727 480 726 480 726 479 727 1581 726 1581 726 480 726 480 726 480 726 480 726 480 725 1581 726 480 726 1583 725 480 726 480 726 1582 725 481 725 19911 727 1581 727 479 727 479 727 479 727 1581 726 480 727 479 727 479 727 479 727 1581 726 480 726 480 725 480 726 479 727 1581 726 479 726 480 726 480 726 480 725 480 726 480 726 480 725 480 726 480 726 481 725 481 725 482 724 481 725 1607 700 505 701 1582 725 505 701 39925 9075 4405 728 477 729 1579 728 1579 728 1579 728 1579 728 478 728 1579 728 478 728 478 728 478 727 478 728 1579 728 478 728 478 728 478 727 478 728 478 728 478 728 478 728 478 728 478 728 1579 728 1580 727 478 728 478 728 479 728 479 728 478 727 1580 727 1580 727 1580 727 479 727 478 729 1580 727 478 728 19913 728 478 728 477 729 478 728 478 728 478 728 478 728 478 728 478 728 478 728 478 728 477 729 478 728 478 728 478 728 478 727 478 728 478 728 477 728 478 728 478 728 1579 728 478 728 478 728 478 728 478 728 478 728 478 728 478 727 1580 727 478 728 478 728 479 727 +# LEFT-RIGHT +name: SWING +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9080 4428 704 501 704 1602 705 1602 705 1602 705 1602 705 501 705 1602 705 501 704 501 705 501 705 502 704 1604 728 478 728 478 728 478 728 504 728 478 728 478 728 478 727 479 727 478 728 1580 728 1580 728 478 728 478 727 478 728 478 728 478 728 1580 728 478 728 1579 729 478 728 479 727 1580 727 478 728 19913 727 478 728 478 727 478 727 478 728 1580 728 478 728 478 728 479 727 479 727 1580 727 479 727 479 727 479 727 479 727 1580 727 478 727 478 728 479 727 479 726 478 727 479 727 479 727 479 727 479 727 479 727 479 727 479 727 479 727 1581 727 479 727 1581 726 479 727 39924 9050 4430 704 502 729 1579 729 1579 729 1579 728 1579 728 478 727 1580 728 478 727 478 728 478 728 478 727 1580 727 478 728 478 728 478 728 478 728 478 728 478 728 478 727 479 727 479 727 1581 727 1580 727 478 728 478 728 478 728 478 728 478 727 1580 728 1581 727 1581 727 479 727 479 727 1579 728 479 727 19912 728 478 728 478 728 478 728 478 728 478 728 478 728 478 728 478 728 478 728 478 728 478 728 478 728 478 728 478 727 478 728 478 728 478 728 478 728 479 727 479 727 1580 728 479 727 479 727 479 727 479 727 479 727 479 727 479 726 1581 726 479 727 480 727 479 727 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 190 26844 503 368 475 395 475 395 475 367 476 395 475 25347 3527 1695 451 1290 451 420 476 394 476 394 475 1264 475 370 471 399 470 400 470 400 443 1298 443 400 470 1270 471 1270 471 400 470 1270 471 1271 470 1270 471 1270 471 1271 470 400 470 373 470 1271 470 400 470 400 470 400 443 400 470 400 470 400 443 400 470 400 470 400 470 374 469 1271 470 400 470 1271 470 400 470 401 442 401 469 1272 469 1272 469 401 469 401 469 374 469 401 469 401 469 401 442 401 469 401 469 401 469 374 469 401 469 401 469 374 469 401 469 401 469 401 442 1300 441 1299 442 1300 441 402 468 1272 469 402 468 1272 469 1273 468 34903 3522 1703 471 1270 471 400 470 373 470 400 470 1270 471 400 470 400 443 400 470 400 470 1270 471 400 443 1298 443 1297 444 400 470 1270 471 1270 471 1270 471 1270 471 1270 471 400 470 400 470 1270 471 373 470 400 470 400 470 400 443 400 470 400 470 400 470 373 470 400 470 400 470 400 443 1298 443 400 470 400 470 400 443 400 470 1271 470 400 470 1271 470 1271 470 400 470 1271 470 1271 470 1271 470 373 470 400 470 1271 470 1271 470 400 470 1271 470 1271 470 400 443 400 470 400 470 400 470 1271 470 373 470 1271 470 401 469 1271 470 400 470 1271 470 34903 3522 1702 471 1269 472 373 470 399 471 399 471 1270 471 399 444 399 471 399 471 399 471 1270 471 372 471 1270 471 1270 471 399 471 1270 471 1270 471 1270 471 1270 471 1270 471 399 471 399 444 1297 444 399 471 399 471 399 471 372 471 399 471 399 471 373 470 399 471 399 471 399 444 400 470 399 471 399 471 373 470 400 470 400 470 399 444 400 470 1270 471 400 470 400 470 1270 471 1271 470 1271 470 372 471 400 470 400 470 400 443 1298 443 400 470 1271 470 1271 470 400 470 400 443 401 469 400 470 401 469 373 470 401 469 401 469 401 442 401 469 401 469 401 469 375 468 402 468 401 469 1273 468 1296 445 426 416 426 445 426 444 426 417 426 444 426 444 426 444 399 444 426 444 426 444 426 417 426 444 426 444 426 444 399 444 426 444 426 444 426 417 1324 417 1325 416 426 444 426 444 427 416 427 444 426 444 426 444 399 444 427 443 427 443 426 417 1325 416 1325 416 427 443 427 443 426 444 399 444 427 443 427 444 427 416 427 444 427 443 427 443 400 443 427 443 427 443 400 443 427 443 427 443 427 416 1325 416 427 443 427 443 427 443 400 443 427 443 1298 443 1298 443 427 443 427 416 427 443 427 443 427 443 400 443 427 443 1298 443 427 443 400 443 428 442 427 443 427 416 428 443 427 443 427 443 400 443 1298 443 1298 443 428 442 428 442 428 415 428 442 1299 442 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 473 397 473 397 473 397 472 371 472 397 473 25350 3523 1697 449 1292 449 422 474 397 473 397 473 1267 473 372 470 401 468 402 468 402 441 1300 441 402 468 1272 469 1272 469 402 468 1273 468 1273 468 1272 469 1273 468 1273 468 403 440 402 468 1273 468 402 468 402 468 375 468 402 468 402 468 402 441 402 468 402 468 402 468 376 467 1273 468 402 468 1273 468 402 468 402 441 402 468 1273 468 1273 468 402 468 403 467 376 467 403 467 403 467 376 467 403 467 403 467 403 440 403 467 403 467 403 467 376 467 403 467 403 467 403 440 1302 439 1302 439 1302 439 404 466 1274 467 403 467 1274 467 1275 466 34905 3520 1705 469 1273 468 402 441 402 468 402 468 1273 468 402 468 376 467 402 468 402 468 1273 468 402 441 1300 441 1300 441 403 467 1273 468 1273 468 1273 468 1273 468 1273 468 403 467 402 468 1273 468 376 467 402 468 402 468 402 441 403 467 403 468 402 441 403 467 403 467 403 467 376 467 1274 467 403 467 403 467 403 440 403 467 1274 467 403 467 403 467 376 467 1274 467 1274 467 1274 467 1274 467 403 467 404 466 1274 467 1274 467 404 439 1302 439 1302 439 404 466 404 466 404 439 1302 439 1302 439 404 466 1275 466 404 466 1275 466 404 466 1275 466 34906 3519 1705 468 1273 468 376 467 402 468 402 468 1273 468 402 441 402 468 402 468 402 468 1273 468 376 467 1273 468 1273 468 402 468 1273 468 1273 468 1273 468 1273 468 1273 468 402 468 376 467 1273 468 402 468 402 468 403 440 403 467 403 467 403 467 376 467 403 467 403 467 403 440 403 467 403 468 402 468 376 467 403 467 403 467 403 440 403 467 403 467 403 440 403 467 1274 467 1274 467 1274 467 403 467 404 466 376 467 1274 467 1275 466 404 466 1275 466 1275 466 404 466 404 439 404 466 404 467 404 466 377 466 404 466 404 466 404 439 404 466 404 466 405 438 405 465 404 466 405 465 1276 465 1299 442 378 465 406 464 405 465 406 437 405 465 428 442 428 442 401 442 428 442 428 442 428 415 429 441 428 442 429 414 429 441 429 441 428 442 401 442 1300 441 1299 442 429 441 429 441 429 414 429 441 429 441 429 441 402 441 429 441 429 441 429 414 1327 414 1327 414 429 441 429 441 429 441 402 441 429 441 429 441 402 441 429 441 429 441 429 414 429 441 429 441 429 441 402 441 429 441 429 441 429 414 1328 413 430 441 429 441 429 441 402 441 429 441 1300 441 1301 440 429 441 402 441 430 440 430 440 430 413 430 441 429 441 1301 440 430 440 403 440 430 440 430 440 430 413 430 440 430 440 430 440 1301 440 1301 440 1301 440 403 440 430 440 430 440 403 440 1301 440 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 504 342 501 394 476 394 475 367 476 394 476 25347 3528 1694 451 1290 451 420 476 393 477 394 475 1264 476 369 472 398 471 399 471 399 444 1297 444 399 471 1269 472 1269 472 399 471 1269 472 1270 471 1270 471 1269 472 1270 471 399 471 372 471 1270 471 399 471 399 471 399 444 399 471 399 471 399 444 399 471 399 471 399 471 372 471 1270 471 399 471 1270 471 399 471 399 444 399 471 1270 471 1270 471 399 471 399 471 372 471 399 471 399 471 399 444 400 470 399 471 400 443 400 471 399 471 400 471 372 471 400 470 399 471 400 443 1298 443 1298 443 1298 443 400 470 1271 470 400 470 1271 470 1271 470 34902 3523 1702 471 1269 472 399 471 372 471 399 471 1270 471 399 471 372 471 399 471 399 471 1269 472 399 444 1297 444 1297 444 399 471 1270 471 1270 471 1270 471 1269 472 1270 471 399 471 399 471 1270 471 372 471 399 471 399 471 399 444 399 471 399 471 399 471 373 470 399 471 399 471 399 444 1297 444 399 471 400 470 400 443 400 470 1270 471 399 471 399 471 373 470 1270 471 1271 470 1270 471 1270 471 400 470 400 470 1271 470 1271 470 400 443 1298 443 1298 443 400 470 400 470 400 470 1271 470 1271 470 373 470 1271 470 400 470 1271 470 400 470 1271 470 34902 3523 1702 471 1270 471 373 470 399 471 399 471 1270 471 399 444 400 470 399 471 399 471 1270 471 373 470 1270 471 1270 471 399 471 1270 471 1270 471 1270 471 1270 471 1270 471 399 471 400 443 1298 443 400 470 400 470 400 470 373 470 400 470 399 471 373 470 400 470 400 470 400 443 400 470 400 470 400 470 373 470 400 470 400 470 400 443 400 470 400 470 400 470 373 470 1271 470 1271 470 1271 470 400 470 400 470 373 470 400 470 1271 470 401 469 1272 469 1271 470 401 442 401 469 401 469 401 469 374 469 401 469 401 469 401 442 401 469 401 469 401 469 374 469 401 469 401 469 401 442 1300 441 1300 441 401 469 401 469 402 441 402 468 402 468 402 468 375 468 402 468 402 468 402 441 402 468 402 468 402 468 375 468 403 467 403 467 403 440 403 467 1274 467 1273 468 404 466 427 416 403 467 404 466 427 443 400 443 427 443 427 443 427 416 427 444 1298 443 1298 443 427 443 427 443 400 443 427 443 427 443 427 416 427 443 427 443 427 416 427 443 427 443 427 443 400 443 427 443 427 443 427 416 428 442 1298 443 427 443 428 442 400 443 427 443 427 443 1298 443 1298 443 427 416 428 442 427 443 428 442 400 443 428 442 428 442 1299 442 400 443 428 442 428 442 428 415 428 442 428 442 428 442 401 442 1299 442 428 442 1299 442 428 442 428 415 428 442 428 442 1299 442 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1286 428 1251 408 442 1241 1289 399 1280 431 408 1247 1283 431 408 1248 442 1242 437 1246 443 1240 439 8135 1279 434 1256 430 409 1246 1284 430 1260 426 413 1242 1287 426 413 1243 436 1247 443 1241 438 1245 445 8130 1284 428 1262 424 415 1240 1279 435 1255 430 409 1247 1283 430 409 1247 443 1241 438 1245 434 1249 441 8133 1281 432 1258 428 411 1244 1286 428 1251 407 443 1240 1279 434 416 1240 439 1244 435 1249 441 1243 436 8137 1287 400 1279 433 417 1239 1280 433 1257 430 409 1246 1284 429 410 1246 444 1240 439 1245 445 1239 440 8133 1281 405 1285 428 411 1245 1285 429 1261 425 414 1241 1289 425 414 1242 437 1246 444 1240 439 1245 434 8139 1285 428 1251 435 415 1240 1279 434 1256 430 409 1246 1283 430 409 1247 443 1241 438 1245 445 1239 440 8133 1281 432 1258 428 411 1244 1286 428 1251 407 443 1240 1279 434 416 1240 439 1244 435 1249 441 1243 436 8137 1287 426 1253 432 407 1248 1281 432 1258 401 438 1244 1285 427 412 1244 435 1248 442 1242 437 1247 442 8131 1283 403 1287 426 413 1242 1287 426 1253 433 417 1239 1280 432 418 1238 441 1243 436 1247 442 1241 438 8136 1288 398 1281 404 435 1248 1281 404 1286 400 439 1244 1285 400 439 1244 435 1249 441 1243 436 1247 442 8131 1283 404 1285 400 439 1243 1286 401 1278 407 443 1240 1289 397 442 1240 439 1245 434 1249 441 1243 436 8138 1286 401 1278 407 443 1240 1279 407 1283 403 436 1246 1283 403 436 1246 444 1240 439 1245 434 1249 441 8134 1280 406 1284 402 437 1246 1283 402 1288 398 441 1242 1288 399 440 1242 437 1247 443 1241 438 1245 434 8140 1284 402 1288 398 441 1242 1287 425 1254 432 407 1249 1281 432 407 1249 441 1243 436 1247 443 1241 438 8136 1288 425 1254 405 434 1248 1281 406 1284 401 438 1245 1285 402 437 1246 433 1250 440 1244 435 1248 442 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1281 406 1284 403 436 1246 1284 403 1276 409 441 1242 437 1246 433 1250 440 1244 1286 401 438 1244 435 8137 1287 400 1279 407 432 1250 1280 407 1283 403 436 1246 433 1250 440 1244 435 1248 1282 405 434 1248 442 8131 1283 404 1286 401 438 1244 1285 401 1278 408 442 1240 439 1244 435 1248 442 1242 1287 400 439 1243 436 8137 1287 399 1280 406 433 1250 1280 407 1283 403 436 1246 433 1250 440 1244 435 1248 1282 406 433 1249 441 8132 1282 404 1286 402 437 1244 1285 402 1277 408 442 1240 439 1244 435 1249 441 1243 1286 401 438 1243 436 8136 1277 409 1281 407 432 1249 1280 406 1284 402 437 1244 435 1249 441 1242 437 1247 1282 405 434 1248 442 8130 1283 403 1287 401 438 1243 1286 400 1279 407 432 1249 441 1243 436 1247 443 1241 1278 409 441 1241 438 8134 1279 407 1283 431 408 1247 1282 404 1286 427 412 1243 436 1247 442 1241 438 1245 1284 403 436 1245 434 8139 1285 401 1278 435 415 1240 1279 408 1282 404 435 1246 433 1250 440 1244 435 1249 1280 407 432 1249 441 8132 1281 405 1285 402 437 1244 1285 401 1278 407 432 1250 440 1243 436 1247 443 1241 1278 409 441 1241 438 8135 1279 407 1283 431 408 1246 1283 404 1275 436 414 1242 437 1246 433 1250 440 1244 1285 401 438 1244 435 8137 1277 409 1281 433 406 1249 1281 405 1285 401 438 1243 436 1247 442 1241 438 1245 1284 402 437 1246 433 8138 1286 401 1278 408 442 1241 1278 408 1281 404 435 1246 433 1249 440 1243 436 1247 1282 405 434 1247 442 8130 1283 404 1285 401 438 1243 1286 400 1279 406 433 1248 442 1242 437 1246 433 1250 1279 408 442 1240 439 8133 1280 407 1282 403 436 1245 1284 403 1276 409 441 1240 439 1244 435 1249 440 1243 1286 400 439 1243 436 8136 1287 400 1279 406 433 1248 1281 406 1284 401 438 1243 436 1247 442 1241 438 1245 1284 402 437 1245 434 8138 1286 401 1278 407 432 1250 1279 406 1284 402 437 1245 434 1249 441 1242 437 1246 1283 403 436 1247 432 8139 1285 401 1278 435 415 1241 1278 407 1283 404 435 1246 433 1250 440 1243 436 1247 1282 404 435 1247 443 8130 1283 402 1277 436 414 1241 1278 408 1282 404 435 1247 432 1251 439 1244 435 1249 1280 406 433 1249 441 8131 1283 403 1276 411 439 1242 1277 410 1280 405 434 1248 442 1241 438 1245 434 1249 1280 407 432 1249 441 8132 1282 404 1286 428 411 1243 1286 400 1279 406 433 1248 442 1242 437 1246 433 1250 1279 408 431 1250 440 8132 1281 405 1285 429 410 1244 1285 401 1278 407 432 1250 439 1243 436 1247 443 1241 1278 409 441 1241 438 8134 1280 406 1284 404 435 1245 1284 403 1276 435 415 1241 438 1244 435 1249 440 1242 1287 400 439 1242 437 8135 1279 407 1283 431 408 1246 1283 403 1276 409 441 1241 438 1245 434 1249 441 1243 1286 400 439 1243 436 8136 1278 408 1313 401 407 1248 1281 404 1286 400 439 1243 436 1247 443 1240 439 1245 1284 402 437 1245 434 8138 1286 400 1310 403 405 1250 1279 406 1304 382 437 1245 434 1249 441 1243 436 1247 1282 404 435 1248 442 8130 1283 403 1287 401 438 1244 1285 401 1309 377 442 1240 439 1244 435 1248 441 1242 1287 401 438 1244 435 8137 1287 402 1308 405 414 1241 1309 405 1284 402 406 1249 440 1242 437 1247 442 1241 1309 406 412 1242 437 8137 1307 407 1251 434 416 1240 1289 424 1255 431 408 1247 442 1241 438 1245 444 1240 1289 424 415 1240 439 8134 1310 404 1254 432 407 1248 1281 432 1257 428 411 1244 435 1249 440 1243 436 1247 1313 401 407 1248 442 8131 1313 401 1257 428 411 1244 1285 429 1261 425 414 1241 438 1245 434 1250 440 1244 1285 428 411 1245 434 8139 1316 399 1259 426 413 1242 1287 426 1253 433 406 1249 440 1243 436 1247 443 1241 1288 399 440 1242 437 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1280 406 1283 431 408 1246 1283 403 1276 437 413 1243 436 1247 1282 404 435 1248 441 1242 437 1246 433 8140 1283 404 1285 428 411 1244 1285 400 1279 408 431 1250 439 1244 1285 401 438 1245 434 1249 440 1243 436 8137 1286 401 1278 409 441 1241 1278 407 1282 404 435 1247 442 1241 1278 408 442 1241 438 1245 434 1250 440 8134 1279 407 1282 405 434 1247 1282 404 1285 401 438 1244 435 1248 1281 405 434 1249 440 1242 437 1247 442 8131 1282 405 1284 402 437 1245 1284 401 1278 409 441 1241 438 1245 1284 402 437 1246 433 1250 439 1244 435 8139 1284 402 1277 410 440 1242 1277 409 1280 432 407 1248 441 1242 1277 410 440 1243 436 1247 442 1241 438 8135 1278 409 1280 433 406 1249 1280 406 1283 403 436 1246 433 1250 1279 408 442 1241 438 1246 433 1250 439 8134 1279 408 1281 407 432 1249 1280 406 1283 404 435 1247 442 1241 1278 409 441 1242 437 1247 442 1241 438 8136 1287 400 1279 408 442 1240 1279 407 1282 431 408 1248 441 1241 1278 409 441 1243 436 1247 442 1241 438 8136 1287 400 1279 408 442 1241 1278 408 1281 405 434 1249 440 1242 1287 400 439 1244 435 1248 442 1242 437 8137 1286 401 1278 409 441 1242 1277 409 1281 406 433 1249 440 1243 1286 400 439 1244 435 1248 441 1243 436 8137 1286 402 1277 409 441 1242 1277 409 1280 406 433 1249 440 1242 1287 400 439 1243 436 1247 442 1241 438 8136 1287 400 1279 407 432 1250 1279 408 1281 404 435 1246 443 1240 1279 409 441 1241 438 1245 434 1249 440 8134 1279 409 1280 405 434 1247 1282 405 1284 402 437 1243 436 1248 1281 406 433 1248 441 1242 437 1247 442 8132 1280 407 1282 430 409 1245 1284 404 1275 410 440 1242 437 1246 1283 405 434 1247 442 1241 438 1272 407 8141 1282 405 1284 402 437 1243 1286 402 1277 408 442 1240 439 1244 1285 403 436 1245 434 1249 440 1244 435 8138 1285 403 1276 410 440 1242 1287 400 1279 407 432 1248 441 1242 1287 401 438 1243 436 1247 442 1242 437 8136 1287 401 1278 408 431 1249 1280 408 1281 404 435 1245 434 1250 1279 408 442 1240 439 1244 435 1249 440 8133 1279 408 1281 404 435 1246 1283 404 1285 400 439 1242 437 1247 1282 405 434 1247 442 1241 438 1246 433 8140 1283 405 1284 401 438 1243 1286 401 1278 408 431 1249 440 1243 1286 402 437 1244 435 1248 441 1242 437 8137 1286 401 1278 408 442 1240 1279 408 1281 404 435 1246 433 1250 1279 408 442 1240 439 1244 435 1249 440 8133 1280 408 1282 404 435 1246 1283 431 1259 401 438 1242 437 1247 1282 405 434 1247 442 1241 438 1246 443 8130 1283 405 1284 401 438 1269 1260 427 1252 433 406 1276 413 1243 1286 401 438 1270 409 1248 441 1242 437 8136 1287 401 1278 407 432 1249 1280 434 1255 403 436 1245 444 1240 1279 408 442 1240 439 1245 434 1249 440 8134 1278 435 1254 405 434 1247 1282 405 1284 401 438 1243 436 1248 1281 406 433 1249 440 1243 436 1247 442 8131 1281 406 1283 401 438 1244 1285 401 1288 397 442 1241 438 1245 1284 402 437 1246 443 1240 439 1244 435 8139 1284 402 1287 398 441 1241 1288 398 1281 404 435 1248 441 1242 1287 398 441 1242 437 1246 443 1240 439 8134 1289 397 1282 403 436 1246 1283 403 1286 398 441 1242 437 1246 1283 403 436 1247 442 1241 438 1245 434 # # ON name: POWER @@ -1287,3 +1378,82 @@ frequency: 38000 duty_cycle: 0.330000 data: 3302 1618 435 393 436 391 438 1207 431 396 433 1213 435 392 437 391 438 388 441 1206 432 1214 434 392 437 389 440 388 431 1215 433 1213 435 392 437 390 439 388 431 396 433 395 434 392 437 390 439 388 431 396 433 394 435 392 437 390 439 388 441 1205 433 395 434 392 437 389 440 388 431 396 433 394 435 392 437 1209 439 388 431 397 432 394 435 393 436 1210 438 387 432 396 433 394 435 392 437 390 439 388 431 1215 433 394 435 1211 437 1208 440 1205 433 1213 435 1211 437 1209 439 # +# POWER_OFF +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9048 4430 705 500 706 1602 705 1602 729 476 730 1579 728 478 728 1580 727 480 726 480 726 480 726 480 726 1606 702 505 701 481 725 481 724 505 701 481 725 504 702 505 701 504 701 505 701 1606 702 504 702 504 701 504 701 504 702 505 701 505 701 1606 701 504 702 1607 701 504 701 504 701 1607 701 505 701 19915 726 1582 726 481 725 504 701 505 701 1606 701 505 701 505 701 505 701 505 700 1606 701 505 701 505 701 505 701 504 702 1606 701 505 701 504 701 505 701 504 702 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 1606 701 505 701 1607 700 1606 701 39924 9075 4406 728 478 728 1581 727 1581 726 481 725 1582 725 480 725 1606 701 504 702 481 725 505 701 505 701 1606 701 504 702 505 701 505 701 505 701 505 701 505 701 505 701 505 701 505 701 1606 701 505 701 505 701 505 701 505 701 505 701 505 701 1606 701 1607 701 1607 701 505 701 505 701 1607 701 506 701 19913 726 480 726 480 726 480 726 480 726 479 727 479 727 480 725 479 727 479 726 480 726 480 726 480 726 480 726 480 726 480 726 504 701 480 726 480 725 505 702 481 725 1606 701 505 701 481 725 481 725 505 700 505 701 504 701 505 701 1607 701 504 701 505 700 1607 701 +# POWER_OFF +name: POWER +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 +# +# Model: Olimpia Splendid OS-SEAMH09EI +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4402 4336 561 1641 538 554 535 1616 563 1640 539 554 535 557 532 1645 534 560 539 551 538 1639 540 553 536 556 533 1644 535 1642 537 556 533 1620 558 558 531 561 538 554 535 1641 538 1639 540 1638 530 1620 559 1647 532 1643 536 1641 538 1613 566 553 536 557 532 560 539 553 536 558 531 560 529 1647 532 535 564 1613 555 537 563 1614 565 554 535 559 530 1645 534 559 530 1647 532 560 539 1612 557 562 537 1640 539 1611 557 5189 4398 4344 564 1638 530 563 537 1640 539 1639 529 563 537 530 559 1644 535 560 529 535 564 1639 529 537 563 556 533 1644 535 1643 536 557 532 1647 532 559 530 536 564 555 534 1643 536 1616 563 1640 539 1613 555 1624 565 1610 558 1619 560 1617 562 557 532 560 539 554 535 557 532 562 537 553 536 1641 538 555 534 1643 536 530 559 1644 535 558 531 564 536 1639 539 553 536 1641 537 555 534 1617 562 557 532 1645 534 1645 534 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4404 4345 563 1613 566 553 536 1641 538 1614 565 528 561 557 532 1645 534 560 539 551 538 1639 529 563 536 556 533 1618 561 1642 537 556 533 1645 534 557 532 561 538 1638 530 1646 533 1645 554 1623 556 1621 558 1596 562 1613 586 1591 588 531 537 555 534 559 530 562 537 555 534 561 538 552 537 555 534 559 530 562 537 555 534 559 530 562 537 557 532 1643 536 1615 564 1639 560 1591 588 1616 563 1588 580 1623 556 1621 537 5183 4404 4339 579 1597 581 538 530 1646 533 1619 580 539 529 563 536 1615 564 557 532 532 557 1646 533 560 539 553 536 1641 538 1639 539 554 535 1643 536 555 534 559 530 1647 532 1646 533 1644 535 1617 582 1620 559 1621 537 1612 587 1590 589 530 538 554 535 558 531 561 538 554 535 560 539 551 538 554 535 558 531 561 538 554 535 557 532 561 538 556 533 1642 537 1640 538 1613 565 1638 561 1590 589 1588 580 1623 556 1598 560 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4400 4357 561 1616 563 530 559 1619 559 1644 535 557 532 535 564 1613 555 565 534 556 533 1645 534 533 556 536 563 1614 565 1639 529 537 563 1643 536 529 560 558 531 1647 532 1645 533 1618 561 1643 536 1642 537 1642 537 1613 565 1638 530 537 563 556 533 559 530 563 536 556 533 562 538 527 562 1615 563 555 534 1643 536 531 558 561 538 554 535 560 539 1610 558 560 539 1612 556 563 537 1640 538 1639 539 1612 556 1620 558 5189 4398 4345 562 1614 564 529 560 1643 536 1616 563 531 558 534 565 1639 529 565 534 556 533 1619 559 559 530 563 537 1641 538 1640 538 554 535 1645 534 531 558 560 539 1639 529 1622 556 1621 558 1646 532 1619 559 1646 533 1617 562 1642 537 557 532 560 539 554 535 557 532 561 538 556 533 558 531 1620 559 560 539 1638 530 563 536 556 533 560 539 555 534 1642 537 556 533 1619 559 559 530 1622 557 1621 558 1620 558 1647 532 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4402 4346 561 1615 564 555 534 1644 535 1643 536 531 558 561 538 1639 539 555 534 531 558 1645 533 560 529 563 536 1641 537 1640 538 528 561 1645 533 531 558 561 538 1639 539 1638 530 1622 557 1647 532 1646 564 1590 557 1619 559 1618 561 558 531 562 537 555 534 559 530 563 536 558 531 1645 533 559 530 1648 530 1647 531 1646 532 1646 532 560 539 556 533 558 531 1621 557 561 538 555 534 558 531 562 537 1641 537 1639 539 5183 4405 4339 558 1645 533 559 530 1648 530 1647 532 561 538 555 534 1644 535 560 539 551 538 1614 564 555 534 559 530 1647 532 1620 558 561 538 1615 563 553 536 531 558 1620 558 1644 534 1643 536 1642 537 1641 537 1642 536 1613 565 1638 530 563 536 557 532 534 565 554 535 557 532 563 536 1613 565 554 535 1642 537 1641 537 1640 538 1640 538 554 535 560 539 551 538 1614 564 528 561 558 531 562 537 555 534 1644 534 1645 533 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4404 4354 564 1614 564 528 561 1617 562 1642 537 530 559 534 565 1613 555 539 560 556 533 1619 559 534 555 563 536 1642 537 1641 537 555 534 1646 533 532 557 562 537 1640 538 1613 565 1613 555 1648 531 1647 532 1622 557 1619 559 1618 560 559 530 563 536 556 533 560 539 554 535 559 530 561 538 1639 539 554 535 1616 563 1615 564 1615 563 555 534 561 538 1612 556 563 536 1641 538 529 560 533 556 563 537 1641 538 1614 564 5183 4404 4342 555 1622 557 562 537 1641 537 1640 538 529 560 533 556 1648 531 538 562 555 534 1618 560 559 530 562 537 1615 563 1640 538 529 560 1646 532 532 557 562 537 1640 538 1639 539 1639 539 1638 530 1648 530 1649 540 1611 557 1646 532 561 538 554 535 558 531 562 537 555 534 561 538 552 537 1641 537 555 534 1644 534 1643 536 1617 562 557 532 563 536 1639 539 553 536 1642 537 556 533 560 539 554 535 1642 537 1643 535 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4403 4333 563 1615 563 528 561 1618 560 1618 560 531 558 561 538 1614 564 556 533 558 541 1611 557 562 537 555 534 1619 559 1618 560 558 541 1613 565 551 538 1614 564 1614 564 1614 564 1613 565 528 561 1617 561 1619 559 1617 561 557 532 535 564 528 561 533 566 1611 557 562 537 558 531 1619 559 1619 559 1619 559 559 540 553 536 557 532 561 538 557 532 559 540 553 536 557 532 1620 558 1620 558 1620 558 1620 558 1618 560 5188 4398 4346 561 1618 560 558 531 1621 557 1621 557 561 538 555 534 1619 559 561 538 553 536 1616 562 556 533 560 539 1614 564 1613 565 554 535 1619 559 557 532 1621 557 1620 558 1620 558 1620 558 560 539 1613 565 1615 563 1613 565 553 536 557 532 535 564 554 535 1618 560 558 531 564 535 1615 563 1615 563 1614 564 555 534 559 540 552 537 530 559 536 563 528 561 532 557 562 537 1615 563 1615 563 1614 564 1614 564 1616 562 diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 3a7c796fa..d37627982 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,6 +1,67 @@ Filetype: IR library file Version: 1 -# Last Updated 4th Oct, 2022 +# Last Updated 14th Nov, 2022 +# +name: VOL+ +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 14 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 10 00 00 00 +# +# ON +name: POWER +type: parsed +protocol: RC5 +address: 10 00 00 00 +command: 0E 00 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 0C F3 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 09 F6 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3309 1906 410 1178 411 1177 412 416 435 445 406 448 414 440 411 1150 439 415 436 1178 411 1177 412 442 409 444 407 1181 408 419 432 1182 407 420 442 1146 433 448 414 440 411 442 409 444 407 446 416 438 413 441 410 443 408 419 432 1182 407 446 405 422 440 414 437 416 435 445 406 1181 408 446 405 448 414 440 411 442 409 418 433 446 416 438 413 1175 414 413 438 1176 413 414 437 443 408 419 432 421 441 413 438 42493 3308 3343 355 43011 3309 3316 382 43009 3310 3314 384 43007 3303 3347 361 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3307 1879 437 1177 412 1176 413 415 436 417 434 446 405 448 414 1174 415 412 439 1149 440 1147 442 438 413 440 411 1177 412 441 410 1178 411 442 409 1179 410 417 434 419 432 448 414 440 411 442 409 444 407 446 416 438 413 441 410 1151 438 415 436 444 407 446 416 412 439 414 437 1177 412 1176 413 414 437 416 435 1152 437 1177 412 416 435 444 407 446 416 1146 433 421 441 1173 406 422 440 413 438 442 409 444 407 40133 3308 3341 357 42862 3301 3347 361 42859 3304 3319 379 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3310 1876 440 1174 415 1173 405 422 440 414 437 443 408 445 406 1182 407 420 442 1173 406 1182 407 420 442 412 439 1175 414 440 411 1150 439 415 436 1152 437 416 435 419 432 447 415 440 411 442 409 418 433 420 442 438 413 440 411 1177 412 415 436 418 433 420 442 438 413 441 410 1151 438 1150 439 415 436 1178 411 1150 439 1149 440 414 437 417 434 445 406 1155 434 420 442 438 413 1175 414 413 438 442 409 444 407 39503 3303 3320 388 42857 3304 3319 379 42867 3305 3317 381 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3305 1907 409 1178 411 1177 412 442 409 418 433 447 415 439 412 1176 413 441 410 1177 412 1176 413 441 410 443 408 1180 409 445 406 1181 408 446 416 1172 406 448 413 440 411 442 409 445 406 447 414 439 412 442 409 444 407 447 414 1173 405 449 413 441 410 443 408 446 405 448 413 1174 415 440 411 442 409 1178 411 1177 412 1177 412 442 409 444 407 447 415 439 412 441 410 444 407 1180 409 445 406 448 414 440 411 41125 3303 3347 360 42906 3308 3315 382 +# +name: MUTE +type: parsed +protocol: NECext +address: BA A0 00 00 +command: 01 FE 00 00 # name: POWER type: raw @@ -1538,3 +1599,10 @@ protocol: NECext address: 12 36 00 00 command: 01 FE 00 00 # +# OFF +name: POWER +type: parsed +protocol: RC5 +address: 10 00 00 00 +command: 0F 00 00 00 +# diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index a693c0037..d7582f8a8 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,6 +1,49 @@ Filetype: IR library file Version: 1 -# Last Updated 9th Oct, 2022 +# Last Updated 14th Nov, 2022 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2262 692 881 585 883 583 903 564 903 1294 882 584 881 584 882 559 820 673 895 1300 882 611 881 584 882 584 882 584 882 585 881 584 881 584 882 585 880 1317 879 586 829 1371 827 640 825 51208 2245 690 772 1424 831 50893 2240 691 772 1429 771 50906 2244 690 773 1400 772 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2264 668 770 721 745 694 772 694 796 1401 826 639 771 694 771 693 772 694 771 1426 798 668 769 1429 795 670 795 1404 794 672 794 1405 767 1431 767 1430 794 672 794 1404 794 1404 794 1404 766 50878 2261 645 817 1405 794 50857 2238 668 795 1428 769 +# +name: SPEED- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2264 642 823 669 797 643 822 644 822 1376 822 642 799 668 797 669 823 645 820 1401 796 670 794 1379 792 700 793 1381 790 1407 793 674 791 700 793 1406 792 673 793 673 792 673 793 673 792 50785 2262 666 796 1401 797 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2323 611 796 669 800 664 802 692 773 1424 774 691 801 664 774 691 774 692 773 1423 800 668 822 667 798 1376 769 1427 771 696 769 696 770 1429 795 1404 794 672 794 1404 794 671 795 1404 794 51269 2214 691 769 1427 796 +# +# ON/SPEED +name: POWER +type: parsed +protocol: NECext +address: 00 FC 00 00 +command: 84 7B 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1285 426 1258 427 414 1267 1248 436 1258 427 414 1267 406 1276 408 1248 435 1247 437 1271 413 1244 1282 7286 1279 432 1252 433 409 1273 1253 431 1253 432 410 1273 411 1271 413 1243 440 1241 442 1240 433 1275 1251 7292 1283 427 1257 428 413 1268 1258 427 1257 428 414 1268 416 1240 433 1249 434 1248 435 1246 438 1245 1281 7287 1278 406 1278 434 408 1274 1252 432 1252 433 409 1274 410 1246 438 1244 440 1243 440 1241 443 1240 1275 7294 1281 402 1282 430 412 1270 1256 428 1256 429 412 1243 441 1241 443 1240 433 1249 434 1247 436 1246 1280 7288 1277 433 1251 434 408 1248 1278 432 1251 433 409 1273 411 1245 439 1244 440 1242 442 1240 433 1249 1277 7292 1283 400 1304 406 435 1246 1248 436 1279 405 436 1246 406 1249 434 1247 436 1245 438 1244 440 1242 1284 7285 1280 430 1285 400 442 1241 1285 399 1285 400 441 1240 444 1212 440 1241 443 1240 433 1249 434 1273 1284 7259 1306 404 1280 405 436 1245 1281 403 1281 404 438 1244 440 1216 468 1214 438 1244 440 1242 442 1241 1305 7262 1303 408 1276 407 434 1249 1277 407 1276 407 435 1248 436 1220 464 1219 464 1217 466 1242 441 1214 1312 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1280 431 1252 431 410 1244 1281 430 1253 430 411 1270 413 1242 441 1240 433 1249 434 1247 1278 433 408 8129 1277 433 1250 433 408 1274 1251 432 1251 433 409 1273 410 1271 412 1269 414 1241 432 1250 1275 435 406 8133 1283 426 1257 427 414 1267 1248 436 1247 436 405 1276 407 1248 435 1247 436 1245 438 1244 1281 429 412 8126 1280 430 1253 430 411 1270 1255 429 1254 429 412 1269 414 1241 432 1276 407 1248 435 1247 1278 432 410 8129 1277 432 1251 433 408 1273 1252 431 1252 431 411 1272 411 1269 414 1241 442 1240 433 1275 1250 434 408 8130 1276 434 1249 434 407 1275 1250 433 1250 433 408 1274 409 1272 411 1244 439 1268 415 1240 1275 436 405 8133 1283 427 1256 427 414 1267 1248 436 1258 426 405 1277 406 1274 409 1273 410 1245 438 1244 1281 429 412 8125 1281 429 1254 429 412 1269 1256 428 1255 428 413 1268 415 1266 407 1248 435 1247 436 1245 1280 430 411 8127 1279 431 1252 431 411 1271 1254 430 1253 430 412 1270 413 1268 415 1240 433 1275 408 1247 1278 432 410 8128 1278 432 1251 432 410 1272 1253 431 1252 431 410 1271 412 1242 441 1267 406 1250 433 1248 1277 433 409 8129 1277 433 1250 433 409 1272 1253 431 1252 431 410 1271 412 1242 441 1241 432 1250 433 1248 1277 433 409 8129 1277 433 1250 434 408 1246 1279 432 1251 432 409 1245 438 1244 439 1242 441 1240 433 1249 1276 434 407 # name: POWER type: raw diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 461841977..56eada138 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,6 +1,31 @@ Filetype: IR library file Version: 1 -# Last Updated 30th Sept, 2022 +# Last Updated 15th Oct, 2022 +# +# ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9096 4436 620 505 647 478 648 501 623 1599 647 1624 623 502 623 503 621 504 619 1628 618 507 617 507 617 1630 617 508 616 1630 617 1630 617 1631 616 508 616 508 617 508 616 1631 616 508 617 508 617 508 616 508 616 1630 616 1630 616 1631 616 508 616 1630 617 1630 617 1630 617 1631 617 509 616 508 616 509 616 509 616 509 616 509 615 509 616 508 617 1631 616 1631 615 1631 616 1631 616 1631 616 1631 616 1631 615 1631 616 14435 9093 2186 615 96359 9095 2184 617 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9091 4465 594 530 595 530 594 530 594 1651 595 1652 595 529 621 504 620 504 619 1628 618 507 617 508 616 1631 616 509 615 1631 616 1631 616 1632 615 509 616 509 616 509 615 1631 616 509 616 508 616 1631 616 509 616 1631 615 1631 616 1631 617 508 616 1631 616 1631 616 508 616 1631 617 508 617 509 616 509 616 509 616 509 616 509 616 509 616 509 616 1631 616 1631 616 1631 616 1631 616 1631 615 1631 615 1631 615 1631 616 14435 9090 2190 615 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 619 506 618 530 593 1627 620 1630 643 504 620 505 618 506 617 1630 617 508 616 508 616 1632 616 508 617 1631 616 1631 616 1631 616 1631 616 509 616 508 616 1631 616 509 616 509 615 1632 616 509 616 508 616 1631 616 1631 616 508 616 1631 615 1631 616 509 615 1632 615 509 616 509 616 509 616 509 616 509 616 510 615 509 616 509 616 1631 616 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 615 14434 9088 2191 615 96339 9115 2189 616 96343 9117 2189 616 96343 9114 2189 616 +# AV-MUTE +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 618 506 618 530 594 1627 619 1629 643 505 619 505 619 506 617 1629 617 508 616 508 616 1631 616 508 616 1630 616 1630 616 1630 617 1630 616 1630 616 1631 616 508 616 508 616 508 616 1631 616 508 617 508 616 508 616 508 616 1630 616 1631 615 1631 616 508 616 1631 616 508 617 508 616 509 615 509 616 508 616 509 615 509 616 508 616 1631 615 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 616 14433 9088 2191 615 # name: POWER type: raw @@ -445,4 +470,10 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# Standby/Off +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616 # diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 0f6d9bb42..b97b85bb6 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,6 +1,114 @@ Filetype: IR library file Version: 1 -# Last Updated 4th Oct, 2022 +# Last Updated 14th Nov, 2022 +# +name: MUTE +type: parsed +protocol: NECext +address: 00 FB 00 00 +command: 0F F0 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 00 FB 00 00 +command: 0A F5 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 00 FB 00 00 +command: 58 A7 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 00 FB 00 00 +command: 4B B4 00 00 +# +name: CH+ +type: parsed +protocol: NECext +address: 00 FB 00 00 +command: 1F E0 00 00 +# +name: CH- +type: parsed +protocol: NECext +address: 00 FB 00 00 +command: 1E E1 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 1C E3 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4B B4 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4F B0 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 08 F7 00 00 +# +name: CH+ +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 09 F6 00 00 +# +name: CH- +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 05 FA 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3562 1687 494 382 493 1257 493 382 492 382 493 383 492 383 492 384 491 384 490 384 491 384 491 384 491 384 491 384 490 1260 491 383 492 384 491 384 491 384 491 384 491 384 491 384 491 383 492 383 492 1259 491 383 492 384 491 384 491 384 491 384 491 384 490 384 491 384 491 1260 490 384 491 1260 490 1259 492 1259 490 1260 490 384 490 384 491 1260 490 383 492 1259 491 1259 491 1259 491 1259 491 384 490 1259 491 75054 3561 1687 494 381 494 1256 494 382 492 382 493 383 492 383 491 383 492 384 490 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 383 492 384 491 384 491 384 491 383 492 383 492 383 492 384 491 1259 491 383 492 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 1259 491 1259 491 1259 491 1259 491 384 491 384 491 1259 491 383 492 1259 491 1259 491 1259 491 1259 491 384 491 1259 491 75053 3560 1688 493 382 492 1258 492 383 492 384 491 384 491 384 491 384 491 384 491 384 491 383 492 384 491 384 491 384 491 1259 491 384 491 384 491 384 491 384 491 384 491 384 491 383 492 384 491 384 491 1259 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 1259 491 1259 491 1259 491 1259 491 384 491 384 491 1259 492 383 492 1259 491 1259 491 1259 491 1259 491 384 491 1259 491 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3565 1685 496 380 494 1255 495 381 493 381 494 381 493 382 492 382 493 382 492 383 492 383 492 383 492 384 491 383 491 1260 491 384 491 384 491 384 491 384 491 384 491 384 491 384 490 384 491 384 491 1259 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 490 385 490 384 491 384 491 384 491 1260 490 384 491 384 491 384 491 384 490 384 491 384 491 384 491 1260 490 384 491 1259 491 74918 3534 1690 491 383 492 1259 491 383 492 384 490 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 384 491 384 491 384 491 384 491 384 490 385 490 384 491 384 491 1260 491 384 491 384 491 384 491 385 490 384 491 384 491 384 491 385 490 384 491 385 490 384 490 384 491 385 490 1260 490 384 491 385 490 384 490 384 491 384 491 384 491 385 490 1260 490 385 490 1260 490 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3563 1686 495 381 494 1256 493 381 494 382 492 382 493 382 493 383 491 383 491 384 491 383 491 384 491 384 491 384 491 1260 490 384 491 384 490 385 490 384 491 385 490 385 490 384 491 385 490 384 491 1260 490 385 490 385 490 384 491 385 490 385 490 384 490 385 490 385 490 1260 490 385 490 385 490 385 490 385 490 1261 489 385 490 386 489 1260 490 385 490 385 490 385 490 385 490 1260 490 385 490 1261 489 75231 3533 1690 491 385 490 1260 490 384 491 384 491 384 491 384 491 384 491 384 490 385 490 384 490 385 490 385 489 385 490 1260 490 385 490 384 491 384 491 385 490 385 490 385 490 384 491 385 490 385 490 1260 490 385 490 385 490 385 490 384 491 384 491 385 490 385 490 385 490 1261 489 385 490 385 489 385 490 385 490 1260 490 385 490 385 490 1260 490 385 490 385 490 385 490 385 490 1260 490 385 490 1260 491 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3570 1654 527 375 499 1224 526 376 499 376 498 377 497 377 497 378 496 378 496 379 495 379 495 380 494 380 494 381 493 1256 494 381 493 382 492 383 492 383 492 383 492 383 492 383 492 383 492 383 492 1259 491 383 492 383 492 383 492 383 492 383 492 383 492 383 492 383 492 383 492 1258 492 383 492 383 492 1259 491 1259 491 383 492 383 492 383 492 1259 492 383 492 383 492 1259 491 1258 492 383 492 1259 491 74993 3561 1688 493 382 492 1258 492 383 492 383 492 383 492 383 492 383 492 383 492 383 492 383 492 384 491 384 491 384 491 1259 491 384 491 384 491 383 492 383 492 384 491 384 491 384 491 384 491 384 491 1259 492 383 492 383 491 384 491 383 492 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 384 491 1259 491 1259 491 384 491 384 491 384 491 1259 491 384 491 384 491 1259 491 1259 491 384 491 1259 491 +# +name: CH+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3568 1655 526 376 498 1226 524 377 497 378 496 379 495 379 496 379 495 380 494 381 493 381 493 381 493 382 493 382 492 1258 492 383 492 383 492 383 492 384 491 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 384 491 383 492 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 1259 491 1259 491 384 491 383 492 384 491 384 491 1259 491 384 491 1259 491 1259 491 384 491 1259 491 74991 3558 1689 492 383 491 1259 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1260 490 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1260 490 384 491 384 491 384 491 384 491 384 490 384 491 384 491 384 491 384 491 384 491 1260 490 384 491 1260 490 1260 491 384 490 384 491 384 491 384 491 1260 490 384 491 1260 490 1260 490 384 491 1260 490 74966 3559 1689 492 383 492 1259 491 383 492 383 492 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1259 491 383 492 383 492 384 491 384 491 384 491 384 491 384 490 384 491 384 491 1260 490 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1260 490 384 491 1259 491 1260 490 384 491 384 491 384 491 384 491 1260 490 384 491 1260 490 1260 490 384 491 1260 490 74966 3559 1690 491 383 492 1259 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1260 490 384 491 384 491 384 491 384 491 385 490 384 491 384 491 384 491 384 491 384 491 1260 490 384 491 1260 490 1260 490 384 491 384 491 384 491 384 491 1260 490 384 491 1260 490 1260 490 385 490 1260 491 75014 3535 1690 491 383 492 1259 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1259 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1260 490 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 384 491 1260 490 384 491 1260 490 1260 490 384 491 384 491 384 491 385 490 1260 490 384 491 1260 490 1260 490 384 491 1260 490 +# +name: CH- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3568 1655 526 376 498 1223 553 349 499 376 498 377 498 377 498 377 497 377 498 377 497 377 497 378 496 378 496 379 495 1255 494 381 493 382 492 383 491 384 491 385 489 385 490 385 490 385 490 385 490 1260 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 1261 489 385 490 1261 489 385 490 1261 489 1261 489 386 489 385 490 1261 489 385 490 1261 489 386 489 1261 489 1261 489 386 489 1261 489 74774 3560 1690 491 383 491 1260 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 1260 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 1261 489 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 1261 489 385 490 1261 489 386 489 1262 488 1261 489 386 489 386 489 1261 489 386 489 1261 489 386 489 1261 489 1262 488 386 489 1287 463 74751 3559 1690 491 384 490 1260 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 1261 489 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 385 490 1261 489 385 490 385 490 385 490 385 490 386 489 386 489 385 490 386 489 1261 489 386 489 1261 489 386 489 1261 489 1261 489 386 489 386 489 1261 489 386 489 1262 488 387 488 1262 488 1262 488 386 489 1262 488 # name: POWER type: raw diff --git a/assets/resources/lfrfid/rfidfuzzer/example_uids.txt b/assets/resources/lfrfid/rfidfuzzer/example_uids.txt deleted file mode 100644 index 46ce16ba8..000000000 --- a/assets/resources/lfrfid/rfidfuzzer/example_uids.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Example file, P.S. keep empty line at the end! -0000000000 -FE00000000 -CAFE000000 -00CAFE0000 -0000CAFE00 -000000CAFE -00000000CA diff --git a/assets/resources/rfidfuzzer/example_uids_em4100.txt b/assets/resources/lfrfid/rfidfuzzer/example_uids_em4100.txt similarity index 91% rename from assets/resources/rfidfuzzer/example_uids_em4100.txt rename to assets/resources/lfrfid/rfidfuzzer/example_uids_em4100.txt index 46ce16ba8..25a4f0e8f 100644 --- a/assets/resources/rfidfuzzer/example_uids_em4100.txt +++ b/assets/resources/lfrfid/rfidfuzzer/example_uids_em4100.txt @@ -6,3 +6,4 @@ CAFE000000 0000CAFE00 000000CAFE 00000000CA +DEADBEEFC0 diff --git a/assets/resources/rfidfuzzer/example_uids_h10301.txt b/assets/resources/lfrfid/rfidfuzzer/example_uids_h10301.txt similarity index 100% rename from assets/resources/rfidfuzzer/example_uids_h10301.txt rename to assets/resources/lfrfid/rfidfuzzer/example_uids_h10301.txt diff --git a/assets/resources/rfidfuzzer/example_uids_hidprox.txt b/assets/resources/lfrfid/rfidfuzzer/example_uids_hidprox.txt similarity index 100% rename from assets/resources/rfidfuzzer/example_uids_hidprox.txt rename to assets/resources/lfrfid/rfidfuzzer/example_uids_hidprox.txt diff --git a/assets/resources/rfidfuzzer/example_uids_pac.txt b/assets/resources/lfrfid/rfidfuzzer/example_uids_pac.txt similarity index 100% rename from assets/resources/rfidfuzzer/example_uids_pac.txt rename to assets/resources/lfrfid/rfidfuzzer/example_uids_pac.txt diff --git a/assets/resources/nfc/SmashAmiibo/Gan0n50.nfc b/assets/resources/nfc/SmashAmiibo/Gan0n50.nfc deleted file mode 100644 index c414653ef..000000000 --- a/assets/resources/nfc/SmashAmiibo/Gan0n50.nfc +++ /dev/null @@ -1,153 +0,0 @@ -Filetype: Flipper NFC device -Version: 2 -# Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card -Device type: NTAG215 -# UID, ATQA and SAK are common for all formats -UID: 04 35 8C 5A B3 49 81 -ATQA: 44 00 -SAK: 00 -# Mifare Ultralight specific data -Signature: 6B 66 DB 55 0B 4C B7 EF 73 E3 E8 E7 2C D3 74 E3 90 17 FC 4E C6 10 B5 CC A2 65 48 16 ED AF C4 20 -Mifare version: 00 04 04 02 01 00 11 03 -Counter 0: 0 -Tearing 0: 00 -Counter 1: 0 -Tearing 1: 00 -Counter 2: 0 -Tearing 2: 00 -Pages total: 135 -Page 0: 04 35 8C 35 -Page 1: 5A B3 49 81 -Page 2: 21 48 0F E0 -Page 3: F1 10 FF EE -Page 4: A5 00 10 00 -Page 5: 79 C4 86 CE -Page 6: B4 3D 5D 43 -Page 7: 92 04 65 98 -Page 8: EC 01 C1 C5 -Page 9: 87 B3 C5 0B -Page 10: 7B 6D 6C CA -Page 11: 8A 95 20 3C -Page 12: F1 6F D9 F5 -Page 13: C9 1C D6 F9 -Page 14: E8 6D 9C E2 -Page 15: 26 89 9B 59 -Page 16: 86 F1 7E B9 -Page 17: 5E 07 DE 5A -Page 18: 0D 8F 20 1E -Page 19: EC 7E 5A 15 -Page 20: 6B 87 71 85 -Page 21: 01 02 01 00 -Page 22: 00 1B 00 02 -Page 23: 0D 12 A5 17 -Page 24: F8 9F EB B1 -Page 25: 17 49 6E B1 -Page 26: 3D B7 BF CA -Page 27: 1F E1 66 AC -Page 28: 6C 6F 58 EE -Page 29: E3 5F 5D 5A -Page 30: 41 75 D8 B4 -Page 31: FD BB 5E 91 -Page 32: 81 3A FE EF -Page 33: 5B 38 62 92 -Page 34: AB E4 D8 46 -Page 35: F6 47 50 65 -Page 36: EB 62 A1 09 -Page 37: BB EF 27 F6 -Page 38: 82 C5 30 C4 -Page 39: 2E 1A C8 AE -Page 40: EB 93 68 1F -Page 41: 78 7C CA 1B -Page 42: 25 AA 16 1F -Page 43: 78 39 BE E4 -Page 44: BA 60 CA 11 -Page 45: 14 27 C2 2F -Page 46: 30 16 AC 3D -Page 47: C3 6D 95 A3 -Page 48: FD F9 D1 D3 -Page 49: 58 37 C0 13 -Page 50: C9 D2 39 24 -Page 51: B9 CF C8 5E -Page 52: 0A 9D 6E 87 -Page 53: 6A E0 80 23 -Page 54: A9 77 71 07 -Page 55: 4B 93 2F CD -Page 56: 5F 88 56 A2 -Page 57: E4 A7 82 59 -Page 58: 62 02 15 CE -Page 59: A8 79 4D F5 -Page 60: 7D C0 F3 43 -Page 61: 98 16 AC 7D -Page 62: 13 59 BF 1E -Page 63: A0 C6 56 A4 -Page 64: C8 CE 97 BE -Page 65: 2D DE 2A 10 -Page 66: 41 DA 64 EF -Page 67: 5E 02 A4 07 -Page 68: 5E DB 1D 56 -Page 69: 11 E1 62 DD -Page 70: 66 30 F3 7F -Page 71: 80 A0 56 76 -Page 72: AF 5F 80 07 -Page 73: B0 EC CF 67 -Page 74: 52 62 A7 F6 -Page 75: B5 7E 6E 54 -Page 76: 06 4A 07 89 -Page 77: F1 FF 28 31 -Page 78: BA 30 70 B9 -Page 79: F7 5E 8C F7 -Page 80: A6 1E 28 91 -Page 81: D3 C0 D3 DE -Page 82: 10 DF E8 D6 -Page 83: 00 74 53 8B -Page 84: BF C9 9F 26 -Page 85: 00 6B 55 60 -Page 86: 71 35 C3 1F -Page 87: 2D 97 D2 1E -Page 88: 44 97 88 F5 -Page 89: E6 3E AC A0 -Page 90: C0 AE 5E 8B -Page 91: E2 34 03 C9 -Page 92: DD 60 21 C4 -Page 93: C2 C8 FD D2 -Page 94: C9 8B 3B 5D -Page 95: 98 F8 F3 43 -Page 96: F3 CA 80 EA -Page 97: 82 30 51 59 -Page 98: CE A9 6F 8D -Page 99: 93 5D 23 1D -Page 100: 20 86 B9 41 -Page 101: 31 D4 9B 2B -Page 102: A9 09 B3 AD -Page 103: 7E 17 76 D8 -Page 104: C3 B3 30 CE -Page 105: 20 40 40 52 -Page 106: E4 52 33 E4 -Page 107: 7A EF F6 DF -Page 108: DB 9D 27 B4 -Page 109: FE C6 33 54 -Page 110: A2 09 43 12 -Page 111: 10 F3 E1 CE -Page 112: 81 3C D4 40 -Page 113: A4 99 7F 76 -Page 114: 70 A0 CD AB -Page 115: A2 29 25 08 -Page 116: FD E6 3E 3B -Page 117: 3D 10 95 00 -Page 118: 4E 2C 13 74 -Page 119: C8 F8 50 DE -Page 120: 12 AB A5 38 -Page 121: 73 79 47 74 -Page 122: 06 F2 8D 2D -Page 123: F8 83 13 C1 -Page 124: 60 5D E7 AB -Page 125: F2 60 79 A8 -Page 126: 9F 05 47 36 -Page 127: 5E E5 F1 D6 -Page 128: E6 A7 23 A6 -Page 129: 76 74 4C 80 -Page 130: 01 00 0F BD -Page 131: 00 00 00 04 -Page 132: 5F 00 00 00 -Page 133: 00 00 00 00 -Page 134: 00 00 00 00 diff --git a/assets/resources/nfc/SmashAmiibo/L1NK50_G0DL1NK.nfc b/assets/resources/nfc/SmashAmiibo/L1NK50_G0DL1NK.nfc new file mode 100644 index 000000000..6183b7223 --- /dev/null +++ b/assets/resources/nfc/SmashAmiibo/L1NK50_G0DL1NK.nfc @@ -0,0 +1,153 @@ +Filetype: Flipper NFC device +Version: 2 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card +Device type: NTAG215 +# UID, ATQA and SAK are common for all formats +UID: 04 95 48 FF 0F 85 32 +ATQA: 44 00 +SAK: 00 +# Mifare Ultralight specific data +Signature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Mifare version: 00 04 04 02 01 00 11 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 0 +Tearing 2: 00 +Pages total: 135 +Page 0: 04 95 48 51 +Page 1: FF 0F 85 32 +Page 2: 47 48 0F E0 +Page 3: F1 10 FF EE +Page 4: A5 00 93 00 +Page 5: 37 A6 71 FB +Page 6: 12 17 27 45 +Page 7: 9B 03 F9 7C +Page 8: DF D5 DF 44 +Page 9: 6C 3D 61 BF +Page 10: 86 31 24 42 +Page 11: 88 BB E5 68 +Page 12: BB E4 DC 94 +Page 13: CB 8A 0E D1 +Page 14: 13 06 1E C1 +Page 15: 2F 9A 13 78 +Page 16: 59 07 AF 75 +Page 17: 87 FF 63 C1 +Page 18: 20 4B 72 68 +Page 19: C5 53 64 AE +Page 20: AB A6 9F 22 +Page 21: 01 00 00 00 +Page 22: 00 04 00 02 +Page 23: 04 12 84 17 +Page 24: BA 10 B4 0E +Page 25: E6 F7 85 10 +Page 26: F8 77 E1 28 +Page 27: 3D C7 CA F2 +Page 28: CE BF 98 9D +Page 29: C7 4F DA D5 +Page 30: 03 00 3D 4F +Page 31: 51 00 BE FF +Page 32: 0B 44 76 D7 +Page 33: 7E 1A 1C DF +Page 34: 9B 03 B8 BC +Page 35: 5B D1 9F 4E +Page 36: 45 74 F0 83 +Page 37: EA 73 B1 19 +Page 38: CB D7 F9 E3 +Page 39: 23 AE 26 84 +Page 40: 51 11 1F 83 +Page 41: 5F 70 83 E2 +Page 42: F3 4D 97 8D +Page 43: AF 1E 67 FC +Page 44: B4 86 8D 54 +Page 45: 5A 1D 8A D4 +Page 46: 83 AE 0D 18 +Page 47: A8 29 1B 0A +Page 48: CE 79 60 55 +Page 49: B0 4E 0D E0 +Page 50: D5 7F D0 7C +Page 51: 9F 67 B4 3A +Page 52: 1E CF F6 E6 +Page 53: D8 75 22 AA +Page 54: 64 FC 05 59 +Page 55: 51 86 F0 0F +Page 56: 07 66 68 04 +Page 57: 90 ED 79 D4 +Page 58: 34 50 48 43 +Page 59: 8B 46 75 F0 +Page 60: 97 B8 2E A7 +Page 61: 24 32 3F 5E +Page 62: 98 78 98 54 +Page 63: AD 16 1C AA +Page 64: C0 D1 3C C1 +Page 65: 70 77 E9 9A +Page 66: AA 06 B4 B1 +Page 67: 1F D9 39 F3 +Page 68: 49 EB 26 81 +Page 69: 0A 77 53 26 +Page 70: 53 E0 CA 9F +Page 71: C1 EB 69 20 +Page 72: 0C 60 8D 33 +Page 73: DB 51 A2 89 +Page 74: A0 64 80 C0 +Page 75: 8A 03 BD D1 +Page 76: 7D 13 6B 63 +Page 77: 28 BD B7 79 +Page 78: 4A FA A8 BB +Page 79: D6 A7 6A 0E +Page 80: C5 97 C5 01 +Page 81: AC 36 F6 CF +Page 82: 1E 0A 5F 4A +Page 83: 6C A6 A1 54 +Page 84: 46 35 86 FB +Page 85: 15 21 59 07 +Page 86: ED C1 09 90 +Page 87: C6 B4 B1 CB +Page 88: 31 2D 39 37 +Page 89: D2 6C 92 D8 +Page 90: C7 60 7E F1 +Page 91: 73 1D 15 79 +Page 92: 98 56 B3 4C +Page 93: CC 01 A5 9D +Page 94: 2E 5A E5 EC +Page 95: CC 69 79 9E +Page 96: 34 5C 06 2C +Page 97: D8 6D F4 0D +Page 98: 40 1F CB 95 +Page 99: 12 54 87 17 +Page 100: 28 F4 45 2D +Page 101: 4F 7E 1E AB +Page 102: DC 2B F5 E4 +Page 103: 91 EB 2D CD +Page 104: 0E FE 2D 45 +Page 105: D8 DD C8 E6 +Page 106: DB BE 92 A2 +Page 107: A1 32 04 F7 +Page 108: B8 B9 75 93 +Page 109: BF DB 17 BE +Page 110: 5D AB 9D 9B +Page 111: 59 EC 0A 12 +Page 112: 2C 9F 76 B6 +Page 113: B5 F3 37 8F +Page 114: 9E DD F5 8E +Page 115: 06 B6 DA D2 +Page 116: 1E 8B 69 C7 +Page 117: FE 98 95 7A +Page 118: 08 81 BA 11 +Page 119: 26 71 70 C4 +Page 120: A6 B7 59 1B +Page 121: 29 AF FB 6F +Page 122: 67 59 9F F1 +Page 123: 42 34 5F AA +Page 124: 81 2A 88 A9 +Page 125: 66 24 C7 3D +Page 126: 82 4F 5F 0E +Page 127: 57 77 7C 03 +Page 128: A9 83 E3 7F +Page 129: EF 83 4B 35 +Page 130: 01 00 0F BD +Page 131: 00 00 00 04 +Page 132: 5F 00 00 00 +Page 133: C0 12 D0 68 +Page 134: 80 80 00 00 \ No newline at end of file diff --git a/assets/resources/nfc/assets/mf_classic_dict.nfc b/assets/resources/nfc/assets/mf_classic_dict.nfc index 427e92c92..8e55ffbfa 100644 --- a/assets/resources/nfc/assets/mf_classic_dict.nfc +++ b/assets/resources/nfc/assets/mf_classic_dict.nfc @@ -1,8 +1,7 @@ ########################### # Do not edit, this file will be overwritten after firmware update # Use the user_dict file for user keys -########################### -# Last update 15th August, 2022 +# Last update 19th October, 2022 # ------------------------- # MIFARE DEFAULT KEYS # -- ICEMAN FORK VERSION -- @@ -3706,3 +3705,79 @@ AFC984A3576E # data from http://www.proxmark.org/forum/viewtopic.php?pid=45100#p45100 7CB033257498 1153AABAFF6C +# iGuard Simple and Reverse Keys +D537320FF90E +36E1765CE3E8 +C608E13ADD50 +ED0EC56EEFDD +9716D5241E28 +2E52ABE0CE95 +61D030C0D7A8 +# BadgeMaker Leaked from https://github.com/UberGuidoZ +E167EC67C7FF +# Schlage 9691T Keyfob from seasnaill Added by VideoMan. +3111A3A303EB +# Transport cards +E954024EE754 +0CD464CDC100 +BC305FE2DA65 +CF0EC6ACF2F9 +F7A545095C49 +6862FD600F78 +#MISC KEYS +36El765CE3E8 +9716D524lE28 +C608El3ADD50 +El67EC67C7FF +# RENFE MADRID (TRAIN) Extracted with detect reader +701AA491A4A5 +12BA20088ED3 +# MISC KEYS FROM MY OLD ACCESS CARDS +F18D91EE3033 +0E726E11CFCC +1D14130D1A0B +4ADLE273EAFL +3DFL4C8000A1 +E64Q986Q5D94 +201106141030 +D144BD193063 +01E2C14F1B18 +0380293A9E6D +08A55BC96DC1 +08ED3F92AA53 +0D61BA88789C +190E6242CE7B +1A8CFF2F1BC7 +25FE2B4E4FA9 +321803E38664 +381F84DB8134 +38540EEE8B1C +42F82DB5C4AF +507A6181E4BF +549BB4FD70C4 +57059FFD3EE6 +5E696FA0EAD1 +6036F9D72D68 +6611DFFAAE32 +6AC79644E0CD +735DD20237A9 +79E8B59A51E0 +7B6C00CBAC92 +7C3AF198425F +81C0BBCE32E9 +8D2B780A148D +9001D0E23F8C +A8700E07A58F +AE683AC2A232 +B6728D9B95BA +C290A397F84A +CA22AF33A19B +CBC83E1548B4 +CC5F59A0CE0A +D410EFF9113E +DE5865F29C44 +E108EA397A9A +E10F0E7A8DD5 +F833E24C3F1C +FA8CA10C7D59 +FE98F38F3EE2 diff --git a/assets/resources/subghz/assets/README.md b/assets/resources/subghz/assets/README.md index a583784dd..8d8fabd76 100644 --- a/assets/resources/subghz/assets/README.md +++ b/assets/resources/subghz/assets/README.md @@ -8,7 +8,7 @@ Officially supported frequencies: 300-348 MHz, 387-464 MHz, and 779-928 MHz (fro Unofficially supported frequencies: 281-361 MHz, 378-481 MHz, and 749-962 MHz (from [YARD Stick One](https://greatscottgadgets.com/yardstickone/) CC1111 docs) Official & Unleashed currently do not allow anything outside of the officially supported CC1101 specs. -RogueMaster allows unofficially supported frequencies with the extend_range file +RogueMaster allows unofficially supported frequencies with the extend_range.txt file **NOTE: Going outside the supported frequencies may DAMAGE YOUR FLIPPER AMP.
Please understand what you're doing if trying to break out of official frequencies.** diff --git a/assets/resources/subghz/assets/keeloq_mfcodes b/assets/resources/subghz/assets/keeloq_mfcodes index 1e6cf9a36..31619d6e7 100644 --- a/assets/resources/subghz/assets/keeloq_mfcodes +++ b/assets/resources/subghz/assets/keeloq_mfcodes @@ -1,52 +1,57 @@ Filetype: Flipper SubGhz Keystore File Version: 0 Encryption: 1 -IV: 3B 23 A4 B0 BA B1 BA 2C 45 B7 3D AF AE B1 D8 53 -7C22D7C0AD9099E4DD9BE3DD97F4E5171F0502B89FF7CA7B63A218EC70EA66FC -8468A45C97CC0718E3FE756DE72584C1C7F9FEB3E2873C6DE27A753017D69883 -A5983C8C10314C8BED1258B3CF7B81A43CFDAE0CB4A7E94EBDEC6B9876709157 -7B5C74E9011244735DB27F6D57BE32DA1DB26E1F60CE7C8D1029FDDCE3B12695 -940EF169CA9FE65E7E76FC4B3C8D67DF879D7D165A02E602DAF0B31FA0426B785D0E2500288603346505251D993F28F3 -639BBF96DBBA66408ABB04775689E7C85F4CBE3B1D94B834E4D371F6A262FF75 -1B4935A15D4C019C08E215025E6C1F2C89890C307CEF2ABAD9C1A1C84745B4FB -5796A104EA1D22A0B9000DA4630248635D1772FDB620B937963D1C8454CD2927 -661EBB18ECC20A236C0AE6BB0AE21B2BF3BDE64A42BA7616D2D87C2992C157FB -804352D4445CF4E08A7C7CEEA21DCCE0E2A0E436BA148C1E24DDDDA0B8ED3A67 -92D9D6C16DECD2E320D42818CC509FB3C22B8CA096630A50344FB2969653A6696FD46940C347205946358689D80F101E -02176E6C111509AB74FC6C88FE578F1C035A2676D93FA0AD595771A8A9BC536A842141B6FC0B56EFE1601614B931064A -5210722EA559F6F8ADD0E79F99A83265C2661DFDD7D1B1EEDAC02F641027EFCE -CCF01357E399E4F7F2FB32D1E697BF777948FF875A954F55F31BF02C6790EDDC -98E7CE7D325ED70B32200D809C109718DE633A5A286A3CDC889D9A241475AA4B -A7ECDEE650749FB4A7764F459AD49EE9F8FDC54B0E83A385BDB6413DD2D5DED1 -B2303659591EE6E9BDF56C1E70AEE6FAF21B97D7FEC026A96CFD780A7C3BC1FF5A4F868DC8BD4DD9160C3C8F715C6DC0 -869316BD6BB3E641A4C2BE32EF160E1B1405B16DD53A3042464CF5E07BBB7AC0 -72D442A018CD2875143AAFAE3D7C4AD1467BCBE0BE71119D123A7941EB0A56EA -32CC4E29CB6E03ECAE66BC38046D4BB1BDA7E3D0580F3F141008077EE906FA2EE6D2329706B96F49FA4E1E63EA974490 -791E004A574CC7A934DDE0ECAA097593721D71B9EE67995197CAB8349F166E230E5BA6488527C2BB55EA3E5BFD059F44 -2229CC65C99F31E6DB19B29CB625BF4EB95B5EB4AACC60BBB8C65BB7CBB07C2C -EA04BAF7B59B8D93678544D97BB0EDF81D043C97B740435AFA9EA9F3414829D6 -300A9BD3E912C5D09EFDB50F96704B8287BFDCBA82EF5D0E9D2306A4995B13BF -0C2EB6DBCB583977789DA234BCF186A0BE3008131360E46A85C13F7EA96469BC -2417D60848938A3DEAFC63344B4F49A68DC168FD6CE9D7F1CB7A0D70B2ABE32A -DF14D34ED4B03638926F1C4FF217AD3512A2C94C2A7B518CFCEB161FE9E0860E -AF920E82A02B46F3743626DB0FCB8F7EC1B444FA82F213C19F68E6A745BA5A8D -D7A5A38C2CEDA048F9FA5F091E3F36DC564698EF9BDC2B68A31188DF6294CA09 -5358679FC5EB2BC8C85ACA2957B24ACFD966FE53B9E9003264B06CDF900417F8 -D10608BCB41260D5A74E3224C820700E4D03C1CDC379E20D7C03564B3019189B -EE8EEA79570BB81660ED133D470E051FF45B4E5E8442D5931275353F6A434D66 -019AC0D9AF968B5CE43DA3FC1A6B11770B2CA801829BC05F88BB8A68AC074EDB -44EF315DA7310A4EE926B734B8F29A2241AAF631AF605B1FA9DD6C2AB2786695 -536FBE9BDD6FDED8F29CB6DC2935781874F8408DF6DB622CE8335B1BD21AB1C7 -76245B84908ECBC102E2F09FAB668D36A68D3C8BD2AB04A4FA119EBECAF232CA80A71D8BB379E59C62B6FB92717DB2FB -9EA362345970E8CA049119E5211BA78FDBCB33F5FD4B93B4189BE7FFA690ADA9 -40228619492D62DE5D98333265B2AF50773BDD0AC2C192A7C11073A37507CD12 -F6D31A4C37F591CD1C6FD2004501662549319C1862954A10CF5970099381BECC -8F917EAB00A545F6DEBF67A5DE9C12AA87AFE3C70C832EB4CC4F377A464F43C4 -E78E7F43A40C9AB1EA9DC9F9D2D7381ED037169928F8FEA2299C98C4B0F22569 -983E86FFEE7252E47AB1E0A0FB3CEEC901FF9627DD5242C9A688186F9889BEF0 -2B477C1E5DCD318DD039810D78879185BD8F1DF8C7BB0E330316A4D85A46A7BE -645F7E9A2EC08168432B3B9D80F45D30BA27E35C1D7075D14882EF50D00C811ED37E39F87C2115B47E302D7A90B2EC4F -20713989A6FAFC8B4F43A141AED90A67CA4C49D858EEB6E9BC802EA395D5553C -AB312E5A23E5804D089224BFC62252C92BC23F712C9426DEA1B3742F2A6A3502 -AAE727A60B82B21EA9E7E8111C048581F11F770AF34C619E85932C63C8636895 -1FE3C9BD1241B49E2737B6D760040050D984E4EBF39B5F7C04D16FD84604129C +IV: 5C AD F1 B1 BA B0 BA 11 AB 0B A3 6C F7 E3 6A 21 +AA7C98C6E94AA4062F875AB40BA3CE977D18B89830CCEB30179C5F2788E8941C +5B2D494F1AC8971B5F758D56D72F709E674B92F4B8D5BF9E4BBD48DBEFD11678 +AF3FFC3AB39E07694127DC64DD9BB371461200F4A6ED4622C5BA21F854394BE4 +2D889E3B426032518D20A7B35718FCCDA6769E310021BD41FF87D2A4DC4C1ED8 +7FB119118304533D748BAF06AAF69E838D6B6B20A84B51AEEBE6001FD5307DC8C98B46CF249E3E24FF64A033C470135B +B38B5D82576D4BFB7E5B600711695E409C1E33B539F167BE54572F9721D75566 +CAB78E2DE09DA67C95E0302A9A0010204A715CE61AD0884DEF018A15971D0DA8 +9794862BCFD7F31676245AB8BF369C754CBD6E818E8866CB2C743C3DC7F455BA +77AAE6912434A95B46A6C07B5A59EF287B83CE2620FEECF92CDAE7858F6A8050 +1A55BDD4F0E5F555EA3DF74182CE871343C955FD2349FB37B1B21FAC170511EB +892FAE550B5BF6CE2C6C85B74C8AD45D37E174CFD9EE0767927BCBCC904AA7CB464451E43EF9435E61635DC6EEFAD5E5 +26A7511E2213053BA49868CEE9FB56474CFB5AA422AFBF4C0FB5D5285E6C474DB7EF74A731D259A18AD1EF6EE015A9B2 +F3B14CFE82809DD20A8CE0EF9806CA40E3BA6EF8D62495AF9DEF0F92625F05E7 +BAC0B60E24FE913885FE3FF0A7E9EAB2B224471ABF738507E14060747792783E +070E67988EFF2B31EEE0AD2E8663EE8CEB4DACBB9D8D1159E7C9E84E18F1F7D4 +1755162EEA343C8D9D09852FA5B90E05DDE129145442822F937CE64EA168DE78 +70CF01EB50613461B447C6F54A2E28CAFDD39776B90BD88FDE3A2F5BE8ED5950387819C5F8BCAEEE0DE1A16692F5F07C +8152ECF2A96968C7803803851C70489B19846AB4450B5B5398A86E05D76A5E85 +D9A44B2BC3920622F8ED3EC2A18756612D05C3BF3555F752520AA206128576B3 +AD156C422650D482BFD399926330C849F61C145C6874EAE2826AEDA6BFEFB9892FF615E9CE4005274BF2217733629510 +1735246565057FF7A91DF91CE87A82F1823AC53282377CF188245C4E0A0A12420DFDDA0B70D2498D1C6C5456527FBF66 +E25E66ADCCF628CD21C14D61D47952816EE2265AD7664645A321D45197E1703B +07ACA960A68DAD72B3F7C51AB8573AB0BD08531390226F5404B0098A9FCAC239 +577209D5117506CEF9B6D4EA9F9783CE0EA57415EBBE5318B057D7E0AEFBDFC9 +708B950FCF78EFC2FEE1969A2590EBD8BDF95ACD2B052E96017B39EA667C2AAB +05D7DD808F82F97848B695D5C302FDF20F55D2FD56E386227726691ED581EC9A +08F1AEF4B9F694DF723813613FE0208A4ACF7079773B37CEFCAF7A132EBD4DBA +F607CEBFA3E307F27AC484B9E08FB96A2B678FBA4963AAC479F53D9CAC3530DF +578BBAD7BD2818DC9CC6F711035C267AF005B23A24BA79C66D408474CECEA799 +CA9A8ACCB5BEA6B53C4CE302CC052FC560317410A977425D0E7924AE10D4ED85 +523FE24DD7A7F4E2DF0E09771C863A934662A564152966B67DFECE7D37A53A50 +A606653C13720134F4564DD96FCD1AD44E66D9405BA95CFD0D91FE8828D0B615 +3ED9295F9C5D3C4E949B8E83CF63EBFD0482D224F4015451AD31646FD91F95D2 +7077291A87C15DE4605989467D38283F5416D6AD6D9B2458CE5A75D40A61B3D9 +08A7FECA1D73D605D4E71E69AD2112915A28DBE5A6817C52C2C451A9E1435CF1 +76FBB6AA098D630B5297261090F50011FFEEE6611347A2DF65E0C1C88C4DFFDD879A7C9134D2C51A61C3C7796833ADA6 +2315673B961F8D1C46020AAAB3BC73EE480496F6F5C6B9BF7175DF32A92D1E29 +851620CB407C3CA9A26CF6F6E3350A13FC241A99D7FCAF5EC41A90C40F6518A2 +F97AB6A733F1C288585629CC89A8AFA50B0D7B4CC2E0D41CA0B7118B37EB5170 +E725E6B06E9C8A575609E28DB887508D6CD6623BFD2151BDE0984E1E014A3FAB +138082F590404A5A335601C9872D08F0950EEEB7B28C5D6238F6A4C7FD73CFAC +22687BB4B5044A02C4F86FFA6B8113A3D315325CBAC503498284FA44C3566EE3 +1A5D93DE887B497D3904BD5C29F456EB6D8EA7D6B79962769F1862F21C6CC257 +F3ADA4CA246DC47577A40F7F24213A1E773CE7C9D3F5B0A054EE0807C81CB0B6 +CBA2632796ADB82F57A0B701BF1EF9787C27F4DD14C79D0FD09CD88447A8368B +5CF50ED192649B1F769451EEA7FC9D1F6585990D4D7618FB56A7C41DC3A93904 +62062BFC4F4CA5C7568F042832B27777BDB79522BB7BA3BB4F9644D35416ADC9 +AE7EED026C970F8A8AA2B33C01B7660BFD83B550367CACA4D9B266E2756C3A7E6C3BD0D2EEA3F6ED7C9A4023C575750A +A04D0A134EF9B67BAC02ABDD2E355422C96665D99484DF4E4EFD2CE4AA66F4BB +D7209CB12AFC47261B223C8EC48BDA3131C006FA997396F050FCBEEEFAC7B65A +2E58BF2F918C44F01DD3DFBB49C5FABB33FFFF25B6AC975CA1819A7554093C25 +96C5A04C551604AA8069E213A7FFCE7EA8D8BCF9B61F93BA4E912630AF1BA886 +9219823986AE58A5FD919E809478AF894A73A413F7C12D8B29DF8BD3ADE730652FB7EB3115CF2914F748AE64F996A4B3 diff --git a/assets/resources/subghz/assets/keeloq_mfcodes_user b/assets/resources/subghz/assets/keeloq_mfcodes_user deleted file mode 100644 index f013afa2d..000000000 --- a/assets/resources/subghz/assets/keeloq_mfcodes_user +++ /dev/null @@ -1,11 +0,0 @@ -# for adding manufacture keys -# AABBCCDDEEFFAABB:X:NAME\r\n -# AABBCCDDEEFFAABB - man 64 bit -# X - encryption method 1 - Simple Learning, 2 - Normal_Learning, 3 - Secure_Learning, 4 - Magic_xor_type1 Learning -# 0 - iterates over both previous and man in direct and reverse byte sequence -# NAME - name (string without spaces) max 64 characters long -Filetype: Flipper SubGhz Keystore File -Version: 0 -Encryption: 0 -AABBCCDDEEFFAABB:1:Test1 -AABBCCDDEEFFAABB:1:Test2 diff --git a/assets/resources/subghz/assets/keeloq_mfcodes_user_example b/assets/resources/subghz/assets/keeloq_mfcodes_user.example similarity index 100% rename from assets/resources/subghz/assets/keeloq_mfcodes_user_example rename to assets/resources/subghz/assets/keeloq_mfcodes_user.example diff --git a/assets/resources/subghz/assets/setting_user.txt b/assets/resources/subghz/assets/setting_user.txt index 2caa6ec37..9a1176a2f 100644 --- a/assets/resources/subghz/assets/setting_user.txt +++ b/assets/resources/subghz/assets/setting_user.txt @@ -1,3 +1,4 @@ +# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user Filetype: Flipper SubGhz Setting File Version: 1 @@ -24,8 +25,10 @@ Frequency: 313000000 Frequency: 313850000 Frequency: 314000000 Frequency: 314350000 +Frequency: 314980000 Frequency: 315000000 Frequency: 318000000 +Frequency: 330000000 Frequency: 345000000 Frequency: 348000000 Frequency: 387000000 @@ -37,10 +40,15 @@ Frequency: 433420000 Frequency: 433657070 Frequency: 433889000 Frequency: 433920000 +Frequency: 434075000 Frequency: 434176948 +Frequency: 434190000 +Frequency: 434390000 Frequency: 434420000 +Frequency: 434620000 Frequency: 434775000 Frequency: 438900000 +Frequency: 440175000 Frequency: 464000000 Frequency: 779000000 Frequency: 868350000 @@ -56,13 +64,12 @@ Frequency: 928000000 Hopper_frequency: 310000000 Hopper_frequency: 313000000 Hopper_frequency: 315000000 -Hopper_frequency: 318000000 Hopper_frequency: 390000000 Hopper_frequency: 433920000 Hopper_frequency: 434420000 Hopper_frequency: 868350000 -# Custom preset +# Custom preset examples # format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register #Custom_preset_name: AM_1 #Custom_preset_module: CC1101 @@ -71,6 +78,17 @@ Hopper_frequency: 868350000 #Custom_preset_module: CC1101 #Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 +# Custom presets added in Unleashed FW +# -- Some presets from forum.flipperzero.one -- + +#2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping +Custom_preset_name: FM15k +Custom_preset_module: CC1101 +Custom_preset_data: 02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0 + +# -- Other presets -- + +# Honda Presets Custom_preset_name: Honda1 Custom_preset_module: CC1101 # G2 G3 G4 D L0 L1 L2 diff --git a/assets/unit_tests/infrared/test_kaseikyo.irtest b/assets/unit_tests/infrared/test_kaseikyo.irtest new file mode 100644 index 000000000..d0142fecd --- /dev/null +++ b/assets/unit_tests/infrared/test_kaseikyo.irtest @@ -0,0 +1,105 @@ +Filetype: IR tests file +Version: 1 +# +name: decoder_input1 +type: raw +data: 1000000 3363 1685 407 436 411 432 415 1240 434 410 437 1245 439 404 433 1249 435 408 439 431 406 1249 435 435 412 405 442 1241 433 1249 435 408 439 405 442 428 409 434 413 430 407 411 436 433 414 429 408 1248 436 407 440 1243 441 428 409 434 413 431 406 1249 435 1248 436 406 441 1242 442 1240 434 409 438 431 416 428 409 408 439 430 407 411 436 407 440 429 408 436 411 432 415 402 435 1247 437 1245 439 1243 441 1238 436 +# +name: decoder_expected1 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1B 00 00 00 +repeat: false +# +name: decoder_input2 +type: raw +data: 1000000 3365 1683 409 434 413 431 406 1276 408 435 412 1270 414 429 408 1248 436 434 413 430 407 1275 409 434 413 431 406 1276 408 1248 436 433 414 430 407 437 410 433 414 429 408 436 411 432 415 428 409 1246 438 432 415 1267 407 437 410 433 414 429 408 436 411 432 415 1266 408 1250 434 1248 436 432 415 429 408 435 412 432 415 428 409 434 413 430 407 437 410 433 414 429 408 436 411 432 415 428 409 435 412 1240 434 +# +name: decoder_expected2 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1C 00 00 00 +repeat: false +# +name: decoder_input3 +type: raw +data: 1000000 3361 1661 442 427 410 434 413 1243 441 428 409 1247 437 432 415 1241 433 410 437 407 440 1242 432 437 410 407 440 1242 442 1241 433 436 411 407 440 430 407 436 411 406 441 402 435 435 412 431 416 1240 434 410 437 1245 439 404 433 411 436 407 440 403 434 436 411 432 415 429 408 1249 435 1247 437 1245 439 430 407 1250 434 434 413 404 433 438 409 434 413 1243 441 1241 433 410 437 1245 439 430 407 1250 434 432 415 +# +name: decoder_expected3 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +repeat: false +# +name: decoder_input4 +type: raw +data: 1000000 3365 1656 436 406 441 402 435 1248 436 406 441 1242 432 410 437 1246 438 404 433 410 437 1246 438 404 433 437 410 1245 491 1190 442 401 436 435 412 431 416 427 410 433 414 429 408 435 412 431 416 1240 434 435 412 1244 440 1241 433 436 411 433 414 402 435 409 438 405 442 402 435 1247 437 1244 440 1241 433 437 410 1245 439 430 407 410 437 406 441 402 435 409 438 1243 441 402 435 1247 437 406 441 1240 434 433 414 +# +name: decoder_expected4 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 70 01 00 00 +repeat: false +# +name: decoder_input5 +type: raw +data: 1000000 3357 1665 438 431 416 428 409 1247 437 432 415 1241 433 436 411 1245 439 430 407 436 411 1245 439 430 407 437 410 1246 438 1243 441 428 409 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 427 410 1247 437 1245 439 430 407 437 410 1246 438 1244 440 429 408 1250 434 1248 488 355 440 429 408 436 411 432 415 428 408 435 412 431 416 428 409 1247 437 432 415 428 409 1248 436 1246 490 1191 441 1240 434 +# +name: decoder_expected5 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 1B 00 00 00 +repeat: false +# +name: decoder_input6 +type: raw +data: 1000000 3358 1664 439 430 407 437 410 1245 439 430 407 1250 434 434 413 1243 441 428 409 435 412 1244 440 428 409 435 412 1244 440 1242 432 437 410 434 413 430 407 436 411 432 415 428 409 435 412 431 416 1240 434 435 412 1244 440 1242 442 427 410 434 413 1243 441 427 409 1247 437 433 414 429 408 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 1240 486 357 438 432 415 1240 434 436 411 432 415 425 412 +# +name: decoder_expected6 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 05 00 00 00 +repeat: false +# +name: encoder_decoder_input1 +type: parsed_array +count: 4 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1B 00 00 00 +repeat: false +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +repeat: false +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 05 00 00 00 +repeat: false +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 1B 00 00 00 +repeat: false +# diff --git a/assets/unit_tests/subghz/ansonic.sub b/assets/unit_tests/subghz/ansonic.sub new file mode 100644 index 000000000..a7219b12c --- /dev/null +++ b/assets/unit_tests/subghz/ansonic.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Ansonic +Bit: 12 +Key: 00 00 00 00 00 00 05 5A diff --git a/assets/unit_tests/subghz/ansonic_raw.sub b/assets/unit_tests/subghz/ansonic_raw.sub new file mode 100644 index 000000000..6d4c78ebe --- /dev/null +++ b/assets/unit_tests/subghz/ansonic_raw.sub @@ -0,0 +1,11 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 57 -144 401 -86 173 -202 143 -258 133 -88 257 -144 287 -58 402 -56 259 -230 259 -86 85 -96 95 -174 286 -162 57 -230 253 -400 229 -88 536 -58 85 -72 167 -110 263 -72 229 -58 85 -86 87 -262 119 -288 163 -210 321 -320 186 -140 261 -96 143 -456 117 -216 143 -246 239 -102 121 -72 71 -191 167 -263 191 -96 239 -80 57 -116 143 -118 167 -168 215 -288 191 -106 287 -114 517 -88 113 -394 173 -70 215 -100 661 -86 201 -114 259 -58 287 -86 57 -202 399 -200 57 -288 229 -144 115 -1425 83 -142 173 -86 459 -112 223 -144 201 -116 143 -114 196 -17422 287 -614 1075 -1132 533 -560 1125 -1112 561 -530 1133 -1088 585 -512 1129 -1104 563 -1102 571 -1072 591 -524 1137 -538 1115 -19392 589 -522 1139 -1090 569 -520 1137 -1100 587 -520 1135 -1092 569 -514 1155 -1076 599 -1062 585 -1086 585 -532 1125 -528 1145 -19396 581 -504 1155 -1064 619 -506 1145 -1080 595 -508 1149 -1062 587 -536 1129 -1088 585 -1090 589 -1062 587 -510 1177 -506 1149 -19394 587 -524 1147 -1076 597 -512 1129 -1072 617 -498 1159 -1066 599 -514 1147 -1076 595 -1060 613 -1060 589 -506 1179 -510 1155 -19378 605 -514 1129 -1098 601 -488 1177 -1062 583 -510 1161 -1072 587 -516 1169 -1058 587 -1082 589 -1076 593 -532 1121 -536 1127 -19444 549 -542 1129 -1092 601 -510 1147 -1098 581 -506 1123 -1100 585 -510 1147 -1106 591 -1060 597 -1060 595 -510 1151 -534 1157 -19390 593 -512 1163 -1064 597 -510 1173 -1042 617 -510 1159 -1056 579 -556 1133 -1054 609 -1060 607 -1078 585 -528 1147 -516 1135 -19444 559 -534 1165 -1052 605 -510 1149 -1062 599 -520 1173 -1054 591 -540 1111 -1116 573 -1088 593 -1042 615 -534 1117 -536 1129 -19436 569 -530 1157 -1052 605 -504 1177 -1062 591 -536 1113 -1114 589 -514 1125 -1112 563 -1084 587 -1080 589 -536 1123 -532 1165 -19392 599 -524 1143 -1080 595 -540 1125 -1090 563 -534 1149 -1084 567 -534 1147 -1092 587 -1086 585 -1064 565 -558 1123 -532 1143 -19450 549 -556 1121 -1086 585 -534 1137 -1094 583 -516 1133 -1114 563 -536 1123 -1108 573 -1082 597 -1078 565 -532 1143 -524 1135 -19464 561 -528 1149 -1066 613 -508 1151 -1076 587 -532 1125 -1076 609 -506 1175 -1076 563 -1106 563 -1084 591 -534 1157 -484 1179 -19414 589 -528 1131 -1096 587 -520 1163 -1080 563 -550 1121 -1098 555 -562 1117 -1082 575 -1112 563 -1104 559 -558 1121 -530 1149 -19442 591 -498 1175 -1066 585 -534 1121 -1114 565 -540 1115 -1096 585 -538 1123 -1110 559 -1086 585 -1084 589 -530 1125 -558 1121 -19454 563 -542 1163 -1044 585 -558 1131 -1092 571 -536 1133 -1088 587 -518 1149 -1086 601 -1058 587 -1080 611 -510 +RAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 1141 -1090 587 -516 1121 -1114 591 -1062 595 -1092 567 -538 1131 -536 1145 -19448 589 -516 1143 -1076 591 -540 1115 -1090 591 -538 1115 -1096 597 -512 1155 -1078 581 -1082 585 -1104 559 -530 1153 -516 1147 -19438 589 -526 1157 -1056 609 -534 1137 -1078 585 -532 1149 -1076 585 -536 1123 -1104 587 -1062 597 -1082 587 -536 1121 -554 1121 -19464 555 -560 1123 -1104 563 -542 1131 -1096 565 -536 1143 -1060 621 -492 1175 -1078 571 -1100 577 -1092 591 -494 1167 -538 1117 -19452 593 -538 1133 -1074 603 -510 1157 -1062 595 -548 1115 -1102 573 -538 1139 -1094 567 -1094 583 -1092 567 -536 1127 -560 1113 -19478 563 -534 1151 -1084 569 -534 1135 -1116 565 -534 1135 -1084 583 -532 1155 -1060 587 -1084 589 -1084 581 -536 1125 -546 1141 -19460 577 -536 1123 -1102 565 -554 1119 -1096 593 -520 1133 -1108 563 -546 1131 -1088 569 -1094 573 -1100 583 -526 1147 -540 1123 -19466 587 -530 1129 -1100 587 -510 1143 -1084 609 -510 1131 -1110 567 -542 1121 -1102 567 -1110 563 -1106 567 -532 1149 -518 1145 -19458 573 -534 1147 -1082 575 -556 1149 -1048 589 -532 1157 -1088 565 -536 1129 -1088 589 -1106 563 -1086 587 -536 1129 -548 1147 -4298 103 -56 87 -344 87 -258 85 -278 215 -70 77 -344 279 -56 87 -56 197 -198 143 -288 362 -86 605 -144 57 -268 95 -114 143 -144 173 -144 143 -402 57 -202 259 -391 652 -340 427 -230 173 -356 97 -144 111 -246 219 -96 191 -114 173 -288 115 -56 109 -106 199 -106 73 -130 57 -172 85 -260 373 -56 629 -200 690 -230 273 -120 85 -460 85 -314 77 -78 111 -88 401 -116 171 -312 71 -500 81 -224 229 -88 257 -370 181 -172 200 -116 535 -174 113 -294 213 -359 445 -144 258 -114 115 -202 675 -509 239 -432 373 -538 85 -58 113 -86 761 -104 113 -318 443 -70 143 -144 647 -204 111 -334 87 -114 115 -144 113 -188 177 -144 199 -260 143 -86 87 -622 57 -116 171 -58 139 -222 55 -346 315 -76 345 -114 139 -171 195 -52 53 -98 119 -144 143 -244 95 -72 95 -96 167 -302 253 -186 307 -444 287 -449 115 -172 57 -172 316 -202 85 -370 697 -116 57 -144 171 -202 259 -114 85 -144 87 -315 85 -58 201 -116 171 -272 121 -358 171 -403 113 -86 115 -202 489 -229 115 -392 95 -116 171 -140 93 -102 143 -543 245 -358 215 -120 387 -288 171 -202 221 -202 115 -748 57 -316 143 -260 143 -288 115 -316 115 -58 85 -288 143 -460 485 -96 71 -104 199 -96 199 -202 143 -86 201 -116 85 -230 211 -288 115 -605 365 -126 53 -172 +RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477 +RAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100 +RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86 +RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144 \ No newline at end of file diff --git a/assets/unit_tests/subghz/magellan.sub b/assets/unit_tests/subghz/magellan.sub new file mode 100644 index 000000000..11684803d --- /dev/null +++ b/assets/unit_tests/subghz/magellan.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Magellan +Bit: 32 +Key: 00 00 00 00 37 AE 48 28 diff --git a/assets/unit_tests/subghz/magellen_raw.sub b/assets/unit_tests/subghz/magellan_raw.sub similarity index 100% rename from assets/unit_tests/subghz/magellen_raw.sub rename to assets/unit_tests/subghz/magellan_raw.sub diff --git a/assets/unit_tests/subghz/magellen.sub b/assets/unit_tests/subghz/magellen.sub deleted file mode 100644 index 3317fd4bc..000000000 --- a/assets/unit_tests/subghz/magellen.sub +++ /dev/null @@ -1,7 +0,0 @@ -Filetype: Flipper SubGhz Key File -Version: 1 -Frequency: 433920000 -Preset: FuriHalSubGhzPresetOok650Async -Protocol: Magellen -Bit: 32 -Key: 00 00 00 00 37 AE 48 28 diff --git a/assets/unit_tests/subghz/oregon2_raw.sub b/assets/unit_tests/subghz/oregon2_raw.sub deleted file mode 100644 index 549a60db3..000000000 --- a/assets/unit_tests/subghz/oregon2_raw.sub +++ /dev/null @@ -1,20 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 433920000 -Preset: FuriHalSubGhzPresetOok270Async -Protocol: RAW -RAW_Data: 889 -130 325 -64 457 -560 165 -68 199 -170 67 -66 265 -132 133 -666 67 -166 431 -66 201 -98 297 -100 595 -66 199 -134 65 -100 795 -132 99 -168 501 -200 331 -132 265 -102 265 -134 423 -98 521 -226 65 -166 431 -134 99 -100 133 -464 195 -326 623 -100 673 -98 321 -200 65 -136 369 -166 65 -68 97 -166 165 -334 265 -102 231 -166 101 -170 65 -170 265 -136 931 -100 133 -134 563 -66 333 -100 427 -66 163 -390 231 -66 193 -130 461 -166 557 -100 99 -198 263 -100 197 -294 231 -232 299 -134 199 -170 267 -134 631 -98 235 -100 499 -68 463 -100 65 -134 335 -170 273 -134 297 -100 67 -66 197 -166 67 -134 301 -168 537 -470 99 -134 433 -132 199 -192 261 -100 523 -164 459 -132 259 -332 359 -64 227 -96 131 -132 687 -132 363 -136 329 -434 99 -334 133 -100 401 -132 233 -700 233 -170 337 -66 371 -68 233 -202 531 -266 731 -66 465 -100 167 -100 133 -232 335 -166 239 -102 367 -232 231 -100 167 -134 201 -136 301 -168 199 -300 231 -98 237 -134 233 -102 329 -132 261 -134 199 -66 265 -136 99 -170 167 -134 199 -166 167 -136 367 -298 197 -200 99 -166 469 -136 439 -66 303 -134 295 -100 433 -134 899 -266 363 -132 197 -160 555 -324 129 -96 97 -128 257 -132 97 -394 257 -98 195 -166 459 -332 395 -132 633 -134 301 -100 131 -332 169 -168 395 -166 263 -540 783 -100 287 -130 295 -96 225 -296 133 -98 99 -100 461 -164 545 -130 99 -66 301 -68 265 -100 235 -134 235 -70 333 -102 497 -66 233 -364 301 -170 103 -66 165 -336 733 -200 133 -100 263 -102 65 -136 465 -200 1035 -198 165 -170 67 -302 631 -100 429 -332 65 -128 129 -130 159 -128 159 -66 161 -96 325 -164 261 -100 197 -162 65 -96 99 -130 65 -102 333 -100 199 -98 389 -330 129 -128 229 -66 425 -366 229 -64 261 -100 227 -96 227 -526 301 -200 97 -66 699 -334 67 -100 399 -198 787 -98 297 -134 429 -100 3245 -64 527 -98 131 -526 633 -68 133 -302 1459 -164 971 -102 237 -136 1439 -266 1131 -66 599 -200 303 -332 325 -130 389 -166 371 -66 333 -102 65 -100 233 -234 327 -266 233 -166 297 -100 225 -130 163 -336 99 -596 199 -330 131 -66 331 -338 263 -358 197 -168 877 -66 227 -96 63 -130 263 -162 225 -290 197 -198 357 -132 297 -262 165 -456 227 -98 399 -296 95 -132 99 -98 457 -200 199 -168 535 -100 567 -134 327 -130 193 -130 683 -102 101 -132 233 -170 943 -166 827 -66 267 -102 503 -68 1325 -164 -RAW_Data: 1607 -68 233 -166 1167 -70 531 -134 335 -168 131 -66 299 -402 899 -66 461 -66 457 -98 953 -98 165 -66 293 -230 881 -64 393 -166 589 -66 289 -66 1093 -204 333 -98 2745 -132 2019 -170 925 -68 269 -102 1469 -136 2301 -68 1355 -100 527 -66 975 -68 1445 -98 2397 -100 1733 -66 703 -100 995 -100 135 -136 235 -202 167 -134 2071 -166 339 -170 201 -268 129 -66 465 -66 365 -100 197 -164 129 -98 161 -96 423 -66 675 -66 1543 -136 567 -200 767 -202 65 -100 1401 -66 623 -136 567 -234 67 -236 197 -194 97 -66 263 -66 1827 -392 1893 -98 165 -268 133 -132 231 -162 225 -98 695 -198 563 -100 301 -332 267 -102 341 -66 99 -132 1299 -130 525 -68 161 -96 357 -98 353 -100 131 -100 131 -98 163 -132 323 -100 535 -66 1323 -130 133 -66 235 -134 1497 -132 387 -98 129 -162 2623 -134 163 -68 167 -66 959 -232 495 -68 131 -134 867 -134 865 -66 333 -98 305 -134 231 -98 765 -198 397 -432 165 -66 165 -366 265 -102 541 -100 261 -162 331 -134 457 -66 491 -196 97 -266 193 -262 65 -166 231 -266 497 -360 263 -98 587 -164 259 -98 231 -66 359 -100 267 -102 271 -168 97 -262 63 -66 261 -130 227 -130 295 -164 65 -66 265 -200 597 -134 267 -170 603 -100 97 -466 231 -264 97 -168 99 -66 65 -200 199 -100 267 -404 303 -102 201 -204 235 -134 131 -198 335 -298 327 -130 291 -164 63 -162 295 -262 197 -130 95 -130 195 -96 159 -130 161 -66 231 -100 165 -66 199 -134 363 -66 267 -168 165 -168 167 -100 165 -530 363 -432 99 -232 65 -132 395 -328 229 -98 197 -132 161 -96 191 -292 197 -204 133 -100 399 -166 531 -332 235 -168 99 -66 325 -158 553 -132 129 -226 231 -134 99 -462 129 -64 289 -100 193 -66 355 -164 291 -198 131 -298 197 -198 373 -268 335 -234 427 -68 199 -132 267 -232 131 -66 783 -326 63 -162 161 -130 227 -66 259 -562 233 -464 303 -102 201 -334 301 -134 297 -198 229 -66 127 -166 99 -100 197 -198 571 -66 457 -134 361 -424 131 -328 163 -98 63 -100 505 -102 201 -1094 229 -164 65 -230 789 -236 2505 -166 201 -170 163 -64 1139 -66 927 -100 295 -198 723 -100 365 -66 459 -196 3033 -272 199 -66 499 -202 1319 -232 295 -298 131 -362 97 -164 129 -132 65 -98 197 -130 129 -98 261 -130 97 -98 229 -96 425 -66 227 -166 483 -66 163 -326 567 -68 235 -68 67 -66 167 -66 235 -330 425 -164 63 -66 427 -102 167 -66 669 -132 429 -200 65 -102 133 -100 197 -368 -RAW_Data: 65 -134 2481 -228 65 -130 229 -228 763 -136 603 -166 1619 -98 1763 -102 837 -166 321 -66 951 -130 2067 -66 259 -132 1835 -66 437 -102 701 -66 565 -68 363 -70 1113 -66 1989 -164 257 -128 351 -162 1055 -232 265 -170 309 -200 435 -166 833 -102 2467 -132 595 -66 773 -166 1615 -98 131 -96 485 -64 517 -166 197 -68 1231 -68 403 -100 263 -134 233 -100 503 -100 333 -266 729 -66 199 -100 369 -68 1239 -100 197 -68 299 -170 337 -100 825 -132 163 -66 4205 -64 161 -100 635 -66 907 -66 1017 -166 1709 -100 201 -266 657 -68 463 -166 331 -164 293 -64 259 -162 129 -262 597 -134 701 -136 67 -168 235 -136 303 -170 1417 -66 263 -98 857 -100 659 -166 97 -100 2497 -64 2495 -98 719 -128 227 -130 2217 -164 623 -264 719 -134 329 -98 1371 -100 553 -294 165 -66 1163 -100 329 -196 649 -200 1123 -68 263 -100 593 -266 333 -102 1133 -136 131 -132 603 -200 1819 -66 489 -66 563 -266 1113 -230 165 -66 423 -68 335 -100 101 -100 1073 -132 897 -100 101 -100 499 -134 173 -138 763 -238 371 -130 403 -166 203 -102 271 -136 269 -166 99 -168 263 -96 425 -66 331 -234 133 -400 231 -132 453 -66 459 -164 199 -68 237 -132 163 -198 161 -196 265 -132 65 -64 195 -130 357 -164 663 -68 167 -600 131 -98 133 -304 203 -134 433 -98 261 -130 199 -100 237 -100 229 -326 99 -98 331 -132 99 -294 165 -66 303 -134 99 -232 133 -136 99 -68 267 -198 233 -138 67 -166 367 -100 333 -168 267 -200 369 -266 135 -404 1939 -132 231 -160 161 -64 293 -98 331 -132 339 -104 135 -100 197 -430 263 -202 233 -64 195 -162 129 -64 227 -298 265 -68 697 -66 301 -68 231 -300 131 -368 769 -234 265 -98 195 -324 97 -752 229 -126 355 -98 257 -98 287 -64 427 -132 295 -262 197 -170 369 -102 267 -100 169 -68 201 -102 2551 -136 635 -134 639 -134 99 -132 197 -200 371 -66 731 -132 199 -138 733 -304 433 -68 729 -440 197 -68 99 -102 165 -266 261 -164 491 -296 489 -194 257 -164 133 -134 237 -68 335 -98 227 -130 229 -98 295 -98 231 -202 267 -236 233 -136 331 -130 195 -128 261 -430 261 -162 97 -224 99 -130 193 -96 197 -162 229 -396 97 -98 227 -364 267 -100 99 -100 233 -236 697 -164 227 -196 63 -98 327 -230 325 -66 129 -196 95 -98 195 -130 325 -430 131 -194 129 -454 161 -196 235 -68 433 -134 667 -164 355 -236 101 -98 2143 -134 1827 -198 63 -198 65 -64 2859 -64 619 -66 97 -130 3157 -66 679 -194 1491 -98 -RAW_Data: 951 -64 393 -100 955 -132 4715 -100 131 -66 199 -204 1541 -66 929 -130 1347 -166 665 -132 233 -132 67 -102 433 -100 595 -228 997 -66 505 -68 133 -98 231 -68 571 -134 1371 -232 231 -270 135 -102 97 -66 867 -100 269 -68 967 -100 1649 -66 65 -66 951 -68 65 -202 363 -200 779 -102 1449 -294 419 -130 361 -230 1079 -164 163 -260 893 -102 333 -100 533 -166 467 -100 135 -66 135 -202 369 -100 199 -100 269 -134 301 -166 229 -66 101 -134 199 -134 1293 -64 779 -62 831 -66 1243 -68 267 -102 197 -100 395 -98 455 -64 621 -132 877 -98 199 -100 2101 -134 503 -100 2035 -134 735 -236 475 -136 237 -132 133 -134 1229 -100 133 -66 167 -68 2655 -100 1807 -100 1095 -264 825 -98 163 -66 491 -98 161 -128 953 -100 773 -100 131 -66 67 -134 457 -130 63 -64 389 -98 715 -66 425 -300 97 -100 1515 -66 303 -68 99 -98 721 -64 887 -132 65 -132 165 -66 635 -68 2801 -66 1561 -100 751 -98 129 -64 725 -136 201 -100 333 -204 573 -104 1745 -134 99 -66 129 -64 595 -134 167 -102 337 -134 567 -134 1131 -138 1207 -100 269 -68 135 -100 1143 -134 2139 -68 1701 -162 991 -596 431 -66 99 -132 657 -66 391 -320 357 -260 259 -98 429 -66 163 -228 65 -130 227 -66 261 -166 99 -98 131 -366 199 -134 463 -102 201 -98 231 -102 639 -238 301 -568 169 -610 265 -102 841 -198 297 -100 335 -132 263 -266 265 -68 469 -134 267 -68 933 -298 333 -298 729 -168 135 -136 437 -132 1137 -134 199 -68 265 -132 463 -166 129 -130 227 -98 297 -98 65 -132 97 -202 199 -232 305 -66 165 -198 365 -66 99 -98 299 -170 65 -136 301 -232 99 -564 133 -132 233 -170 99 -102 131 -134 65 -204 101 -98 297 -98 167 -762 233 -298 99 -326 395 -66 299 -132 369 -504 333 -98 483 -200 457 -164 63 -164 329 -162 65 -622 231 -268 131 -132 133 -134 131 -134 131 -66 99 -100 231 -66 167 -336 165 -98 197 -100 97 -264 321 -98 521 -132 163 -130 129 -294 297 -134 101 -102 265 -168 497 -68 197 -68 499 -134 269 -398 267 -130 203 -302 65 -498 271 -136 465 -292 131 -294 163 -198 329 -96 129 -98 193 -130 391 -330 165 -134 167 -170 297 -102 133 -136 135 -366 199 -132 423 -132 395 -168 65 -166 401 -98 229 -98 329 -98 99 -130 129 -228 261 -160 127 -426 389 -162 193 -132 131 -100 231 -168 67 -304 201 -68 765 -132 161 -162 193 -64 195 -64 295 -130 787 -98 419 -528 429 -66 363 -134 131 -100 133 -200 331 -98 -RAW_Data: 431 -66 1167 -68 937 -68 1003 -66 99 -132 941 -134 65 -66 365 -274 165 -236 367 -96 557 -134 675 -66 261 -164 127 -96 391 -164 161 -98 391 -292 163 -98 519 -196 165 -98 523 -66 195 -160 3343 -66 661 -100 2589 -136 307 -100 629 -136 639 -100 133 -168 405 -100 267 -66 465 -132 1171 -64 749 -64 165 -98 983 -100 163 -202 537 -66 327 -100 669 -100 401 -236 2885 -164 439 -134 97 -426 1931 -66 1385 -98 715 -98 519 -66 289 -162 97 -360 297 -166 163 -66 289 -66 555 -334 167 -230 429 -102 267 -132 943 -136 401 -68 929 -130 193 -68 467 -198 335 -66 963 -100 597 -132 197 -260 523 -232 1115 -102 1935 -66 1395 -134 305 -100 99 -66 199 -66 1071 -66 2357 -66 367 -498 769 -234 163 -130 191 -64 1211 -200 133 -102 201 -100 561 -366 361 -98 195 -100 537 -64 165 -196 1041 -332 133 -102 441 -230 4217 -66 1033 -66 167 -66 933 -100 565 -66 331 -164 673 -104 441 -66 533 -66 2095 -164 525 -66 297 -170 965 -198 421 -100 663 -832 65 -100 331 -164 231 -166 135 -168 237 -466 761 -134 891 -196 791 -198 257 -160 161 -98 293 -66 1081 -98 229 -130 327 -66 1301 -200 331 -166 101 -66 461 -100 2619 -132 1663 -98 1609 -134 499 -332 165 -370 67 -264 97 -96 259 -98 701 -402 197 -128 527 -236 233 -102 167 -134 303 -134 99 -166 299 -132 165 -200 467 -68 305 -168 207 -102 465 -102 729 -136 101 -374 327 -96 259 -98 467 -202 65 -66 673 -98 335 -404 135 -66 339 -204 99 -366 233 -68 365 -166 133 -102 867 -198 163 -162 163 -294 463 -332 165 -68 269 -268 331 -100 131 -166 299 -132 231 -400 263 -164 131 -266 267 -264 367 -66 371 -134 229 -104 267 -232 67 -466 265 -100 101 -100 165 -200 65 -200 301 -66 199 -168 233 -98 267 -66 67 -134 261 -196 261 -234 427 -294 65 -194 193 -66 259 -132 849 -96 63 -198 167 -294 95 -98 361 -164 261 -196 131 -132 437 -100 597 -262 327 -162 295 -98 295 -164 259 -196 425 -230 321 -66 195 -66 261 -496 99 -200 529 -132 133 -966 133 -132 165 -66 63 -128 491 -402 65 -262 299 -66 299 -202 265 -100 99 -668 97 -134 65 -100 101 -66 65 -266 691 -66 431 -166 167 -134 199 -370 899 -134 99 -100 1093 -166 163 -166 399 -98 327 -100 99 -168 135 -200 133 -202 429 -98 65 -98 197 -556 65 -66 97 -326 331 -166 333 -200 135 -100 235 -234 265 -98 65 -68 135 -66 335 -66 133 -298 99 -66 233 -164 435 -232 97 -132 97 -392 -RAW_Data: 99 -198 819 -66 1235 -98 321 -132 1091 -66 1307 -98 3059 -164 3305 -64 227 -98 591 -98 129 -66 229 -98 2143 -98 939 -68 563 -100 361 -232 945 -164 257 -96 229 -230 387 -64 195 -130 981 -294 587 -162 193 -98 1337 -66 293 -98 2665 -66 297 -98 647 -66 459 -132 491 -164 489 -96 595 -66 899 -66 837 -64 1151 -196 259 -98 357 -164 891 -132 1359 -134 197 -98 97 -98 261 -64 229 -96 461 -136 693 -100 201 -98 865 -66 599 -100 517 -132 709 -66 293 -298 655 -66 197 -130 129 -66 197 -98 4291 -66 673 -66 667 -132 1473 -132 133 -104 99 -66 163 -168 333 -134 1743 -132 1097 -132 99 -68 167 -602 1323 -352 99 -166 753 -98 423 -98 97 -66 1317 -228 1309 -98 1849 -66 1939 -132 601 -100 665 -100 1875 -66 695 -132 425 -66 425 -66 263 -134 165 -134 99 -98 829 -66 601 -166 131 -102 565 -66 301 -100 1099 -100 601 -138 533 -66 667 -234 561 -66 99 -68 2741 -98 199 -100 531 -168 101 -434 1027 -68 431 -66 403 -132 99 -98 565 -132 135 -100 399 -166 271 -236 233 -166 197 -366 99 -66 99 -168 503 -66 199 -170 207 -100 673 -368 99 -66 263 -168 133 -98 397 -268 337 -66 131 -132 231 -132 501 -134 99 -168 567 -138 103 -136 267 -298 231 -134 197 -160 321 -332 231 -98 131 -164 257 -64 163 -328 395 -66 331 -202 65 -168 133 -68 167 -100 233 -102 335 -66 197 -326 1101 -132 589 -100 811 -132 399 -136 269 -102 497 -66 559 -100 129 -98 855 -68 637 -102 65 -200 875 -68 233 -166 167 -66 529 -202 235 -102 231 -66 1237 -66 733 -98 1723 -132 101 -100 297 -66 829 -232 197 -100 367 -134 169 -166 167 -434 633 -100 235 -200 131 -134 233 -100 131 -100 331 -134 495 -432 65 -528 161 -130 295 -132 337 -136 133 -166 165 -100 269 -240 201 -336 133 -166 165 -238 199 -202 431 -434 99 -134 501 -166 231 -96 559 -202 167 -66 717 -98 987 -198 65 -64 163 -64 227 -98 555 -164 199 -64 361 -66 163 -98 129 -162 97 -130 161 -460 197 -230 681 -98 197 -98 329 -100 267 -266 291 -264 65 -100 329 -100 459 -200 363 -98 165 -134 231 -134 301 -134 231 -302 99 -132 101 -134 267 -136 233 -68 393 -422 163 -166 361 -166 99 -134 365 -134 133 -336 401 -66 495 -132 401 -168 133 -402 501 -136 1093 -862 165 -132 293 -300 289 -66 131 -164 391 -134 99 -360 359 -130 323 -200 423 -98 195 -162 295 -132 161 -98 129 -782 131 -426 227 -64 259 -166 63 -160 323 -98 261 -230 -RAW_Data: 231 -66 921 -66 355 -64 1019 -98 227 -258 163 -66 597 -232 1313 -132 163 -404 467 -236 901 -164 483 -98 195 -96 489 -134 103 -238 169 -66 67 -68 299 -100 497 -68 65 -134 1635 -304 1153 -100 539 -168 265 -200 499 -166 535 -100 397 -168 931 -100 131 -66 631 -134 897 -270 1233 -100 65 -132 131 -334 663 -66 163 -66 131 -132 705 -98 571 -200 433 -100 237 -234 229 -132 1627 -66 569 -100 715 -66 1863 -272 265 -68 301 -98 465 -68 97 -134 99 -66 395 -136 1405 -66 529 -132 63 -196 579 -132 413 -260 129 -136 101 -166 1201 -134 833 -134 393 -66 335 -172 201 -68 1027 -96 753 -64 815 -66 97 -64 1341 -132 289 -160 127 -66 99 -228 1083 -96 163 -66 259 -64 159 -98 2409 -168 767 -200 367 -66 1675 -66 1067 -98 3407 -200 99 -66 1403 -166 99 -134 439 -200 329 -136 599 -66 637 -66 835 -66 1099 -98 99 -66 463 -166 165 -100 461 -164 3037 -66 655 -66 97 -98 229 -130 355 -132 1443 -66 527 -98 881 -98 229 -162 127 -96 583 -64 65 -162 489 -166 885 -194 257 -98 1539 -66 293 -166 229 -132 655 -98 757 -49522 271 -758 689 -1264 737 -670 293 -1152 811 -1144 341 -664 773 -678 327 -1118 807 -1144 835 -1146 781 -1126 873 -1096 347 -622 877 -624 321 -1106 843 -1098 871 -1098 843 -1106 379 -610 841 -584 381 -1122 365 -602 845 -1116 837 -610 381 -1056 889 -1078 383 -614 827 -1110 877 -592 353 -1108 845 -1120 839 -1120 347 -602 849 -1110 865 -612 361 -1072 869 -1114 351 -618 861 -618 343 -1090 853 -1106 387 -618 797 -674 347 -1084 389 -574 867 -584 381 -1114 841 -1102 845 -1116 839 -1112 843 -1098 875 -1086 383 -584 865 -588 375 -1100 861 -1112 851 -1084 853 -1108 847 -1106 381 -584 857 -610 383 -1080 357 -602 871 -602 385 -1084 383 -616 823 -610 373 -1086 381 -590 871 -1084 839 -628 353 -1102 875 -1100 349 -9404 875 -1060 871 -1086 887 -1088 879 -1058 863 -1086 855 -1132 845 -1078 871 -1076 857 -1098 881 -1082 861 -1088 843 -1120 853 -1074 879 -1074 879 -1068 889 -614 341 -1090 387 -616 863 -624 345 -1088 391 -590 857 -612 385 -1058 393 -596 843 -1088 889 -1078 879 -578 387 -1082 875 -1076 415 -550 881 -1070 877 -592 391 -1114 821 -1104 373 -620 821 -624 361 -1072 903 -1086 855 -1092 843 -1086 905 -1054 387 -614 863 -618 347 -1088 853 -1114 845 -1090 867 -1070 381 -610 885 -584 385 -1052 407 -578 877 -1052 899 -600 389 -1048 907 -1074 383 -586 877 -1072 877 -594 359 -1076 875 -1082 891 -1088 363 -616 855 -1084 857 -592 381 -1088 883 -1086 385 -572 -RAW_Data: 889 -624 353 -1082 853 -1096 379 -594 853 -624 353 -1092 417 -582 847 -612 385 -1076 847 -1080 883 -1052 913 -1044 907 -1076 849 -1088 383 -602 867 -616 361 -1068 901 -1072 865 -1104 831 -1080 879 -1098 397 -586 855 -626 355 -1084 381 -592 873 -616 351 -1084 385 -624 821 -620 359 -1086 387 -584 883 -1086 877 -592 355 -1106 853 -1086 387 -69570 97 -100 99 -2620 131 -636 333 -102 235 -236 67 -68 363 -66 201 -100 567 -102 267 -164 101 -134 65 -68 197 -68 297 -166 671 -100 469 -336 165 -100 201 -66 169 -230 169 -204 329 -624 67 -98 265 -232 193 -168 299 -100 235 -138 101 -370 165 -294 333 -622 231 -130 129 -130 353 -132 195 -162 359 -164 67 -68 333 -100 133 -688 235 -236 497 -198 293 -98 129 -296 293 -164 229 -128 229 -132 193 -400 165 -66 163 -98 361 -164 355 -196 587 -164 131 -98 263 -554 99 -130 129 -130 191 -464 99 -132 67 -100 167 -604 329 -66 199 -68 133 -102 163 -66 2971 -132 785 -66 329 -96 323 -100 201 -136 301 -66 1959 -166 867 -134 467 -66 297 -100 835 -100 753 -166 165 -64 67 -370 335 -66 559 -232 165 -334 65 -162 129 -354 163 -64 131 -134 265 -300 263 -132 267 -296 327 -198 99 -132 535 -132 469 -866 231 -860 99 -232 503 -134 99 -198 233 -134 267 -200 97 -358 297 -164 259 -98 227 -166 135 -66 323 -100 97 -294 131 -164 129 -98 295 -96 129 -426 299 -100 67 -102 623 -100 163 -194 127 -360 563 -134 199 -428 493 -98 229 -130 257 -64 165 -100 131 -98 163 -692 357 -64 161 -98 321 -64 389 -230 65 -692 227 -130 261 -132 231 -162 287 -298 97 -460 393 -130 301 -168 331 -100 269 -202 101 -134 201 -102 99 -132 199 -204 235 -664 65 -562 133 -328 463 -100 291 -194 159 -162 227 -98 293 -328 165 -128 227 -574 535 -332 197 -168 65 -300 131 -66 389 -1078 131 -64 259 -64 223 -98 257 -164 63 -328 433 -134 65 -602 131 -68 333 -136 369 -66 297 -264 427 -66 97 -130 429 -102 133 -136 203 -240 167 -236 329 -526 67 -132 133 -168 331 -360 65 -66 331 -296 267 -134 469 -132 595 -230 661 -662 299 -100 265 -200 203 -168 801 -100 133 -68 399 -132 99 -100 161 -390 65 -298 65 -98 261 -130 161 -128 257 -66 67 -134 621 -98 227 -328 99 -230 129 -294 193 -96 195 -318 425 -526 129 -196 163 -162 65 -132 293 -130 63 -66 325 -128 63 -130 293 -66 199 -200 269 -206 133 -198 325 -98 163 -100 97 -98 261 -164 67 -98 167 -430 131 -494 131 -164 -RAW_Data: 97 -98 861 -66 1199 -166 231 -100 651 -166 197 -104 439 -98 131 -64 493 -98 883 -96 99 -98 3327 -66 131 -264 733 -134 2133 -166 131 -102 303 -136 535 -134 701 -98 355 -228 131 -202 99 -134 99 -100 791 -166 169 -202 671 -100 741 -100 263 -66 165 -68 935 -132 197 -198 673 -100 605 -66 1457 -98 1195 -166 2347 -134 505 -100 1469 -66 391 -100 229 -100 1171 -98 939 -100 459 -170 369 -134 231 -162 127 -98 95 -66 195 -98 195 -66 299 -100 331 -98 65 -232 369 -132 201 -68 167 -166 1481 -102 501 -160 1257 -66 2307 -64 623 -164 2079 -66 1101 -98 423 -64 659 -68 431 -136 99 -100 435 -130 167 -168 835 -200 135 -104 133 -100 503 -68 1437 -232 821 -132 357 -96 463 -66 263 -64 683 -132 165 -96 655 -166 3939 -100 1169 -132 2443 -98 197 -132 425 -234 233 -162 1043 -66 197 -100 2793 -134 167 -104 675 -100 197 -134 1367 -102 763 -132 265 -230 133 -102 365 -100 167 -66 1069 -66 837 -100 295 -160 97 -64 129 -132 617 -164 197 -100 133 -136 337 -172 133 -66 557 -98 951 -66 263 -130 587 -66 729 -196 335 -166 933 -432 369 -100 199 -296 225 -98 355 -66 129 -64 557 -98 289 -66 355 -128 193 -162 267 -134 299 -98 165 -170 303 -640 1031 -134 99 -66 135 -68 771 -166 171 -104 201 -134 131 -68 635 -428 661 -292 749 -430 1161 -100 905 -98 65 -98 657 -262 2837 -132 67 -66 265 -132 631 -66 1037 -296 97 -98 1703 -302 367 -100 505 -232 497 -362 333 -134 591 -100 755 -232 67 -130 587 -66 231 -168 65 -332 99 -66 267 -232 393 -134 65 -132 131 -428 133 -200 165 -202 199 -168 165 -102 269 -100 333 -852 201 -134 233 -202 65 -200 563 -768 265 -136 169 -102 169 -598 333 -202 267 -134 267 -328 163 -130 625 -500 199 -200 99 -270 65 -134 65 -198 65 -100 99 -596 493 -66 99 -66 331 -232 103 -136 373 -168 831 -170 65 -672 163 -102 133 -136 331 -100 333 -234 101 -100 99 -200 99 -100 201 -302 199 -600 301 -202 135 -134 705 -166 435 -530 97 -198 131 -198 195 -66 163 -392 293 -66 295 -370 229 -198 65 -100 405 -134 165 -134 133 -170 337 -236 205 -274 267 -134 329 -132 195 -132 503 -132 133 -136 133 -334 197 -196 299 -168 101 -100 233 -100 439 -134 301 -332 331 -298 433 -406 433 -68 167 -100 203 -100 101 -102 99 -328 397 -234 205 -168 133 -364 63 -202 397 -198 95 -394 267 -134 569 -66 201 -102 133 -136 101 -102 99 -132 99 -196 197 -498 197 -102 135 -170 -RAW_Data: 331 -164 63 -162 1267 -66 163 -130 129 -66 725 -164 231 -64 853 -66 101 -134 199 -102 99 -68 365 -66 357 -130 815 -64 357 -98 97 -98 97 -66 65 -466 231 -172 3749 -66 849 -130 917 -64 327 -64 1013 -98 555 -332 795 -100 571 -132 769 -132 401 -134 1297 -134 377 -138 435 -100 401 -100 667 -100 1761 -66 667 -66 1533 -236 233 -98 885 -130 457 -66 999 -66 165 -66 833 -134 695 -166 501 -66 499 -200 329 -64 197 -134 441 -100 2099 -98 491 -134 197 -130 2225 -132 65 -100 689 -64 193 -160 159 -96 195 -98 323 -164 259 -98 535 -472 771 -66 665 -270 665 -66 595 -266 2191 -64 643 -98 1287 -98 741 -100 233 -200 569 -194 261 -68 637 -100 97 -66 491 -158 395 -138 1017 -66 627 -262 559 -64 327 -98 263 -134 99 -102 201 -102 337 -66 167 -68 679 -100 471 -134 195 -66 133 -202 693 -96 197 -98 391 -164 99 -98 3883 -194 461 -100 237 -168 1891 -68 301 -68 969 -166 1439 -294 551 -130 389 -98 99 -196 167 -102 505 -66 569 -234 901 -98 407 -136 469 -66 769 -98 769 -166 1263 -266 297 -98 1701 -200 203 -168 329 -232 65 -100 329 -164 803 -100 135 -200 233 -166 135 -272 265 -134 197 -100 133 -134 539 -232 197 -396 165 -366 263 -68 233 -102 365 -132 233 -100 135 -266 199 -234 167 -232 97 -524 127 -128 389 -98 305 -364 261 -130 257 -162 589 -464 361 -66 229 -134 161 -100 203 -432 265 -66 199 -66 199 -366 229 -236 99 -134 99 -100 131 -168 133 -100 131 -236 267 -132 297 -264 291 -132 167 -234 65 -100 199 -66 333 -730 237 -440 365 -102 99 -100 99 -132 99 -100 1429 -134 427 -100 97 -100 131 -164 799 -170 1077 -100 431 -66 133 -168 737 -134 197 -230 65 -102 803 -132 491 -98 429 -198 471 -134 365 -66 299 -236 65 -66 2837 -102 399 -64 585 -64 523 -196 97 -98 295 -196 555 -160 261 -500 299 -396 333 -236 133 -68 327 -100 199 -204 699 -66 701 -100 65 -164 65 -370 195 -196 97 -66 193 -130 129 -360 195 -130 231 -96 291 -64 455 -228 293 -196 291 -162 97 -194 621 -130 847 -66 395 -66 161 -128 193 -130 293 -98 231 -170 67 -134 297 -360 167 -266 263 -526 263 -132 229 -98 191 -160 159 -100 721 -234 101 -100 99 -130 259 -258 265 -632 687 -164 133 -134 631 -100 199 -102 165 -560 299 -200 265 -332 431 -870 99 -266 503 -364 135 -66 269 -68 499 -100 265 -102 263 -102 569 -234 719 -132 99 -196 419 -262 163 -688 95 -66 165 -128 95 -66 -RAW_Data: 295 -98 987 -196 517 -100 489 -66 355 -132 563 -198 867 -134 1413 -134 541 -134 767 -100 193 -98 1799 -102 467 -134 299 -96 323 -66 261 -100 259 -66 229 -96 851 -66 369 -266 469 -66 101 -98 163 -136 267 -432 859 -130 523 -66 197 -134 1027 -132 227 -194 393 -98 807 -166 235 -100 133 -66 165 -102 133 -136 371 -162 1411 -132 865 -200 471 -100 133 -68 299 -66 633 -98 329 -234 401 -98 1505 -132 133 -134 331 -262 163 -66 261 -98 289 -64 201 -68 1055 -96 391 -66 951 -298 265 -202 297 -66 401 -68 131 -100 1733 -98 941 -66 803 -98 847 -64 3701 -100 721 -160 357 -166 1799 -66 329 -100 99 -102 363 -198 167 -136 197 -66 567 -66 199 -236 1247 -166 2455 -68 1107 -200 235 -100 2355 -130 913 -98 877 -98 163 -196 97 -66 427 -100 801 -134 867 -98 263 -68 441 -134 561 -98 1671 -134 865 -68 935 -132 163 -102 975 -66 1343 -132 1339 -134 369 -100 1107 -66 1167 -168 631 -232 835 -66 1027 -132 333 -166 265 -98 1207 -98 223 -98 455 -64 2095 -134 933 -136 233 -68 335 -136 305 -100 1737 -66 427 -100 263 -130 323 -66 227 -66 717 -100 265 -100 65 -128 355 -66 367 -132 95 -230 229 -100 131 -64 493 -132 291 -396 393 -130 259 -196 227 -288 397 -68 229 -430 99 -302 237 -700 65 -66 65 -100 133 -200 101 -336 133 -166 237 -202 67 -302 67 -68 333 -132 263 -102 267 -296 163 -166 233 -168 363 -64 295 -298 537 -166 431 -200 431 -166 63 -258 363 -164 563 -234 199 -68 299 -100 325 -754 295 -196 65 -98 165 -132 301 -134 131 -134 97 -68 405 -68 233 -134 271 -134 67 -168 101 -136 133 -366 99 -132 67 -132 265 -200 233 -100 201 -136 101 -66 263 -132 129 -66 293 -582 263 -132 1103 -134 203 -168 97 -66 197 -264 131 -168 133 -132 65 -134 199 -134 101 -100 131 -436 99 -232 97 -398 231 -362 65 -202 301 -396 297 -98 199 -134 265 -164 101 -168 267 -102 405 -170 99 -102 397 -132 97 -98 295 -98 1179 -100 135 -136 131 -134 765 -134 465 -168 439 -232 403 -100 65 -134 931 -100 169 -136 237 -68 231 -234 199 -68 401 -134 541 -166 429 -166 1607 -368 533 -66 363 -66 133 -134 433 -166 297 -238 201 -100 201 -170 199 -134 273 -136 99 -134 167 -238 133 -66 265 -134 165 -132 165 -132 97 -228 723 -198 415 -64 491 -298 257 -66 231 -192 225 -96 227 -98 193 -96 521 -198 65 -66 231 -166 163 -98 465 -66 133 -132 195 -130 225 -162 521 -130 63 -66 199 -228 -RAW_Data: 817 -162 449 -160 719 -198 469 -68 133 -68 1101 -132 593 -230 1105 -100 131 -134 231 -66 329 -196 685 -96 557 -68 1263 -68 101 -68 397 -100 65 -66 625 -66 97 -132 1099 -66 493 -66 757 -98 1151 -66 303 -134 1901 -66 99 -100 665 -262 991 -98 791 -66 1925 -168 865 -232 835 -98 505 -102 99 -100 535 -100 169 -134 427 -132 863 -68 167 -134 975 -100 133 -268 1339 -100 1453 -66 1445 -162 195 -64 3623 -66 237 -68 1063 -308 1449 -98 1111 -132 167 -102 855 -270 199 -134 297 -134 267 -168 863 -234 637 -66 567 -230 99 -200 3325 -198 845 -66 289 -66 131 -66 815 -130 1093 -100 167 -100 429 -98 1703 -166 195 -64 971 -98 163 -192 195 -168 439 -132 329 -132 67 -134 67 -134 1591 -168 407 -100 867 -68 399 -134 661 -100 663 -66 237 -136 395 -232 131 -66 695 -100 627 -264 913 -66 1083 -98 287 -66 199 -132 335 -100 1031 -68 99 -100 3815 -98 165 -66 129 -98 163 -128 563 -98 779 -96 223 -64 161 -164 2025 -66 1741 -172 101 -136 203 -102 665 -100 475 -64 167 -100 637 -98 997 -170 1207 -136 233 -166 233 -168 635 -132 199 -100 235 -270 199 -98 131 -102 169 -170 293 -98 323 -164 427 -334 233 -168 267 -68 369 -100 263 -368 101 -66 665 -98 265 -100 133 -100 99 -168 133 -66 133 -132 133 -66 269 -134 435 -68 267 -136 271 -500 163 -100 163 -166 355 -132 97 -98 323 -194 63 -688 463 -130 97 -396 65 -100 357 -194 461 -98 161 -130 223 -162 165 -352 461 -300 267 -166 233 -464 329 -100 293 -362 163 -228 289 -66 229 -66 195 -162 325 -66 261 -98 127 -424 299 -302 367 -68 265 -272 429 -98 161 -98 393 -296 65 -130 161 -196 261 -66 473 -234 97 -98 263 -160 323 -98 67 -132 697 -298 99 -134 233 -202 97 -134 301 -200 307 -100 101 -134 865 -166 231 -202 233 -100 301 -170 169 -102 169 -200 65 -98 595 -166 231 -234 661 -66 473 -334 165 -304 365 -266 97 -502 363 -134 133 -236 65 -100 99 -134 99 -170 235 -66 333 -100 195 -100 133 -300 133 -102 301 -304 65 -100 99 -100 131 -202 135 -134 65 -200 363 -66 263 -498 67 -68 295 -194 321 -368 435 -100 97 -664 99 -100 569 -66 133 -66 67 -134 199 -136 101 -68 301 -68 405 -198 133 -132 581 -132 165 -98 159 -98 197 -66 229 -130 131 -294 133 -96 423 -100 427 -300 357 -132 291 -64 95 -194 455 -98 263 -100 359 -196 65 -162 227 -162 157 -96 157 -230 589 -132 325 -134 535 -66 267 -100 135 -302 -RAW_Data: 131 -134 599 -166 393 -98 369 -236 197 -100 401 -232 569 -134 135 -70 337 -134 101 -136 135 -100 1895 -66 401 -170 503 -66 1633 -66 601 -66 355 -96 683 -100 729 -68 133 -132 433 -68 569 -100 133 -68 201 -132 835 -100 465 -68 527 -98 193 -200 1129 -166 535 -100 199 -98 259 -132 227 -64 1597 -98 261 -192 753 -100 911 -66 667 -298 131 -100 263 -66 1051 -230 787 -66 935 -66 233 -98 885 -236 431 -66 197 -162 521 -68 167 -196 263 -96 589 -98 517 -66 1439 -64 777 -66 3219 -132 679 -134 205 -68 507 -198 749 -200 199 -168 167 -100 133 -134 201 -68 731 -66 495 -198 737 -66 237 -68 135 -100 167 -234 1535 -68 873 -66 373 -66 67 -232 297 -68 65 -66 1095 -68 327 -130 63 -132 1715 -66 2261 -100 321 -132 197 -164 457 -232 1291 -132 405 -68 1001 -68 1133 -272 471 -66 99 -134 1403 -68 167 -68 1091 -336 933 -134 1207 -132 265 -68 267 -66 99 -366 265 -66 1469 -258 367 -168 429 -132 129 -66 491 -132 343 -100 65 -100 263 -136 199 -164 273 -204 791 -100 901 -66 167 -98 165 -64 559 -132 619 -132 1087 -128 2283 -398 1467 -164 259 -130 1927 -130 421 -98 1085 -66 705 -68 1843 -168 875 -170 203 -136 341 -640 199 -66 133 -554 161 -196 63 -66 521 -292 163 -160 95 -158 127 -192 197 -100 587 -130 397 -662 261 -66 193 -130 259 -66 361 -64 459 -98 197 -560 655 -130 389 -66 1135 -100 133 -130 131 -98 1011 -100 561 -66 685 -164 457 -132 2469 -200 609 -66 665 -66 67 -132 327 -200 1657 -134 919 -132 651 -100 327 -230 191 -130 263 -358 95 -130 549 -98 99 -68 299 -100 461 -132 99 -472 165 -134 99 -66 99 -132 399 -102 169 -102 697 -166 233 -132 333 -632 197 -164 865 -266 101 -68 533 -166 299 -100 163 -228 259 -66 327 -200 65 -66 229 -100 363 -230 197 -336 165 -102 893 -300 65 -132 231 -370 265 -230 99 -98 229 -518 199 -100 401 -724 225 -98 63 -96 231 -64 291 -292 65 -98 131 -98 159 -158 127 -194 161 -292 65 -98 133 -66 297 -66 303 -168 97 -168 231 -234 269 -532 135 -168 99 -168 301 -528 99 -506 199 -368 399 -132 329 -372 99 -68 133 -264 197 -100 201 -200 67 -134 131 -270 133 -134 133 -198 327 -200 65 -100 331 -262 161 -166 469 -534 167 -738 131 -100 367 -232 101 -100 265 -604 65 -170 99 -166 299 -102 169 -132 99 -398 229 -330 197 -166 335 -366 97 -98 131 -200 269 -100 199 -168 131 -134 537 -98 265 -100 335 -236 99 -366 -RAW_Data: 459 -100 453 -130 419 -130 519 -96 63 -130 2077 -66 767 -64 127 -134 1961 -296 529 -202 637 -134 527 -100 201 -68 633 -66 163 -360 1029 -68 765 -100 867 -66 503 -100 131 -66 841 -98 165 -68 237 -66 509 -100 501 -302 235 -66 99 -164 227 -130 551 -196 327 -66 1571 -132 99 -68 867 -66 163 -96 161 -130 129 -130 549 -130 487 -166 1801 -66 229 -66 197 -232 325 -66 425 -198 131 -64 295 -166 735 -66 533 -98 227 -130 129 -262 425 -100 263 -66 129 -132 97 -168 971 -170 405 -68 199 -134 475 -202 297 -98 1445 -98 395 -196 161 -66 225 -134 1803 -100 473 -102 1499 -66 199 -100 701 -132 165 -68 133 -102 303 -98 735 -102 805 -100 827 -100 235 -100 65 -266 637 -68 693 -66 1383 -228 819 -66 233 -304 435 -198 203 -136 1135 -270 1709 -64 227 -64 581 -134 505 -66 2203 -64 293 -64 753 -66 551 -132 747 -64 1303 -64 463 -66 229 -102 1877 -266 871 -166 1357 -64 819 -66 465 -198 693 -68 165 -64 95 -128 3785 -132 1465 -100 299 -102 329 -164 595 -134 1029 -66 299 -168 1263 -166 331 -68 967 -100 101 -102 603 -260 165 -132 467 -66 233 -66 235 -102 475 -100 135 -68 301 -134 297 -98 131 -102 269 -466 99 -134 237 -166 135 -168 203 -102 265 -68 503 -66 233 -66 637 -134 101 -200 199 -166 293 -554 361 -328 367 -264 533 -238 167 -68 135 -170 99 -300 591 -298 133 -236 299 -66 231 -368 263 -232 435 -136 133 -102 133 -200 133 -134 163 -134 167 -168 299 -66 265 -100 133 -240 135 -132 263 -170 269 -200 501 -396 263 -98 227 -132 129 -292 427 -66 165 -102 627 -602 99 -66 301 -168 199 -100 563 -330 165 -134 233 -136 65 -332 499 -100 131 -232 325 -96 65 -132 195 -98 393 -624 323 -68 133 -98 195 -162 231 -100 263 -132 231 -102 133 -236 99 -236 231 -166 65 -102 133 -268 101 -102 299 -136 267 -164 493 -64 229 -258 291 -326 263 -198 391 -134 167 -202 365 -594 133 -102 201 -134 503 -396 429 -204 169 -400 197 -170 267 -132 403 -466 297 -98 469 -234 395 -132 233 -100 165 -100 165 -66 197 -68 297 -166 501 -134 133 -100 65 -166 631 -68 297 -134 199 -100 165 -68 299 -266 133 -66 165 -100 231 -490 557 -134 371 -164 299 -170 733 -164 239 -334 335 -66 299 -300 199 -170 103 -100 233 -102 641 -168 65 -100 995 -66 265 -160 259 -130 129 -226 425 -100 355 -726 97 -688 99 -66 233 -266 299 -942 167 -102 167 -166 65 -100 367 -136 99 -134 199 -134 267 -164 -RAW_Data: 67 -68 233 -66 899 -66 163 -96 485 -98 355 -130 943 -100 235 -168 499 -104 1367 -98 297 -100 635 -68 1169 -100 67 -134 835 -264 959 -164 129 -98 419 -196 589 -66 421 -66 1717 -100 133 -100 265 -134 227 -356 455 -166 163 -66 1055 -100 1455 -134 463 -98 2191 -132 295 -132 335 -66 709 -64 619 -98 959 -68 835 -170 603 -134 1033 -134 635 -168 759 -232 397 -198 397 -164 1267 -166 257 -198 1295 -100 239 -104 563 -204 335 -198 203 -68 901 -68 1255 -134 1697 -66 793 -66 1691 -68 201 -100 765 -66 165 -132 131 -230 131 -66 917 -66 335 -338 231 -170 827 -98 199 -136 301 -196 65 -98 199 -200 765 -134 403 -98 333 -68 1691 -132 2565 -64 569 -170 1255 -264 65 -132 1243 -132 2527 -66 259 -66 1739 -100 1309 -198 167 -238 337 -66 131 -68 1973 -362 299 -100 1387 -96 129 -164 423 -230 3875 -96 4283 -98 165 -98 515 -134 469 -68 171 -102 1163 -100 65 -298 461 -66 367 -136 205 -168 371 -98 491 -164 161 -262 1093 -100 299 -100 269 -334 1205 -98 63 -98 261 -64 457 -98 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 7d342bb93..a6a3c9866 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -160,3 +160,9 @@ RAW_Data: 817 -162 449 -160 719 -198 469 -68 133 -68 1101 -132 593 -230 1105 -10 RAW_Data: 131 -134 599 -166 393 -98 369 -236 197 -100 401 -232 569 -134 135 -70 337 -134 101 -136 135 -100 1895 -66 401 -170 503 -66 1633 -66 601 -66 355 -96 683 -100 729 -68 133 -132 433 -68 569 -100 133 -68 201 -132 835 -100 465 -68 527 -98 193 -200 1129 -166 535 -100 199 -98 259 -132 227 -64 1597 -98 261 -192 753 -100 911 -66 667 -298 131 -100 263 -66 1051 -230 787 -66 935 -66 233 -98 885 -236 431 -66 197 -162 521 -68 167 -196 263 -96 589 -98 517 -66 1439 -64 777 -66 3219 -132 679 -134 205 -68 507 -198 749 -200 199 -168 167 -100 133 -134 201 -68 731 -66 495 -198 737 -66 237 -68 135 -100 167 -234 1535 -68 873 -66 373 -66 67 -232 297 -68 65 -66 1095 -68 327 -130 63 -132 1715 -66 2261 -100 321 -132 197 -164 457 -232 1291 -132 405 -68 1001 -68 1133 -272 471 -66 99 -134 1403 -68 167 -68 1091 -336 933 -134 1207 -132 265 -68 267 -66 99 -366 265 -66 1469 -258 367 -168 429 -132 129 -66 491 -132 343 -100 65 -100 263 -136 199 -164 273 -204 791 -100 901 -66 167 -98 165 -64 559 -132 619 -132 1087 -128 2283 -398 1467 -164 259 -130 1927 -130 421 -98 1085 -66 705 -68 1843 -168 875 -170 203 -136 341 -640 199 -66 133 -554 161 -196 63 -66 521 -292 163 -160 95 -158 127 -192 197 -100 587 -130 397 -662 261 -66 193 -130 259 -66 361 -64 459 -98 197 -560 655 -130 389 -66 1135 -100 133 -130 131 -98 1011 -100 561 -66 685 -164 457 -132 2469 -200 609 -66 665 -66 67 -132 327 -200 1657 -134 919 -132 651 -100 327 -230 191 -130 263 -358 95 -130 549 -98 99 -68 299 -100 461 -132 99 -472 165 -134 99 -66 99 -132 399 -102 169 -102 697 -166 233 -132 333 -632 197 -164 865 -266 101 -68 533 -166 299 -100 163 -228 259 -66 327 -200 65 -66 229 -100 363 -230 197 -336 165 -102 893 -300 65 -132 231 -370 265 -230 99 -98 229 -518 199 -100 401 -724 225 -98 63 -96 231 -64 291 -292 65 -98 131 -98 159 -158 127 -194 161 -292 65 -98 133 -66 297 -66 303 -168 97 -168 231 -234 269 -532 135 -168 99 -168 301 -528 99 -506 199 -368 399 -132 329 -372 99 -68 133 -264 197 -100 201 -200 67 -134 131 -270 133 -134 133 -198 327 -200 65 -100 331 -262 161 -166 469 -534 167 -738 131 -100 367 -232 101 -100 265 -604 65 -170 99 -166 299 -102 169 -132 99 -398 229 -330 197 -166 335 -366 97 -98 131 -200 269 -100 199 -168 131 -134 537 -98 265 -100 335 -236 99 -366 RAW_Data: 459 -100 453 -130 419 -130 519 -96 63 -130 2077 -66 767 -64 127 -134 1961 -296 529 -202 637 -134 527 -100 201 -68 633 -66 163 -360 1029 -68 765 -100 867 -66 503 -100 131 -66 841 -98 165 -68 237 -66 509 -100 501 -302 235 -66 99 -164 227 -130 551 -196 327 -66 1571 -132 99 -68 867 -66 163 -96 161 -130 129 -130 549 -130 487 -166 1801 -66 229 -66 197 -232 325 -66 425 -198 131 -64 295 -166 735 -66 533 -98 227 -130 129 -262 425 -100 263 -66 129 -132 97 -168 971 -170 405 -68 199 -134 475 -202 297 -98 1445 -98 395 -196 161 -66 225 -134 1803 -100 473 -102 1499 -66 199 -100 701 -132 165 -68 133 -102 303 -98 735 -102 805 -100 827 -100 235 -100 65 -266 637 -68 693 -66 1383 -228 819 -66 233 -304 435 -198 203 -136 1135 -270 1709 -64 227 -64 581 -134 505 -66 2203 -64 293 -64 753 -66 551 -132 747 -64 1303 -64 463 -66 229 -102 1877 -266 871 -166 1357 -64 819 -66 465 -198 693 -68 165 -64 95 -128 3785 -132 1465 -100 299 -102 329 -164 595 -134 1029 -66 299 -168 1263 -166 331 -68 967 -100 101 -102 603 -260 165 -132 467 -66 233 -66 235 -102 475 -100 135 -68 301 -134 297 -98 131 -102 269 -466 99 -134 237 -166 135 -168 203 -102 265 -68 503 -66 233 -66 637 -134 101 -200 199 -166 293 -554 361 -328 367 -264 533 -238 167 -68 135 -170 99 -300 591 -298 133 -236 299 -66 231 -368 263 -232 435 -136 133 -102 133 -200 133 -134 163 -134 167 -168 299 -66 265 -100 133 -240 135 -132 263 -170 269 -200 501 -396 263 -98 227 -132 129 -292 427 -66 165 -102 627 -602 99 -66 301 -168 199 -100 563 -330 165 -134 233 -136 65 -332 499 -100 131 -232 325 -96 65 -132 195 -98 393 -624 323 -68 133 -98 195 -162 231 -100 263 -132 231 -102 133 -236 99 -236 231 -166 65 -102 133 -268 101 -102 299 -136 267 -164 493 -64 229 -258 291 -326 263 -198 391 -134 167 -202 365 -594 133 -102 201 -134 503 -396 429 -204 169 -400 197 -170 267 -132 403 -466 297 -98 469 -234 395 -132 233 -100 165 -100 165 -66 197 -68 297 -166 501 -134 133 -100 65 -166 631 -68 297 -134 199 -100 165 -68 299 -266 133 -66 165 -100 231 -490 557 -134 371 -164 299 -170 733 -164 239 -334 335 -66 299 -300 199 -170 103 -100 233 -102 641 -168 65 -100 995 -66 265 -160 259 -130 129 -226 425 -100 355 -726 97 -688 99 -66 233 -266 299 -942 167 -102 167 -166 65 -100 367 -136 99 -134 199 -134 267 -164 RAW_Data: 67 -68 233 -66 899 -66 163 -96 485 -98 355 -130 943 -100 235 -168 499 -104 1367 -98 297 -100 635 -68 1169 -100 67 -134 835 -264 959 -164 129 -98 419 -196 589 -66 421 -66 1717 -100 133 -100 265 -134 227 -356 455 -166 163 -66 1055 -100 1455 -134 463 -98 2191 -132 295 -132 335 -66 709 -64 619 -98 959 -68 835 -170 603 -134 1033 -134 635 -168 759 -232 397 -198 397 -164 1267 -166 257 -198 1295 -100 239 -104 563 -204 335 -198 203 -68 901 -68 1255 -134 1697 -66 793 -66 1691 -68 201 -100 765 -66 165 -132 131 -230 131 -66 917 -66 335 -338 231 -170 827 -98 199 -136 301 -196 65 -98 199 -200 765 -134 403 -98 333 -68 1691 -132 2565 -64 569 -170 1255 -264 65 -132 1243 -132 2527 -66 259 -66 1739 -100 1309 -198 167 -238 337 -66 131 -68 1973 -362 299 -100 1387 -96 129 -164 423 -230 3875 -96 4283 -98 165 -98 515 -134 469 -68 171 -102 1163 -100 65 -298 461 -66 367 -136 205 -168 371 -98 491 -164 161 -262 1093 -100 299 -100 269 -334 1205 -98 63 -98 261 -64 457 -98 +RAW_Data: 57 -144 401 -86 173 -202 143 -258 133 -88 257 -144 287 -58 402 -56 259 -230 259 -86 85 -96 95 -174 286 -162 57 -230 253 -400 229 -88 536 -58 85 -72 167 -110 263 -72 229 -58 85 -86 87 -262 119 -288 163 -210 321 -320 186 -140 261 -96 143 -456 117 -216 143 -246 239 -102 121 -72 71 -191 167 -263 191 -96 239 -80 57 -116 143 -118 167 -168 215 -288 191 -106 287 -114 517 -88 113 -394 173 -70 215 -100 661 -86 201 -114 259 -58 287 -86 57 -202 399 -200 57 -288 229 -144 115 -1425 83 -142 173 -86 459 -112 223 -144 201 -116 143 -114 196 -17422 287 -614 1075 -1132 533 -560 1125 -1112 561 -530 1133 -1088 585 -512 1129 -1104 563 -1102 571 -1072 591 -524 1137 -538 1115 -19392 589 -522 1139 -1090 569 -520 1137 -1100 587 -520 1135 -1092 569 -514 1155 -1076 599 -1062 585 -1086 585 -532 1125 -528 1145 -19396 581 -504 1155 -1064 619 -506 1145 -1080 595 -508 1149 -1062 587 -536 1129 -1088 585 -1090 589 -1062 587 -510 1177 -506 1149 -19394 587 -524 1147 -1076 597 -512 1129 -1072 617 -498 1159 -1066 599 -514 1147 -1076 595 -1060 613 -1060 589 -506 1179 -510 1155 -19378 605 -514 1129 -1098 601 -488 1177 -1062 583 -510 1161 -1072 587 -516 1169 -1058 587 -1082 589 -1076 593 -532 1121 -536 1127 -19444 549 -542 1129 -1092 601 -510 1147 -1098 581 -506 1123 -1100 585 -510 1147 -1106 591 -1060 597 -1060 595 -510 1151 -534 1157 -19390 593 -512 1163 -1064 597 -510 1173 -1042 617 -510 1159 -1056 579 -556 1133 -1054 609 -1060 607 -1078 585 -528 1147 -516 1135 -19444 559 -534 1165 -1052 605 -510 1149 -1062 599 -520 1173 -1054 591 -540 1111 -1116 573 -1088 593 -1042 615 -534 1117 -536 1129 -19436 569 -530 1157 -1052 605 -504 1177 -1062 591 -536 1113 -1114 589 -514 1125 -1112 563 -1084 587 -1080 589 -536 1123 -532 1165 -19392 599 -524 1143 -1080 595 -540 1125 -1090 563 -534 1149 -1084 567 -534 1147 -1092 587 -1086 585 -1064 565 -558 1123 -532 1143 -19450 549 -556 1121 -1086 585 -534 1137 -1094 583 -516 1133 -1114 563 -536 1123 -1108 573 -1082 597 -1078 565 -532 1143 -524 1135 -19464 561 -528 1149 -1066 613 -508 1151 -1076 587 -532 1125 -1076 609 -506 1175 -1076 563 -1106 563 -1084 591 -534 1157 -484 1179 -19414 589 -528 1131 -1096 587 -520 1163 -1080 563 -550 1121 -1098 555 -562 1117 -1082 575 -1112 563 -1104 559 -558 1121 -530 1149 -19442 591 -498 1175 -1066 585 -534 1121 -1114 565 -540 1115 -1096 585 -538 1123 -1110 559 -1086 585 -1084 589 -530 1125 -558 1121 -19454 563 -542 1163 -1044 585 -558 1131 -1092 571 -536 1133 -1088 587 -518 1149 -1086 601 -1058 587 -1080 611 -510 +RAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 1141 -1090 587 -516 1121 -1114 591 -1062 595 -1092 567 -538 1131 -536 1145 -19448 589 -516 1143 -1076 591 -540 1115 -1090 591 -538 1115 -1096 597 -512 1155 -1078 581 -1082 585 -1104 559 -530 1153 -516 1147 -19438 589 -526 1157 -1056 609 -534 1137 -1078 585 -532 1149 -1076 585 -536 1123 -1104 587 -1062 597 -1082 587 -536 1121 -554 1121 -19464 555 -560 1123 -1104 563 -542 1131 -1096 565 -536 1143 -1060 621 -492 1175 -1078 571 -1100 577 -1092 591 -494 1167 -538 1117 -19452 593 -538 1133 -1074 603 -510 1157 -1062 595 -548 1115 -1102 573 -538 1139 -1094 567 -1094 583 -1092 567 -536 1127 -560 1113 -19478 563 -534 1151 -1084 569 -534 1135 -1116 565 -534 1135 -1084 583 -532 1155 -1060 587 -1084 589 -1084 581 -536 1125 -546 1141 -19460 577 -536 1123 -1102 565 -554 1119 -1096 593 -520 1133 -1108 563 -546 1131 -1088 569 -1094 573 -1100 583 -526 1147 -540 1123 -19466 587 -530 1129 -1100 587 -510 1143 -1084 609 -510 1131 -1110 567 -542 1121 -1102 567 -1110 563 -1106 567 -532 1149 -518 1145 -19458 573 -534 1147 -1082 575 -556 1149 -1048 589 -532 1157 -1088 565 -536 1129 -1088 589 -1106 563 -1086 587 -536 1129 -548 1147 -4298 103 -56 87 -344 87 -258 85 -278 215 -70 77 -344 279 -56 87 -56 197 -198 143 -288 362 -86 605 -144 57 -268 95 -114 143 -144 173 -144 143 -402 57 -202 259 -391 652 -340 427 -230 173 -356 97 -144 111 -246 219 -96 191 -114 173 -288 115 -56 109 -106 199 -106 73 -130 57 -172 85 -260 373 -56 629 -200 690 -230 273 -120 85 -460 85 -314 77 -78 111 -88 401 -116 171 -312 71 -500 81 -224 229 -88 257 -370 181 -172 200 -116 535 -174 113 -294 213 -359 445 -144 258 -114 115 -202 675 -509 239 -432 373 -538 85 -58 113 -86 761 -104 113 -318 443 -70 143 -144 647 -204 111 -334 87 -114 115 -144 113 -188 177 -144 199 -260 143 -86 87 -622 57 -116 171 -58 139 -222 55 -346 315 -76 345 -114 139 -171 195 -52 53 -98 119 -144 143 -244 95 -72 95 -96 167 -302 253 -186 307 -444 287 -449 115 -172 57 -172 316 -202 85 -370 697 -116 57 -144 171 -202 259 -114 85 -144 87 -315 85 -58 201 -116 171 -272 121 -358 171 -403 113 -86 115 -202 489 -229 115 -392 95 -116 171 -140 93 -102 143 -543 245 -358 215 -120 387 -288 171 -202 221 -202 115 -748 57 -316 143 -260 143 -288 115 -316 115 -58 85 -288 143 -460 485 -96 71 -104 199 -96 199 -202 143 -86 201 -116 85 -230 211 -288 115 -605 365 -126 53 -172 +RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477 +RAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100 +RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86 +RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144 \ No newline at end of file diff --git a/buildRelease.sh b/buildRelease.sh old mode 100644 new mode 100755 index 60dcc8579..1190e2b5d --- a/buildRelease.sh +++ b/buildRelease.sh @@ -1,6 +1,12 @@ +rm -rf RM*-*-*.tgz RM*-*-*.zip .sconsign.dblite dist build assets/resources/apps git pull ./fbt updater_package DATE_VAR=`date +%m%d` TIME_VAR=`date +%H%M` VER_VAR=`cat scripts/version.py | awk '/VERSION/{ gsub(/[",]/,"",$2); print $2}' | tail -1` -mv dist/f7-C/flipper-z-f7-update-RM420FAP.tgz "$VER_VAR-$DATE_VAR-RM-$TIME_VAR.tgz" +HASH_VAR=`git rev-parse \`git branch -r --sort=committerdate | tail -1\` | awk '{print substr($0,1,7)}' | tail -1` +mv dist/f7-C/f7-update-RM420FAP "$VER_VAR-$DATE_VAR-RM$TIME_VAR" +zip -r "RM$DATE_VAR$TIME_VAR-$VER_VAR-$HASH_VAR.zip" "$VER_VAR-$DATE_VAR-RM$TIME_VAR" +tar -czvf "RM$DATE_VAR$TIME_VAR-$VER_VAR-$HASH_VAR.tgz" "$VER_VAR-$DATE_VAR-RM$TIME_VAR" +rm -rf "$VER_VAR-$DATE_VAR-RM$TIME_VAR" +echo " BUILD COMPLETED, ZIP AND TGZ GENERATED" diff --git a/debug/flipperapps.py b/debug/flipperapps.py index 8e1aa2daf..e815e40b1 100644 --- a/debug/flipperapps.py +++ b/debug/flipperapps.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Tuple, Dict +from typing import Optional, Tuple, Dict, ClassVar import struct import posixpath import os @@ -22,14 +22,18 @@ class AppState: debug_link_elf: str = "" debug_link_crc: int = 0 + DEBUG_ELF_ROOT: ClassVar[Optional[str]] = None + def __post_init__(self): if self.other_sections is None: self.other_sections = {} - def get_original_elf_path(self, elf_path="build/latest/.extapps") -> str: + def get_original_elf_path(self) -> str: + if self.DEBUG_ELF_ROOT is None: + raise ValueError("DEBUG_ELF_ROOT not set; call fap-set-debug-elf-root") return ( - posixpath.join(elf_path, self.debug_link_elf) - if elf_path + posixpath.join(self.DEBUG_ELF_ROOT, self.debug_link_elf) + if self.DEBUG_ELF_ROOT else self.debug_link_elf ) @@ -84,7 +88,9 @@ class AppState: if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]): debug_link_data = ( gdb.selected_inferior() - .read_memory(int(app_state["debug_link_info"]["debug_link"]), debug_link_size) + .read_memory( + int(app_state["debug_link_info"]["debug_link"]), debug_link_size + ) .tobytes() ) state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( @@ -103,6 +109,29 @@ class AppState: return state +class SetFapDebugElfRoot(gdb.Command): + """Set path to original ELF files for debug info""" + + def __init__(self): + super().__init__( + "fap-set-debug-elf-root", gdb.COMMAND_FILES, gdb.COMPLETE_FILENAME + ) + self.dont_repeat() + + def invoke(self, arg, from_tty): + AppState.DEBUG_ELF_ROOT = arg + try: + global helper + print(f"Set '{arg}' as debug info lookup path for Flipper external apps") + helper.attach_fw() + gdb.events.stop.connect(helper.handle_stop) + except gdb.error as e: + print(f"Support for Flipper external apps debug is not available: {e}") + + +SetFapDebugElfRoot() + + class FlipperAppDebugHelper: def __init__(self): self.app_ptr = None @@ -149,9 +178,4 @@ class FlipperAppDebugHelper: helper = FlipperAppDebugHelper() -try: - helper.attach_fw() - print("Support for Flipper external apps debug is enabled") - gdb.events.stop.connect(helper.handle_stop) -except gdb.error as e: - print(f"Support for Flipper external apps debug is not available: {e}") +print("Support for Flipper external apps debug is loaded") diff --git a/documentation/.gitignore b/documentation/.gitignore new file mode 100644 index 000000000..c18ff03bb --- /dev/null +++ b/documentation/.gitignore @@ -0,0 +1,2 @@ +/html +/latex \ No newline at end of file diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index 7943540eb..c156c0698 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -40,6 +40,7 @@ Only 2 parameters are mandatory: ***appid*** and ***apptype***, others are optio * **icon**: Animated icon name from built-in assets to be used when building app as a part of firmware. * **order**: Order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. *Used for ordering startup hooks and menu entries.* * **sdk_headers**: List of C header files from this app's code to include in API definitions for external applications. +* **targets**: list of strings, target names, which this application is compatible with. If not specified, application is built for all targets. Default value is `["all"]`. #### Parameters for external applications @@ -76,10 +77,10 @@ Each library is defined as a call to `Lib()` function, accepting the following p - **name**: name of library's folder. Required. - **fap_include_paths**: list of library's relative paths to add to parent fap's include path list. Default value is `["."]` meaning library's source root. - - **sources**: list of filename masks to be used for gathering include files for this library. Default value is `["*.c*"]`. + - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to library's source root. Default value is `["*.c*"]`. - **cflags**: list of additional compiler flags to be used for building this library. Default value is `[]`. - **cdefines**: list of additional preprocessor definitions to be used for building this library. Default value is `[]`. - - **cincludes**: list of additional include paths to be used for building this library. Can be used for providing external search paths for this library's code - for configuration headers. Default value is `[]`. + - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to application's root. Can be used for providing external search paths for this library's code - for configuration headers. Default value is `[]`. Example for building an app with a private library: diff --git a/documentation/CustomFlipperName.md b/documentation/CustomFlipperName.md index 666d2d7d4..1111693bd 100644 --- a/documentation/CustomFlipperName.md +++ b/documentation/CustomFlipperName.md @@ -6,7 +6,7 @@ 2. Follow how to build instructions to prepare all things before continuing 3. Run release build to verify all is ok - `./fbt COMPACT=1 DEBUG=0 updater_package` 4. Clear build files - `./fbt COMPACT=1 DEBUG=0 updater_package -c` -5. Run command with extra enviroment var before `./fbt` that variable should contain your custom name in alphanumeric characters - max length 8 chars +5. Run command with extra environment var before `./fbt` that variable should contain your custom name in alphanumeric characters - max length 8 chars `CUSTOM_FLIPPER_NAME=Name ./fbt COMPACT=1 DEBUG=0 updater_package` - where `Name` write your custom name 6. Copy `dist/f7-C/f7-update-local` folder to microSD `update/myfw/` and run `update` file on flipper from file manager app (Archive) 7. Flash from microSD card only!!!! .dfu update from qFlipper will not work properly since name and serial number will be changed diff --git a/documentation/Doxyfile b/documentation/Doxyfile index 6d6bb8aa8..1824e5a52 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -872,12 +872,9 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = applications \ - core \ - lib/infrared \ - lib/subghz \ - lib/toolbox \ - lib/onewire \ - firmware/targets/furi_hal_include + lib \ + firmware \ + furi # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -930,7 +927,18 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = \ + lib/mlib \ + lib/STM32CubeWB \ + lib/littlefs \ + lib/nanopb \ + assets/protobuf \ + lib/libusb_stm32 \ + lib/FreeRTOS-Kernel \ + lib/microtar \ + lib/mbedtls \ + lib/cxxheaderparser \ + applications/plugins/dap_link/lib/free-dap # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index 5ccb129ad..06f5fbc7e 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -28,6 +28,39 @@

+## With Android mobile app (with .tgz download) + +- Be sure you updated to latest official release before(if installing for the first time), and verify that microSD card is installed +- Open latest release page - [Releases](https://github.com/DarkFlippers/unleashed-firmware/releases/latest) +- Download `flipper-z-f7-update-(version).tgz` +- In flipper app click `Update channel` button, select `Custom` +- Select downloaded `.tgz` file +- Click Update +- Wait until update is finished +- And if all flashed successfully - you will have all needed assets pre installed +- Done + +![andro_tgz](https://user-images.githubusercontent.com/10697207/197042029-a5824787-08bc-4fd8-93ee-b7faff082c54.jpg) + +
+
+ +## With Android mobile app (via web updater link) + +- Be sure you updated to latest official release before(if installing for the first time), and verify that microSD card is installed +- Open latest release page - [Releases](https://github.com/DarkFlippers/unleashed-firmware/releases/latest) +- Click `Install via Web Updater` +- It will ask to open with browser or Flipper app, select Flipper App +- Continue to install +- Wait until update is finished +- And if all flashed successfully - you will have all needed assets pre installed +- Done + +![andro_web](https://user-images.githubusercontent.com/10697207/197042413-cfc93f31-8b84-4cdb-967b-276f46224e50.jpg) + +
+
+ ## With qFlipper (1.2.0+) - Download qFlipper that allows `.tgz` installation [Download qFlipper 1.2.1 (official link)](https://update.flipperzero.one/builds/qFlipper/1.2.1/) diff --git a/documentation/KeyCombo.md b/documentation/KeyCombo.md index 359fd5b9b..e6f55dc52 100644 --- a/documentation/KeyCombo.md +++ b/documentation/KeyCombo.md @@ -1,7 +1,7 @@ # Key Combos -There are times when your flipper feels blue and don't respond to your commands. -In that case you may find this guide useful. +There are times when your flipper feels blue and doesn't respond to your commands. +In that case, you may find this guide useful. ## Basic Combos @@ -9,7 +9,7 @@ In that case you may find this guide useful. ### Hardware Reset -- Press `LEFT` and `BACK` and hold for couple seconds +- Press `LEFT` and `BACK` and hold for a couple of seconds - Release `LEFT` and `BACK` This combo performs hardware reset by pulling MCU reset line down. @@ -29,7 +29,7 @@ There is 1 case when it's not working: - If you have not disconnected USB, then disconnect USB and repeat previous step - Release `BACK` key -This combo performs reset by switching SYS power line off and then on. +This combo performs a reset by switching SYS power line off and then on. Main components involved: Keys -> DD6(bq25896, charger) There is 1 case when it's not working: @@ -60,13 +60,13 @@ There is 1 case when it's not working: ### Hardware Reset + Software DFU -- Press `LEFT` and `BACK` and hold for couple seconds +- Press `LEFT` and `BACK` and hold for a couple of seconds - Release `BACK` - Device will enter DFU with indication (Blue LED + DFU Screen) - Release `LEFT` This combo performs hardware reset by pulling MCU reset line down. -Then `LEFT` key indicates to boot-loader that DFU mode requested. +Then `LEFT` key indicates to boot-loader that DFU mode is requested. There are 2 cases when it's not working: @@ -76,7 +76,7 @@ There are 2 cases when it's not working: ### Hardware Reset + Hardware DFU -- Press `LEFT` and `BACK` and `OK` and hold for couple seconds +- Press `LEFT` and `BACK` and `OK` and hold for a couple of seconds - Release `BACK` and `LEFT` - Device will enter DFU without indication @@ -127,8 +127,8 @@ There are 2 cases when it's not working: If none of the described methods were useful: -- Ensure battery charged -- Disconnect battery and connect again (Requires disassembly) -- Try to Flash device with ST-Link or other programmer that support SWD +- Ensure the battery charged +- Disconnect the battery and connect again (Requires disassembly) +- Try to Flash device with ST-Link or other programmer that supports SWD -If you still here and your device is not working: it's not software issue. +If you still here and your device is not working: it's not a software issue. diff --git a/documentation/MultiConverter.md b/documentation/MultiConverter.md index 820563622..5ce78c749 100644 --- a/documentation/MultiConverter.md +++ b/documentation/MultiConverter.md @@ -56,6 +56,6 @@ This is an initial release, so expect some bugs and issues (also I don't work wi - The way some long numbers are shown could probably be improved to look fancier. - Both _origin_ and _destination buffers_ are the same. The destination one could probably be longer in order to avoid certain _overflow scenarios_. - The GUI needs improvement too: there's a whole __widget/views system__ built in the Flipper that allows things like setting up keys, showing "Save/Back/Cancel" messages with -callbacks and stuff like that. Didn't know anything about them, so I moved on with something more basic (which is probably fince since it's not a "very big project"); but +callbacks and stuff like that. Didn't know anything about them, so I moved on with something more basic (which is probably fine since it's not a "very big project"); but a more "standard" way with the regular GUI stuff provided by the firmware will be interesting... - More GUI stuff: the _long click buttons_ for adding a decimal point / negative number aren't very clear on the view itself (I tried to add a small dot / dash symbol, but I think those are small enough to be a little bit confusing) diff --git a/documentation/SubGHzRemotePlugin.md b/documentation/SubGHzRemotePlugin.md index 615c8ba09..8563dbd5d 100644 --- a/documentation/SubGHzRemotePlugin.md +++ b/documentation/SubGHzRemotePlugin.md @@ -61,6 +61,6 @@ OKLABEL: Garage CLOSE * ##### Universal RF Map - File path should not have any spaces or special characters (- and _ excluded). - - Labels are limited to 12 characters. + - Labels are limited to 16 characters. - Why? This is to prevent overlapping elements on screen. - For example: If you set your label or file to ```WWWWWWWWWWWWWWW``` you'll be over the screen limits. diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index ff08041d4..41e50f55a 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -5,7 +5,7 @@ Edit user settings file located on your microSD card - `subghz/assets/setting_user.txt` in this file you will find we already have extra frequencies added -if you need your custom one, make sure it doesnt listed here +if you need your custom one, make sure it doesn't listed here ### Default frequency list ``` diff --git a/documentation/UnitTests.md b/documentation/UnitTests.md new file mode 100644 index 000000000..896426567 --- /dev/null +++ b/documentation/UnitTests.md @@ -0,0 +1,49 @@ +# Unit tests +## Intro +Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they were correct. +They are crucial for writing robust, bug-free code. + +Flipper Zero firmware includes a separate application called [unit_tests](/applications/debug/unit_tests). +It is run directly on the Flipper Zero in order to employ its hardware features and to rule out any platform-related differences. + +When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features. +Running existing unit tests is useful to ensure that the new code doesn't introduce any regressions. + +## Running unit tests +In order to run the unit tests, follow these steps: +1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests`. +2. Flash the firmware using your preferred method. +3. Copy the [assets/unit_tests](assets/unit_tests) folder to the root of your Flipper Zero's SD card. +4. Launch the CLI session and run the `unit_tests` command. + +**NOTE:** To run a particular test (and skip all others), specify its name as the command argument. +See [test_index.c](applications/debug/unit_tests/test_index.c) for the complete list of test names. + +## Adding unit tests +### General +#### Entry point +The common entry point for all tests is the [unit_tests](applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](applications/debug/unit_tests/test_index.c) source file. +#### Test assets +Some unit tests require external data in order to function. These files (commonly called assets) reside in the [assets/unit_tests](/assets/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat(FFF), binary, etc). +### Application-specific +#### Infrared +Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol. +In order to add unit tests for your protocol, follow these steps: +1. Create a file named `test_.irtest` in the [assets](assets/unit_tests/infrared) directory. +2. Fill it with the test data (more on it below). +3. Add the test code to [infrared_test.c](applications/debug/unit_tests/infrared/infrared_test.c). +4. Update the [assets](assets/unit_tests/infrared) on your Flipper Zero and run the tests to see if they pass. + +##### Test data format +Each unit test has 3 sections: +1. `decoder` - takes in raw signal and outputs decoded messages. +2. `encoder` - takes in decoded messages and outputs a raw signal. +3. `encoder_decoder` - takes in decoded messages, turns them into a raw signal, and then decodes again. + +Infrared test asset files have an `.irtest` extension and are regular `.ir` files with a few additions. +Decoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder these two are switched. + +Decoded data is represented in arrays (since a single raw signal may decode to several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples. + +##### Getting raw signals +Recording raw IR signals are possible using the Flipper Zero. Launch the CLI session, run `ir rx raw`, then point the remote towards Flipper's receiver and send the signals. The raw signal data will be printed to the console in a convenient format. diff --git a/documentation/fbt.md b/documentation/fbt.md index a4c3727a2..736e15a21 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -54,7 +54,8 @@ FBT keeps track of internal dependencies, so you only need to build the highest- - `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board) - `openocd` - just start OpenOCD - `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration -- `lint`, `format` - run clang-tidy on C source code to check and reformat it according to `.clang-format` specs +- `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/fbt b/fbt index 981489dd1..e576f37ac 100755 --- a/fbt +++ b/fbt @@ -7,6 +7,7 @@ set -eu; # private variables SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"; SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; +SCONS_EP="python3 -m SCons" # public variables FBT_NOENV="${FBT_NOENV:-""}"; @@ -25,4 +26,4 @@ if [ -z "$FBT_NO_SYNC" ]; then git submodule update --init; fi -python3 "$SCRIPT_PATH/lib/scons/scripts/scons.py" $SCONS_DEFAULT_FLAGS "$@" +$SCONS_EP $SCONS_DEFAULT_FLAGS "$@" diff --git a/fbt.cmd b/fbt.cmd index f09b98382..197f2359c 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -1,7 +1,7 @@ @echo off call "%~dp0scripts\toolchain\fbtenv.cmd" env -set SCONS_EP=%~dp0\lib\scons\scripts\scons.py +set SCONS_EP=python -m SCons if [%FBT_NO_SYNC%] == [] ( if exist ".git" ( @@ -13,4 +13,4 @@ if [%FBT_NO_SYNC%] == [] ( ) set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" -python lib\scons\scripts\scons.py %SCONS_DEFAULT_FLAGS% %* +%SCONS_EP% %SCONS_DEFAULT_FLAGS% %* diff --git a/fbt_options.py b/fbt_options.py index 4601fa0a6..236b229d0 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -49,14 +49,12 @@ OPENOCD_OPTS = [ "-c", "transport select hla_swd", "-f", - "debug/stm32wbx.cfg", + "${FBT_DEBUG_DIR}/stm32wbx.cfg", "-c", "stm32wbx.cpu configure -rtos auto", - "-c", - "init", ] -SVD_FILE = "debug/STM32WB55_CM4.svd" +SVD_FILE = "${FBT_DEBUG_DIR}/STM32WB55_CM4.svd" # Look for blackmagic probe on serial ports and local network BLACKMAGIC = "auto" @@ -79,6 +77,12 @@ FIRMWARE_APPS = { # Debug # "debug_apps", ], + "unit_tests": [ + "basic_services", + "updater_app", + "unit_tests", + "nfc", + ], "debug_pack": [ # Svc "basic_services", @@ -91,6 +95,9 @@ FIRMWARE_APPS = { # "basic_plugins", # Debug # "debug_apps", + # "updater_app", + # "unit_tests", + # "nfc", ], } diff --git a/firmware.scons b/firmware.scons index d28309d0e..d674bf160 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,16 +15,14 @@ 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"], IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", FW_FLAVOR=fw_build_meta["flavor"], - PLUGIN_ELF_DIR="${BUILD_DIR}", - LIB_DIST_DIR="${BUILD_DIR}/lib", + LIB_DIST_DIR=fw_build_meta["build_dir"].Dir("lib"), LINT_SOURCES=[ - "applications", + Dir("applications"), ], LIBPATH=[ "${LIB_DIST_DIR}", @@ -41,11 +40,11 @@ env = ENV.Clone( FW_LIB_OPTS={ "Default": { "CCFLAGS": [ - "-Os", + "-Og" if ENV["LIB_DEBUG"] else "-Os", ], "CPPDEFINES": [ "NDEBUG", - "FURI_NDEBUG", + "FURI_DEBUG" if ENV["LIB_DEBUG"] else "FURI_NDEBUG", ], # You can add other entries named after libraries # If they are present, they have precedence over Default @@ -86,21 +85,6 @@ env.AddMethod(ApplyLibFlags) Export("env") -if not env["VERBOSE"]: - env.SetDefault( - HEXCOMSTR="\tHEX\t${TARGET}", - BINCOMSTR="\tBIN\t${TARGET}", - DFUCOMSTR="\tDFU\t${TARGET}", - SDK_PREGEN_COMSTR="\tPREGEN\t${TARGET}", - SDK_COMSTR="\tSDKSRC\t${TARGET}", - SDKSYM_UPDATER_COMSTR="\tSDKCHK\t${TARGET}", - SDKSYM_GENERATOR_COMSTR="\tSDKSYM\t${TARGET}", - APPMETA_COMSTR="\tAPPMETA\t${TARGET}", - APPMETAEMBED_COMSTR="\tFAP\t${TARGET}", - APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", - ) - - if env["IS_BASE_FIRMWARE"]: env.Append( FIRMWARE_BUILD_CFG="firmware", @@ -127,7 +111,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}) @@ -145,13 +131,24 @@ 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 -extapps = fwenv["FW_EXTAPPS"] = SConscript( - "site_scons/extapps.scons", exports={"ENV": fwenv} -) +# Build external apps + configure SDK +if env["IS_BASE_FIRMWARE"]: + fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT="${BUILD_DIR}/.extapps") + fwenv["FW_EXTAPPS"] = SConscript( + "site_scons/extapps.scons", + exports={"ENV": fwenv}, + ) + fw_artifacts.append(fwenv["FW_EXTAPPS"].sdk_tree) # Add preprocessor definitions for current set of apps @@ -192,23 +189,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()) @@ -251,7 +231,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 @@ -259,7 +242,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}") @@ -269,17 +252,22 @@ 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() # without filtering, both updater & firmware commands would be generated in same file -fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*")) +fwenv.Replace( + COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"), + COMPILATIONDB_SRCPATH_FILTER="*.c*", +) AlwaysBuild(fwcdb) Precious(fwcdb) NoClean(fwcdb) @@ -303,45 +291,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", - [], - # Filtering out things cxxheaderparser cannot handle - SDK_PP_FLAGS=[ - '-D"_Static_assert(x,y)="', - '-D"__asm__(x)="', - '-D"__attribute__(x)="', - "-Drestrict=", - "-D_Noreturn=", - "-D__restrict=", - "-D__extension__=", - "-D__inline=inline", - "-D__inline__=inline", - ], - ) - # Depends(sdk_source, (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"])) - 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.subst("$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 eee2f1151..3581dc3eb 100644 --- a/firmware/SConscript +++ b/firmware/SConscript @@ -1,17 +1,18 @@ Import("env") env.Append( - LINT_SOURCES=["firmware"], - # SDK_HEADERS=[env.File("#/firmware/targets/furi_hal_include/furi_hal.h")], + LINT_SOURCES=[Dir(".")], 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"), File("#/firmware/targets/furi_hal_include/furi_hal_subghz.h"), ], ) -env.SetDefault(SDK_DEFINITION=env.File("./targets/f${TARGET_HW}/api_symbols.csv")) +env.SetDefault( + SDK_DEFINITION=env.File("./targets/f${TARGET_HW}/api_symbols.csv").srcnode() +) libenv = env.Clone(FW_LIB_NAME="flipper${TARGET_HW}") libenv.Append( diff --git a/firmware/targets/f7/Src/main.c b/firmware/targets/f7/Src/main.c index 5f33569ae..d9a2221a2 100644 --- a/firmware/targets/f7/Src/main.c +++ b/firmware/targets/f7/Src/main.c @@ -26,10 +26,7 @@ int main() { // Flipper critical FURI HAL furi_hal_init_early(); - FuriThread* main_thread = furi_thread_alloc(); - furi_thread_set_name(main_thread, "Init"); - furi_thread_set_stack_size(main_thread, 4096); - furi_thread_set_callback(main_thread, init_task); + FuriThread* main_thread = furi_thread_alloc_ex("Init", 4096, init_task, NULL); #ifdef FURI_RAM_EXEC furi_thread_start(main_thread); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 706cee357..b566d21fd 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,+,3.2,, +Version,+,7.52,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -8,6 +8,7 @@ Header,+,applications/services/dolphin/dolphin.h,, Header,+,applications/services/gui/canvas_i.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,17 +111,47 @@ Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_wwdg.h,, Header,-,lib/STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h,, Header,+,lib/STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h,, -Header,+,lib/drivers/nrf24.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/nfc/nfc_device.h,, Header,+,lib/one_wire/ibutton/ibutton_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_device.h,, @@ -128,10 +159,17 @@ Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_host_timing.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, +Header,+,lib/subghz/blocks/const.h,, +Header,+,lib/subghz/blocks/decoder.h,, +Header,+,lib/subghz/blocks/encoder.h,, +Header,+,lib/subghz/blocks/generic.h,, +Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, +Header,+,lib/subghz/protocols/protocol_items.h,, Header,+,lib/subghz/protocols/raw.h,, -Header,+,lib/subghz/protocols/registry.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,, Header,+,lib/subghz/transmitter.h,, @@ -151,6 +189,7 @@ Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, +Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* @@ -260,6 +299,8 @@ Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" Function,+,__errno,int*, Function,-,__fpclassifyd,int,double Function,-,__fpclassifyf,int,float +Function,+,__furi_crash,void, +Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" Function,-,__isinfd,int,double @@ -405,6 +446,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" @@ -452,6 +494,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 @@ -551,7 +595,6 @@ Function,+,byte_input_free,void,ByteInput* Function,+,byte_input_get_view,View*,ByteInput* Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" -Function,+,bytes_to_int32,uint32_t,"uint8_t*, _Bool" Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* @@ -633,6 +676,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" @@ -644,7 +688,17 @@ Function,-,coshl,long double,long double 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,-,crypto1_bit,uint8_t,"Crypto1*, uint8_t, int" +Function,-,crypto1_byte,uint8_t,"Crypto1*, uint8_t, int" +Function,-,crypto1_decrypt,void,"Crypto1*, uint8_t*, uint16_t, uint8_t*" +Function,-,crypto1_encrypt,void,"Crypto1*, uint8_t*, uint8_t*, uint16_t, uint8_t*, uint8_t*" +Function,-,crypto1_filter,uint32_t,uint32_t +Function,-,crypto1_init,void,"Crypto1*, uint64_t" +Function,-,crypto1_reset,void,Crypto1* +Function,-,crypto1_word,uint32_t,"Crypto1*, uint32_t, int" 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*, @@ -671,6 +725,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* @@ -723,6 +778,8 @@ Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* +Function,-,emv_card_emulation,_Bool,FuriHalNfcTxRxContext* +Function,-,emv_read_bank_card,_Bool,"FuriHalNfcTxRxContext*, EmvApplication*" Function,-,erand48,double,unsigned short[3] Function,-,erf,double,double Function,-,erfc,double,double @@ -905,7 +962,6 @@ Function,-,ftello,off_t,FILE* Function,-,ftrylockfile,int,FILE* Function,-,funlockfile,void,FILE* Function,-,funopen,FILE*,"const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" -Function,+,furi_crash,void,const char* Function,+,furi_delay_ms,void,uint32_t Function,+,furi_delay_tick,void,uint32_t Function,+,furi_delay_until_tick,FuriStatus,uint32_t @@ -949,6 +1005,7 @@ Function,+,furi_hal_bt_nvm_sram_sem_release,void, Function,+,furi_hal_bt_reinit,void, Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void, Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*" +Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus Function,+,furi_hal_bt_serial_start,void, Function,+,furi_hal_bt_serial_stop,void, Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" @@ -989,7 +1046,7 @@ Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**" Function,-,furi_hal_compress_icon_init,void, Function,+,furi_hal_console_disable,void, Function,+,furi_hal_console_enable,void, -Function,-,furi_hal_console_init,void, +Function,+,furi_hal_console_init,void, Function,+,furi_hal_console_printf,void,"const char[], ..." Function,+,furi_hal_console_puts,void,const char* Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" @@ -998,6 +1055,9 @@ Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" Function,+,furi_hal_cortex_delay_us,void,uint32_t Function,-,furi_hal_cortex_init_early,void, Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, +Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t +Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer +Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,-,furi_hal_crypto_init,void, @@ -1131,6 +1191,7 @@ Function,+,furi_hal_nfc_ll_set_fdt_poll,void,uint32_t Function,+,furi_hal_nfc_ll_set_guard_time,void,uint32_t Function,+,furi_hal_nfc_ll_set_mode,FuriHalNfcReturn,"FuriHalNfcMode, FuriHalNfcBitrate, FuriHalNfcBitrate" Function,+,furi_hal_nfc_ll_txrx,FuriHalNfcReturn,"uint8_t*, uint16_t, uint8_t*, uint16_t, uint16_t*, uint32_t, uint32_t" +Function,+,furi_hal_nfc_ll_txrx_bits,FuriHalNfcReturn,"uint8_t*, uint16_t, uint8_t*, uint16_t, uint16_t*, uint32_t, uint32_t" Function,+,furi_hal_nfc_ll_txrx_off,void, Function,+,furi_hal_nfc_ll_txrx_on,void, Function,+,furi_hal_nfc_sleep,void, @@ -1218,9 +1279,11 @@ Function,+,furi_hal_rtc_deinit_early,void, Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_get_fault_data,uint32_t, +Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister +Function,+,furi_hal_rtc_get_timestamp,uint32_t, Function,-,furi_hal_rtc_init,void, Function,-,furi_hal_rtc_init_early,void, Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag @@ -1229,6 +1292,7 @@ Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_set_fault_data,void,uint32_t Function,+,furi_hal_rtc_set_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1255,7 +1319,7 @@ Function,+,furi_hal_subghz_flush_tx,void, Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, Function,+,furi_hal_subghz_idle,void, -Function,+,furi_hal_subghz_init,void, +Function,-,furi_hal_subghz_init,void, Function,+,furi_hal_subghz_is_async_tx_complete,_Bool, Function,+,furi_hal_subghz_is_frequency_valid,_Bool,uint32_t Function,+,furi_hal_subghz_is_rx_data_crc_valid,_Bool, @@ -1320,7 +1384,6 @@ Function,+,furi_hal_version_uid,const uint8_t*, Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool -Function,+,furi_halt,void,const char* Function,-,furi_init,void, Function,+,furi_kernel_get_tick_frequency,uint32_t, Function,+,furi_kernel_lock,int32_t, @@ -1429,6 +1492,7 @@ Function,+,furi_string_utf8_length,size_t,FuriString* Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue" Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" Function,+,furi_thread_alloc,FuriThread*, +Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" Function,+,furi_thread_catch,void, Function,-,furi_thread_disable_heap_trace,void,FuriThread* Function,+,furi_thread_enable_heap_trace,void,FuriThread* @@ -1488,6 +1552,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* @@ -1548,12 +1614,46 @@ 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 Function,+,input_get_type_name,const char*,InputType -Function,+,int32_to_bytes,void,"uint32_t, uint8_t*, _Bool" -Function,-,int64_to_bytes,void,"uint64_t, uint8_t*, _Bool" Function,-,iprintf,int,"const char*, ..." Function,-,isalnum,int,int Function,-,isalnum_l,int,"int, locale_t" @@ -1576,6 +1676,8 @@ Function,-,islower,int,int Function,-,islower_l,int,"int, locale_t" Function,-,isnan,int,double Function,-,isnanf,int,float +Function,-,iso7816_tlv_parse,TlvInfo,const uint8_t* +Function,-,iso7816_tlv_select,TlvInfo,"const uint8_t*, size_t, const uint16_t[], size_t" Function,-,isprint,int,int Function,-,isprint_l,int,"int, locale_t" Function,-,ispunct,int,int @@ -1651,6 +1753,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 @@ -1679,6 +1783,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" @@ -1713,16 +1847,140 @@ Function,+,menu_free,void,Menu* Function,+,menu_get_view,View*,Menu* Function,+,menu_reset,void,Menu* Function,+,menu_set_selected_item,void,"Menu*, uint32_t" +Function,-,mf_classic_auth_attempt,_Bool,"FuriHalNfcTxRxContext*, MfClassicAuthContext*, uint64_t" +Function,-,mf_classic_auth_init_context,void,"MfClassicAuthContext*, uint8_t" +Function,-,mf_classic_authenticate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey" +Function,-,mf_classic_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" +Function,-,mf_classic_dict_add_key,_Bool,"MfClassicDict*, uint8_t*" +Function,-,mf_classic_dict_add_key_str,_Bool,"MfClassicDict*, FuriString*" +Function,-,mf_classic_dict_alloc,MfClassicDict*,MfClassicDictType +Function,-,mf_classic_dict_check_presence,_Bool,MfClassicDictType +Function,-,mf_classic_dict_delete_index,_Bool,"MfClassicDict*, uint32_t" +Function,-,mf_classic_dict_find_index,_Bool,"MfClassicDict*, uint8_t*, uint32_t*" +Function,-,mf_classic_dict_find_index_str,_Bool,"MfClassicDict*, FuriString*, uint32_t*" +Function,-,mf_classic_dict_free,void,MfClassicDict* +Function,-,mf_classic_dict_get_key_at_index,_Bool,"MfClassicDict*, uint64_t*, uint32_t" +Function,-,mf_classic_dict_get_key_at_index_str,_Bool,"MfClassicDict*, FuriString*, uint32_t" +Function,-,mf_classic_dict_get_next_key,_Bool,"MfClassicDict*, uint64_t*" +Function,-,mf_classic_dict_get_next_key_str,_Bool,"MfClassicDict*, FuriString*" +Function,-,mf_classic_dict_get_total_keys,uint32_t,MfClassicDict* +Function,-,mf_classic_dict_is_key_present,_Bool,"MfClassicDict*, uint8_t*" +Function,-,mf_classic_dict_is_key_present_str,_Bool,"MfClassicDict*, FuriString*" +Function,-,mf_classic_dict_rewind,_Bool,MfClassicDict* +Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*" +Function,-,mf_classic_get_classic_type,MfClassicType,"int8_t, uint8_t, uint8_t" +Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*, uint8_t*" +Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t +Function,-,mf_classic_get_sector_trailer_block_num_by_sector,uint8_t,uint8_t +Function,-,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"MfClassicData*, uint8_t" +Function,-,mf_classic_get_total_block_num,uint16_t,MfClassicType +Function,-,mf_classic_get_total_sectors_num,uint8_t,MfClassicType +Function,-,mf_classic_get_type_str,const char*,MfClassicType +Function,-,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" +Function,-,mf_classic_is_allowed_access_sector_trailer,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" +Function,-,mf_classic_is_block_read,_Bool,"MfClassicData*, uint8_t" +Function,-,mf_classic_is_card_read,_Bool,MfClassicData* +Function,-,mf_classic_is_key_found,_Bool,"MfClassicData*, uint8_t, MfClassicKey" +Function,-,mf_classic_is_sector_data_read,_Bool,"MfClassicData*, uint8_t" +Function,-,mf_classic_is_sector_read,_Bool,"MfClassicData*, uint8_t" +Function,-,mf_classic_is_sector_trailer,_Bool,uint8_t +Function,-,mf_classic_read_card,uint8_t,"FuriHalNfcTxRxContext*, MfClassicReader*, MfClassicData*" +Function,-,mf_classic_read_sector,void,"FuriHalNfcTxRxContext*, MfClassicData*, uint8_t" +Function,-,mf_classic_reader_add_sector,void,"MfClassicReader*, uint8_t, uint64_t, uint64_t" +Function,-,mf_classic_set_block_read,void,"MfClassicData*, uint8_t, MfClassicBlock*" +Function,-,mf_classic_set_key_found,void,"MfClassicData*, uint8_t, MfClassicKey, uint64_t" +Function,-,mf_classic_set_key_not_found,void,"MfClassicData*, uint8_t, MfClassicKey" +Function,-,mf_classic_set_sector_data_not_read,void,MfClassicData* +Function,-,mf_classic_update_card,uint8_t,"FuriHalNfcTxRxContext*, MfClassicData*" +Function,-,mf_classic_write_block,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t" +Function,-,mf_classic_write_sector,_Bool,"FuriHalNfcTxRxContext*, MfClassicData*, MfClassicData*, uint8_t" +Function,-,mf_df_cat_application,void,"MifareDesfireApplication*, FuriString*" +Function,-,mf_df_cat_application_info,void,"MifareDesfireApplication*, FuriString*" +Function,-,mf_df_cat_card_info,void,"MifareDesfireData*, FuriString*" +Function,-,mf_df_cat_data,void,"MifareDesfireData*, FuriString*" +Function,-,mf_df_cat_file,void,"MifareDesfireFile*, FuriString*" +Function,-,mf_df_cat_free_mem,void,"MifareDesfireFreeMemory*, FuriString*" +Function,-,mf_df_cat_key_settings,void,"MifareDesfireKeySettings*, FuriString*" +Function,-,mf_df_cat_version,void,"MifareDesfireVersion*, FuriString*" +Function,-,mf_df_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" +Function,-,mf_df_clear,void,MifareDesfireData* +Function,-,mf_df_parse_get_application_ids_response,_Bool,"uint8_t*, uint16_t, MifareDesfireApplication**" +Function,-,mf_df_parse_get_file_ids_response,_Bool,"uint8_t*, uint16_t, MifareDesfireFile**" +Function,-,mf_df_parse_get_file_settings_response,_Bool,"uint8_t*, uint16_t, MifareDesfireFile*" +Function,-,mf_df_parse_get_free_memory_response,_Bool,"uint8_t*, uint16_t, MifareDesfireFreeMemory*" +Function,-,mf_df_parse_get_key_settings_response,_Bool,"uint8_t*, uint16_t, MifareDesfireKeySettings*" +Function,-,mf_df_parse_get_key_version_response,_Bool,"uint8_t*, uint16_t, MifareDesfireKeyVersion*" +Function,-,mf_df_parse_get_version_response,_Bool,"uint8_t*, uint16_t, MifareDesfireVersion*" +Function,-,mf_df_parse_read_data_response,_Bool,"uint8_t*, uint16_t, MifareDesfireFile*" +Function,-,mf_df_parse_select_application_response,_Bool,"uint8_t*, uint16_t" +Function,-,mf_df_prepare_get_application_ids,uint16_t,uint8_t* +Function,-,mf_df_prepare_get_file_ids,uint16_t,uint8_t* +Function,-,mf_df_prepare_get_file_settings,uint16_t,"uint8_t*, uint8_t" +Function,-,mf_df_prepare_get_free_memory,uint16_t,uint8_t* +Function,-,mf_df_prepare_get_key_settings,uint16_t,uint8_t* +Function,-,mf_df_prepare_get_key_version,uint16_t,"uint8_t*, uint8_t" +Function,-,mf_df_prepare_get_value,uint16_t,"uint8_t*, uint8_t" +Function,-,mf_df_prepare_get_version,uint16_t,uint8_t* +Function,-,mf_df_prepare_read_data,uint16_t,"uint8_t*, uint8_t, uint32_t, uint32_t" +Function,-,mf_df_prepare_read_records,uint16_t,"uint8_t*, uint8_t, uint32_t, uint32_t" +Function,-,mf_df_prepare_select_application,uint16_t,"uint8_t*, uint8_t[3]" +Function,-,mf_df_read_card,_Bool,"FuriHalNfcTxRxContext*, MifareDesfireData*" +Function,-,mf_ul_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" +Function,-,mf_ul_is_full_capture,_Bool,MfUltralightData* +Function,-,mf_ul_prepare_emulation,void,"MfUltralightEmulator*, MfUltralightData*" +Function,-,mf_ul_prepare_emulation_response,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*, uint32_t*, void*" +Function,-,mf_ul_pwdgen_amiibo,uint32_t,FuriHalNfcDevData* +Function,-,mf_ul_pwdgen_xiaomi,uint32_t,FuriHalNfcDevData* +Function,-,mf_ul_read_card,_Bool,"FuriHalNfcTxRxContext*, MfUltralightReader*, MfUltralightData*" +Function,-,mf_ul_reset,void,MfUltralightData* +Function,-,mf_ul_reset_emulation,void,"MfUltralightEmulator*, _Bool" +Function,-,mf_ultralight_authenticate,_Bool,"FuriHalNfcTxRxContext*, uint32_t, uint16_t*" +Function,-,mf_ultralight_fast_read_pages,_Bool,"FuriHalNfcTxRxContext*, MfUltralightReader*, MfUltralightData*" +Function,-,mf_ultralight_get_config_pages,MfUltralightConfigPages*,MfUltralightData* +Function,-,mf_ultralight_read_counters,_Bool,"FuriHalNfcTxRxContext*, MfUltralightData*" +Function,-,mf_ultralight_read_pages,_Bool,"FuriHalNfcTxRxContext*, MfUltralightReader*, MfUltralightData*" +Function,-,mf_ultralight_read_pages_direct,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint8_t*" +Function,-,mf_ultralight_read_signature,_Bool,"FuriHalNfcTxRxContext*, MfUltralightData*" +Function,-,mf_ultralight_read_tearing_flags,_Bool,"FuriHalNfcTxRxContext*, MfUltralightData*" +Function,-,mf_ultralight_read_version,_Bool,"FuriHalNfcTxRxContext*, MfUltralightReader*, MfUltralightData*" Function,-,mkdtemp,char*,char* Function,-,mkostemp,int,"char*, int" 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*" Function,-,mrand48,long, +Function,-,mrtd_alloc_init,MrtdApplication*,"FuriHalNfcTxRxContext*, MrtdData*" +Function,-,mrtd_auth_method_parse_string,_Bool,"MrtdAuthMethod*, const char*" +Function,-,mrtd_auth_method_string,const char*,MrtdAuthMethod +Function,-,mrtd_auth_params_load,_Bool,"Storage*, DialogsApp*, MrtdAuthData*, const char*, _Bool" +Function,-,mrtd_auth_params_save,_Bool,"Storage*, DialogsApp*, MrtdAuthData*, const char*" +Function,-,mrtd_auth_params_save_file,_Bool,"Storage*, DialogsApp*, MrtdAuthData*, const char*, const char*, const char*" +Function,-,mrtd_authenticate,_Bool,MrtdApplication* +Function,-,mrtd_bac_check_digit,uint8_t,"const char*, const uint8_t" +Function,-,mrtd_bac_decrypt,_Bool,"const uint8_t*, size_t, uint8_t*, uint8_t*" +Function,-,mrtd_bac_decrypt_verify,_Bool,"const uint8_t*, size_t, uint8_t*, uint8_t*, uint8_t*" +Function,-,mrtd_bac_decrypt_verify_sm,uint16_t,"const uint8_t*, size_t, uint8_t*, uint8_t*, uint64_t, uint8_t*, size_t*" +Function,-,mrtd_bac_encrypt,_Bool,"const uint8_t*, size_t, const uint8_t*, uint8_t*" +Function,-,mrtd_bac_get_kmrz,_Bool,"MrtdAuthData*, char*, uint8_t" +Function,-,mrtd_bac_keys,_Bool,"MrtdAuthData*, uint8_t[16], uint8_t[16]" +Function,-,mrtd_bac_keys_from_seed,_Bool,"const uint8_t*, uint8_t*, uint8_t*" +Function,-,mrtd_bac_mac,_Bool,"const uint8_t*, size_t, const uint8_t*, uint8_t*" +Function,-,mrtd_bac_mac_finalize,_Bool,"mrtd_bac_mac_ctx*, uint8_t[8]" +Function,-,mrtd_bac_mac_init,_Bool,"mrtd_bac_mac_ctx*, const uint8_t[16]" +Function,-,mrtd_bac_mac_pad,_Bool,mrtd_bac_mac_ctx* +Function,-,mrtd_bac_mac_update,_Bool,"mrtd_bac_mac_ctx*, const uint8_t*, size_t" +Function,-,mrtd_bac_padded_mac,_Bool,"const uint8_t*, size_t, uint8_t*, uint8_t*" +Function,-,mrtd_parse_date,void,"MrtdDate*, const unsigned char*" +Function,-,mrtd_print_date,void,"char*, MrtdDate*" +Function,-,mrtd_protect_apdu,size_t,"uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, const void*, int16_t, const uint8_t*, const uint8_t*, uint64_t, uint8_t*" +Function,-,mrtd_read_parse_file,_Bool,"MrtdApplication*, EFFile" +Function,-,mrtd_select_app,_Bool,"MrtdApplication*, AIDValue" +Function,-,mrtd_tag_to_file,const EFFile*,uint8_t Function,-,nan,double,const char* Function,-,nanf,float,const char* Function,-,nanl,long double,const char* @@ -1735,6 +1993,19 @@ Function,-,nextafterl,long double,"long double, long double" Function,-,nexttoward,double,"double, long double" Function,-,nexttowardf,float,"float, long double" Function,-,nexttowardl,long double,"long double, long double" +Function,+,nfc_device_alloc,NfcDevice*, +Function,+,nfc_device_clear,void,NfcDevice* +Function,+,nfc_device_data_clear,void,NfcDeviceData* +Function,+,nfc_device_delete,_Bool,"NfcDevice*, _Bool" +Function,+,nfc_device_free,void,NfcDevice* +Function,+,nfc_device_load,_Bool,"NfcDevice*, const char*, _Bool" +Function,+,nfc_device_load_key_cache,_Bool,NfcDevice* +Function,+,nfc_device_restore,_Bool,"NfcDevice*, _Bool" +Function,+,nfc_device_save,_Bool,"NfcDevice*, const char*" +Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" +Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" +Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" +Function,+,nfc_file_select,_Bool,NfcDevice* Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" @@ -1746,35 +2017,6 @@ Function,+,notification_internal_message_block,void,"NotificationApp*, const Not Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" Function,-,nrand48,long,unsigned short[3] -Function,-,nrf24_configure,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t*, uint8_t, uint8_t, _Bool, _Bool" -Function,+,nrf24_find_channel,uint8_t,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, uint8_t, uint8_t, uint8_t, uint8_t, _Bool" -Function,-,nrf24_flush_rx,uint8_t,FuriHalSpiBusHandle* -Function,-,nrf24_flush_tx,uint8_t,FuriHalSpiBusHandle* -Function,-,nrf24_get_chan,uint8_t,FuriHalSpiBusHandle* -Function,-,nrf24_get_dst_mac,uint8_t,"FuriHalSpiBusHandle*, uint8_t*" -Function,-,nrf24_get_maclen,uint8_t,FuriHalSpiBusHandle* -Function,-,nrf24_get_packetlen,uint8_t,FuriHalSpiBusHandle* -Function,-,nrf24_get_rate,uint32_t,FuriHalSpiBusHandle* -Function,-,nrf24_get_src_mac,uint8_t,"FuriHalSpiBusHandle*, uint8_t*" -Function,+,nrf24_init,void, -Function,+,nrf24_init_promisc_mode,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" -Function,-,nrf24_power_up,uint8_t,FuriHalSpiBusHandle* -Function,-,nrf24_read_reg,uint8_t,"FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t" -Function,-,nrf24_rxpacket,uint8_t,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, _Bool" -Function,-,nrf24_set_chan,uint8_t,"FuriHalSpiBusHandle*, uint8_t" -Function,-,nrf24_set_dst_mac,uint8_t,"FuriHalSpiBusHandle*, uint8_t*, uint8_t" -Function,+,nrf24_set_idle,uint8_t,FuriHalSpiBusHandle* -Function,-,nrf24_set_maclen,uint8_t,"FuriHalSpiBusHandle*, uint8_t" -Function,-,nrf24_set_packetlen,uint8_t,"FuriHalSpiBusHandle*, uint8_t" -Function,-,nrf24_set_rate,uint8_t,"FuriHalSpiBusHandle*, uint32_t" -Function,-,nrf24_set_rx_mode,uint8_t,FuriHalSpiBusHandle* -Function,+,nrf24_set_src_mac,uint8_t,"FuriHalSpiBusHandle*, uint8_t*, uint8_t" -Function,-,nrf24_set_tx_mode,uint8_t,FuriHalSpiBusHandle* -Function,+,nrf24_sniff_address,_Bool,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" -Function,-,nrf24_status,uint8_t,FuriHalSpiBusHandle* -Function,+,nrf24_txpacket,uint8_t,"FuriHalSpiBusHandle*, uint8_t*, uint8_t, _Bool" -Function,-,nrf24_write_buf_reg,uint8_t,"FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t" -Function,-,nrf24_write_reg,uint8_t,"FuriHalSpiBusHandle*, uint8_t, uint8_t" Function,-,on_exit,int,"void (*)(int, void*), void*" Function,+,onewire_device_alloc,OneWireDevice*,"uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,onewire_device_attach,void,"OneWireDevice*, OneWireSlave*" @@ -1842,12 +2084,14 @@ Function,-,pow10f,float,float Function,-,power_enable_low_battery_level_notification,void,"Power*, _Bool" Function,+,power_get_info,void,"Power*, PowerInfo*" Function,+,power_get_pubsub,FuriPubSub*,Power* +Function,+,power_get_settings_events_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,-,printf,int,"const char*, ..." +Function,-,prng_successor,uint32_t,"uint32_t, uint32_t" Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" @@ -1863,8 +2107,8 @@ Function,+,protocol_dict_get_manufacturer,const char*,"ProtocolDict*, size_t" Function,+,protocol_dict_get_max_data_size,size_t,ProtocolDict* Function,+,protocol_dict_get_name,const char*,"ProtocolDict*, size_t" Function,+,protocol_dict_get_protocol_by_name,ProtocolId,"ProtocolDict*, const char*" -Function,-,protocol_dict_get_validate_count,uint32_t,"ProtocolDict*, size_t" -Function,-,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*" +Function,+,protocol_dict_get_validate_count,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*" Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" @@ -2064,6 +2308,7 @@ Function,-,rfalT1TPollerRall,ReturnCode,"const uint8_t*, uint8_t*, uint16_t, uin Function,-,rfalT1TPollerRid,ReturnCode,rfalT1TRidRes* Function,-,rfalT1TPollerWrite,ReturnCode,"const uint8_t*, uint8_t, uint8_t" Function,-,rfalTransceiveBitsBlockingTx,ReturnCode,"uint8_t*, uint16_t, uint8_t*, uint16_t, uint16_t*, uint32_t, uint32_t" +Function,-,rfalTransceiveBitsBlockingTxRx,ReturnCode,"uint8_t*, uint16_t, uint8_t*, uint16_t, uint16_t*, uint32_t, uint32_t" Function,-,rfalTransceiveBlockingRx,ReturnCode, Function,-,rfalTransceiveBlockingTx,ReturnCode,"uint8_t*, uint16_t, uint8_t*, uint16_t, uint16_t*, uint32_t, uint32_t" Function,-,rfalTransceiveBlockingTxRx,ReturnCode,"uint8_t*, uint16_t, uint8_t*, uint16_t, uint16_t*, uint32_t, uint32_t" @@ -2127,6 +2372,7 @@ Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" Function,-,serial_svc_is_started,_Bool, Function,-,serial_svc_notify_buffer_is_empty,void, Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*" +Function,+,serial_svc_set_rpc_status,void,SerialServiceRpcStatus Function,-,serial_svc_start,void, Function,-,serial_svc_stop,void, Function,-,serial_svc_update_tx,_Bool,"uint8_t*, uint16_t" @@ -2172,6 +2418,7 @@ Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*" Function,+,storage_common_remove,FS_Error,"Storage*, const char*" Function,+,storage_common_rename,FS_Error,"Storage*, const char*, const char*" Function,+,storage_common_stat,FS_Error,"Storage*, const char*, FileInfo*" +Function,+,storage_common_timestamp,FS_Error,"Storage*, const char*, uint32_t*" Function,+,storage_dir_close,_Bool,File* Function,+,storage_dir_open,_Bool,"File*, const char*" Function,+,storage_dir_read,_Bool,"File*, FileInfo*, char*, uint16_t" @@ -2257,6 +2504,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" @@ -2271,6 +2520,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 @@ -2296,14 +2547,20 @@ Function,-,strupr,char*,char* Function,-,strverscmp,int,"const char*, const char*" Function,-,strxfrm,size_t,"char*, const char*, size_t" Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" +Function,+,subghz_block_generic_deserialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*" +Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" +Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* -Function,-,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* -Function,-,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* -Function,-,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* +Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* +Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* +Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* +Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" +Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" -Function,-,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" -Function,-,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" Function,-,subghz_keystore_alloc,SubGhzKeystore*, Function,-,subghz_keystore_free,void,SubGhzKeystore* Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* @@ -2311,10 +2568,39 @@ Function,-,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*" Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" 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_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*" -Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzPresetDefinition*" +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_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[], 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" +Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_ansonic_free,void,void* +Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_ansonic_reset,void,void* +Function,-,subghz_protocol_decoder_ansonic_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +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*" +Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_bett_deserialize,_Bool,"void*, FlipperFormat*" @@ -2323,7 +2609,7 @@ Function,-,subghz_protocol_decoder_bett_free,void,void* Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_bett_reset,void,void* -Function,-,subghz_protocol_decoder_bett_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_bett_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" @@ -2332,14 +2618,14 @@ Function,-,subghz_protocol_decoder_came_atomo_free,void,void* Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* -Function,-,subghz_protocol_decoder_came_atomo_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_came_atomo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_came_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_came_free,void,void* Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_came_reset,void,void* -Function,-,subghz_protocol_decoder_came_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_came_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" @@ -2347,7 +2633,7 @@ Function,-,subghz_protocol_decoder_came_twee_free,void,void* Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_came_twee_reset,void,void* -Function,-,subghz_protocol_decoder_came_twee_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_came_twee_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" @@ -2355,7 +2641,7 @@ Function,-,subghz_protocol_decoder_chamb_code_free,void,void* Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* -Function,-,subghz_protocol_decoder_chamb_code_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_chamb_code_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" @@ -2363,7 +2649,7 @@ Function,-,subghz_protocol_decoder_clemsa_free,void,void* Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_clemsa_reset,void,void* -Function,-,subghz_protocol_decoder_clemsa_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_clemsa_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" @@ -2371,7 +2657,7 @@ Function,-,subghz_protocol_decoder_doitrand_free,void,void* Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_doitrand_reset,void,void* -Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" @@ -2379,7 +2665,7 @@ Function,-,subghz_protocol_decoder_faac_slh_free,void,void* Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* -Function,-,subghz_protocol_decoder_faac_slh_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_faac_slh_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" @@ -2387,7 +2673,7 @@ Function,-,subghz_protocol_decoder_gate_tx_free,void,void* Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* -Function,-,subghz_protocol_decoder_gate_tx_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_gate_tx_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" @@ -2395,7 +2681,7 @@ Function,-,subghz_protocol_decoder_holtek_free,void,void* Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_holtek_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_holtek_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" @@ -2403,7 +2689,7 @@ Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" @@ -2411,7 +2697,7 @@ Function,-,subghz_protocol_decoder_hormann_free,void,void* Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_hormann_reset,void,void* -Function,-,subghz_protocol_decoder_hormann_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_hormann_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_ido_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" @@ -2419,7 +2705,7 @@ Function,-,subghz_protocol_decoder_ido_free,void,void* Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_ido_reset,void,void* -Function,-,subghz_protocol_decoder_ido_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_ido_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" @@ -2427,7 +2713,7 @@ Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_intertechno_v3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" @@ -2435,7 +2721,7 @@ Function,-,subghz_protocol_decoder_keeloq_free,void,void* Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_keeloq_reset,void,void* -Function,-,subghz_protocol_decoder_keeloq_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_keeloq_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_kia_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" @@ -2443,7 +2729,7 @@ Function,-,subghz_protocol_decoder_kia_free,void,void* Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_kia_reset,void,void* -Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_linear_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" @@ -2451,15 +2737,15 @@ Function,-,subghz_protocol_decoder_linear_free,void,void* Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_linear_reset,void,void* -Function,-,subghz_protocol_decoder_linear_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" -Function,-,subghz_protocol_decoder_magellen_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_magellen_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_magellen_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_magellen_free,void,void* -Function,-,subghz_protocol_decoder_magellen_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_magellen_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_magellen_reset,void,void* -Function,-,subghz_protocol_decoder_magellen_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_linear_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_magellan_free,void,void* +Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_magellan_reset,void,void* +Function,-,subghz_protocol_decoder_magellan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" @@ -2467,7 +2753,7 @@ Function,-,subghz_protocol_decoder_marantec_free,void,void* Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_marantec_reset,void,void* -Function,-,subghz_protocol_decoder_marantec_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_marantec_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" @@ -2475,7 +2761,7 @@ Function,-,subghz_protocol_decoder_megacode_free,void,void* Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_megacode_reset,void,void* -Function,-,subghz_protocol_decoder_megacode_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_megacode_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" @@ -2483,7 +2769,7 @@ Function,-,subghz_protocol_decoder_nero_radio_free,void,void* Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* -Function,-,subghz_protocol_decoder_nero_radio_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_nero_radio_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" @@ -2491,7 +2777,7 @@ Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_nero_sketch_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" @@ -2499,7 +2785,7 @@ Function,-,subghz_protocol_decoder_nice_flo_free,void,void* Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flo_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_nice_flo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" @@ -2507,7 +2793,7 @@ Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_nice_flor_s_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" @@ -2515,7 +2801,7 @@ Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_phoenix_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" @@ -2523,26 +2809,26 @@ Function,-,subghz_protocol_decoder_power_smart_free,void,void* Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_power_smart_reset,void,void* -Function,-,subghz_protocol_decoder_power_smart_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" -Function,-,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_princeton_free,void,void* -Function,-,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_princeton_reset,void,void* -Function,-,subghz_protocol_decoder_princeton_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_power_smart_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_princeton_free,void,void* +Function,+,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_princeton_reset,void,void* +Function,+,subghz_protocol_decoder_princeton_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" Function,+,subghz_protocol_decoder_raw_free,void,void* -Function,-,subghz_protocol_decoder_raw_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_raw_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_raw_reset,void,void* -Function,-,subghz_protocol_decoder_raw_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" -Function,-,subghz_protocol_decoder_raw_set_auto_mode,void,"void*, _Bool" -Function,-,subghz_protocol_decoder_raw_set_rssi_threshold,void,"void*, int" -Function,-,subghz_protocol_decoder_raw_write_data,_Bool,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_raw_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_raw_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_raw_reset,void,void* +Function,+,subghz_protocol_decoder_raw_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_raw_set_auto_mode,void,"void*, _Bool" +Function,+,subghz_protocol_decoder_raw_set_rssi_threshold,void,"void*, int" +Function,+,subghz_protocol_decoder_raw_write_data,_Bool,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_scher_khan_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" @@ -2550,7 +2836,7 @@ Function,-,subghz_protocol_decoder_scher_khan_free,void,void* Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_scher_khan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" @@ -2558,7 +2844,7 @@ Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_secplus_v1_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" @@ -2566,7 +2852,7 @@ Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_secplus_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" @@ -2574,7 +2860,7 @@ Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_somfy_keytis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" @@ -2582,7 +2868,7 @@ Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_somfy_telis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" @@ -2590,7 +2876,12 @@ Function,-,subghz_protocol_decoder_star_line_free,void,void* Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_star_line_reset,void,void* -Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_ansonic_free,void,void* +Function,-,subghz_protocol_encoder_ansonic_stop,void,void* +Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_bett_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_bett_free,void,void* @@ -2667,11 +2958,11 @@ Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperForma Function,-,subghz_protocol_encoder_linear_free,void,void* Function,-,subghz_protocol_encoder_linear_stop,void,void* Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_magellen_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_magellen_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_magellen_free,void,void* -Function,-,subghz_protocol_encoder_magellen_stop,void,void* -Function,-,subghz_protocol_encoder_magellen_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_magellan_free,void,void* +Function,-,subghz_protocol_encoder_magellan_stop,void,void* +Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_marantec_free,void,void* @@ -2712,11 +3003,11 @@ Function,-,subghz_protocol_encoder_power_smart_deserialize,_Bool,"void*, Flipper Function,-,subghz_protocol_encoder_power_smart_free,void,void* Function,-,subghz_protocol_encoder_power_smart_stop,void,void* Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_princeton_free,void,void* -Function,-,subghz_protocol_encoder_princeton_stop,void,void* -Function,-,subghz_protocol_encoder_princeton_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_princeton_free,void,void* +Function,+,subghz_protocol_encoder_princeton_stop,void,void* +Function,+,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_encoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* @@ -2737,21 +3028,22 @@ Function,-,subghz_protocol_encoder_star_line_deserialize,_Bool,"void*, FlipperFo Function,-,subghz_protocol_encoder_star_line_free,void,void* Function,-,subghz_protocol_encoder_star_line_stop,void,void* Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* -Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzPresetDefinition*" -Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzPresetDefinition*" -Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" +Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" +Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" 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*, SubGhzPresetDefinition*" +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, -Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,size_t -Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,const char* +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_protocol_secplus_v1_check_fixed,_Bool,uint32_t -Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzPresetDefinition*" -Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzPresetDefinition*" +Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* @@ -2760,6 +3052,24 @@ Function,+,subghz_receiver_reset,void,SubGhzReceiver* Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*" Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*" +Function,+,subghz_setting_alloc,SubGhzSetting*, +Function,+,subghz_setting_delete_custom_preset,_Bool,"SubGhzSetting*, const char*" +Function,+,subghz_setting_free,void,SubGhzSetting* +Function,+,subghz_setting_get_default_frequency,uint32_t,SubGhzSetting* +Function,+,subghz_setting_get_frequency,uint32_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_frequency_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting* +Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*" +Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_preset_data_by_name,uint8_t*,"SubGhzSetting*, const char*" +Function,+,subghz_setting_get_preset_data_size,size_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_preset_name,const char*,"SubGhzSetting*, size_t" +Function,+,subghz_setting_load,void,"SubGhzSetting*, const char*, _Bool" +Function,+,subghz_setting_load_custom_preset,_Bool,"SubGhzSetting*, const char*, FlipperFormat*" +Function,+,subghz_setting_set_default_frequency,void,"SubGhzSetting*, uint32_t" Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*" Function,+,subghz_transmitter_deserialize,_Bool,"SubGhzTransmitter*, FlipperFormat*" Function,+,subghz_transmitter_free,void,SubGhzTransmitter* @@ -2835,6 +3145,7 @@ Function,-,tga_save,void,const char* 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 @@ -2842,6 +3153,7 @@ Function,-,timerStopwatchMeasure,uint32_t, Function,-,timerStopwatchStart,void, Function,-,timingsafe_bcmp,int,"const void*, const void*, size_t" Function,-,timingsafe_memcmp,int,"const void*, const void*, size_t" +Function,-,tlv_number,int,TlvInfo Function,-,tmpfile,FILE*, Function,-,tmpnam,char*,char* Function,-,toascii,int,int @@ -2853,6 +3165,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,-,u8g2_AddPolygonXY,void,"u8g2_t*, int16_t, int16_t" Function,-,u8g2_ClearBuffer,void,u8g2_t* Function,-,u8g2_ClearDisplay,void,u8g2_t* @@ -3994,6 +4307,9 @@ Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* +Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" +Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" @@ -4006,6 +4322,7 @@ Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, Variab Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t" Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" +Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" @@ -4051,17 +4368,17 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,-,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* Function,+,view_port_get_width,uint8_t,ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" Function,+,view_port_is_enabled,_Bool,ViewPort* -Function,-,view_port_set_height,void,"ViewPort*, uint8_t" +Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" Function,+,view_port_update,void,ViewPort* Function,+,view_set_context,void,"View*, void*" -Function,-,view_set_custom_callback,void,"View*, ViewCustomCallback" +Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback" Function,+,view_set_enter_callback,void,"View*, ViewCallback" Function,+,view_set_exit_callback,void,"View*, ViewCallback" @@ -4158,203 +4475,21 @@ Function,-,y1f,float,float Function,-,yn,double,"int, double" Function,-,ynf,float,"int, float" Variable,-,AHBPrescTable,const uint32_t[16], +Variable,-,AID,AIDSet, Variable,-,APBPrescTable,const uint32_t[8], -Variable,+,A_125khz_14,const Icon, -Variable,+,A_BadUsb_14,const Icon, -Variable,+,A_Clock_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_UniRFRemix_14,const Icon, -Variable,+,A_iButton_14,const Icon, +Variable,-,EF,const EFFormat, Variable,-,ITM_RxBuffer,volatile int32_t, -Variable,+,I_125_10px,const Icon, -Variable,+,I_ActiveConnection_50x64,const Icon, -Variable,+,I_Apps_10px,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_Cry_dolph_55x52,const Icon, -Variable,+,I_DFU_128x50,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_G0ku,const Icon, -Variable,+,I_GameMode_11x8,const Icon, -Variable,+,I_Health_16x16,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_Mode_25x27,const Icon, -Variable,+,I_Mode_hvr_25x27,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_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_cell_13x13,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_RFIDDolphinReceive_97x61,const Icon, -Variable,+,I_RFIDDolphinSend_97x61,const Icon, -Variable,+,I_RFIDDolphinSuccess_108x57,const Icon, -Variable,+,I_RFIDSmallChip_14x14,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_Rotate_25x27,const Icon, -Variable,+,I_Rotate_hvr_25x27,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_Swing_25x27,const Icon, -Variable,+,I_Swing_hvr_25x27,const Icon, -Variable,+,I_Tap_reader_36x38,const Icon, -Variable,+,I_Temperature_16x16,const Icon, -Variable,+,I_Timer_25x27,const Icon, -Variable,+,I_Timer_hvr_25x27,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_g0ku_1,const Icon, -Variable,+,I_g0ku_2,const Icon, -Variable,+,I_g0ku_3,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_keyboard_10px,const Icon, -Variable,+,I_loading_10px,const Icon, -Variable,+,I_music_10px,const Icon, -Variable,+,I_passport_DB,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, 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, @@ -4614,6 +4749,9 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, +Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, +Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_bett,const SubGhzProtocol, Variable,-,subghz_protocol_bett_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_bett_encoder,const SubGhzProtocolEncoder, @@ -4665,9 +4803,9 @@ Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_linear,const SubGhzProtocol, Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_magellen,const SubGhzProtocol, -Variable,-,subghz_protocol_magellen_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_magellen_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_magellan,const SubGhzProtocol, +Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_marantec,const SubGhzProtocol, Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder, @@ -4686,7 +4824,6 @@ Variable,-,subghz_protocol_nice_flo_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_nice_flor_s,const SubGhzProtocol, Variable,-,subghz_protocol_nice_flor_s_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_nice_flor_s_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_oregon2,const SubGhzProtocol, Variable,-,subghz_protocol_phoenix_v2,const SubGhzProtocol, Variable,-,subghz_protocol_phoenix_v2_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_phoenix_v2_encoder,const SubGhzProtocolEncoder, @@ -4696,9 +4833,10 @@ Variable,-,subghz_protocol_power_smart_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_princeton,const SubGhzProtocol, Variable,-,subghz_protocol_princeton_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_raw,const SubGhzProtocol, -Variable,-,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_raw,const SubGhzProtocol, +Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, Variable,-,subghz_protocol_scher_khan,const SubGhzProtocol, Variable,-,subghz_protocol_scher_khan_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_scher_khan_encoder,const SubGhzProtocolEncoder, diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 3cf02009f..30be3c7ce 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -40,11 +40,7 @@ bool ble_app_init() { ble_app->hci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); ble_app->hci_sem = furi_semaphore_alloc(1, 0); // HCI transport layer thread to handle user asynch events - ble_app->thread = furi_thread_alloc(); - furi_thread_set_name(ble_app->thread, "BleHciDriver"); - furi_thread_set_stack_size(ble_app->thread, 1024); - furi_thread_set_context(ble_app->thread, ble_app); - furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); + ble_app->thread = furi_thread_alloc_ex("BleHciDriver", 1024, ble_app_hci_thread, ble_app); furi_thread_start(ble_app->thread); // Initialize Ble Transport Layer diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 87af5f2a8..b3752f17f 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -78,11 +78,7 @@ void ble_glue_init() { ble_glue->shci_sem = furi_semaphore_alloc(1, 0); // FreeRTOS system task creation - ble_glue->thread = furi_thread_alloc(); - furi_thread_set_name(ble_glue->thread, "BleShciDriver"); - furi_thread_set_stack_size(ble_glue->thread, 1024); - furi_thread_set_context(ble_glue->thread, ble_glue); - furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); + ble_glue->thread = furi_thread_alloc_ex("BleShciDriver", 1024, ble_glue_shci_thread, ble_glue); furi_thread_start(ble_glue->thread); // System channel initialization 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/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index aa8cd2c90..3e29527ec 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -179,7 +179,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { case EVT_BLUE_GAP_PASS_KEY_REQUEST: { // Generate random PIN code - uint32_t pin = rand() % 999999; + uint32_t pin = rand() % 999999; //-V1064 aci_gap_pass_key_resp(gap->service.connection_handle, pin); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { FURI_LOG_I(TAG, "Pass key request event. Pin: ******"); @@ -478,7 +478,6 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap = malloc(sizeof(Gap)); gap->config = config; - srand(DWT->CYCCNT); // Create advertising timer gap->advertise_timer = furi_timer_alloc(gap_advetise_timer_callback, FuriTimerTypeOnce, NULL); // Initialization of GATT & GAP layer @@ -493,11 +492,7 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap->enable_adv = true; // Thread configuration - gap->thread = furi_thread_alloc(); - furi_thread_set_name(gap->thread, "BleGapDriver"); - furi_thread_set_stack_size(gap->thread, 1024); - furi_thread_set_context(gap->thread, gap); - furi_thread_set_callback(gap->thread, gap_app); + gap->thread = furi_thread_alloc_ex("BleGapDriver", 1024, gap_app, gap); furi_thread_start(gap->thread); // Command queue allocation diff --git a/firmware/targets/f7/ble_glue/serial_service.c b/firmware/targets/f7/ble_glue/serial_service.c index 536557cce..c6421dc28 100644 --- a/firmware/targets/f7/ble_glue/serial_service.c +++ b/firmware/targets/f7/ble_glue/serial_service.c @@ -11,6 +11,7 @@ typedef struct { uint16_t rx_char_handle; uint16_t tx_char_handle; uint16_t flow_ctrl_char_handle; + uint16_t rpc_status_char_handle; FuriMutex* buff_size_mtx; uint32_t buff_size; uint16_t bytes_ready_to_receive; @@ -28,6 +29,8 @@ static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; static const uint8_t flow_ctrl_uuid[] = {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t rpc_status_uuid[] = + {0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; @@ -67,6 +70,17 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } ret = SVCCTL_EvtAckFlowEnable; + } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { + SerialServiceRpcStatus* rpc_status = + (SerialServiceRpcStatus*)attribute_modified->Attr_Data; + if(*rpc_status == SerialServiceRpcStatusNotActive) { + if(serial_svc->callback) { + SerialServiceEvent event = { + .event = SerialServiceEventTypesBleResetRequest, + }; + serial_svc->callback(event, serial_svc->context); + } + } } } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { FURI_LOG_T(TAG, "Ack received"); @@ -82,6 +96,18 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { return ret; } +static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { + tBleStatus ble_status = aci_gatt_update_char_value( + serial_svc->svc_handle, + serial_svc->rpc_status_char_handle, + 0, + sizeof(SerialServiceRpcStatus), + (uint8_t*)&status); + if(ble_status) { + FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status); + } +} + void serial_svc_start() { tBleStatus status; serial_svc = malloc(sizeof(SerialSvc)); @@ -90,7 +116,7 @@ void serial_svc_start() { // Add service status = aci_gatt_add_service( - UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 10, &serial_svc->svc_handle); + UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); } @@ -141,6 +167,22 @@ void serial_svc_start() { if(status) { FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); } + // Add RPC status characteristic + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)rpc_status_uuid, + sizeof(SerialServiceRpcStatus), + CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_CONSTANT, + &serial_svc->rpc_status_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status); + } + serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); // Allocate buffer size mutex serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); } @@ -198,6 +240,10 @@ void serial_svc_stop() { if(status) { FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status); + } // Delete service status = aci_gatt_del_service(serial_svc->svc_handle); if(status) { @@ -242,3 +288,8 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { return true; } + +void serial_svc_set_rpc_status(SerialServiceRpcStatus status) { + furi_assert(serial_svc); + serial_svc_update_rpc_char(status); +} diff --git a/firmware/targets/f7/ble_glue/serial_service.h b/firmware/targets/f7/ble_glue/serial_service.h index a1e5bc1cc..7d38066f4 100644 --- a/firmware/targets/f7/ble_glue/serial_service.h +++ b/firmware/targets/f7/ble_glue/serial_service.h @@ -10,9 +10,15 @@ extern "C" { #endif +typedef enum { + SerialServiceRpcStatusNotActive = 0UL, + SerialServiceRpcStatusActive = 1UL, +} SerialServiceRpcStatus; + typedef enum { SerialServiceEventTypeDataReceived, SerialServiceEventTypeDataSent, + SerialServiceEventTypesBleResetRequest, } SerialServiceEventType; typedef struct { @@ -34,6 +40,8 @@ void serial_svc_set_callbacks( SerialServiceEventCallback callback, void* context); +void serial_svc_set_rpc_status(SerialServiceRpcStatus status); + void serial_svc_notify_buffer_is_empty(); void serial_svc_stop(); diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 7ff8cd573..81a8bf60a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -60,26 +60,25 @@ void furi_hal_init() { furi_hal_crypto_init(); - // USB -#ifndef FURI_RAM_EXEC - furi_hal_usb_init(); - FURI_LOG_I(TAG, "USB OK"); -#endif - furi_hal_i2c_init(); // High Level furi_hal_power_init(); furi_hal_light_init(); + + furi_hal_bt_init(); + furi_hal_memory_init(); + furi_hal_compress_icon_init(); + #ifndef FURI_RAM_EXEC + // USB + furi_hal_usb_init(); + FURI_LOG_I(TAG, "USB OK"); furi_hal_vibro_init(); furi_hal_subghz_init(); furi_hal_nfc_init(); furi_hal_rfid_init(); #endif - furi_hal_bt_init(); - furi_hal_memory_init(); - furi_hal_compress_icon_init(); // FatFS driver initialization MX_FATFS_Init(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c index 9bdad5bf2..aa09dde52 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -31,6 +31,16 @@ void furi_hal_bt_serial_notify_buffer_is_empty() { serial_svc_notify_buffer_is_empty(); } +void furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatus status) { + SerialServiceRpcStatus st; + if(status == FuriHalBtSerialRpcStatusActive) { + st = SerialServiceRpcStatusActive; + } else { + st = SerialServiceRpcStatusNotActive; + } + serial_svc_set_rpc_status(st); +} + bool furi_hal_bt_serial_tx(uint8_t* data, uint16_t size) { if(size > FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX) { return false; diff --git a/firmware/targets/f7/furi_hal/furi_hal_cortex.c b/firmware/targets/f7/furi_hal/furi_hal_cortex.c index c9c8400a7..192b83ee4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_cortex.c +++ b/firmware/targets/f7/furi_hal/furi_hal_cortex.c @@ -2,6 +2,8 @@ #include +#define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) + void furi_hal_cortex_init_early() { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; @@ -13,11 +15,27 @@ void furi_hal_cortex_init_early() { void furi_hal_cortex_delay_us(uint32_t microseconds) { uint32_t start = DWT->CYCCNT; - uint32_t time_ticks = SystemCoreClock / 1000000 * microseconds; + uint32_t time_ticks = FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND * microseconds; while((DWT->CYCCNT - start) < time_ticks) { }; } uint32_t furi_hal_cortex_instructions_per_microsecond() { - return SystemCoreClock / 1000000; + return FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND; } + +FuriHalCortexTimer furi_hal_cortex_timer_get(uint32_t timeout_us) { + FuriHalCortexTimer cortex_timer = {0}; + cortex_timer.start = DWT->CYCCNT; + cortex_timer.value = FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND * timeout_us; + return cortex_timer; +} + +bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer) { + return !((DWT->CYCCNT - cortex_timer.start) < cortex_timer.value); +} + +void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer) { + while(!furi_hal_cortex_timer_is_expired(cortex_timer)) + ; +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index dbd8c58c2..e0ed3ab9b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -91,7 +91,7 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) { uint8_t keys_nb = 0; uint8_t valid_keys_nb = 0; uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; - uint8_t empty_iv[16]; + uint8_t empty_iv[16] = {0}; furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb); if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key if(key_slot > keys_nb) return false; diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c.c b/firmware/targets/f7/furi_hal/furi_hal_i2c.c index 36f5230c2..6c17d6ade 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -60,11 +61,11 @@ bool furi_hal_i2c_tx( furi_assert(timeout > 0); bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); do { while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -89,7 +90,7 @@ bool furi_hal_i2c_tx( size--; } - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -111,11 +112,11 @@ bool furi_hal_i2c_rx( furi_assert(timeout > 0); bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); do { while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -140,7 +141,7 @@ bool furi_hal_i2c_rx( size--; } - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -175,11 +176,11 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, furi_assert(timeout > 0); bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); do { while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -190,14 +191,14 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, while((!LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) && (!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c))) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } if(LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) { while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -214,7 +215,7 @@ bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, } while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -308,11 +309,11 @@ bool furi_hal_i2c_write_mem( bool ret = true; uint8_t size = len + 1; - uint32_t timeout_tick = furi_get_tick() + timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); do { while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } @@ -341,7 +342,7 @@ bool furi_hal_i2c_write_mem( size--; } - if(furi_get_tick() >= timeout_tick) { + if(furi_hal_cortex_timer_is_expired(timer)) { ret = false; break; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 069ac4ea4..2d27313ae 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; @@ -782,6 +786,17 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx( return rfalTransceiveBlockingTxRx(txBuf, txBufLen, rxBuf, rxBufLen, actLen, flags, fwt); } +FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( + uint8_t* txBuf, + uint16_t txBufLen, + uint8_t* rxBuf, + uint16_t rxBufLen, + uint16_t* actLen, + uint32_t flags, + uint32_t fwt) { + return rfalTransceiveBitsBlockingTxRx(txBuf, txBufLen, rxBuf, rxBufLen, actLen, flags, fwt); +} + void furi_hal_nfc_ll_poll() { rfalWorker(); } \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index d16c567e6..64e5e19f9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -10,7 +10,7 @@ extern "C" { #endif /* Input Related Constants */ -#define INPUT_DEBOUNCE_TICKS 30 +#define INPUT_DEBOUNCE_TICKS 4 /* Input Keys */ typedef enum { @@ -20,6 +20,7 @@ typedef enum { InputKeyLeft, InputKeyOk, InputKeyBack, + InputKeyMAX, /**< Special value */ } InputKey; /* Light */ diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 24dad38fa..c38cbfec5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -30,7 +30,8 @@ typedef struct { uint8_t log_reserved : 4; uint8_t flags; uint8_t boot_mode : 4; - uint16_t reserved : 12; + uint8_t heap_track_mode : 2; + uint16_t reserved : 10; } DeveloperReg; _Static_assert(sizeof(DeveloperReg) == 4, "DeveloperReg size mismatch"); @@ -224,6 +225,19 @@ FuriHalRtcBootMode furi_hal_rtc_get_boot_mode() { return (FuriHalRtcBootMode)data->boot_mode; } +void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + DeveloperReg* data = (DeveloperReg*)&data_reg; + data->heap_track_mode = mode; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + DeveloperReg* data = (DeveloperReg*)&data_reg; + return (FuriHalRtcHeapTrackMode)data->heap_track_mode; +} + void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { furi_assert(datetime); @@ -318,6 +332,12 @@ uint32_t furi_hal_rtc_get_pin_fails() { return furi_hal_rtc_get_register(FuriHalRtcRegisterPinFails); } +uint32_t furi_hal_rtc_get_timestamp() { + FuriHalRtcDateTime datetime = {0}; + furi_hal_rtc_get_datetime(&datetime); + return furi_hal_rtc_datetime_to_timestamp(&datetime); +} + uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { uint32_t timestamp = 0; uint8_t years = 0; diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 581351b07..5b59aa44e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -19,7 +19,6 @@ #define TAG "FuriHalSubGhz" - /* * Uncomment define to enable duplication of * IO GO0 CC1101 to an external comb. @@ -35,7 +34,7 @@ * Attention this setting switches pin to output. * Make sure it is not connected directly to power or ground */ - + //#define SUBGHZ_DEBUG_CC1101_PIN gpio_ext_pa7 #ifdef SUBGHZ_DEBUG_CC1101_PIN uint32_t subghz_debug_gpio_buff[2]; diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index 86b10c79c..1eaeffd6a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -80,10 +80,8 @@ void furi_hal_usb_init(void) { NVIC_EnableIRQ(USB_LP_IRQn); NVIC_EnableIRQ(USB_HP_IRQn); - usb.thread = furi_thread_alloc(); - furi_thread_set_name(usb.thread, "UsbDriver"); - furi_thread_set_stack_size(usb.thread, 1024); - furi_thread_set_callback(usb.thread, furi_hal_usb_thread); + usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL); + furi_thread_mark_as_service(usb.thread); furi_thread_start(usb.thread); FURI_LOG_I(TAG, "Init OK"); diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h index e1b7af224..1b6e79ab0 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h @@ -8,6 +8,11 @@ extern "C" { #define FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX SERIAL_SVC_DATA_LEN_MAX +typedef enum { + FuriHalBtSerialRpcStatusNotActive, + FuriHalBtSerialRpcStatusActive, +} FuriHalBtSerialRpcStatus; + /** Serial service callback type */ typedef SerialServiceEventCallback FuriHalBtSerialCallback; @@ -30,6 +35,12 @@ void furi_hal_bt_serial_set_event_callback( FuriHalBtSerialCallback callback, void* context); +/** Set BLE RPC status + * + * @param status FuriHalBtSerialRpcStatus instance + */ +void furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatus status); + /** Notify that application buffer is empty */ void furi_hal_bt_serial_notify_buffer_is_empty(); diff --git a/firmware/targets/furi_hal_include/furi_hal_cortex.h b/firmware/targets/furi_hal_include/furi_hal_cortex.h index 13035161d..91596ffe3 100644 --- a/firmware/targets/furi_hal_include/furi_hal_cortex.h +++ b/firmware/targets/furi_hal_include/furi_hal_cortex.h @@ -6,11 +6,18 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { #endif +/** Cortex timer provides high precision low level expiring timer */ +typedef struct { + uint32_t start; + uint32_t value; +} FuriHalCortexTimer; + /** Early init stage for cortex */ void furi_hal_cortex_init_early(); @@ -27,6 +34,28 @@ void furi_hal_cortex_delay_us(uint32_t microseconds); */ uint32_t furi_hal_cortex_instructions_per_microsecond(); +/** Get Timer + * + * @param[in] timeout_us The expire timeout in us + * + * @return The FuriHalCortexTimer + */ +FuriHalCortexTimer furi_hal_cortex_timer_get(uint32_t timeout_us); + +/** Check if timer expired + * + * @param[in] cortex_timer The FuriHalCortexTimer + * + * @return true if expired + */ +bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer); + +/** Wait for timer expire + * + * @param[in] cortex_timer The FuriHalCortexTimer + */ +void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 537e0abf0..d3f6de602 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); @@ -375,6 +398,7 @@ void furi_hal_nfc_ll_txrx_on(); void furi_hal_nfc_ll_txrx_off(); +// TODO rework all pollers with furi_hal_nfc_ll_txrx_bits FuriHalNfcReturn furi_hal_nfc_ll_txrx( uint8_t* txBuf, uint16_t txBufLen, @@ -384,6 +408,15 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx( uint32_t flags, uint32_t fwt); +FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( + uint8_t* txBuf, + uint16_t txBufLen, + uint8_t* rxBuf, + uint16_t rxBufLen, + uint16_t* actLen, + uint32_t flags, + uint32_t fwt); + void furi_hal_nfc_ll_poll(); #ifdef __cplusplus diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index bdae3b931..5ce122271 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -39,6 +39,13 @@ typedef enum { FuriHalRtcBootModePostUpdate, /**< Boot to Update, post update */ } FuriHalRtcBootMode; +typedef enum { + FuriHalRtcHeapTrackModeNone = 0, /**< Disable allocation tracking */ + FuriHalRtcHeapTrackModeMain, /**< Enable allocation tracking for main application thread */ + FuriHalRtcHeapTrackModeTree, /**< Enable allocation tracking for main and children application threads */ + FuriHalRtcHeapTrackModeAll, /**< Enable allocation tracking for all threads */ +} FuriHalRtcHeapTrackMode; + typedef enum { FuriHalRtcRegisterHeader, /**< RTC structure header */ FuriHalRtcRegisterSystem, /**< Various system bits */ @@ -79,6 +86,10 @@ void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode); FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(); +void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode); + +FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(); + void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime); void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime); @@ -93,6 +104,8 @@ void furi_hal_rtc_set_pin_fails(uint32_t value); uint32_t furi_hal_rtc_get_pin_fails(); +uint32_t furi_hal_rtc_get_timestamp(); + uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); #ifdef __cplusplus diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h index b6d132ac3..9a0e0bac5 100644 --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h @@ -180,7 +180,7 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); */ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); -/** Π‘heck if transmission is allowed on this frequency for your flipper region +/** Π‘heck if transmission is allowed on this frequency with your current config * * @param value frequency in Hz * diff --git a/furi/SConscript b/furi/SConscript index a751eb6e1..8f8caeb87 100644 --- a/furi/SConscript +++ b/furi/SConscript @@ -1,6 +1,10 @@ Import("env") -env.Append(LINT_SOURCES=["furi"]) +env.Append( + LINT_SOURCES=[ + Dir("."), + ] +) libenv = env.Clone(FW_LIB_NAME="furi") diff --git a/furi/core/check.c b/furi/core/check.c index 613b52f46..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 @@ -11,6 +12,45 @@ #include #include +PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; +PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; + +/** Load r12 value to __furi_check_message and store registers to __furi_check_registers */ +#define GET_MESSAGE_AND_STORE_REGISTERS() \ + asm volatile("ldr r11, =__furi_check_message \n" \ + "str r12, [r11] \n" \ + "ldr r12, =__furi_check_registers \n" \ + "stm r12, {r0-r11} \n" \ + : \ + : \ + : "memory"); + +/** 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" \ + "wfi \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); extern size_t xPortGetFreeHeapSize(void); extern size_t xPortGetMinimumEverFreeHeapSize(void); @@ -52,62 +92,61 @@ static void __furi_print_name(bool isr) { } } -static FURI_NORETURN void __furi_halt() { - asm volatile( -#ifdef FURI_DEBUG - "bkpt 0x00 \n" -#endif - "loop%=: \n" - "wfi \n" - "b loop%= \n" - : - : - : "memory"); - __builtin_unreachable(); -} - -FURI_NORETURN void furi_crash(const char* message) { - bool isr = FURI_IS_ISR(); +FURI_NORETURN void __furi_crash() { __disable_irq(); + GET_MESSAGE_AND_STORE_REGISTERS(); - if(message == NULL) { - message = "Fatal Error"; + bool isr = FURI_IS_IRQ_MODE(); + + if(__furi_check_message == NULL) { + __furi_check_message = "Fatal Error"; } furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); - furi_hal_console_puts(message); + furi_hal_console_puts(__furi_check_message); if(!isr) { __furi_print_stack_info(); } __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"); - __furi_halt(); -#else - furi_hal_rtc_set_fault_data((uint32_t)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(); } -FURI_NORETURN void furi_halt(const char* message) { - bool isr = FURI_IS_ISR(); +FURI_NORETURN void __furi_halt() { __disable_irq(); + GET_MESSAGE_AND_STORE_REGISTERS(); - if(message == NULL) { - message = "System halt requested."; + bool isr = FURI_IS_IRQ_MODE(); + + if(__furi_check_message == NULL) { + __furi_check_message = "System halt requested."; } furi_hal_console_puts("\r\n\033[0;31m[HALT]"); __furi_print_name(isr); - furi_hal_console_puts(message); + 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"); - __furi_halt(); + + // 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(); } diff --git a/furi/core/check.h b/furi/core/check.h index 30efdf010..192c5260e 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -1,3 +1,16 @@ +/** + * @file check.h + * + * Furi crash and assert functions. + * + * The main problem with crashing is that you can't do anything without disturbing registers, + * and if you disturb registers, you won't be able to see the correct register values in the debugger. + * + * Current solution works around it by passing the message through r12 and doing some magic with registers in crash function. + * r0-r10 are stored in the ram2 on crash routine start and restored at the end. + * The only register that is going to be lost is r11. + * + */ #pragma once #ifdef __cplusplus @@ -8,12 +21,44 @@ extern "C" { #define FURI_NORETURN noreturn #endif +/** Crash system */ +FURI_NORETURN void __furi_crash(); + +/** Halt system */ +FURI_NORETURN void __furi_halt(); + +/** Crash system with message. Show message after reboot. */ +#define furi_crash(message) \ + do { \ + register const void* r12 asm("r12") = (void*)message; \ + asm volatile("sukima%=:" : : "r"(r12)); \ + __furi_crash(); \ + } while(0) + +/** Halt system with message. */ +#define furi_halt(message) \ + do { \ + register const void* r12 asm("r12") = (void*)message; \ + asm volatile("sukima%=:" : : "r"(r12)); \ + __furi_halt(); \ + } while(0) + /** Check condition and crash if check failed */ -#define furi_check(__e) ((__e) ? (void)0 : furi_crash("furi_check failed\r\n")) +#define furi_check(__e) \ + do { \ + if(!(__e)) { \ + furi_crash("furi_check failed\r\n"); \ + } \ + } while(0) /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) ((__e) ? (void)0 : furi_crash("furi_assert failed\r\n")) +#define furi_assert(__e) \ + do { \ + if(!(__e)) { \ + furi_crash("furi_assert failed\r\n"); \ + } \ + } while(0) #else #define furi_assert(__e) \ do { \ @@ -21,12 +66,6 @@ extern "C" { } while(0) #endif -/** Crash system */ -FURI_NORETURN void furi_crash(const char* message); - -/** Halt system */ -FURI_NORETURN void furi_halt(const char* message); - #ifdef __cplusplus } #endif diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index 5d2a49910..07dd30a16 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -25,7 +25,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { uint32_t rflags; BaseType_t yield; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { rflags = (uint32_t)FuriStatusErrorResource; @@ -48,7 +48,7 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; uint32_t rflags; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { rflags = xEventGroupGetBitsFromISR(hEventGroup); if(xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) { @@ -73,7 +73,7 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) { EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; uint32_t rflags; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { rflags = xEventGroupGetBitsFromISR(hEventGroup); } else { rflags = xEventGroupGetBits(hEventGroup); diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 32b2875cc..ac51b4a20 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -150,8 +150,7 @@ void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) { vTaskSuspendAll(); { memmgr_heap_thread_trace_depth++; - furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) != NULL); - MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id); + furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id)); memmgr_heap_thread_trace_depth--; } (void)xTaskResumeAll(); @@ -212,7 +211,8 @@ static inline void traceFREE(void* pointer, size_t size) { MemmgrHeapAllocDict_t* alloc_dict = MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); if(alloc_dict) { - MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); + // In some cases thread may want to release memory that was not allocated by it + (void)MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); } memmgr_heap_thread_trace_depth--; } diff --git a/furi/core/mutex.c b/furi/core/mutex.c index 78ea05196..ab66b0f18 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -45,7 +45,7 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(hMutex == NULL) { stat = FuriStatusErrorParameter; @@ -85,7 +85,7 @@ FuriStatus furi_mutex_release(FuriMutex* instance) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(hMutex == NULL) { stat = FuriStatusErrorParameter; @@ -111,7 +111,7 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - if((FURI_IS_IRQ_MODE() != 0U) || (hMutex == NULL)) { + if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { owner = 0; } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); diff --git a/furi/core/semaphore.c b/furi/core/semaphore.c index a204cbe6e..8c99bfc54 100644 --- a/furi/core/semaphore.c +++ b/furi/core/semaphore.c @@ -45,7 +45,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { if(timeout != 0U) { stat = FuriStatusErrorParameter; } else { @@ -80,7 +80,7 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) { @@ -104,7 +104,7 @@ uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; uint32_t count; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore); } else { count = (uint32_t)uxSemaphoreGetCount(hSemaphore); diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c index b9d0629fe..2df84fa5b 100644 --- a/furi/core/stream_buffer.c +++ b/furi/core/stream_buffer.c @@ -23,7 +23,7 @@ size_t furi_stream_buffer_send( uint32_t timeout) { size_t ret; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { BaseType_t yield; ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield); portYIELD_FROM_ISR(yield); @@ -41,7 +41,7 @@ size_t furi_stream_buffer_receive( uint32_t timeout) { size_t ret; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { BaseType_t yield; ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield); portYIELD_FROM_ISR(yield); diff --git a/furi/core/string.c b/furi/core/string.c index 099f70c11..901b1f625 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -29,13 +29,13 @@ FuriString* furi_string_alloc() { } FuriString* furi_string_alloc_set(const FuriString* s) { - FuriString* string = malloc(sizeof(FuriString)); + FuriString* string = malloc(sizeof(FuriString)); //-V773 string_init_set(string->string, s->string); return string; } FuriString* furi_string_alloc_set_str(const char cstr[]) { - FuriString* string = malloc(sizeof(FuriString)); + FuriString* string = malloc(sizeof(FuriString)); //-V773 string_init_set(string->string, cstr); return string; } diff --git a/furi/core/string.h b/furi/core/string.h index 01b26cce1..0523d3ba0 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -718,22 +718,27 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico */ #define F_STR_INIT_SET(a, b) ((a) = furi_string_alloc_set(b)) +/** + * @brief INIT MOVE OPLIST for FuriString. + */ +#define F_STR_INIT_MOVE(a, b) ((a) = furi_string_alloc_move(b)) + /** * @brief OPLIST for FuriString. */ -#define FURI_STRING_OPLIST \ - (INIT(F_STR_INIT), \ - INIT_SET(F_STR_INIT_SET), \ - SET(furi_string_set), \ - INIT_MOVE(furi_string_alloc_move), \ - MOVE(furi_string_move), \ - SWAP(furi_string_swap), \ - RESET(furi_string_reset), \ - EMPTY_P(furi_string_empty), \ - CLEAR(furi_string_free), \ - HASH(furi_string_hash), \ - EQUAL(furi_string_equal), \ - CMP(furi_string_cmp), \ +#define FURI_STRING_OPLIST \ + (INIT(F_STR_INIT), \ + INIT_SET(F_STR_INIT_SET), \ + SET(furi_string_set), \ + INIT_MOVE(F_STR_INIT_MOVE), \ + MOVE(furi_string_move), \ + SWAP(furi_string_swap), \ + RESET(furi_string_reset), \ + EMPTY_P(furi_string_empty), \ + CLEAR(furi_string_free), \ + HASH(furi_string_hash), \ + EQUAL(furi_string_equal), \ + CMP(furi_string_cmp), \ TYPE(FuriString*)) #ifdef __cplusplus diff --git a/furi/core/thread.c b/furi/core/thread.c index 508146f63..34cc7d987 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -12,6 +12,8 @@ #include #include +#define TAG "FuriThread" + #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers typedef struct FuriThreadStdout FuriThreadStdout; @@ -50,6 +52,7 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread); __attribute__((__noreturn__)) void furi_thread_catch() { asm volatile("nop"); // extra magic furi_crash("You are doing it wrong"); + __builtin_unreachable(); } static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) { @@ -81,6 +84,12 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); + furi_log_print_format( + thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, + TAG, + "%s allocation balance: %d", + thread->name ? thread->name : "Thread", + thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); } @@ -88,8 +97,8 @@ static void furi_thread_body(void* context) { if(thread->is_service) { FURI_LOG_E( - "Service", - "%s thread exited. Thread memory cannot be reclaimed.", + TAG, + "%s service thread exited. Thread memory cannot be reclaimed.", thread->name ? thread->name : ""); } @@ -112,6 +121,30 @@ FuriThread* furi_thread_alloc() { FuriThread* thread = malloc(sizeof(FuriThread)); thread->output.buffer = furi_string_alloc(); thread->is_service = false; + + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode == FuriHalRtcHeapTrackModeAll) { + thread->heap_trace_enabled = true; + } else if(mode == FuriHalRtcHeapTrackModeTree && furi_thread_get_current_id()) { + FuriThread* parent = pvTaskGetThreadLocalStoragePointer(NULL, 0); + if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled; + } else { + thread->heap_trace_enabled = false; + } + + return thread; +} + +FuriThread* furi_thread_alloc_ex( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context) { + FuriThread* thread = furi_thread_alloc(); + furi_thread_set_name(thread, name); + furi_thread_set_stack_size(thread, stack_size); + furi_thread_set_callback(thread, callback); + furi_thread_set_context(thread, context); return thread; } @@ -228,14 +261,12 @@ FuriThreadId furi_thread_get_id(FuriThread* thread) { void furi_thread_enable_heap_trace(FuriThread* thread) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); - furi_assert(thread->heap_trace_enabled == false); thread->heap_trace_enabled = true; } void furi_thread_disable_heap_trace(FuriThread* thread) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); - furi_assert(thread->heap_trace_enabled == true); thread->heap_trace_enabled = false; } diff --git a/furi/core/thread.h b/furi/core/thread.h index fda81bb3a..6ec8ebfec 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -60,6 +60,20 @@ typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context); */ FuriThread* furi_thread_alloc(); +/** Allocate FuriThread, shortcut version + * + * @param name + * @param stack_size + * @param callback + * @param context + * @return FuriThread* + */ +FuriThread* furi_thread_alloc_ex( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context); + /** Release FuriThread * * @param thread FuriThread instance 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 index 2acfea015..73899e58b --- a/furi/flipper.c +++ b/furi/flipper.c @@ -34,11 +34,11 @@ void flipper_init() { for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { FURI_LOG_I(TAG, "starting service %s", FLIPPER_SERVICES[i].name); - FuriThread* thread = furi_thread_alloc(); - - furi_thread_set_name(thread, FLIPPER_SERVICES[i].name); - furi_thread_set_stack_size(thread, FLIPPER_SERVICES[i].stack_size); - furi_thread_set_callback(thread, FLIPPER_SERVICES[i].app); + FuriThread* thread = furi_thread_alloc_ex( + FLIPPER_SERVICES[i].name, + FLIPPER_SERVICES[i].stack_size, + FLIPPER_SERVICES[i].app, + NULL); furi_thread_mark_as_service(thread); furi_thread_start(thread); diff --git a/lib/SConscript b/lib/SConscript index 0b1800a02..7cc0acf2d 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -2,28 +2,28 @@ Import("env") env.Append( LINT_SOURCES=[ - "lib/app-scened-template", - "lib/digital_signal", - "lib/drivers", - "lib/flipper_format", - "lib/infrared", - "lib/nfc", - "lib/one_wire", - "lib/ST25RFAL002", - "lib/subghz", - "lib/toolbox", - "lib/u8g2", - "lib/update_util", - "lib/print", + Dir("app-scened-template"), + Dir("digital_signal"), + Dir("drivers"), + Dir("flipper_format"), + Dir("infrared"), + Dir("nfc"), + Dir("one_wire"), + Dir("ST25RFAL002"), + Dir("subghz"), + Dir("toolbox"), + Dir("u8g2"), + Dir("update_util"), + Dir("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("#/lib/u8g2/u8g2.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"), + File("u8g2/u8g2.h"), ], ) diff --git a/lib/ST25RFAL002/include/rfal_rf.h b/lib/ST25RFAL002/include/rfal_rf.h index e1b864830..35e9a4454 100644 --- a/lib/ST25RFAL002/include/rfal_rf.h +++ b/lib/ST25RFAL002/include/rfal_rf.h @@ -1496,6 +1496,15 @@ ReturnCode rfalTransceiveBlockingTxRx( uint32_t flags, uint32_t fwt); +ReturnCode rfalTransceiveBitsBlockingTxRx( + uint8_t* txBuf, + uint16_t txBufLen, + uint8_t* rxBuf, + uint16_t rxBufLen, + uint16_t* actLen, + uint32_t flags, + uint32_t fwt); + ReturnCode rfalTransceiveBitsBlockingTx( uint8_t* txBuf, uint16_t txBufLen, diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index af9dc6ff7..754e25650 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -45,11 +45,9 @@ void platformDisableIrqCallback() { void platformSetIrqCallback(PlatformIrqCallback callback) { rfal_platform.callback = callback; - rfal_platform.thread = furi_thread_alloc(); - - furi_thread_set_name(rfal_platform.thread, "RfalIrqDriver"); - furi_thread_set_callback(rfal_platform.thread, rfal_platform_irq_thread); - furi_thread_set_stack_size(rfal_platform.thread, 1024); + rfal_platform.thread = + furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); + furi_thread_mark_as_service(rfal_platform.thread); furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); furi_thread_start(rfal_platform.thread); diff --git a/lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c b/lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c index 9ad35bcb6..0bad67a6d 100644 --- a/lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c +++ b/lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c @@ -1607,6 +1607,23 @@ ReturnCode rfalTransceiveBlockingTxRx( return ret; } +ReturnCode rfalTransceiveBitsBlockingTxRx( + uint8_t* txBuf, + uint16_t txBufLen, + uint8_t* rxBuf, + uint16_t rxBufLen, + uint16_t* actLen, + uint32_t flags, + uint32_t fwt) { + ReturnCode ret; + + EXIT_ON_ERR( + ret, rfalTransceiveBitsBlockingTx(txBuf, txBufLen, rxBuf, rxBufLen, actLen, flags, fwt)); + ret = rfalTransceiveBlockingRx(); + + return ret; +} + /*******************************************************************************/ static ReturnCode rfalRunTransceiveWorker(void) { if(gRFAL.state == RFAL_STATE_TXRX) { diff --git a/lib/STM32CubeWB.scons b/lib/STM32CubeWB.scons index a00b07d98..cbdde9814 100644 --- a/lib/STM32CubeWB.scons +++ b/lib/STM32CubeWB.scons @@ -15,8 +15,8 @@ env.Append( ], SDK_HEADERS=[ *env.GlobRecursive("*_ll_*.h", "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/", exclude="*usb.h"), - File("#/lib/STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h"), - File("#/lib/STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h"), + File("STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h"), + File("STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h"), ], ) diff --git a/lib/drivers/SConscript b/lib/drivers/SConscript index 1ca613289..3b7ee2401 100644 --- a/lib/drivers/SConscript +++ b/lib/drivers/SConscript @@ -4,9 +4,6 @@ env.Append( CPPPATH=[ "#/lib/drivers", ], - SDK_HEADERS=[ - File("#/lib/drivers/nrf24.h"), - ], ) diff --git a/lib/drivers/nrf24.c b/lib/drivers/nrf24.c deleted file mode 100644 index 2905072d3..000000000 --- a/lib/drivers/nrf24.c +++ /dev/null @@ -1,513 +0,0 @@ -#include "nrf24.h" -#include -#include -#include -#include -#include - -void nrf24_init() { - furi_hal_spi_bus_handle_init(nrf24_HANDLE); - furi_hal_spi_acquire(nrf24_HANDLE); - furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); - furi_hal_gpio_write(nrf24_CE_PIN, false); -} - -void nrf24_spi_trx( - FuriHalSpiBusHandle* handle, - uint8_t* tx, - uint8_t* rx, - uint8_t size, - uint32_t timeout) { - UNUSED(timeout); - furi_hal_gpio_write(handle->cs, false); - furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT); - furi_hal_gpio_write(handle->cs, true); -} - -uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { - uint8_t tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data}; - uint8_t rx[2] = {0}; - nrf24_spi_trx(handle, tx, rx, 2, nrf24_TIMEOUT); - return rx[0]; -} - -uint8_t - nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { - uint8_t tx[size + 1]; - uint8_t rx[size + 1]; - memset(rx, 0, size + 1); - tx[0] = W_REGISTER | (REGISTER_MASK & reg); - memcpy(&tx[1], data, size); - nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); - return rx[0]; -} - -uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { - uint8_t tx[size + 1]; - uint8_t rx[size + 1]; - memset(rx, 0, size + 1); - tx[0] = R_REGISTER | (REGISTER_MASK & reg); - memset(&tx[1], 0, size); - nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); - memcpy(data, &rx[1], size); - return rx[0]; -} - -uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) { - uint8_t tx[] = {FLUSH_RX}; - uint8_t rx[] = {0}; - nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); - return rx[0]; -} - -uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle) { - uint8_t tx[] = {FLUSH_TX}; - uint8_t rx[] = {0}; - nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); - return rx[0]; -} - -uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle) { - uint8_t maclen; - nrf24_read_reg(handle, REG_SETUP_AW, &maclen, 1); - maclen &= 3; - return maclen + 2; -} - -uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) { - assert(maclen > 1 && maclen < 6); - uint8_t status = 0; - status = nrf24_write_reg(handle, REG_SETUP_AW, maclen - 2); - return status; -} - -uint8_t nrf24_status(FuriHalSpiBusHandle* handle) { - uint8_t status; - uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)}; - nrf24_spi_trx(handle, tx, &status, 1, nrf24_TIMEOUT); - return status; -} - -uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) { - uint8_t setup = 0; - uint32_t rate = 0; - nrf24_read_reg(handle, REG_RF_SETUP, &setup, 1); - setup &= 0x28; - if(setup == 0x20) - rate = 250000; // 250kbps - else if(setup == 0x08) - rate = 2000000; // 2Mbps - else if(setup == 0x00) - rate = 1000000; // 1Mbps - - return rate; -} - -uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate) { - uint8_t r6 = 0; - uint8_t status = 0; - if(!rate) rate = 2000000; - - nrf24_read_reg(handle, REG_RF_SETUP, &r6, 1); // RF_SETUP register - r6 = r6 & (~0x28); // Clear rate fields. - if(rate == 2000000) - r6 = r6 | 0x08; - else if(rate == 1000000) - r6 = r6; - else if(rate == 250000) - r6 = r6 | 0x20; - - status = nrf24_write_reg(handle, REG_RF_SETUP, r6); // Write new rate. - return status; -} - -uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle) { - uint8_t channel = 0; - nrf24_read_reg(handle, REG_RF_CH, &channel, 1); - return channel; -} - -uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan) { - uint8_t status; - status = nrf24_write_reg(handle, REG_RF_CH, chan); - return status; -} - -uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { - uint8_t size = 0; - uint8_t status = 0; - size = nrf24_get_maclen(handle); - status = nrf24_read_reg(handle, REG_RX_ADDR_P0, mac, size); - return status; -} - -uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { - uint8_t status = 0; - uint8_t clearmac[] = {0, 0, 0, 0, 0}; - nrf24_set_maclen(handle, size); - nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, clearmac, 5); - status = nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, mac, size); - return status; -} - -uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { - uint8_t size = 0; - uint8_t status = 0; - size = nrf24_get_maclen(handle); - status = nrf24_read_reg(handle, REG_TX_ADDR, mac, size); - return status; -} - -uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { - uint8_t status = 0; - uint8_t clearmac[] = {0, 0, 0, 0, 0}; - nrf24_set_maclen(handle, size); - nrf24_write_buf_reg(handle, REG_TX_ADDR, clearmac, 5); - status = nrf24_write_buf_reg(handle, REG_TX_ADDR, mac, size); - return status; -} - -uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle) { - uint8_t len = 0; - nrf24_read_reg(handle, RX_PW_P0, &len, 1); - return len; -} - -uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { - uint8_t status = 0; - status = nrf24_write_reg(handle, RX_PW_P0, len); - return status; -} - -uint8_t - nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full) { - uint8_t status = 0; - uint8_t size = 0; - uint8_t tx_pl_wid[] = {R_RX_PL_WID, 0}; - uint8_t rx_pl_wid[] = {0, 0}; - uint8_t tx_cmd[33] = {0}; // 32 max payload size + 1 for command - uint8_t tmp_packet[33] = {0}; - - status = nrf24_status(handle); - - if(status & 0x40) { - if(full) - size = nrf24_get_packetlen(handle); - else { - nrf24_spi_trx(handle, tx_pl_wid, rx_pl_wid, 2, nrf24_TIMEOUT); - size = rx_pl_wid[1]; - } - - tx_cmd[0] = R_RX_PAYLOAD; - nrf24_spi_trx(handle, tx_cmd, tmp_packet, size + 1, nrf24_TIMEOUT); - nrf24_write_reg(handle, REG_STATUS, 0x40); // clear bit. - memcpy(packet, &tmp_packet[1], size); - } else if(status == 0) { - nrf24_flush_rx(handle); - nrf24_write_reg(handle, REG_STATUS, 0x40); // clear bit. - } - - *packetsize = size; - return status; -} - -uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) { - uint8_t status = 0; - uint8_t tx[size + 1]; - uint8_t rx[size + 1]; - memset(tx, 0, size + 1); - memset(rx, 0, size + 1); - - if(!ack) - tx[0] = W_TX_PAYLOAD_NOACK; - else - tx[0] = W_TX_PAYLOAD; - - memcpy(&tx[1], payload, size); - nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); - nrf24_set_tx_mode(handle); - - while(!(status & (TX_DS | MAX_RT))) status = nrf24_status(handle); - - if(status & MAX_RT) nrf24_flush_tx(handle); - - nrf24_set_idle(handle); - nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); - return status & TX_DS; -} - -uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle) { - uint8_t status = 0; - uint8_t cfg = 0; - nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); - cfg = cfg | 2; - status = nrf24_write_reg(handle, REG_CONFIG, cfg); - furi_delay_ms(5000); - return status; -} - -uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) { - uint8_t status = 0; - uint8_t cfg = 0; - nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); - cfg &= 0xfc; // clear bottom two bits to power down the radio - status = nrf24_write_reg(handle, REG_CONFIG, cfg); - //nr204_write_reg(handle, REG_EN_RXADDR, 0x0); - furi_hal_gpio_write(nrf24_CE_PIN, false); - return status; -} - -uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) { - uint8_t status = 0; - uint8_t cfg = 0; - //status = nrf24_write_reg(handle, REG_CONFIG, 0x0F); // enable 2-byte CRC, PWR_UP, and PRIM_RX - nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); - cfg |= 0x03; // PWR_UP, and PRIM_RX - status = nrf24_write_reg(handle, REG_CONFIG, cfg); - //nr204_write_reg(REG_EN_RXADDR, 0x03) // Set RX Pipe 0 and 1 - furi_hal_gpio_write(nrf24_CE_PIN, true); - furi_delay_ms(2000); - return status; -} - -uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) { - uint8_t status = 0; - uint8_t cfg = 0; - furi_hal_gpio_write(nrf24_CE_PIN, false); - nrf24_write_reg(handle, REG_STATUS, 0x30); - //status = nrf24_write_reg(handle, REG_CONFIG, 0x0E); // enable 2-byte CRC, PWR_UP - nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); - cfg &= 0xfe; // disable PRIM_RX - cfg |= 0x02; // PWR_UP - status = nrf24_write_reg(handle, REG_CONFIG, cfg); - furi_hal_gpio_write(nrf24_CE_PIN, true); - furi_delay_ms(2); - return status; -} - -void nrf24_configure( - FuriHalSpiBusHandle* handle, - uint8_t rate, - uint8_t* srcmac, - uint8_t* dstmac, - uint8_t maclen, - uint8_t channel, - bool noack, - bool disable_aa) { - assert(channel <= 125); - assert(rate == 1 || rate == 2); - if(rate == 2) - rate = 8; // 2Mbps - else - rate = 0; // 1Mbps - - nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF - nrf24_set_idle(handle); - nrf24_write_reg(handle, REG_STATUS, 0x1c); // clear interrupts - if(disable_aa) - nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst - else - nrf24_write_reg(handle, REG_EN_AA, 0x1F); // Enable Shockburst - - nrf24_write_reg(handle, REG_DYNPD, 0x3F); // enable dynamic payload length on all pipes - if(noack) - nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack - else { - nrf24_write_reg(handle, REG_CONFIG, 0x0C); // 2 byte CRC - nrf24_write_reg(handle, REG_FEATURE, 0x07); // enable dyn payload and ack - nrf24_write_reg( - handle, REG_SETUP_RETR, 0x1f); // 15 retries for AA, 500us auto retransmit delay - } - - nrf24_set_idle(handle); - nrf24_flush_rx(handle); - nrf24_flush_tx(handle); - - if(maclen) nrf24_set_maclen(handle, maclen); - if(srcmac) nrf24_set_src_mac(handle, srcmac, maclen); - if(dstmac) nrf24_set_dst_mac(handle, dstmac, maclen); - - nrf24_write_reg(handle, REG_RF_CH, channel); - nrf24_write_reg(handle, REG_RF_SETUP, rate); - furi_delay_ms(200); -} - -void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate) { - //uint8_t preamble[] = {0x55, 0x00}; // little endian - uint8_t preamble[] = {0xAA, 0x00}; // little endian - //uint8_t preamble[] = {0x00, 0x55}; // little endian - //uint8_t preamble[] = {0x00, 0xAA}; // little endian - nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF - nrf24_write_reg(handle, REG_STATUS, 0x1c); // clear interrupts - nrf24_write_reg(handle, REG_DYNPD, 0x0); // disable shockburst - nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst - nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack - nrf24_set_maclen(handle, 2); // shortest address - nrf24_set_src_mac(handle, preamble, 2); // set src mac to preamble bits to catch everything - nrf24_set_packetlen(handle, 32); // set max packet length - nrf24_set_idle(handle); - nrf24_flush_rx(handle); - nrf24_flush_tx(handle); - nrf24_write_reg(handle, REG_RF_CH, channel); - nrf24_write_reg(handle, REG_RF_SETUP, rate); - - // prime for RX, no checksum - nrf24_write_reg(handle, REG_CONFIG, 0x03); // PWR_UP and PRIM_RX, disable AA and CRC - furi_hal_gpio_write(nrf24_CE_PIN, true); - furi_delay_ms(100); -} - -void hexlify(uint8_t* in, uint8_t size, char* out) { - memset(out, 0, size * 2); - for(int i = 0; i < size; i++) - snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]); -} - -uint64_t bytes_to_int64(uint8_t* bytes, uint8_t size, bool bigendian) { - uint64_t ret = 0; - for(int i = 0; i < size; i++) - if(bigendian) - ret |= bytes[i] << ((size - 1 - i) * 8); - else - ret |= bytes[i] << (i * 8); - - return ret; -} - -void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian) { - for(int i = 0; i < 8; i++) { - if(bigendian) - out[i] = (val >> ((7 - i) * 8)) & 0xff; - else - out[i] = (val >> (i * 8)) & 0xff; - } -} - -uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian) { - uint32_t ret = 0; - for(int i = 0; i < 4; i++) - if(bigendian) - ret |= bytes[i] << ((3 - i) * 8); - else - ret |= bytes[i] << (i * 8); - - return ret; -} - -void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian) { - for(int i = 0; i < 4; i++) { - if(bigendian) - out[i] = (val >> ((3 - i) * 8)) & 0xff; - else - out[i] = (val >> (i * 8)) & 0xff; - } -} - -uint64_t bytes_to_int16(uint8_t* bytes, bool bigendian) { - uint16_t ret = 0; - for(int i = 0; i < 2; i++) - if(bigendian) - ret |= bytes[i] << ((1 - i) * 8); - else - ret |= bytes[i] << (i * 8); - - return ret; -} - -void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) { - for(int i = 0; i < 2; i++) { - if(bigendian) - out[i] = (val >> ((1 - i) * 8)) & 0xff; - else - out[i] = (val >> (i * 8)) & 0xff; - } -} - -// handle iffyness with preamble processing sometimes being a bit (literally) off -void alt_address_old(uint8_t* packet, uint8_t* altaddr) { - uint8_t macmess_hi_b[4]; - uint8_t macmess_lo_b[2]; - uint32_t macmess_hi; - uint16_t macmess_lo; - uint8_t preserved; - - // get first 6 bytes into 32-bit and 16-bit variables - memcpy(macmess_hi_b, packet, 4); - memcpy(macmess_lo_b, packet + 4, 2); - - macmess_hi = bytes_to_int32(macmess_hi_b, true); - - //preserve least 7 bits from hi that will be shifted down to lo - preserved = macmess_hi & 0x7f; - macmess_hi >>= 7; - - macmess_lo = bytes_to_int16(macmess_lo_b, true); - macmess_lo >>= 7; - macmess_lo = (preserved << 9) | macmess_lo; - int32_to_bytes(macmess_hi, macmess_hi_b, true); - int16_to_bytes(macmess_lo, macmess_lo_b, true); - memcpy(altaddr, &macmess_hi_b[1], 3); - memcpy(altaddr + 3, macmess_lo_b, 2); -} - -bool validate_address(uint8_t* addr) { - uint8_t bad[][3] = {{0x55, 0x55}, {0xAA, 0xAA}, {0x00, 0x00}, {0xFF, 0xFF}}; - for(int i = 0; i < 4; i++) - for(int j = 0; j < 2; j++) - if(!memcmp(addr + j * 2, bad[i], 2)) return false; - - return true; -} - -bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address) { - bool found = false; - uint8_t packet[32] = {0}; - uint8_t packetsize; - //char printit[65]; - uint8_t status = 0; - status = nrf24_rxpacket(handle, packet, &packetsize, true); - if(status & 0x40) { - if(validate_address(packet)) { - for(int i = 0; i < maclen; i++) address[i] = packet[maclen - 1 - i]; - - /* - alt_address(packet, packet); - - for(i = 0; i < maclen; i++) - address[i + 5] = packet[maclen - 1 - i]; - */ - - //memcpy(address, packet, maclen); - //hexlify(packet, packetsize, printit); - found = true; - } - } - - return found; -} - -uint8_t nrf24_find_channel( - FuriHalSpiBusHandle* handle, - uint8_t* srcmac, - uint8_t* dstmac, - uint8_t maclen, - uint8_t rate, - uint8_t min_channel, - uint8_t max_channel, - bool autoinit) { - uint8_t ping_packet[] = {0x0f, 0x0f, 0x0f, 0x0f}; // this can be anything, we just need an ack - uint8_t ch = max_channel + 1; // means fail - nrf24_configure(handle, rate, srcmac, dstmac, maclen, 2, false, false); - for(ch = min_channel; ch <= max_channel + 1; ch++) { - nrf24_write_reg(handle, REG_RF_CH, ch); - if(nrf24_txpacket(handle, ping_packet, 4, true)) break; - } - - if(autoinit) { - FURI_LOG_D("nrf24", "initializing radio for channel %d", ch); - nrf24_configure(handle, rate, srcmac, dstmac, maclen, ch, false, false); - return ch; - } - - return ch; -} \ No newline at end of file diff --git a/lib/drivers/nrf24.h b/lib/drivers/nrf24.h deleted file mode 100644 index 584eaaea7..000000000 --- a/lib/drivers/nrf24.h +++ /dev/null @@ -1,361 +0,0 @@ -#pragma once -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define R_REGISTER 0x00 -#define W_REGISTER 0x20 -#define REGISTER_MASK 0x1F -#define ACTIVATE 0x50 -#define R_RX_PL_WID 0x60 -#define R_RX_PAYLOAD 0x61 -#define W_TX_PAYLOAD 0xA0 -#define W_TX_PAYLOAD_NOACK 0xB0 -#define W_ACK_PAYLOAD 0xA8 -#define FLUSH_TX 0xE1 -#define FLUSH_RX 0xE2 -#define REUSE_TX_PL 0xE3 -#define RF24_NOP 0xFF - -#define REG_CONFIG 0x00 -#define REG_EN_AA 0x01 -#define REG_EN_RXADDR 0x02 -#define REG_SETUP_AW 0x03 -#define REG_SETUP_RETR 0x04 -#define REG_DYNPD 0x1C -#define REG_FEATURE 0x1D -#define REG_RF_SETUP 0x06 -#define REG_STATUS 0x07 -#define REG_RX_ADDR_P0 0x0A -#define REG_RF_CH 0x05 -#define REG_TX_ADDR 0x10 - -#define RX_PW_P0 0x11 -#define TX_DS 0x20 -#define MAX_RT 0x10 - -#define nrf24_TIMEOUT 500 -#define nrf24_CE_PIN &gpio_ext_pb2 -#define nrf24_HANDLE &furi_hal_spi_bus_handle_external - -/* Low level API */ - -/** Write device register - * - * @param handle - pointer to FuriHalSpiHandle - * @param reg - register - * @param data - data to write - * - * @return device status - */ -uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); - -/** Write buffer to device register - * - * @param handle - pointer to FuriHalSpiHandle - * @param reg - register - * @param data - data to write - * @param size - size of data to write - * - * @return device status - */ -uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); - -/** Read device register - * - * @param handle - pointer to FuriHalSpiHandle - * @param reg - register - * @param[out] data - pointer to data - * - * @return device status - */ -uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); - -/** Power up the radio for operation - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return device status - */ -uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle); - -/** Power down the radio - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return device status - */ -uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle); - -/** Sets the radio to RX mode - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return device status - */ -uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle); - -/** Sets the radio to TX mode - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return device status - */ -uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle); - -/*=============================================================================================================*/ - -/* High level API */ - -/** Must call this before using any other nrf24 API - * - */ -void nrf24_init(); - -/** Send flush rx command - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return device status - */ -uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle); - -/** Send flush tx command - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return device status - */ -uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle); - -/** Gets the RX packet length in data pipe 0 - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return packet length in data pipe 0 - */ -uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle); - -/** Sets the RX packet length in data pipe 0 - * - * @param handle - pointer to FuriHalSpiHandle - * @param len - length to set - * - * @return device status - */ -uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len); - -/** Gets configured length of MAC address - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return MAC address length - */ -uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle); - -/** Sets configured length of MAC address - * - * @param handle - pointer to FuriHalSpiHandle - * @param maclen - length to set MAC address to, must be greater than 1 and less than 6 - * - * @return MAC address length - */ -uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen); - -/** Gets the current status flags from the STATUS register - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return status flags - */ -uint8_t nrf24_status(FuriHalSpiBusHandle* handle); - -/** Gets the current transfer rate - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return transfer rate in bps - */ -uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle); - -/** Sets the transfer rate - * - * @param handle - pointer to FuriHalSpiHandle - * @param rate - the transfer rate in bps - * - * @return device status - */ -uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate); - -/** Gets the current channel - * In nrf24, the channel number is multiplied times 1MHz and added to 2400MHz to get the frequency - * - * @param handle - pointer to FuriHalSpiHandle - * - * @return channel - */ -uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle); - -/** Sets the channel - * - * @param handle - pointer to FuriHalSpiHandle - * @param frequency - the frequency in hertz - * - * @return device status - */ -uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan); - -/** Gets the source mac address - * - * @param handle - pointer to FuriHalSpiHandle - * @param[out] mac - the source mac address - * - * @return device status - */ -uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); - -/** Sets the source mac address - * - * @param handle - pointer to FuriHalSpiHandle - * @param mac - the mac address to set - * @param size - the size of the mac address (2 to 5) - * - * @return device status - */ -uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); - -/** Gets the dest mac address - * - * @param handle - pointer to FuriHalSpiHandle - * @param[out] mac - the source mac address - * - * @return device status - */ -uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); - -/** Sets the dest mac address - * - * @param handle - pointer to FuriHalSpiHandle - * @param mac - the mac address to set - * @param size - the size of the mac address (2 to 5) - * - * @return device status - */ -uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); - -/** Reads RX packet - * - * @param handle - pointer to FuriHalSpiHandle - * @param[out] packet - the packet contents - * @param[out] packetsize - size of the received packet - * @param full - boolean set to true, packet length is determined by RX_PW_P0 register, false it is determined by dynamic payload length command - * - * @return device status - */ -uint8_t - nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full); - -/** Sends TX packet - * - * @param handle - pointer to FuriHalSpiHandle - * @param packet - the packet contents - * @param size - packet size - * @param ack - boolean to determine whether an ACK is required for the packet or not - * - * @return device status - */ -uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack); - -/** Configure the radio - * This is not comprehensive, but covers a lot of the common configuration options that may be changed - * @param handle - pointer to FuriHalSpiHandle - * @param rate - transfer rate in Mbps (1 or 2) - * @param srcmac - source mac address - * @param dstmac - destination mac address - * @param maclen - length of mac address - * @param channel - channel to tune to - * @param noack - if true, disable auto-acknowledge - * @param disable_aa - if true, disable ShockBurst - * - */ -void nrf24_configure( - FuriHalSpiBusHandle* handle, - uint8_t rate, - uint8_t* srcmac, - uint8_t* dstmac, - uint8_t maclen, - uint8_t channel, - bool noack, - bool disable_aa); - -/** Configures the radio for "promiscuous mode" and primes it for rx - * This is not an actual mode of the nrf24, but this function exploits a few bugs in the chip that allows it to act as if it were. - * See http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html for details. - * @param handle - pointer to FuriHalSpiHandle - * @param channel - channel to tune to - * @param rate - transfer rate in Mbps (1 or 2) - */ -void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate); - -/** Listens for a packet and returns first possible address sniffed - * Call this only after calling nrf24_init_promisc_mode - * @param handle - pointer to FuriHalSpiHandle - * @param maclen - length of target mac address - * @param[out] addresses - sniffed address - * - * @return success - */ -bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address); - -/** Sends ping packet on each channel for designated tx mac looking for ack - * - * @param handle - pointer to FuriHalSpiHandle - * @param srcmac - source address - * @param dstmac - destination address - * @param maclen - length of address - * @param rate - transfer rate in Mbps (1 or 2) - * @param min_channel - channel to start with - * @param max_channel - channel to end at - * @param autoinit - if true, automatically configure radio for this channel - * - * @return channel that the address is listening on, if this value is above the max_channel param, it failed - */ -uint8_t nrf24_find_channel( - FuriHalSpiBusHandle* handle, - uint8_t* srcmac, - uint8_t* dstmac, - uint8_t maclen, - uint8_t rate, - uint8_t min_channel, - uint8_t max_channel, - bool autoinit); - -/** Converts 64 bit value into uint8_t array - * @param val - 64-bit integer - * @param[out] out - bytes out - * @param bigendian - if true, convert as big endian, otherwise little endian - */ -void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian); - -/** Converts 32 bit value into uint8_t array - * @param val - 32-bit integer - * @param[out] out - bytes out - * @param bigendian - if true, convert as big endian, otherwise little endian - */ -void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian); - -/** Converts uint8_t array into 32 bit value - * @param bytes - uint8_t array - * @param bigendian - if true, convert as big endian, otherwise little endian - * - * @return 32-bit value - */ -uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); - -#ifdef __cplusplus -} -#endif \ No newline at end of file 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_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 3fe57162f..2082e550f 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -786,6 +786,16 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache)); AddressCache_clear(elf->relocation_cache); + { + size_t total_size = 0; + for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); + ELFSectionDict_next(it)) { + ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); + total_size += itref->value.size; + } + FURI_LOG_I(TAG, "Total size of loaded sections: %u", total_size); + } + return status; } diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index cf44eebb2..618a36231 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -104,11 +104,8 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); furi_check(manifest->stack_size > 0); - app->thread = furi_thread_alloc(); - furi_thread_set_stack_size(app->thread, manifest->stack_size); - furi_thread_set_name(app->thread, manifest->name); - furi_thread_set_callback(app->thread, flipper_application_thread); - furi_thread_set_context(app->thread, args); + app->thread = furi_thread_alloc_ex( + manifest->name, manifest->stack_size, flipper_application_thread, args); return app->thread; } 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/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index 41934a3b1..9cce95d47 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -313,7 +313,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa furi_crash("Unknown FF type"); } - if((size_t)(i + 1) < write_data->data_size) { + if(((size_t)i + 1) < write_data->data_size) { furi_string_cat(value, " "); } 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/infrared/encoder_decoder/common/infrared_common_decoder.c b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c index bff4c73db..7f1c3a4fd 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c @@ -85,8 +85,8 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder if(timings->min_split_time && !level) { if(timing > timings->min_split_time) { /* long low timing - check if we're ready for any of protocol modification */ - for(size_t i = 0; decoder->protocol->databit_len[i] && - (i < COUNT_OF(decoder->protocol->databit_len)); + for(size_t i = 0; i < COUNT_OF(decoder->protocol->databit_len) && + decoder->protocol->databit_len[i]; ++i) { if(decoder->protocol->databit_len[i] == decoder->databit_cnt) { return InfraredStatusReady; @@ -199,7 +199,7 @@ InfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* deco bool found_length = false; for(size_t i = 0; - decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len)); + i < COUNT_OF(decoder->protocol->databit_len) && decoder->protocol->databit_len[i]; ++i) { if(decoder->protocol->databit_len[i] == decoder->databit_cnt) { found_length = true; diff --git a/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c b/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c index e8f7664a7..3dd26e9d8 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c @@ -115,3 +115,26 @@ const InfraredCommonProtocolSpec protocol_sirc = { .decode_repeat = NULL, .encode_repeat = infrared_encoder_sirc_encode_repeat, }; + +const InfraredCommonProtocolSpec protocol_kaseikyo = { + .timings = + { + .preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK, + .preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE, + .bit1_mark = INFRARED_KASEIKYO_BIT1_MARK, + .bit1_space = INFRARED_KASEIKYO_BIT1_SPACE, + .bit0_mark = INFRARED_KASEIKYO_BIT0_MARK, + .bit0_space = INFRARED_KASEIKYO_BIT0_SPACE, + .preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE, + .silence_time = INFRARED_KASEIKYO_SILENCE, + .min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME, + }, + .databit_len[0] = 48, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_kaseikyo_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; diff --git a/lib/infrared/encoder_decoder/infrared.c b/lib/infrared/encoder_decoder/infrared.c index 71bd6bb6d..2c5ef0fff 100644 --- a/lib/infrared/encoder_decoder/infrared.c +++ b/lib/infrared/encoder_decoder/infrared.c @@ -110,6 +110,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .free = infrared_encoder_sirc_free}, .get_protocol_spec = infrared_sirc_get_spec, }, + { + .decoder = + {.alloc = infrared_decoder_kaseikyo_alloc, + .decode = infrared_decoder_kaseikyo_decode, + .reset = infrared_decoder_kaseikyo_reset, + .check_ready = infrared_decoder_kaseikyo_check_ready, + .free = infrared_decoder_kaseikyo_free}, + .encoder = + {.alloc = infrared_encoder_kaseikyo_alloc, + .encode = infrared_encoder_kaseikyo_encode, + .reset = infrared_encoder_kaseikyo_reset, + .free = infrared_encoder_kaseikyo_free}, + .get_protocol_spec = infrared_kaseikyo_get_spec, + }, }; static int infrared_find_index_by_protocol(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/infrared.h b/lib/infrared/encoder_decoder/infrared.h index 945913000..086950f1e 100644 --- a/lib/infrared/encoder_decoder/infrared.h +++ b/lib/infrared/encoder_decoder/infrared.h @@ -31,6 +31,7 @@ typedef enum { InfraredProtocolSIRC, InfraredProtocolSIRC15, InfraredProtocolSIRC20, + InfraredProtocolKaseikyo, InfraredProtocolMAX, } InfraredProtocol; diff --git a/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h b/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h index 575961f13..6146f7b4e 100644 --- a/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h +++ b/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h @@ -267,3 +267,54 @@ InfraredStatus infrared_encoder_sirc_encode_repeat( bool* level); extern const InfraredCommonProtocolSpec protocol_sirc; + +/*************************************************************************************************** +* Kaseikyo protocol description +* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble +* mark space Modulation up to period repeat repeat +* mark space +* +* 3360 1665 48 bit ...130000 3456 1728 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________ +* +***************************************************************************************************/ + +#define INFRARED_KASEIKYO_UNIT 432 +#define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_REPEAT_PERIOD 130000 +#define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD +#define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN +#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000 +#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000 +#define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK +#define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000) +#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us + +void* infrared_decoder_kaseikyo_alloc(void); +void infrared_decoder_kaseikyo_reset(void* decoder); +void infrared_decoder_kaseikyo_free(void* decoder); +InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder); +InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration); +void* infrared_encoder_kaseikyo_alloc(void); +InfraredStatus + infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_kaseikyo_free(void* encoder_ptr); +bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_kaseikyo_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); +const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol); + +extern const InfraredCommonProtocolSpec protocol_kaseikyo; diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c new file mode 100644 index 000000000..b8db81d7e --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c @@ -0,0 +1,54 @@ +#include "infrared.h" +#include "infrared_protocol_defs_i.h" +#include +#include +#include +#include "../infrared_i.h" + +InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) { + return infrared_common_decoder_check_ready(ctx); +} + +bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) { + furi_assert(decoder); + + bool result = false; + uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0]; + uint8_t vendor_parity = decoder->data[2] & 0x0f; + uint8_t genre1 = decoder->data[2] >> 4; + uint8_t genre2 = decoder->data[3] & 0x0f; + uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4); + uint8_t id = decoder->data[4] >> 6; + uint8_t parity = decoder->data[5]; + + uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1]; + vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4); + uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4]; + + if(vendor_parity == vendor_parity_check && parity == parity_check) { + decoder->message.command = (uint32_t)data; + decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) | + ((uint32_t)genre1 << 4) | (uint32_t)genre2; + decoder->message.protocol = InfraredProtocolKaseikyo; + decoder->message.repeat = false; + result = true; + } + + return result; +} + +void* infrared_decoder_kaseikyo_alloc(void) { + return infrared_common_decoder_alloc(&protocol_kaseikyo); +} + +InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) { + return infrared_common_decode(decoder, level, duration); +} + +void infrared_decoder_kaseikyo_free(void* decoder) { + infrared_common_decoder_free(decoder); +} + +void infrared_decoder_kaseikyo_reset(void* decoder) { + infrared_common_decoder_reset(decoder); +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c new file mode 100644 index 000000000..5814c7255 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c @@ -0,0 +1,45 @@ +#include +#include "common/infrared_common_i.h" +#include +#include "../infrared_i.h" +#include "infrared_protocol_defs_i.h" +#include + +void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) { + furi_assert(encoder_ptr); + + InfraredCommonEncoder* encoder = encoder_ptr; + infrared_common_encoder_reset(encoder); + + uint32_t address = message->address; + uint16_t command = message->command; + + uint8_t id = (address >> 24) & 3; + uint16_t vendor_id = (address >> 8) & 0xffff; + uint8_t genre1 = (address >> 4) & 0xf; + uint8_t genre2 = address & 0xf; + + encoder->data[0] = (uint8_t)(vendor_id & 0xff); + encoder->data[1] = (uint8_t)(vendor_id >> 8); + uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1]; + vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4); + encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4); + encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4); + encoder->data[4] = (id << 6) | (uint8_t)(command >> 4); + encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4]; + + encoder->bits_to_encode = encoder->protocol->databit_len[0]; +} + +void* infrared_encoder_kaseikyo_alloc(void) { + return infrared_common_encoder_alloc(&protocol_kaseikyo); +} + +void infrared_encoder_kaseikyo_free(void* encoder_ptr) { + infrared_common_encoder_free(encoder_ptr); +} + +InfraredStatus + infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return infrared_common_encode(encoder_ptr, duration, level); +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c new file mode 100644 index 000000000..87c86c7b3 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_kaseikyo_spec.c @@ -0,0 +1,17 @@ +#include "../infrared_i.h" +#include "infrared_protocol_defs_i.h" + +static const InfraredProtocolSpecification infrared_kaseikyo_protocol_specification = { + .name = "Kaseikyo", + .address_length = 26, + .command_length = 10, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, +}; + +const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol) { + if(protocol == InfraredProtocolKaseikyo) + return &infrared_kaseikyo_protocol_specification; + else + return NULL; +} diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index 86b19114c..57288f3c3 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -57,6 +57,7 @@ struct InfraredWorker { InfraredDecoderHandler* infrared_decoder; NotificationApp* notification; bool blink_enable; + bool decode_enable; union { struct { @@ -131,7 +132,8 @@ static void infrared_worker_process_timeout(InfraredWorker* instance) { static void infrared_worker_process_timings(InfraredWorker* instance, uint32_t duration, bool level) { const InfraredMessage* message_decoded = - infrared_decode(instance->infrared_decoder, level, duration); + instance->decode_enable ? infrared_decode(instance->infrared_decoder, level, duration) : + NULL; if(message_decoded) { instance->signal.message = *message_decoded; instance->signal.timings_cnt = 0; @@ -221,10 +223,7 @@ void infrared_worker_rx_set_received_signal_callback( InfraredWorker* infrared_worker_alloc() { InfraredWorker* instance = malloc(sizeof(InfraredWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "InfraredWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); + instance->thread = furi_thread_alloc_ex("InfraredWorker", 2048, NULL, instance); size_t buffer_size = MAX(sizeof(InfraredWorkerTiming) * (MAX_TIMINGS_AMOUNT + 1), @@ -233,6 +232,7 @@ InfraredWorker* infrared_worker_alloc() { instance->infrared_decoder = infrared_alloc_decoder(); instance->infrared_encoder = infrared_alloc_encoder(); instance->blink_enable = false; + instance->decode_enable = true; instance->notification = furi_record_open(RECORD_NOTIFICATION); instance->state = InfraredWorkerStateIdle; @@ -316,6 +316,11 @@ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool instance->blink_enable = enable; } +void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable) { + furi_assert(instance); + instance->decode_enable = enable; +} + void infrared_worker_tx_start(InfraredWorker* instance) { furi_assert(instance); furi_assert(instance->state == InfraredWorkerStateIdle); diff --git a/lib/infrared/worker/infrared_worker.h b/lib/infrared/worker/infrared_worker.h index c6617e501..26919c4f5 100644 --- a/lib/infrared/worker/infrared_worker.h +++ b/lib/infrared/worker/infrared_worker.h @@ -76,6 +76,14 @@ void infrared_worker_rx_set_received_signal_callback( */ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool enable); +/** Enable decoding of received infrared signals. + * + * @param[in] instance - instance of InfraredWorker + * @param[in] enable - true if you want to enable decoding + * false otherwise + */ +void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable); + /** Clarify is received signal either decoded or raw * * @param[in] signal - received signal diff --git a/lib/lfrfid/SConscript b/lib/lfrfid/SConscript index fd29ca2ef..f9431ca75 100644 --- a/lib/lfrfid/SConscript +++ b/lib/lfrfid/SConscript @@ -2,18 +2,18 @@ Import("env") env.Append( LINT_SOURCES=[ - "#/lib/lfrfid", + Dir("."), ], CPPPATH=[ "#/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/lfrfid/lfrfid_raw_worker.c b/lib/lfrfid/lfrfid_raw_worker.c index 1547d20f9..8c69acedb 100644 --- a/lib/lfrfid/lfrfid_raw_worker.c +++ b/lib/lfrfid/lfrfid_raw_worker.c @@ -61,10 +61,7 @@ static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context); LFRFIDRawWorker* lfrfid_raw_worker_alloc() { LFRFIDRawWorker* worker = malloc(sizeof(LFRFIDRawWorker)); - worker->thread = furi_thread_alloc(); - furi_thread_set_name(worker->thread, "lfrfid_raw_worker"); - furi_thread_set_context(worker->thread, worker); - furi_thread_set_stack_size(worker->thread, 2048); + worker->thread = furi_thread_alloc_ex("LfrfidRawWorker", 2048, NULL, worker); worker->events = furi_event_flag_alloc(NULL); diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c index 8b4f8b6a9..f33c1aed6 100644 --- a/lib/lfrfid/lfrfid_worker.c +++ b/lib/lfrfid/lfrfid_worker.c @@ -29,11 +29,7 @@ LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict) { worker->raw_filename = NULL; worker->mode_storage = NULL; - worker->thread = furi_thread_alloc(); - furi_thread_set_name(worker->thread, "lfrfid_worker"); - furi_thread_set_callback(worker->thread, lfrfid_worker_thread); - furi_thread_set_context(worker->thread, worker); - furi_thread_set_stack_size(worker->thread, 2048); + worker->thread = furi_thread_alloc_ex("LfrfidWorker", 2048, lfrfid_worker_thread, worker); worker->protocols = dict; @@ -140,9 +136,8 @@ size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol pro static int32_t lfrfid_worker_thread(void* thread_context) { LFRFIDWorker* worker = thread_context; - bool running = true; - while(running) { + while(true) { uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever); if(flags != FuriFlagErrorTimeout) { // stop thread 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; } 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/nfc/SConscript b/lib/nfc/SConscript index 657f3a9e5..b086298de 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -4,6 +4,9 @@ env.Append( CPPPATH=[ "#/lib/nfc", ], + SDK_HEADERS=[ + File("nfc_device.h"), + ], ) libenv = env.Clone(FW_LIB_NAME="nfc") diff --git a/lib/nfc/helpers/iso7816.c b/lib/nfc/helpers/iso7816.c new file mode 100644 index 000000000..03e695e4b --- /dev/null +++ b/lib/nfc/helpers/iso7816.c @@ -0,0 +1,85 @@ +#include "iso7816.h" + +// ISO7816-5 +// Simple-TLV (Β§5.2.1) +// BER-TLV (Β§5.2.2) +TlvInfo iso7816_tlv_parse(const uint8_t* data) { + TlvInfo tlv; + + // Simple-TLV: tag can be any value from 1 to 254 (not '00' or 'FF') + // BER-TLV: TODO describe + // 00000 - 11110 => 0 - 30 (single byte) + // 11111 00011111 - 11111 01111111 => 31 - 127 (2 byte) + // 11111 10000001 00000001 - 11111 11111111 01111111 => 128 - 16383 (3 byte) + + tlv.tag = *(data++); + tlv.ber.constructed = ((tlv.tag & 0x20) != 0); + tlv.ber.classVar = (tlv.tag >> 6) & 0x03; + if((tlv.tag & 0x1f) == 0x1f) { + // BER-TLV, multi byte tag + tlv.tag <<= 8; + tlv.tag |= *(data++); + tlv.ber.tag = tlv.tag & 0x7f; + if(tlv.tag & 0x80) { + // BER-TLV, 3 byte tag + tlv.tag &= ~0x80; + tlv.tag <<= 7; + tlv.tag |= *(data++) & 0x7f; + tlv.ber.tag = tlv.tag & 0x3fff; + } + } else { + tlv.ber.tag = tlv.tag & 0x1f; + } + + //TODO: check for invalid 'indefinite length' + tlv.length = *(data++); + if(tlv.length == 0xff) { + // Simple-TLV 2 byte length + tlv.length = *(data++) << 8; + tlv.length += *(data++); + } else if(tlv.length > 0x7f) { + uint8_t length_bytes = tlv.length & 0x7f; + //printf("BER length of %d bytes\n", length_bytes); + if(length_bytes < 1 || length_bytes > 4) { + //TODO: error: ISO7816 doesn't support more than 4 length bytes + return (TlvInfo){.tag = 0}; + } + tlv.length = 0; + for(uint8_t i = 0; i < length_bytes; ++i) { + //printf("byte %d: %02x\n", i, *data); + tlv.length <<= 8; + tlv.length |= *(data++); + } + } + tlv.value = data; + tlv.next = data + tlv.length; + + return tlv; +} + +TlvInfo + iso7816_tlv_select(const uint8_t* data, size_t length, const uint16_t tags[], size_t num_tags) { + TlvInfo tlv; + size_t offset = 0; + + if(num_tags == 0) { + return (TlvInfo){.tag = 0x0000}; + } + + while(offset < length) { + tlv = iso7816_tlv_parse(data + offset); + + if(tlv.tag == tags[0]) { + if(num_tags == 1) { + return tlv; + } else { + return iso7816_tlv_select(tlv.value, tlv.length, tags + 1, num_tags - 1); + } + } + + offset = + tlv.next - data; // TODO: use some length value of TlvInfo instead of this monstrosity + } + + return (TlvInfo){.tag = 0x0000}; +} diff --git a/lib/nfc/helpers/iso7816.h b/lib/nfc/helpers/iso7816.h new file mode 100644 index 000000000..ae3c85871 --- /dev/null +++ b/lib/nfc/helpers/iso7816.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include + +#define BER_CLASS_UNIVERSAL 0x0 +#define BER_CLASS_APPLICATION 0x1 +#define BER_CLASS_CONTEXT 0x2 +#define BER_CLASS_PRIVATE 0x3 + +typedef struct { + uint16_t tag; // TODO: use define/typedef for this data format? + struct { + uint16_t tag; + uint8_t constructed : 1; + uint8_t classVar : 2; + } ber; + size_t length; + const uint8_t* value; + + const uint8_t* next; +} TlvInfo; + +// ISO7816-5 Β§5.2 +// Simple-TLV and BER-TLV parsing +TlvInfo iso7816_tlv_parse(const uint8_t* data); + +TlvInfo + iso7816_tlv_select(const uint8_t* data, size_t length, const uint16_t tags[], size_t num_tags); diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/helpers/mf_classic_dict.c index a842ed921..690bba61b 100644 --- a/lib/nfc/helpers/mf_classic_dict.c +++ b/lib/nfc/helpers/mf_classic_dict.c @@ -44,7 +44,10 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { do { if(dict_type == MfClassicDictTypeFlipper) { if(!buffered_file_stream_open( - dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + dict->stream, + MF_CLASSIC_DICT_FLIPPER_PATH, + FSAM_READ_WRITE, + FSOM_OPEN_EXISTING)) { buffered_file_stream_close(dict->stream); break; } @@ -59,12 +62,24 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { dict->stream, MF_CLASSIC_DICT_UNIT_TEST_PATH, FSAM_READ_WRITE, - FSOM_CREATE_ALWAYS)) { + FSOM_OPEN_ALWAYS)) { buffered_file_stream_close(dict->stream); break; } } + // Check for new line ending + if(!stream_eof(dict->stream)) { + if(!stream_seek(dict->stream, -1, StreamOffsetFromEnd)) break; + uint8_t last_char = 0; + if(stream_read(dict->stream, &last_char, 1) != 1) break; + if(last_char != '\n') { + FURI_LOG_D(TAG, "Adding new line ending"); + if(stream_write_char(dict->stream, '\n') != 1) break; + } + if(!stream_rewind(dict->stream)) break; + } + // Read total amount of keys FuriString* next_line; next_line = furi_string_alloc(); @@ -73,14 +88,13 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { FURI_LOG_T(TAG, "No keys left in dict"); break; } - furi_string_trim(next_line); FURI_LOG_T( TAG, "Read line: %s, len: %d", furi_string_get_cstr(next_line), furi_string_size(next_line)); if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN - 1) continue; + if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; dict->total_keys++; } furi_string_free(next_line); diff --git a/lib/nfc/helpers/mrtd_helpers.c b/lib/nfc/helpers/mrtd_helpers.c new file mode 100644 index 000000000..26eda51ef --- /dev/null +++ b/lib/nfc/helpers/mrtd_helpers.c @@ -0,0 +1,658 @@ +#include "mrtd_helpers.h" +#include "../helpers/iso7816.h" + +#include //TODO: remove +#include + +#include +#include + +static inline unsigned char* ucstr(const char* str) { + return (unsigned char*)str; +} + +const char* mrtd_auth_method_string(MrtdAuthMethod method) { + switch(method) { + case MrtdAuthMethodBac: + return "BAC"; + case MrtdAuthMethodPace: + return "PACE"; + case MrtdAuthMethodNone: + return "None"; + case MrtdAuthMethodAny: + return "Any"; + default: + return "Unknown"; + } +} + +bool mrtd_auth_method_parse_string(MrtdAuthMethod* method, const char* str) { + if(!strcmp(str, "BAC")) { + *method = MrtdAuthMethodBac; + return true; + } + if(!strcmp(str, "PACE")) { + *method = MrtdAuthMethodPace; + return true; + } + if(!strcmp(str, "None")) { + *method = MrtdAuthMethodNone; + return true; + } + if(!strcmp(str, "Any")) { + *method = MrtdAuthMethodAny; + return true; + } + return false; +} + +uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length) { + const uint8_t num_weights = 3; + uint8_t weights[] = {7, 3, 1}; + uint8_t check_digit = 0; + uint8_t idx; + + for(uint8_t i = 0; i < length; ++i) { + char c = input[i]; + if(c >= 'A' && c <= 'Z') { + idx = c - 'A' + 10; + } else if(c >= 'a' && c <= 'z') { + idx = c - 'a' + 10; + } else if(c >= '0' && c <= '9') { + idx = c - '0'; + } else { + idx = 0; + } + check_digit = (check_digit + idx * weights[i % num_weights]) % 10; + } + return check_digit; +} + +void mrtd_print_date(char* output, MrtdDate* date) { + output[0] = (date->year / 10) + '0'; + output[1] = (date->year % 10) + '0'; + output[2] = (date->month / 10) + '0'; + output[3] = (date->month % 10) + '0'; + output[4] = (date->day / 10) + '0'; + output[5] = (date->day % 10) + '0'; +} + +uint8_t charval(char c) { + if(c >= '0' && c <= '9') { + return c - '0'; + } + return 0; +} + +void mrtd_parse_date(MrtdDate* date, const unsigned char* input) { + date->year = charval(input[0]) * 10 + charval(input[1]); + date->month = charval(input[2]) * 10 + charval(input[3]); + date->day = charval(input[4]) * 10 + charval(input[5]); +} + +bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size) { + uint8_t idx = 0; + uint8_t docnr_length = strlen(auth->doc_number); + uint8_t cd_idx = 0; + if(output_size < docnr_length + 16) { + return false; + } + + cd_idx = idx; + for(uint8_t i = 0; i < docnr_length; ++i) { + char c = auth->doc_number[i]; + if(c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + output[idx++] = c; + } + + if(docnr_length < 9) { + memset(output + idx, '<', 9 - docnr_length); + idx += 9 - docnr_length; + } + + output[idx++] = mrtd_bac_check_digit(output + cd_idx, docnr_length) + '0'; + + cd_idx = idx; + mrtd_print_date(output + idx, &auth->birth_date); + idx += 6; + output[idx++] = mrtd_bac_check_digit(output + cd_idx, 6) + '0'; + + cd_idx = idx; + mrtd_print_date(output + idx, &auth->expiry_date); + idx += 6; + output[idx++] = mrtd_bac_check_digit(output + cd_idx, 6) + '0'; + + output[idx++] = '\x00'; + return true; +} + +bool mrtd_bac_keys_from_seed(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16]) { + uint8_t hash[20]; + mbedtls_sha1_context ctx; + mbedtls_sha1_init(&ctx); + + do { + for(uint8_t i = 1; i <= 2; ++i) { + if(mbedtls_sha1_starts(&ctx)) break; + if(mbedtls_sha1_update(&ctx, kseed, 16)) break; + if(mbedtls_sha1_update(&ctx, ucstr("\x00\x00\x00"), 3)) break; + if(mbedtls_sha1_update(&ctx, &i, 1)) break; + if(mbedtls_sha1_finish(&ctx, hash)) break; + + switch(i) { + case 1: + memcpy(ksenc, hash, 16); + mbedtls_des_key_set_parity(ksenc); + mbedtls_des_key_set_parity(ksenc + 8); + break; + case 2: + memcpy(ksmac, hash, 16); + mbedtls_des_key_set_parity(ksmac); + mbedtls_des_key_set_parity(ksmac + 8); + break; + } + } + } while(false); + + mbedtls_sha1_free(&ctx); + return true; +} + +bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]) { + uint8_t kmrz_max_length = MRTD_DOCNR_MAX_LENGTH + 16; + char kmrz[kmrz_max_length]; + if(!mrtd_bac_get_kmrz(auth, kmrz, kmrz_max_length)) { + return false; + } + + printf("kmrz: %s\r\n", kmrz); //TODO: remove + + uint8_t hash[20]; + mbedtls_sha1((uint8_t*)kmrz, strlen(kmrz), hash); + + if(!mrtd_bac_keys_from_seed(hash, ksenc, ksmac)) { + return false; + } + + return true; +} + +//NOTE: output size will be ((data_length+8)/8)*8 +bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output) { + uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_enc(&ctx, key); + if(mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, data_length, IV, data, output)) { + return false; + } + mbedtls_des3_free(&ctx); + + return true; +} + +bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_dec(&ctx, key); + if(mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, data_length, IV, data, output)) { + return false; + } + mbedtls_des3_free(&ctx); + + return true; +} + +bool mrtd_bac_decrypt_verify( + const uint8_t* data, + size_t data_length, + uint8_t* key_enc, + uint8_t* key_mac, + uint8_t* output) { + mrtd_bac_decrypt(data, data_length - 8, key_enc, output); + + uint8_t mac_calc[8]; + mrtd_bac_padded_mac(data, data_length - 8, key_mac, mac_calc); + + if(memcmp(mac_calc, data + data_length - 8, 8)) { + printf("MAC failed\r\n"); + for(uint8_t i = 0; i < 8; ++i) { + printf("%02X <=> %02X\r\n", mac_calc[i], data[data_length - 8 + i]); + } + return false; + } + return true; +} + +// If output or output_written are NULL-pointers, no output is written +// Otherwise, and if DO'87 is present, data is written to *output +// output should have enough room for additional padding (rounded up by 8 bytes) +// output_written will be the length without padding +uint16_t mrtd_bac_decrypt_verify_sm( + const uint8_t* data, + size_t data_length, + uint8_t* key_enc, + uint8_t* key_mac, + uint64_t ssc, + uint8_t* output, + size_t* output_written) { + // Message: [DO'85 or DO'87] || [DO'99] || DO'8E + // Lengths: Var 1+1+2=4 1+1+8=10 + + //TODO: check for DO'99 presence, instead of assuming + uint16_t ret_code = data[data_length - 10 - 2] << 8 | data[data_length - 10 - 1]; + //ntohs(data + data_length - 10 - 2); + + TlvInfo do87 = iso7816_tlv_select(data, data_length, (uint16_t[]){0x87}, 1); + //printf("DO87.Tag: %X\n", do87.tag); + //printf("DO87.Length: %ld\n", do87.length); + //printf("DO87.Value: "); + //for(uint8_t i=1; i= 0; --padidx) { + if(output[padidx] == 0x00) { + continue; + } else if(output[padidx] == 0x80) { + break; + } else { + printf("Invalid padding\r\n"); + return 0xff01; + } + } + printf(" "); + for(int i = 0; i < padidx; ++i) { + printf(" "); + } + printf("^^\r\n"); + printf("Pad starts at: %d\r\n", padidx); + + *output_written = padidx - 1; + } + } else { + if(output_written != NULL) { + *output_written = 0; + } + } + + mrtd_bac_mac_ctx ctx; + mrtd_bac_mac_init(&ctx, key_mac); + uint64_t ssc_n = htonll(ssc); + mrtd_bac_mac_update(&ctx, (uint8_t*)&ssc_n, 8); + mrtd_bac_mac_update( + &ctx, data, data_length - 10); // 10 = len(DO'8E) = len(header + length + MAC) = 1 + 1 + 8 + uint8_t mac_calc[8]; + mrtd_bac_mac_finalize(&ctx, mac_calc); + + if(memcmp(mac_calc, data + data_length - 8, 8)) { + printf("SM MAC failed\r\n"); + for(uint8_t i = 0; i < 8; ++i) { + printf("%02X <=> %02X\r\n", mac_calc[i], data[data_length - 8 + i]); + } + return 0xff02; + } + return ret_code; +} + +bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, const uint8_t key[16]) { + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_enc(&ctx->des, key); + memset(ctx->mac, 0, 8); + ctx->idx_in = 0; + memcpy(ctx->key, key, 16); + return true; +} + +bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length) { + //printf("MAC add %d: ", data_length); print_hex(data, data_length); printf("\n"); + size_t data_idx = 0; + //uint8_t* xormac = ctx->xormac; + + if(ctx->idx_in != 0) { + uint8_t buff_add = 8 - ctx->idx_in; + if(data_length < buff_add) { + buff_add = data_length; + } + memcpy(ctx->buffer_in + ctx->idx_in, data, buff_add); + ctx->idx_in = (ctx->idx_in + buff_add) % 8; + data_idx += buff_add; + + if(ctx->idx_in == 0) { // buffer_in filled + for(uint8_t j = 0; j < 8; ++j) { + ctx->xormac[j] = ctx->mac[j] ^ ctx->buffer_in[j]; + } + mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac); + + printf( + "DES buf: %02X %02X %02X %02X %02X %02X %02X %02X\r\n", + ctx->buffer_in[0], + ctx->buffer_in[1], + ctx->buffer_in[2], + ctx->buffer_in[3], + ctx->buffer_in[4], + ctx->buffer_in[5], + ctx->buffer_in[6], + ctx->buffer_in[7]); + + //printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + //xormac[0], xormac[1], xormac[2], xormac[3], + //xormac[4], xormac[5], xormac[6], xormac[7]); + } + } + + while(true) { + if(data_idx + 8 > data_length) { + // Not a full block + break; + } + for(uint8_t j = 0; j < 8; ++j) { + ctx->xormac[j] = ctx->mac[j] ^ data[data_idx++]; + } + + mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac); + printf( + "DES add: %02X %02X %02X %02X %02X %02X %02X %02X\r\n", + data[data_idx - 8 + 0], + data[data_idx - 8 + 1], + data[data_idx - 8 + 2], + data[data_idx - 8 + 3], + data[data_idx - 8 + 4], + data[data_idx - 8 + 5], + data[data_idx - 8 + 6], + data[data_idx - 8 + 7]); + + //printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + //xormac[0], xormac[1], xormac[2], xormac[3], + //xormac[4], xormac[5], xormac[6], xormac[7]); + } + + if(data_idx < data_length) { + ctx->idx_in = data_length - data_idx; + memcpy(ctx->buffer_in, data + data_idx, ctx->idx_in); + } + + return true; +} + +bool mrtd_bac_mac_pad(mrtd_bac_mac_ctx* ctx) { + memset(ctx->buffer_in + ctx->idx_in, 0x00, 8 - ctx->idx_in); + ctx->buffer_in[ctx->idx_in] = 0x80; + ctx->idx_in = 8; + + mrtd_bac_mac_update(ctx, NULL, 0); // Force processing the buffer_in + return true; +} + +bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]) { + mrtd_bac_mac_pad(ctx); + + uint8_t tmp[8]; + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_dec(&ctx->des, ctx->key + 8); + mbedtls_des_crypt_ecb(&ctx->des, ctx->mac, tmp); + + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_enc(&ctx->des, ctx->key); + mbedtls_des_crypt_ecb(&ctx->des, tmp, output); + + mbedtls_des_free(&ctx->des); + return true; +} + +bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output) { + // MAC + uint8_t mac[8]; + uint8_t xormac[8]; + uint8_t tmp[8]; + mbedtls_des_context ctx; + + mbedtls_des_init(&ctx); + mbedtls_des_setkey_enc(&ctx, key); + + memset(mac, 0, 8); + for(size_t i = 0; i < data_length / 8; ++i) { + for(uint8_t j = 0; j < 8; ++j) { + xormac[j] = mac[j] ^ data[i * 8 + j]; + } + + mbedtls_des_crypt_ecb(&ctx, xormac, mac); + printf( + "DES1: %02X %02X %02X %02X %02X %02X %02X %02X\r\n", + xormac[0], + xormac[1], + xormac[2], + xormac[3], + xormac[4], + xormac[5], + xormac[6], + xormac[7]); + } + + mbedtls_des_init(&ctx); + mbedtls_des_setkey_dec(&ctx, key + 8); + mbedtls_des_crypt_ecb(&ctx, mac, tmp); + + mbedtls_des_init(&ctx); + mbedtls_des_setkey_enc(&ctx, key); + mbedtls_des_crypt_ecb(&ctx, tmp, output); + + mbedtls_des_free(&ctx); + + return true; +} + +bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + //TODO: bufferless padding should be possible with 3DES + size_t newlength = ((data_length + 8) / 8) * 8; // TODO: return this value too? + uint8_t padded[newlength]; //TODO: input parameter + memset(padded, 0, newlength); + memcpy(padded, data, data_length); + padded[data_length] = 0x80; + + if(!mrtd_bac_mac(padded, newlength, key, output)) { + return false; + } + + return true; +} + +size_t mrtd_protect_apdu( + uint8_t cla, + uint8_t ins, + uint8_t p1, + uint8_t p2, + uint8_t lc, + const void* data, + int16_t le, + const uint8_t* key_enc, + const uint8_t* key_mac, + uint64_t ssc, + uint8_t* output) { + //TODO: max size on output? + size_t idx = 0; + + // CC = MAC( SSC || CmdHeader || DO'87 ) + mrtd_bac_mac_ctx mac_ctx; + mrtd_bac_mac_init(&mac_ctx, key_mac); + uint64_t ssc_n = htonll(ssc); + //printf("ssc: %016llx\r\n", ssc); + //printf("ssc_n: "); print_hex(ssc_n, 8); printf("\n"); + mrtd_bac_mac_update(&mac_ctx, (uint8_t*)&ssc_n, 8); + + // Mask cla + output[idx++] = cla | 0x0c; + output[idx++] = ins; + output[idx++] = p1; + output[idx++] = p2; + + // Pad Header + mrtd_bac_mac_update(&mac_ctx, output, idx); + mrtd_bac_mac_pad(&mac_ctx); + + size_t idx_lc = idx; + output[idx++] = 0xff; // place holder for Lc + + // Build DO'87 + // TODO: condition on data presence + // TODO: if ins is odd, use 0x85 + if(lc > 0) { + size_t newlength = ((lc + 8) / 8) * 8; + uint8_t padded[newlength]; + + output[idx++] = 0x87; // Header + output[idx++] = newlength + 1; // Length + output[idx++] = 0x01; //TODO: check this value + + memset(padded, 0, newlength); + memcpy(padded, data, lc); + padded[lc] = 0x80; + + mrtd_bac_encrypt(padded, newlength, key_enc, output + idx); + idx += newlength; + } + + // Build DO'97 + if(le >= 0) { + output[idx++] = 0x97; // Header + output[idx++] = 0x01; // Length + output[idx++] = le; + } + + mrtd_bac_mac_update(&mac_ctx, output + idx_lc + 1, idx - idx_lc - 1); + + // Build DO'8E + // TODO: conditions? + { + output[idx++] = 0x8E; // Header + output[idx++] = 0x08; // Length + + mrtd_bac_mac_finalize(&mac_ctx, output + idx); + idx += 8; + + printf("MAC: "); + for(uint8_t i = 0; i < 8; ++i) { + printf("%02X ", output[idx - 8 + i]); + } + printf("\r\n"); + } + + output[idx_lc] = idx - idx_lc - 1; // Set Lc + + output[idx++] = 0x00; + + return idx; +} + +EFFile EFNone = {.name = NULL, .file_id = 0x0000, .short_id = 0x00, .tag = 0x00}; + +const struct EFFormat EF = { + .ATR = {.name = "ATR", .file_id = 0x2F01, .short_id = 0x01}, + .DIR = {.name = "DIR", .file_id = 0x2F00, .short_id = 0x1E}, + .CardAccess = {.name = "CardAccess", .file_id = 0x011C, .short_id = 0x1C}, + .CardSecurity = {.name = "CardSecurity", .file_id = 0x011D, .short_id = 0x1D}, + .COM = {.name = "COM", .file_id = 0x011E, .short_id = 0x1E, .tag = 0x60}, + .SOD = {.name = "SOD", .file_id = 0X011D, .short_id = 0X1D, .tag = 0x77}, + .DG1 = {.name = "DG1", .file_id = 0X0101, .short_id = 0X01, .tag = 0x61}, + .DG2 = {.name = "DG2", .file_id = 0X0102, .short_id = 0X02, .tag = 0x75}, + .DG3 = {.name = "DG3", .file_id = 0X0103, .short_id = 0X03, .tag = 0x63}, + .DG4 = {.name = "DG4", .file_id = 0X0104, .short_id = 0X04, .tag = 0x76}, + .DG5 = {.name = "DG5", .file_id = 0X0105, .short_id = 0X05, .tag = 0x65}, + .DG6 = {.name = "DG6", .file_id = 0X0106, .short_id = 0X06, .tag = 0x66}, + .DG7 = {.name = "DG7", .file_id = 0X0107, .short_id = 0X07, .tag = 0x67}, + .DG8 = {.name = "DG8", .file_id = 0X0108, .short_id = 0X08, .tag = 0x68}, + .DG9 = {.name = "DG9", .file_id = 0X0109, .short_id = 0X09, .tag = 0x69}, + .DG10 = {.name = "DG10", .file_id = 0X010A, .short_id = 0X0A, .tag = 0x6a}, + .DG11 = {.name = "DG11", .file_id = 0X010B, .short_id = 0X0B, .tag = 0x6b}, + .DG12 = {.name = "DG12", .file_id = 0X010C, .short_id = 0X0C, .tag = 0x6c}, + .DG13 = {.name = "DG13", .file_id = 0X010D, .short_id = 0X0D, .tag = 0x6d}, + .DG14 = {.name = "DG14", .file_id = 0X010E, .short_id = 0X0E, .tag = 0x6e}, + .DG15 = {.name = "DG15", .file_id = 0X010F, .short_id = 0X0F, .tag = 0x6f}, + .DG16 = {.name = "DG16", .file_id = 0X0110, .short_id = 0X10, .tag = 0x70}, +}; + +struct AIDSet AID = { + .eMRTDApplication = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01}, + .TravelRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x01}, + .VisaRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x02}, + .AdditionalBiometrics = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x03}, +}; + +const EFFile* mrtd_tag_to_file(uint8_t tag) { + //TODO: generate this code with macros? + switch(tag) { + case 0x60: + return &EF.COM; + case 0x77: + return &EF.SOD; + case 0x61: + return &EF.DG1; + case 0x75: + return &EF.DG2; + case 0x63: + return &EF.DG3; + case 0x76: + return &EF.DG4; + case 0x65: + return &EF.DG5; + case 0x66: + return &EF.DG6; + case 0x67: + return &EF.DG7; + case 0x68: + return &EF.DG8; + case 0x69: + return &EF.DG9; + case 0x6a: + return &EF.DG10; + case 0x6b: + return &EF.DG11; + case 0x6c: + return &EF.DG12; + case 0x6d: + return &EF.DG13; + case 0x6e: + return &EF.DG14; + case 0x6f: + return &EF.DG15; + case 0x70: + return &EF.DG16; + default: + return &EFNone; + } +}; + +int tlv_number(TlvInfo tlv) { + //TODO: negative numbers? + const uint8_t* str = tlv.value; + size_t length = tlv.length; + + int value = 0; + while(length--) { + char c = *(str++); + + if(c >= '0' && c <= '9') { + value = value * 10 + (c - '0'); + } else { + //TODO: warning? return? crash? + } + } + return value; +} diff --git a/lib/nfc/helpers/mrtd_helpers.h b/lib/nfc/helpers/mrtd_helpers.h new file mode 100644 index 000000000..994e66974 --- /dev/null +++ b/lib/nfc/helpers/mrtd_helpers.h @@ -0,0 +1,228 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "../helpers/iso7816.h" + +typedef struct { + uint8_t year; + uint8_t month; + uint8_t day; +} MrtdDate; + +// NULL terminated document ID +#define MRTD_DOCNR_MAX_LENGTH 21 + +typedef enum { + MrtdAuthMethodNone, + MrtdAuthMethodAny, + MrtdAuthMethodBac, + MrtdAuthMethodPace, +} MrtdAuthMethod; + +typedef enum { + MrtdTypeUnknown, + MrtdTypeTD1, + MrtdTypeTD2, + MrtdTypeTD3, +} MrtdType; + +typedef struct { + MrtdAuthMethod method; + + // BAC input fields + MrtdDate birth_date; + MrtdDate expiry_date; + char doc_number[MRTD_DOCNR_MAX_LENGTH]; + + //TODO: PACE +} MrtdAuthData; + +typedef struct { + mbedtls_des_context des; + uint8_t key[16]; + uint8_t mac[8]; + uint8_t xormac[8]; + + uint8_t buffer_in[8]; + uint8_t idx_in; +} mrtd_bac_mac_ctx; + +typedef struct { + const char* name; + const uint8_t short_id; + const uint16_t file_id; + const uint8_t tag; +} EFFile; + +struct EFFormat { + // Under Master File (MF) + const EFFile ATR; + const EFFile DIR; + const EFFile CardAccess; + const EFFile CardSecurity; + + // Under LDS1 eMRTD Application + const EFFile COM; + const EFFile SOD; + const EFFile DG1; + const EFFile DG2; + const EFFile DG3; + const EFFile DG4; + const EFFile DG5; + const EFFile DG6; + const EFFile DG7; + const EFFile DG8; + const EFFile DG9; + const EFFile DG10; + const EFFile DG11; + const EFFile DG12; + const EFFile DG13; + const EFFile DG14; + const EFFile DG15; + const EFFile DG16; +}; + +extern const struct EFFormat EF; + +typedef uint8_t AIDValue[7]; + +struct AIDSet { + AIDValue eMRTDApplication; + AIDValue TravelRecords; + AIDValue VisaRecords; + AIDValue AdditionalBiometrics; +}; + +extern struct AIDSet AID; + +#define MAX_EFDIR_APPS 4 + +typedef struct { + AIDValue applications[MAX_EFDIR_APPS]; + uint8_t applications_count; +} EF_DIR_contents; + +#define MAX_EFCOM_TAGS 18 + +typedef struct { + uint16_t lds_version; // xxyy => xx.yy (major.minor) + uint32_t unicode_version; // aabbcc => aa.bb.cc (major.minor.release) + uint8_t tag_list[MAX_EFCOM_TAGS]; +} EF_COM_contents; + +typedef struct { + MrtdType type; + // ICAO9303 max sizes + 1 for 0-byte + uint8_t doctype[3]; + uint8_t issuing_state[4]; + uint8_t name[40]; + MrtdDate birth_date; + uint8_t docnr[10]; + uint8_t nationality[4]; + uint8_t sex[2]; + MrtdDate expiry_date; +} EF_DG1_contents; + +typedef struct { + MrtdAuthData auth; + bool auth_success; + MrtdAuthMethod auth_method_used; + + struct { + EF_DIR_contents EF_DIR; + EF_COM_contents EF_COM; + EF_DG1_contents DG1; + } files; +} MrtdData; + +const char* mrtd_auth_method_string(MrtdAuthMethod method); + +bool mrtd_auth_method_parse_string(MrtdAuthMethod* method, const char* str); + +uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length); + +//TODO: swap order, all other functions have output last +void mrtd_print_date(char* output, MrtdDate* date); + +void mrtd_parse_date(MrtdDate* date, const unsigned char* input); + +bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size); + +bool mrtd_bac_keys_from_seed(const uint8_t* kseed, uint8_t* ksenc, uint8_t* ksmac); + +bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]); + +bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output); + +bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output); + +bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, const uint8_t key[16]); + +bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length); + +bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]); + +bool mrtd_bac_mac_pad(mrtd_bac_mac_ctx* ctx); // TODO: internal only, remove from .h? + +bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); + +bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); + +bool mrtd_bac_decrypt_verify( + const uint8_t* data, + size_t data_length, + uint8_t* key_enc, + uint8_t* key_mac, + uint8_t* output); + +//TODO: add some consts +uint16_t mrtd_bac_decrypt_verify_sm( + const uint8_t* data, + size_t data_length, + uint8_t* key_enc, + uint8_t* key_mac, + uint64_t ssc, + uint8_t* output, + size_t* output_written); + +#include +#define htonll(x) ((((uint64_t)__htonl(x)) << 32) + __htonl((x) >> 32)) + +static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd) { +#if _BYTE_ORDER == _LITTLE_ENDIAN + return (((uint64_t)rnd_ic[4] << 56) & 0xff00000000000000) | + (((uint64_t)rnd_ic[5] << 48) & 0x00ff000000000000) | + (((uint64_t)rnd_ic[6] << 40) & 0x0000ff0000000000) | + (((uint64_t)rnd_ic[7] << 32) & 0x000000ff00000000) | + (((uint64_t)rnd_ifd[4] << 24) & 0x00000000ff000000) | + (((uint64_t)rnd_ifd[5] << 16) & 0x0000000000ff0000) | + (((uint64_t)rnd_ifd[6] << 8) & 0x000000000000ff00) | + (((uint64_t)rnd_ifd[7]) & 0x00000000000000ff); +#else +#error Using untested code, please verify first! + return (*((uint64_t*)(rnd_ic + 4)) & 0xffffffff) + (*((uint64_t*)(rnd_ifd + 4)) * 0x100000000); +#endif +} + +size_t mrtd_protect_apdu( + uint8_t cla, + uint8_t ins, + uint8_t p1, + uint8_t p2, + uint8_t lc, + const void* data, + int16_t le, + const uint8_t* key_enc, + const uint8_t* key_mac, + uint64_t ssc, + uint8_t* output); + +int tlv_number(TlvInfo tlv); + +const EFFile* mrtd_tag_to_file(uint8_t tag); diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index eeacbfe6e..4de4d2e69 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -108,11 +108,8 @@ ReaderAnalyzer* reader_analyzer_alloc() { instance->stream = furi_stream_buffer_alloc(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_callback(instance->thread, reader_analyzer_thread); - furi_thread_set_context(instance->thread, instance); + instance->thread = + furi_thread_alloc_ex("ReaderAnalyzerWorker", 2048, reader_analyzer_thread, instance); furi_thread_set_priority(instance->thread, FuriThreadPriorityLow); return instance; @@ -205,10 +202,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..f801c63a4 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -213,6 +213,9 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { uint32_t auth_counter; if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1)) auth_counter = 0; + data->curr_authlim = auth_counter; + + data->auth_success = mf_ul_is_full_capture(data); parsed = true; } while(false); @@ -636,7 +639,35 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } -// Leave for backward compatibility +static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + EmvData* data = &dev->dev_data.emv_data; + uint32_t data_temp = 0; + + do { + // Write Bank card specific data + if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break; + if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break; + if(!flipper_format_write_string_cstr(file, "Name", data->name)) break; + if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break; + if(data->exp_mon) { + uint8_t exp_data[2] = {data->exp_mon, data->exp_year}; + if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break; + } + if(data->country_code) { + data_temp = data->country_code; + if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break; + } + if(data->currency_code) { + data_temp = data->currency_code; + if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break; + } + saved = true; + } while(false); + + return saved; +} + bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; EmvData* data = &dev->dev_data.emv_data; @@ -1006,12 +1037,7 @@ static void nfc_device_get_shadow_path(FuriString* orig_path, FuriString* shadow furi_string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION); } -static bool nfc_device_save_file( - NfcDevice* dev, - const char* dev_name, - const char* folder, - const char* extension, - bool use_load_path) { +bool nfc_device_save(NfcDevice* dev, const char* dev_name) { furi_assert(dev); bool saved = false; @@ -1021,26 +1047,17 @@ static bool nfc_device_save_file( temp_str = furi_string_alloc(); do { - if(use_load_path && !furi_string_empty(dev->load_path)) { - // Get directory name - path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str); - // Create nfc directory if necessary - if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(temp_str))) break; - // Make path to file to save - furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension); - } else { - // Create nfc directory if necessary - if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break; - // First remove nfc device file if it was saved - furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); - } + // Create nfc directory if necessary + if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break; + // First remove nfc device file if it was saved + furi_string_printf(temp_str, "%s", dev_name); // Open file if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; // Write header if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; // Write nfc device type if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic")) + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card")) break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; @@ -1055,6 +1072,8 @@ static bool nfc_device_save_file( if(!nfc_device_save_mifare_ul_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatBankCard) { + if(!nfc_device_save_bank_card_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { // Save data if(!nfc_device_save_mifare_classic_data(file, dev)) break; @@ -1072,13 +1091,19 @@ static bool nfc_device_save_file( return saved; } -bool nfc_device_save(NfcDevice* dev, const char* dev_name) { - return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION, true); -} - -bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) { +bool nfc_device_save_shadow(NfcDevice* dev, const char* path) { dev->shadow_file_exist = true; - return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true); + // Replace extension from .nfc to .shd if necessary + FuriString* orig_path = furi_string_alloc(); + furi_string_set_str(orig_path, path); + FuriString* shadow_path = furi_string_alloc(); + nfc_device_get_shadow_path(orig_path, shadow_path); + + bool file_saved = nfc_device_save(dev, furi_string_get_cstr(shadow_path)); + furi_string_free(orig_path); + furi_string_free(shadow_path); + + return file_saved; } static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dialog) { @@ -1122,6 +1147,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; @@ -1188,7 +1220,7 @@ bool nfc_file_select(NfcDevice* dev) { }; bool res = - dialog_file_browser_show(dev->dialogs, dev->load_path, nfc_app_folder, &browser_options); + dialog_file_browser_show(dev->dialogs, dev->load_path, dev->load_path, &browser_options); furi_string_free(nfc_app_folder); if(res) { diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 6cac72c6b..f4726fe39 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -8,10 +8,15 @@ #include #include #include +#include #include #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define NFC_DEV_NAME_MAX_LEN 22 #define NFC_READER_DATA_MAX_SIZE 64 #define NFC_DICT_KEY_BATCH_SIZE 50 @@ -25,6 +30,7 @@ typedef void (*NfcLoadingCallback)(void* context, bool state); typedef enum { NfcDeviceProtocolUnknown, NfcDeviceProtocolEMV, + NfcDeviceProtocolMRTD, NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, @@ -47,15 +53,27 @@ typedef struct { MfClassicDict* dict; } NfcMfClassicDictAttackData; +typedef enum { + NfcReadModeAuto, + NfcReadModeMfClassic, + NfcReadModeMfUltralight, + NfcReadModeMfDesfire, + NfcReadModeEMV, + NfcReadModeNFCA, +} NfcReadMode; + typedef struct { FuriHalNfcDevData nfc_data; NfcProtocol protocol; + NfcReadMode read_mode; union { NfcReaderRequestData reader_data; NfcMfClassicDictAttackData mf_classic_dict_attack_data; + MfUltralightAuth mf_ul_auth; }; union { EmvData emv_data; + MrtdData mrtd_data; MfUltralightData mf_ul_data; MfClassicData mf_classic_data; MifareDesfireData mf_df_data; @@ -101,3 +119,7 @@ bool nfc_device_delete(NfcDevice* dev, bool use_load_path); bool nfc_device_restore(NfcDevice* dev, bool use_load_path); void nfc_device_set_loading_callback(NfcDevice* dev, NfcLoadingCallback callback, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 013295bc3..3355385b4 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -12,11 +12,7 @@ NfcWorker* nfc_worker_alloc() { NfcWorker* nfc_worker = malloc(sizeof(NfcWorker)); // Worker thread attributes - nfc_worker->thread = furi_thread_alloc(); - furi_thread_set_name(nfc_worker->thread, "NfcWorker"); - furi_thread_set_stack_size(nfc_worker->thread, 8192); - furi_thread_set_callback(nfc_worker->thread, nfc_worker_task); - furi_thread_set_context(nfc_worker->thread, nfc_worker); + nfc_worker->thread = furi_thread_alloc_ex("NfcWorker", 8192, nfc_worker_task, nfc_worker); nfc_worker->callback = NULL; nfc_worker->context = NULL; @@ -75,12 +71,12 @@ void nfc_worker_start( void nfc_worker_stop(NfcWorker* nfc_worker) { furi_assert(nfc_worker); - if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) { - return; + furi_assert(nfc_worker->thread); + if(furi_thread_get_state(nfc_worker->thread) != FuriThreadStateStopped) { + furi_hal_nfc_stop(); + nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); + furi_thread_join(nfc_worker->thread); } - furi_hal_nfc_stop(); - nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); - furi_thread_join(nfc_worker->thread); } void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { @@ -95,7 +91,11 @@ int32_t nfc_worker_task(void* context) { furi_hal_nfc_exit_sleep(); if(nfc_worker->state == NfcWorkerStateRead) { - nfc_worker_read(nfc_worker); + if(nfc_worker->dev_data->read_mode == NfcReadModeAuto) { + nfc_worker_read(nfc_worker); + } else { + nfc_worker_read_type(nfc_worker); + } } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { nfc_worker_emulate_uid(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { @@ -104,6 +104,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) { @@ -291,6 +295,49 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte return read_success; } +static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + MrtdData* mrtd_data = &nfc_worker->dev_data->mrtd_data; + MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx, mrtd_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + do { + // Read passport + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; + + //TODO: try select eMRTDApp first, but when PACE, read CardAccess first! + if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; // Passport app not selected + + if(mrtd_data->auth.method == MrtdAuthMethodNone) { + // Selected the passport app, but auth. not selected + // Successfully read what we could + read_success = true; + break; + } + + if(!mrtd_authenticate(mrtd_app)) { + // At least we're reading an MRTD and should the app switch to the NFC scenes + read_success = true; + break; // Authentication failed + } + + mrtd_read_parse_file(mrtd_app, EF.COM); + mrtd_read_parse_file(mrtd_app, EF.DG1); + + read_success = true; + } while(false); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} + static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; @@ -316,11 +363,51 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t card_read = true; } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { FURI_LOG_I(TAG, "ISO14443-4 card detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; - if(!nfc_worker_read_bank_card(nfc_worker, tx_rx)) { + //TODO: thoughts on improving logic/readability here? + do { + FURI_LOG_D(TAG, "Try reading EMV"); + if(nfc_worker_read_bank_card(nfc_worker, tx_rx)) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; + break; + } + + furi_hal_nfc_sleep(); // Needed between checks + FURI_LOG_D(TAG, "Try reading MRTD"); + if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD; + break; + } + FURI_LOG_I(TAG, "Unknown card. Save UID"); nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; - } + } while(false); + card_read = true; + } else { + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + card_read = true; + } + + return card_read; +} + +static bool nfc_worker_read_nfcb(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + + bool card_read = false; + furi_hal_nfc_sleep(); + if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { + FURI_LOG_I(TAG, "ISO14443-4B card detected"); + //TODO: thoughts on improving logic/readability here? + do { + FURI_LOG_D(TAG, "Try reading MRTD"); + if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD; + break; + } + + FURI_LOG_I(TAG, "Unknown card. Save UID"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + } while(false); card_read = true; } else { nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; @@ -360,6 +447,9 @@ void nfc_worker_read(NfcWorker* nfc_worker) { } else if(dev_data->protocol == NfcDeviceProtocolEMV) { event = NfcWorkerEventReadBankCard; break; + } else if(dev_data->protocol == NfcDeviceProtocolMRTD) { + event = NfcWorkerEventReadPassport; + break; } else if(dev_data->protocol == NfcDeviceProtocolUnknown) { event = NfcWorkerEventReadUidNfcA; break; @@ -371,6 +461,13 @@ void nfc_worker_read(NfcWorker* nfc_worker) { } } } else if(nfc_data->type == FuriHalNfcTypeB) { + if(nfc_worker_read_nfcb(nfc_worker, &tx_rx)) { + if(dev_data->protocol == NfcDeviceProtocolMRTD) { + event = NfcWorkerEventReadPassport; + break; + } + } + event = NfcWorkerEventReadUidNfcB; break; } else if(nfc_data->type == FuriHalNfcTypeF) { @@ -395,6 +492,81 @@ void nfc_worker_read(NfcWorker* nfc_worker) { } } +void nfc_worker_read_type(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + NfcReadMode read_mode = nfc_worker->dev_data->read_mode; + nfc_device_data_clear(nfc_worker->dev_data); + NfcDeviceData* dev_data = nfc_worker->dev_data; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + FuriHalNfcTxRxContext tx_rx = {}; + NfcWorkerEvent event = 0; + bool card_not_detected_notified = false; + + while(nfc_worker->state == NfcWorkerStateRead) { + if(furi_hal_nfc_detect(nfc_data, 300)) { + FURI_LOG_D(TAG, "Card detected"); + furi_hal_nfc_sleep(); + // Process first found device + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + card_not_detected_notified = false; + if(nfc_data->type == FuriHalNfcTypeA) { + if(read_mode == NfcReadModeMfClassic) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; + nfc_worker->dev_data->mf_classic_data.type = mf_classic_get_classic_type( + nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + if(nfc_worker_read_mf_classic(nfc_worker, &tx_rx)) { + FURI_LOG_D(TAG, "Card read"); + dev_data->protocol = NfcDeviceProtocolMifareClassic; + event = NfcWorkerEventReadMfClassicDone; + break; + } else { + FURI_LOG_D(TAG, "Card read failed"); + dev_data->protocol = NfcDeviceProtocolMifareClassic; + event = NfcWorkerEventReadMfClassicDictAttackRequired; + break; + } + } else if(read_mode == NfcReadModeMfUltralight) { + FURI_LOG_I(TAG, "Mifare Ultralight / NTAG"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl; + if(nfc_worker_read_mf_ultralight(nfc_worker, &tx_rx)) { + event = NfcWorkerEventReadMfUltralight; + break; + } + } else if(read_mode == NfcReadModeMfDesfire) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire; + if(nfc_worker_read_mf_desfire(nfc_worker, &tx_rx)) { + event = NfcWorkerEventReadMfDesfire; + break; + } + } else if(read_mode == NfcReadModeEMV) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; + if(nfc_worker_read_bank_card(nfc_worker, &tx_rx)) { + event = NfcWorkerEventReadBankCard; + break; + } + } else if(read_mode == NfcReadModeNFCA) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + event = NfcWorkerEventReadUidNfcA; + break; + } + } else { + if(!card_not_detected_notified) { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + card_not_detected_notified = true; + } + } + } + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + // Notify caller and exit + if(event > NfcWorkerEventReserved) { + nfc_worker->callback(event, nfc_worker->context); + } +} + void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; @@ -453,10 +625,25 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { } } +void nfc_worker_mf_ultralight_auth_received_callback(MfUltralightAuth auth, void* context) { + furi_assert(context); + + NfcWorker* nfc_worker = context; + nfc_worker->dev_data->mf_ul_auth = auth; + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventMfUltralightPwdAuth, nfc_worker->context); + } +} + void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; MfUltralightEmulator emulator = {}; mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); + + // TODO rework with reader analyzer + emulator.auth_received_callback = nfc_worker_mf_ultralight_auth_received_callback; + emulator.context = nfc_worker; + while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { mf_ul_reset_emulation(&emulator, true); furi_hal_nfc_emulate_nfca( @@ -576,18 +763,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { return; } - // Clear found keys if the key cache is incorrect (key set as found but sector not read) - for(uint16_t sector = 0; sector < total_sectors; sector++) { - if(mf_classic_is_key_found(data, sector, MfClassicKeyA) && - !mf_classic_is_sector_read(data, sector)) { - mf_classic_set_key_not_found(data, sector, MfClassicKeyA); - } - if(mf_classic_is_key_found(data, sector, MfClassicKeyB) && - !mf_classic_is_sector_read(data, sector)) { - mf_classic_set_key_not_found(data, sector, MfClassicKeyB); - } - } - FURI_LOG_D( TAG, "Start Dictionary attack, Key Count %ld", mf_classic_dict_get_total_keys(dict)); for(size_t i = 0; i < total_sectors; i++) { @@ -692,6 +867,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); @@ -778,12 +1091,19 @@ static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void* } void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); furi_assert(nfc_worker->callback); 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 1e7fc238f..7d8264ef5 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -7,13 +7,14 @@ typedef struct NfcWorker NfcWorker; typedef enum { // Init states NfcWorkerStateNone, - NfcWorkerStateBroken, NfcWorkerStateReady, // Main worker states NfcWorkerStateRead, NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, + NfcWorkerStateMfClassicWrite, + NfcWorkerStateMfClassicUpdate, NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, @@ -39,6 +40,7 @@ typedef enum { NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, NfcWorkerEventReadBankCard, + NfcWorkerEventReadPassport, // Nfc worker common events NfcWorkerEventSuccess, @@ -48,22 +50,25 @@ typedef enum { NfcWorkerEventNoCardDetected, NfcWorkerEventWrongCardDetected, - // Mifare Classic events + // Read Mifare Classic events NfcWorkerEventNoDictFound, NfcWorkerEventNewSector, NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, - // Mifare Ultralight/NTAG events - NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key - NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + // Write Mifare Classic events + NfcWorkerEventWrongCard, // Detect Reader events NfcWorkerEventDetectReaderDetected, NfcWorkerEventDetectReaderLost, NfcWorkerEventDetectReaderMfkeyCollected, + // Mifare Ultralight events + NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key + NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 5c7592833..f827ae981 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -36,12 +37,18 @@ int32_t nfc_worker_task(void* context); void nfc_worker_read(NfcWorker* nfc_worker); +void nfc_worker_read_type(NfcWorker* nfc_worker); + void nfc_worker_emulate_uid(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); +void nfc_worker_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..b7a52bc01 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) { @@ -95,7 +82,7 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { } } -static uint16_t mf_classic_get_total_block_num(MfClassicType type) { +uint16_t mf_classic_get_total_block_num(MfClassicType type) { if(type == MfClassicType1k) { return 64; } else if(type == MfClassicType4k) { @@ -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..9a0bb5790 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; @@ -84,12 +98,26 @@ MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t S uint8_t mf_classic_get_total_sectors_num(MfClassicType type); +uint16_t mf_classic_get_total_block_num(MfClassicType type); + uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector); bool mf_classic_is_sector_trailer(uint8_t block); uint8_t mf_classic_get_sector_by_block(uint8_t block); +bool mf_classic_is_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 +132,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 +177,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); diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index a8d1f5548..85e234bd9 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -51,7 +51,7 @@ void mf_ul_reset(MfUltralightData* data) { data->data_size = 0; data->data_read = 0; data->curr_authlim = 0; - data->has_auth = false; + data->auth_success = false; } static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { @@ -756,6 +756,34 @@ bool mf_ul_read_card( mf_ultralight_read_tearing_flags(tx_rx, data); } data->curr_authlim = 0; + + if(reader->pages_read == reader->pages_to_read && + reader->supported_features & MfUltralightSupportAuth && !data->auth_success) { + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + if(config->access.authlim == 0) { + // Attempt to auth with default PWD + uint16_t pack; + data->auth_success = mf_ultralight_authenticate(tx_rx, MF_UL_DEFAULT_PWD, &pack); + if(data->auth_success) { + config->auth_data.pwd.value = MF_UL_DEFAULT_PWD; + config->auth_data.pack.value = pack; + } else { + furi_hal_nfc_sleep(); + furi_hal_nfc_activate_nfca(300, NULL); + } + } + } + } + + if(reader->pages_read != reader->pages_to_read) { + if(reader->supported_features & MfUltralightSupportAuth) { + // Probably password protected, fix AUTH0 and PROT so before AUTH0 + // can be written and since AUTH0 won't be readable, like on the + // original card + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + config->auth0 = reader->pages_read; + config->access.prot = true; + } } return card_read; @@ -1201,6 +1229,8 @@ static void mf_ul_emulate_write( } void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) { + emulator->comp_write_cmd_started = false; + emulator->sector_select_cmd_started = false; emulator->curr_sector = 0; emulator->ntag_i2c_plus_sector3_lockout = false; emulator->auth_success = false; @@ -1244,8 +1274,7 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d emulator->config = mf_ultralight_get_config_pages(&emulator->data); emulator->page_num = emulator->data.data_size / 4; emulator->data_changed = false; - emulator->comp_write_cmd_started = false; - emulator->sector_select_cmd_started = false; + memset(&emulator->auth_attempt, 0, sizeof(MfUltralightAuth)); mf_ul_reset_emulation(emulator, true); } @@ -1706,6 +1735,17 @@ bool mf_ul_prepare_emulation_response( } else if(cmd == MF_UL_AUTH) { if(emulator->supported_features & MfUltralightSupportAuth) { if(buff_rx_len == (1 + 4) * 8) { + // Record password sent by PCD + memcpy( + emulator->auth_attempt.pwd.raw, + &buff_rx[1], + sizeof(emulator->auth_attempt.pwd.raw)); + emulator->auth_attempted = true; + if(emulator->auth_received_callback) { + emulator->auth_received_callback( + emulator->auth_attempt, emulator->context); + } + uint16_t scaled_authlim = mf_ultralight_calc_auth_count(&emulator->data); if(scaled_authlim != 0 && emulator->data.curr_authlim >= scaled_authlim) { if(emulator->data.curr_authlim != UINT16_MAX) { @@ -1863,3 +1903,14 @@ bool mf_ul_prepare_emulation_response( return tx_bits > 0; } + +bool mf_ul_is_full_capture(MfUltralightData* data) { + if(data->data_read != data->data_size) return false; + + // Having read all the pages doesn't mean that we've got everything. + // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, + // so a default read on an auth-supported NTAG is never complete. + if(!(mf_ul_get_features(data->type) & MfUltralightSupportAuth)) return true; + MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); + return config->auth_data.pwd.value != 0 || config->auth_data.pack.value != 0; +} diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h index 957d5f9d5..8f5c80fff 100644 --- a/lib/nfc/protocols/mifare_ultralight.h +++ b/lib/nfc/protocols/mifare_ultralight.h @@ -28,6 +28,8 @@ #define MF_UL_NTAG203_COUNTER_PAGE (41) +#define MF_UL_DEFAULT_PWD (0xFFFFFFFF) + typedef enum { MfUltralightAuthMethodManual, MfUltralightAuthMethodAmeebo, @@ -110,7 +112,6 @@ typedef struct { uint8_t signature[32]; uint32_t counter[3]; uint8_t tearing[3]; - bool has_auth; MfUltralightAuthMethod auth_method; uint8_t auth_key[4]; bool auth_success; @@ -169,6 +170,9 @@ typedef struct { MfUltralightFeatures supported_features; } MfUltralightReader; +// TODO rework with reader analyzer +typedef void (*MfUltralightAuthReceivedCallback)(MfUltralightAuth auth, void* context); + typedef struct { MfUltralightData data; MfUltralightConfigPages* config; @@ -187,6 +191,10 @@ typedef struct { bool read_counter_incremented; bool auth_attempted; MfUltralightAuth auth_attempt; + + // TODO rework with reader analyzer + MfUltralightAuthReceivedCallback auth_received_callback; + void* context; } MfUltralightEmulator; void mf_ul_reset(MfUltralightData* data); @@ -243,3 +251,5 @@ bool mf_ul_prepare_emulation_response( uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data); + +bool mf_ul_is_full_capture(MfUltralightData* data); diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c new file mode 100644 index 000000000..e2b3cc4ba --- /dev/null +++ b/lib/nfc/protocols/mrtd.c @@ -0,0 +1,758 @@ +#include +#include +#include +#include + +#include "../helpers/iso7816.h" + +#include "mrtd.h" + +#define TAG "Mrtd" + +//TODO: Check EF.DIR first? Before LDS1 +//TODO: ICAO 9303 p11 Β§4.2 steps +//- Read EF.CardAccess (REQUIRED) +// If not available or does not contain PACE params, try BAC +//- Read EF.DIR (OPTIONAL) +// Check list of applications present +//- PACE (CONDITIONAL) +//- BAC (CONDITIONAL) + +//TODO: idea - generalize ISO7816 reading. List available apps + +#define num_elements(A) (sizeof(A) / sizeof(A[0])) + +static const char* mrtd_auth_file_header = "Flipper MRTD params"; +static const uint32_t mrtd_auth_file_version = 1; + +static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) { + if(furi_log_get_level() >= level) { + printf("%s ", prefix); + for(size_t i = 0; i < length; i++) { + printf("%02X ", ((uint8_t*)data)[i]); + } + printf("\r\n"); + } +} + +static void mrtd_trace(MrtdApplication* app) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + if(furi_log_get_level() == FuriLogLevelTrace) { + printf("TX: "); + for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) { + printf("%02X ", tx_rx->tx_data[i]); + } + printf("\r\nRX: "); + for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) { + printf("%02X ", tx_rx->rx_data[i]); + } + printf("\r\n"); + } +} + +uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) { + // Last two bytes are return code + return (buffer[len - 2] << 8) | buffer[len - 1]; +} + +//TODO: rename to transceive? +//TODO: PRIO output and output written writing seems to crash flipper, sometimes +bool mrtd_send_apdu( + MrtdApplication* app, + uint8_t cla, + uint8_t ins, + uint8_t p1, + uint8_t p2, + uint8_t lc, + const void* data, + int16_t le, + uint8_t* output, + size_t* output_written) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + size_t idx = 0; + + FURI_LOG_T(TAG, "Send APDU, lc: %d, le: %d", lc, le); + + if(app->secure_messaging) { + app->ssc_long++; + idx = mrtd_protect_apdu( + cla, ins, p1, p2, lc, data, le, app->ksenc, app->ksmac, app->ssc_long, tx_rx->tx_data); + } else { + tx_rx->tx_data[idx++] = cla; + tx_rx->tx_data[idx++] = ins; + tx_rx->tx_data[idx++] = p1; + tx_rx->tx_data[idx++] = p2; + if(lc > 0) { + tx_rx->tx_data[idx++] = lc; + memcpy(tx_rx->tx_data + idx, data, lc); + idx += lc; + } + if(le >= 0) { + tx_rx->tx_data[idx++] = le & 0xff; + } + } + + tx_rx->tx_bits = idx * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + //TODO: timeout as param? + if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + mrtd_trace(app); + uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8); + + if(app->secure_messaging && ret_code == 0x9000) { + app->ssc_long++; + ret_code = mrtd_bac_decrypt_verify_sm( + tx_rx->rx_data, + tx_rx->rx_bits / 8 - 2, + app->ksenc, + app->ksmac, + app->ssc_long, + output, + output_written); + //ret_code = 0x1337; //TODO: remove PRIO + } + + //TODO: handle other return codes? + if(ret_code == 0x9000) { + if(!app->secure_messaging && le > 0) { + // Secure Messaging sets output while decrypting + output_written = memcpy(output, tx_rx->rx_data, le); + } + return true; + } else { + FURI_LOG_I(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); + + switch(ret_code) { + case 0x6987: + FURI_LOG_I(TAG, "'expected secure messaging data objects are missing'"); + app->secure_messaging = false; + break; + case 0x6988: + FURI_LOG_I(TAG, "'secure messaging data objects are incorrect'"); + app->secure_messaging = false; + break; + case 0xff01: + //CUSTOM ERROR CODE from mrtd_helpers.c + FURI_LOG_I(TAG, "'invalid padding'"); + break; + case 0xff02: + //CUSTOM ERROR CODE from mrtd_helpers.c + FURI_LOG_I(TAG, "'verify failed'"); + break; + } + + return false; + } + } else { + FURI_LOG_D(TAG, "Sending - failed"); + } + return false; +} + +//TODO: rename commands to "mrtd_cmd_..." +bool mrtd_select_app(MrtdApplication* app, AIDValue aid) { + FURI_LOG_D( + TAG, + "Send select App: %02X %02X %02X %02X %02X %02X %02X", + aid[0], + aid[1], + aid[2], + aid[3], + aid[4], + aid[5], + aid[6]); + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1, NULL, NULL)) { + FURI_LOG_W(TAG, "Failed select App"); + return false; + } + return true; +} + +bool mrtd_get_challenge(MrtdApplication* app, uint8_t challenge[8]) { + FURI_LOG_D(TAG, "Send Get Challenge"); + size_t chal_size; + if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08, challenge, &chal_size)) { + FURI_LOG_W(TAG, "Failed get challenge"); + return false; + } + + return true; +} + +bool mrtd_external_authenticate( + MrtdApplication* app, + uint8_t* cmd_data, + size_t cmd_size, + uint8_t* out_data, + size_t out_size) { + furi_assert(cmd_size == 0x28); + furi_assert(out_size >= 0x28); + + FURI_LOG_D(TAG, "Send External Authenticate"); + if(!mrtd_send_apdu( + app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28, out_data, &out_size)) { + FURI_LOG_W(TAG, "Failed External Authenticate"); + return false; + } + + return true; +} + +bool mrtd_select_file(MrtdApplication* app, EFFile file) { + uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff}; + FURI_LOG_D(TAG, "Send select EF: %s (0x%04X)", file.name, file.file_id); + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, NULL, NULL)) { + FURI_LOG_E(TAG, "Failed select EF 0x%04X", file.file_id); + return false; + } + + return true; +} + +size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, size_t offset) { + UNUSED(buffer); + UNUSED(bufsize); + // 00 B0 offst - + FURI_LOG_D(TAG, "Read binary, offset: %d", offset); + //TODO: read first 4 bytes, determine length, iterate through file + //TODO: limit reading/buffer fill to max bufsize + + //TODO: test with max_read = bufsize (value !0, > file size) + int16_t max_read = 0; // 0 = 'everything', -1 = 'nothing', >0 = amount of bytes + size_t buf_written = 0; + if(!mrtd_send_apdu( + app, 0x00, 0xB0, offset >> 8, offset & 0xff, 0x00, NULL, max_read, buffer, &buf_written)) { + FURI_LOG_E(TAG, "Failed to read"); + return 0; + } + FURI_LOG_D(TAG, "buf_written: %d\n", buf_written); + + return buf_written; +} + +//TODO: use short id to read, because it's mandatory for eMRTD +//TODO: check for support of extended length in EF.ATR/INFO, see ISO7816-4 + +void mrtd_read_dump(MrtdApplication* app, EFFile file) { + FURI_LOG_D(TAG, "Read and dump %s:", file.name); + + if(!mrtd_select_file(app, file)) { + return; + } + + uint8_t data[2048]; + size_t read = 0; + size_t offset = 0; + do { + read = mrtd_read_binary(app, data, sizeof(data), offset); + offset += read; + + hexdump(FuriLogLevelDebug, "Data:", data, read); + } while(read > 0); +} + +bool parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { + size_t offset = 0; + uint8_t app_idx = 0; + + memset(EF_DIR->applications, 0x00, sizeof(EF_DIR->applications)); + EF_DIR->applications_count = 0; + + while(offset < length) { + TlvInfo tlv = iso7816_tlv_parse(data + offset); + + if(tlv.tag != 0x61 || tlv.length != 0x09) { + FURI_LOG_E( + TAG, + "Invalid EF.DIR, tag at offset %d must be '61' and length 9. Got '%02X' and %d", + offset, + tlv.tag, + tlv.length); + return false; + } + + tlv = iso7816_tlv_parse(tlv.value); + if(tlv.tag != 0x4F || tlv.length != 0x07) { + FURI_LOG_E( + TAG, "Invalid EF.DIR, subtag at offset %d must be '4F' and length 7", offset); + return false; + } + + memcpy(EF_DIR->applications[app_idx], tlv.value, tlv.length); + EF_DIR->applications_count = ++app_idx; + + offset = tlv.next - data; + } + + //TODO: remove testing block: + FURI_LOG_D(TAG, "EF.DIR applications: %d", EF_DIR->applications_count); + if(furi_log_get_level() >= FuriLogLevelDebug) { + for(uint8_t i = 0; i < EF_DIR->applications_count; ++i) { + printf("- "); + for(uint8_t n = 0; n < sizeof(AIDValue); ++n) { + printf("%02X ", EF_DIR->applications[i][n]); + } + printf("\r\n"); + } + } + + return true; +} + +bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) { + uint16_t lds_tag_path[] = {0x60, 0x5f01}; + uint16_t unicode_tag_path[] = {0x60, 0x5f36}; + uint16_t tags_tag_path[] = {0x60, 0x5c}; + + TlvInfo tlv_lds_version = + iso7816_tlv_select(data, length, lds_tag_path, num_elements(lds_tag_path)); + if(!tlv_lds_version.tag) { + FURI_LOG_W(TAG, "EF.COM LDS version not found"); + return false; + } + + EF_COM->lds_version = tlv_number(tlv_lds_version); + + TlvInfo tlv_unicode_version = + iso7816_tlv_select(data, length, unicode_tag_path, num_elements(unicode_tag_path)); + if(!tlv_unicode_version.tag) { + FURI_LOG_W(TAG, "EF.COM Unicode info not found!"); + return false; + } + + EF_COM->unicode_version = tlv_number(tlv_unicode_version); + + TlvInfo tlv_tag_list = + iso7816_tlv_select(data, length, tags_tag_path, num_elements(tags_tag_path)); + if(!tlv_tag_list.tag) { + FURI_LOG_W(TAG, "EF.CO Tag List not found!"); + return false; + } + + for(size_t i = 0; i < MAX_EFCOM_TAGS; ++i) { + EF_COM->tag_list[i] = (i < tlv_tag_list.length) ? tlv_tag_list.value[i] : 0x00; + } + + return true; +} + +void mrzcpy(uint8_t* dest, const uint8_t* src, size_t* idx, size_t n) { + //FURI_LOG_D(TAG, "mrzcpy %d: %.*s", n, n, src + *idx); + //memcpy(dest, src + *idx, n); + for(size_t i = 0; i < n; ++i) { + uint8_t c = src[i + *idx]; + if(c == '<') { + c = ' '; + } + dest[i] = c; + } + dest[n] = 0x00; + *idx += n; +} + +bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) { + TlvInfo tlv_mrz = iso7816_tlv_select(data, length, (uint16_t[]){0x61, 0x5f1f}, 2); + + if(!tlv_mrz.tag) { + FURI_LOG_W(TAG, "DG1, unexpected content. Could not find tag 0x61, 0x5f1f"); + return false; + } + + const uint8_t* mrz = tlv_mrz.value; + size_t idx = 0; + + switch(tlv_mrz.length) { + case 90: + DG1->type = MrtdTypeTD1; + mrzcpy(DG1->doctype, mrz, &idx, 2); + mrzcpy(DG1->issuing_state, mrz, &idx, 3); + mrzcpy(DG1->docnr, mrz, &idx, 9); + idx += 1; // docnr check digit + idx += 15; // optional data + mrtd_parse_date(&DG1->birth_date, mrz + idx); + idx += 6; // birth_date + idx += 1; // birth date check digit + mrzcpy(DG1->sex, mrz, &idx, 1); + mrtd_parse_date(&DG1->expiry_date, mrz + idx); + idx += 6; // expiry_date + idx += 1; // expiry date check digit + mrzcpy(DG1->nationality, mrz, &idx, 3); + idx += 11; // optional data + idx += 1; // check digit + mrzcpy(DG1->name, mrz, &idx, 30); + // 30 + 30 + 30 + break; + case 72: + DG1->type = MrtdTypeTD2; + mrzcpy(DG1->doctype, mrz, &idx, 2); + mrzcpy(DG1->issuing_state, mrz, &idx, 3); + mrzcpy(DG1->name, mrz, &idx, 31); + mrzcpy(DG1->docnr, mrz, &idx, 9); + idx += 1; // docnr check digit + mrzcpy(DG1->nationality, mrz, &idx, 3); + mrtd_parse_date(&DG1->birth_date, mrz + idx); + idx += 6; // birth_date + idx += 1; // birth date check digit + mrzcpy(DG1->sex, mrz, &idx, 1); + mrtd_parse_date(&DG1->expiry_date, mrz + idx); + idx += 6; // expiry_date + idx += 1; // expiry date check digit + idx += 7; // optional data + idx += 1; // check digit + // 36 + 36 + break; + case 88: + DG1->type = MrtdTypeTD3; + mrzcpy(DG1->doctype, mrz, &idx, 2); + mrzcpy(DG1->issuing_state, mrz, &idx, 3); + mrzcpy(DG1->name, mrz, &idx, 39); + mrzcpy(DG1->docnr, mrz, &idx, 9); + idx += 1; // docnr check digit + mrzcpy(DG1->nationality, mrz, &idx, 3); + mrtd_parse_date(&DG1->birth_date, mrz + idx); + idx += 1; // birth date check digit + idx += 6; // birth_date + mrzcpy(DG1->sex, mrz, &idx, 1); + mrtd_parse_date(&DG1->expiry_date, mrz + idx); + idx += 6; // expiry_date + idx += 1; // expiry date check digit + idx += 14; // optional data + idx += 1; // check digit + idx += 1; // check digit + // 44 + 44 + break; + default: + FURI_LOG_W( + TAG, "Unexpected MRZ length in DG1: %d. TD1=90, TD2=72, TD3=88.", tlv_mrz.length); + return false; + } + + return true; +} + +bool mrtd_read_parse_file(MrtdApplication* app, EFFile file) { + uint8_t buffer[100]; + size_t buf_len; + + FURI_LOG_D(TAG, "Read and parse %s (%04X)", file.name, file.file_id); + + if(!mrtd_select_file(app, file)) { + FURI_LOG_E(TAG, "Could not select %s", file.name); + return false; + } + + FURI_LOG_D(TAG, "Selected %s", file.name); + + buf_len = mrtd_read_binary(app, buffer, num_elements(buffer), 0); + + if(!buf_len) { + FURI_LOG_E(TAG, "Could not read %s", file.name); + return false; + } + + FURI_LOG_D(TAG, "Read %s", file.name); + + bool result = false; + + if(file.file_id == EF.COM.file_id) { + result = parse_ef_com(&app->mrtd_data->files.EF_COM, buffer, buf_len); + FURI_LOG_D(TAG, "Parsed EF.COM"); + } else if(file.file_id == EF.DIR.file_id) { + result = parse_ef_dir(&app->mrtd_data->files.EF_DIR, buffer, buf_len); + FURI_LOG_D(TAG, "Parsed EF.DIR"); + } else if(file.file_id == EF.DG1.file_id) { + result = parse_ef_dg1(&app->mrtd_data->files.DG1, buffer, buf_len); + } else { + FURI_LOG_W(TAG, "Don't know how to parse file with id 0x%04X", file.file_id); + } + + return result; +} + +MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx, MrtdData* mrtd_data) { + MrtdApplication* app = malloc(sizeof(MrtdApplication)); + + app->tx_rx = tx_rx; + app->mrtd_data = mrtd_data; + + return app; +} + +void mrtd_free(MrtdApplication* app) { + furi_assert(app); + free(app); +} + +bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { + UNUSED(app); + + static bool rand_generator_inited = false; + uint8_t rnd_ic[8]; + uint8_t rnd_ifd[8]; + uint8_t k_ifd[16]; + + if(!rand_generator_inited) { + // TODO: should random initialization maybe be system wide? + srand(DWT->CYCCNT); + rand_generator_inited = true; + } + + mrtd_get_challenge(app, rnd_ic); + //TODO: remove memcpy rnd_ic + //memcpy(rnd_ic, "\x46\x08\xF9\x19\x88\x70\x22\x12", 8); + + furi_hal_random_fill_buf(rnd_ifd, 8); + furi_hal_random_fill_buf(k_ifd, 16); + //TODO: remove testing code: + //memcpy(rnd_ifd, "\x78\x17\x23\x86\x0C\x06\xC2\x26", 8); + //memcpy(k_ifd, "\x0B\x79\x52\x40\xCB\x70\x49\xB0\x1C\x19\xB3\x3E\x32\x80\x4F\x0B", 16); + + hexdump(FuriLogLevelDebug, "rnd_ifd:", rnd_ifd, 8); + hexdump(FuriLogLevelDebug, "k_ifd:", k_ifd, 16); + + uint8_t kenc[16]; + uint8_t kmac[16]; + + if(!mrtd_bac_keys(auth, kenc, kmac)) { + FURI_LOG_E(TAG, "Failed to calculate BAC keys"); + return false; + } + + uint8_t S[32]; + memcpy(S, rnd_ifd, 8); + memcpy(S + 8, rnd_ic, 8); + memcpy(S + 16, k_ifd, 16); + + hexdump(FuriLogLevelDebug, "S:", S, 32); + + uint8_t cmd_data[40]; + uint8_t* eifd = cmd_data; + uint8_t* mifd = cmd_data + 32; + mrtd_bac_encrypt(S, 32, kenc, eifd); + mrtd_bac_padded_mac(eifd, 32, kmac, mifd); + + uint8_t response[40]; + if(!mrtd_external_authenticate(app, cmd_data, 40, response, 40)) { + FURI_LOG_E(TAG, "BAC External Authenticate failed"); + return false; + } + + uint8_t buffer[32]; // Received R = RND.IC (8) || RND.IFD (8) || KIC (16) + if(!mrtd_bac_decrypt_verify(response, 40, kenc, kmac, buffer)) { + FURI_LOG_W(TAG, "BAC DecryptVerify failed"); + } + + uint8_t* rnd_ifd_recv = buffer + 8; + uint8_t* kic = buffer + 16; + + hexdump(FuriLogLevelDebug, "kic:", kic, 16); + + if(memcmp(rnd_ifd, rnd_ifd_recv, 8)) { + FURI_LOG_W(TAG, "BAC RND.IFD sent and received mismatch."); + } + + uint8_t kseed[16]; + for(uint8_t i = 0; i < 16; ++i) { + kseed[i] = k_ifd[i] ^ kic[i]; + //printf("seed %2d = %02X ^ %02X = %02X\r\n", i, k_ifd[i], kic[i], kseed[i]); + } + + hexdump(FuriLogLevelDebug, "kseed:", kseed, 16); + + if(!mrtd_bac_keys_from_seed(kseed, app->ksenc, app->ksmac)) { + FURI_LOG_E(TAG, "BAC error, could not derive KSenc and KSmac"); + return false; + } + hexdump(FuriLogLevelDebug, "ksenc:", app->ksenc, 16); + hexdump(FuriLogLevelDebug, "ksmac:", app->ksmac, 16); + + hexdump(FuriLogLevelTrace, "RND.IC:", rnd_ic, 8); + hexdump(FuriLogLevelTrace, "RND.IFS:", rnd_ifd, 8); + + app->ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd); + FURI_LOG_D(TAG, "SSC: %01llX", app->ssc_long); + + app->secure_messaging = true; + + return true; +} + +bool mrtd_authenticate(MrtdApplication* app) { + MrtdAuthMethod method = app->mrtd_data->auth.method; + app->mrtd_data->auth_success = false; + app->mrtd_data->auth_method_used = MrtdAuthMethodNone; + FURI_LOG_D(TAG, "Auth method: %d", method); + switch(method) { + case MrtdAuthMethodAny: + //TODO: try PACE, then BAC. For now, fall through to just BAC + case MrtdAuthMethodBac: + app->mrtd_data->auth_success = mrtd_bac(app, &app->mrtd_data->auth); + app->mrtd_data->auth_method_used = MrtdAuthMethodBac; + break; + case MrtdAuthMethodPace: + FURI_LOG_E(TAG, "Auth method PACE not implemented"); + break; + case MrtdAuthMethodNone: + default: + break; + } + + if(!app->mrtd_data->auth_success) { + return false; + } + + return true; +} + +bool mrtd_auth_params_save( + Storage* storage, + DialogsApp* dialogs, + MrtdAuthData* auth_data, + const char* file_name) { + return mrtd_auth_params_save_file( + storage, dialogs, auth_data, file_name, MRTD_APP_FOLDER, MRTD_APP_EXTENSION); +} + +void mrtd_date_prepare_format_string(MrtdDate date, FuriString* format_string) { + furi_string_printf(format_string, "%02u%02u%02u", date.year, date.month, date.day); +} + +bool mrtd_date_parse_format_string(MrtdDate* date, FuriString* format_string) { + int year; + int month; + int day; + + int ret = sscanf(furi_string_get_cstr(format_string), "%02d%02d%02d", &year, &month, &day); + if(ret != 3) { + return false; + } + + date->year = year; + date->month = month; + date->day = day; + return true; +} + +bool mrtd_auth_params_save_file( + Storage* storage, + DialogsApp* dialogs, + MrtdAuthData* auth_data, + const char* file_name, + const char* folder, + const char* extension) { + furi_assert(auth_data); + + bool saved = false; + FlipperFormat* file = flipper_format_file_alloc(storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + + do { + // Create mrtd directory if necessary + if(!storage_simply_mkdir(storage, MRTD_APP_FOLDER)) break; + + furi_string_printf(temp_str, "%s/%s%s", folder, file_name, extension); + + // Open file + if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; + // Write header + if(!flipper_format_write_header_cstr(file, mrtd_auth_file_header, mrtd_auth_file_version)) + break; + + // Write auth method + furi_string_set(temp_str, mrtd_auth_method_string(auth_data->method)); + if(!flipper_format_write_string(file, "Method", temp_str)) break; + + // Write birth date + mrtd_date_prepare_format_string(auth_data->birth_date, temp_str); + if(!flipper_format_write_string(file, "BirthDate", temp_str)) break; + + // Write expiry date + mrtd_date_prepare_format_string(auth_data->expiry_date, temp_str); + if(!flipper_format_write_string(file, "ExpiryDate", temp_str)) break; + + // Write docnr + furi_string_set(temp_str, auth_data->doc_number); + if(!flipper_format_write_string(file, "DocNr", temp_str)) break; + + saved = true; + } while(false); + + if(!saved) { + dialog_message_show_storage_error(dialogs, "Can not save\nparams file"); + } + furi_string_free(temp_str); + flipper_format_free(file); + return saved; +} + +bool mrtd_auth_params_load( + Storage* storage, + DialogsApp* dialogs, + MrtdAuthData* auth_data, + const char* file_path, + bool show_dialog) { + furi_assert(storage); + furi_assert(dialogs); + furi_assert(auth_data); + furi_assert(file_path); + + bool parsed = false; + FlipperFormat* file = flipper_format_file_alloc(storage); + bool deprecated_version = false; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + + MrtdAuthData copy; + + FURI_LOG_D(TAG, "Load auth params"); + + do { + if(!flipper_format_file_open_existing(file, file_path)) break; + + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + FURI_LOG_D(TAG, "Version: %s", furi_string_get_cstr(temp_str)); + if(furi_string_cmp_str(temp_str, mrtd_auth_file_header) || + (version != mrtd_auth_file_version)) { + deprecated_version = true; + break; + } + + if(!flipper_format_read_string(file, "Method", temp_str)) break; + FURI_LOG_D(TAG, "Method: %s", furi_string_get_cstr(temp_str)); + if(!mrtd_auth_method_parse_string(©.method, furi_string_get_cstr(temp_str))) break; + + if(!flipper_format_read_string(file, "BirthDate", temp_str)) break; + FURI_LOG_D(TAG, "BirthDate: %s", furi_string_get_cstr(temp_str)); + if(!mrtd_date_parse_format_string(©.birth_date, temp_str)) break; + + if(!flipper_format_read_string(file, "ExpiryDate", temp_str)) break; + FURI_LOG_D(TAG, "ExpiryDate: %s", furi_string_get_cstr(temp_str)); + if(!mrtd_date_parse_format_string(©.expiry_date, temp_str)) break; + + if(!flipper_format_read_string(file, "DocNr", temp_str)) break; + FURI_LOG_D(TAG, "DocNr: %s", furi_string_get_cstr(temp_str)); + strlcpy(copy.doc_number, furi_string_get_cstr(temp_str), MRTD_DOCNR_MAX_LENGTH); + + // Everything went fine. Save copy to pointed auth data + *auth_data = copy; + parsed = true; + } while(false); + + FURI_LOG_D(TAG, "Load done, success: %d", parsed); + + if(!parsed && show_dialog) { + if(deprecated_version) { + dialog_message_show_storage_error(dialogs, "File format deprecated"); + } else { + dialog_message_show_storage_error(dialogs, "Can not parse\nfile"); + } + } + + furi_string_free(temp_str); + flipper_format_free(file); + return parsed; +} diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h new file mode 100644 index 000000000..94cd0ece6 --- /dev/null +++ b/lib/nfc/protocols/mrtd.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#define MRTD_APP_FOLDER NFC_APP_FOLDER "/mrtd" +#define MRTD_APP_EXTENSION ".mrtd" + +typedef struct { + FuriHalNfcTxRxContext* tx_rx; + MrtdData* mrtd_data; + uint16_t file_offset; + uint8_t ksenc[16]; + uint8_t ksmac[16]; + uint64_t ssc_long; // TODO: rename without _long + + bool secure_messaging; +} MrtdApplication; + +//TODO: description +MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx, MrtdData* mrtd_data); +bool mrtd_select_app(MrtdApplication* app, AIDValue aid); +bool mrtd_authenticate(MrtdApplication* app); +bool mrtd_read_parse_file(MrtdApplication* app, EFFile file); + +bool mrtd_auth_params_save( + Storage* storage, + DialogsApp* dialogs, + MrtdAuthData* auth_data, + const char* file_name); +bool mrtd_auth_params_save_file( + Storage* storage, + DialogsApp* dialogs, + MrtdAuthData* auth_data, + const char* file_name, + const char* folder, + const char* extension); + +bool mrtd_auth_params_load( + Storage* storage, + DialogsApp* dialogs, + MrtdAuthData* auth_data, + const char* file_path, + bool show_dialog); diff --git a/lib/one_wire/ibutton/ibutton_worker.c b/lib/one_wire/ibutton/ibutton_worker.c index 26982bcb6..29126d845 100644 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ b/lib/one_wire/ibutton/ibutton_worker.c @@ -37,11 +37,7 @@ iButtonWorker* ibutton_worker_alloc() { worker->emulate_cb = NULL; worker->cb_ctx = NULL; - worker->thread = furi_thread_alloc(); - furi_thread_set_name(worker->thread, "ibutton_worker"); - furi_thread_set_callback(worker->thread, ibutton_worker_thread); - furi_thread_set_context(worker->thread, worker); - furi_thread_set_stack_size(worker->thread, 2048); + worker->thread = furi_thread_alloc_ex("iButtonWorker", 2048, ibutton_worker_thread, worker); worker->protocols = protocol_dict_alloc(ibutton_protocols, iButtonProtocolMax); 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/print/printf_tiny.c b/lib/print/printf_tiny.c index 0db11922d..6e47f6528 100644 --- a/lib/print/printf_tiny.c +++ b/lib/print/printf_tiny.c @@ -541,7 +541,7 @@ static size_t _etoa( exp2 = (int)(expval * 3.321928094887362 + 0.5); const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; + conv.U = ((uint64_t)exp2 + 1023) << 52U; // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); // correct for rounding errors diff --git a/lib/scons b/lib/scons deleted file mode 160000 index c2d1f09f6..000000000 --- a/lib/scons +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c2d1f09f615a9ef3fb5497a7e8e5ee2c900d21a7 diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index dee91c3e2..8fbec94ad 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -5,16 +5,20 @@ 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/registry.h"), - File("#/lib/subghz/protocols/raw.h"), - # File("#/lib/subghz/types.h"), - # File("#/lib/subghz/protocols/keeloq.h"), - # File("#/lib/subghz/protocols/star_line.h"), + File("environment.h"), + File("receiver.h"), + File("subghz_worker.h"), + File("subghz_tx_rx_worker.h"), + File("transmitter.h"), + File("registry.h"), + File("protocols/protocol_items.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/blocks/const.h b/lib/subghz/blocks/const.h index 57b47d503..f32334e2f 100644 --- a/lib/subghz/blocks/const.h +++ b/lib/subghz/blocks/const.h @@ -4,9 +4,17 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { const uint16_t te_long; const uint16_t te_short; const uint16_t te_delta; const uint8_t min_count_bit_for_found; } SubGhzBlockConst; + +#ifdef __cplusplus +} +#endif \ No newline at end of file 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 339e27c15..a5e561e35 100644 --- a/lib/subghz/blocks/decoder.h +++ b/lib/subghz/blocks/decoder.h @@ -4,6 +4,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzBlockDecoder SubGhzBlockDecoder; struct SubGhzBlockDecoder { @@ -20,9 +24,24 @@ 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 * @return hash Hash sum */ uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/encoder.h b/lib/subghz/blocks/encoder.h index 6ad734cbd..1ff077726 100644 --- a/lib/subghz/blocks/encoder.h +++ b/lib/subghz/blocks/encoder.h @@ -6,6 +6,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { bool is_running; size_t repeat; @@ -50,3 +54,7 @@ size_t subghz_protocol_blocks_get_upload( LevelDuration* upload, size_t max_size_upload, uint32_t duration_bit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 7496aea3d..1bad5f0a3 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -23,7 +23,7 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p bool subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(instance); bool res = false; FuriString* temp_str; diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index b4b850f82..a2a0a76cf 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -9,6 +9,10 @@ #include "furi_hal.h" #include "../types.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; struct SubGhzBlockGeneric { @@ -34,13 +38,13 @@ void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* p * Serialize data SubGhzBlockGeneric. * @param instance Pointer to a SubGhzBlockGeneric instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzBlockGeneric. @@ -49,3 +53,7 @@ bool subghz_block_generic_serialize( * @return true On success */ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index fca50c8f8..24202ad1c 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -1,17 +1,244 @@ #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; +} + +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; + + while(size--) { + remainder ^= *message++; + for(bit = 0; bit < 8; bit++) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 1; // LSB is unused + uint8_t poly = polynomial << 1; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 1 & 0x7f; // discard the LSB +} + +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); + polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte] << 8; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + 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) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + // Process message from last byte to first byte (reflected) + for(int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; + // Process individual bits of each byte (reflected) + 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) + if(key & 0x80) + key = (key << 1) ^ gen; + else + key = (key << 1); + } + } + return sum; +} + +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key) { + uint16_t sum = 0; + 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) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +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; +} + +uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; +} + +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 size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++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 85b146ebc..a4f04271a 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -9,19 +9,207 @@ #define bit_clear(value, bit) ((value) &= ~(1UL << (bit))) #define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) #define DURATION_DIFF(x, y) ((x < y) ? (y - x) : (x - y)) +#define abs(x) ((x) > 0 ? (x) : -(x)) -/** - * 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); +#ifdef __cplusplus +extern "C" { +#endif -/** - * Get parity the data bitwise. - * @param key In data - * @param count_bit number of data bits - * @return parity +/** Flip the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return Reverse data */ -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); + +/** 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[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** 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[], + 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 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[], + 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 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[], + 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 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[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** 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[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** 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[], + 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 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[], + size_t size, + uint8_t gen, + uint8_t key); + +/** 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[], + size_t size, + uint16_t gen, + uint16_t key); + +/** 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 + */ +uint8_t subghz_protocol_blocks_parity8(uint8_t byte); + +/** 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 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 +} +#endif diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index dffe10377..0a4b7b606 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -1,7 +1,9 @@ #include "environment.h" +#include "registry.h" struct SubGhzEnvironment { SubGhzKeystore* keystore; + const SubGhzProtocolRegistry* protocol_registry; const char* came_atomo_rainbow_table_file_name; const char* nice_flor_s_rainbow_table_file_name; }; @@ -10,6 +12,7 @@ SubGhzEnvironment* subghz_environment_alloc() { SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment)); instance->keystore = subghz_keystore_alloc(); + instance->protocol_registry = NULL; instance->came_atomo_rainbow_table_file_name = NULL; instance->nice_flor_s_rainbow_table_file_name = NULL; @@ -19,6 +22,7 @@ SubGhzEnvironment* subghz_environment_alloc() { void subghz_environment_free(SubGhzEnvironment* instance) { furi_assert(instance); + instance->protocol_registry = NULL; instance->came_atomo_rainbow_table_file_name = NULL; instance->nice_flor_s_rainbow_table_file_name = NULL; subghz_keystore_free(instance->keystore); @@ -67,3 +71,30 @@ const char* return instance->nice_flor_s_rainbow_table_file_name; } + +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + void* protocol_registry_items) { + furi_assert(instance); + const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; + instance->protocol_registry = protocol_registry; +} + +void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + return (void*)instance->protocol_registry; +} + +const char* + subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(instance->protocol_registry, idx); + if(protocol != NULL) { + return protocol->name; + } else { + return NULL; + } +} \ No newline at end of file diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index d4678e413..5f8fcf1f5 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -69,6 +69,30 @@ void subghz_environment_set_nice_flor_s_rainbow_table_file_name( const char* subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance); +/** + * Set list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @param protocol_registry_items Pointer to a SubGhzProtocolRegistry + */ +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + void* protocol_registry_items); + +/** + * Get list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Pointer to a SubGhzProtocolRegistry + */ +void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); + +/** + * Get list of protocols names. + * @param instance Pointer to a SubGhzEnvironment instance + * @param idx index protocols + * @return Pointer to a SubGhzProtocolRegistry + */ +const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx); + #ifdef __cplusplus } #endif diff --git a/lib/subghz/protocols/ansonic.c b/lib/subghz/protocols/ansonic.c new file mode 100644 index 000000000..81b196e36 --- /dev/null +++ b/lib/subghz/protocols/ansonic.c @@ -0,0 +1,346 @@ +#include "ansonic.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolAnsonic" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ + (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_ansonic_const = { + .te_short = 555, + .te_long = 1111, + .te_delta = 120, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderAnsonic { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderAnsonic { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + AnsonicDecoderStepReset = 0, + AnsonicDecoderStepFoundStartBit, + AnsonicDecoderStepSaveDuration, + AnsonicDecoderStepCheckDuration, +} AnsonicDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = { + .alloc = subghz_protocol_decoder_ansonic_alloc, + .free = subghz_protocol_decoder_ansonic_free, + + .feed = subghz_protocol_decoder_ansonic_feed, + .reset = subghz_protocol_decoder_ansonic_reset, + + .get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data, + .serialize = subghz_protocol_decoder_ansonic_serialize, + .deserialize = subghz_protocol_decoder_ansonic_deserialize, + .get_string = subghz_protocol_decoder_ansonic_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = { + .alloc = subghz_protocol_encoder_ansonic_alloc, + .free = subghz_protocol_encoder_ansonic_free, + + .deserialize = subghz_protocol_encoder_ansonic_deserialize, + .stop = subghz_protocol_encoder_ansonic_stop, + .yield = subghz_protocol_encoder_ansonic_yield, +}; + +const SubGhzProtocol subghz_protocol_ansonic = { + .name = SUBGHZ_PROTOCOL_ANSONIC_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_ansonic_decoder, + .encoder = &subghz_protocol_ansonic_encoder, +}; + +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic)); + + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return true On success + */ +static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + } + } + return true; +} + +bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_ansonic_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_ansonic_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_ansonic_stop(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic)); + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + free(instance); +} + +void subghz_protocol_decoder_ansonic_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + instance->decoder.parser_step = AnsonicDecoderStepReset; +} + +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + + switch(instance->decoder.parser_step) { + case AnsonicDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) < + subghz_protocol_ansonic_const.te_delta * 35)) { + //Found header Ansonic + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + } + break; + case AnsonicDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) { + //Found start bit Ansonic + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) { + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_ansonic_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = AnsonicDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else + instance->decoder.parser_step = AnsonicDecoderStepReset; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * 12345678(10) k 9 + * AAA => 10101010 1 01 0 + * + * 1...10 - DIP + * k- KEY + */ + instance->cnt = instance->data & 0xFFF; + instance->btn = ((instance->data >> 1) & 0x3); +} + +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_ansonic_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + subghz_protocol_ansonic_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%03lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/lib/subghz/protocols/ansonic.h b/lib/subghz/protocols/ansonic.h new file mode 100644 index 000000000..0170a6048 --- /dev/null +++ b/lib/subghz/protocols/ansonic.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_ANSONIC_NAME "Ansonic" + +typedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic; +typedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic; + +extern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder; +extern const SubGhzProtocol subghz_protocol_ansonic; + +/** + * Allocate SubGhzProtocolEncoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderAnsonic. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic 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 subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param output Resulting text + */ +void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/base.c b/lib/subghz/protocols/base.c index 4ee7a3f8a..36f33b9a5 100644 --- a/lib/subghz/protocols/base.c +++ b/lib/subghz/protocols/base.c @@ -26,7 +26,7 @@ bool subghz_protocol_decoder_base_get_string( bool subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { bool status = false; if(decoder_base->protocol && decoder_base->protocol->decoder && diff --git a/lib/subghz/protocols/base.h b/lib/subghz/protocols/base.h index 47b4e482b..1f3d3e1be 100644 --- a/lib/subghz/protocols/base.h +++ b/lib/subghz/protocols/base.h @@ -48,13 +48,13 @@ bool subghz_protocol_decoder_base_get_string( * Serialize data SubGhzProtocolDecoderBase. * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderBase. diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c index 605a922c6..2dd39af9e 100644 --- a/lib/subghz/protocols/bett.c +++ b/lib/subghz/protocols/bett.c @@ -299,7 +299,7 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { bool subghz_protocol_decoder_bett_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/bett.h b/lib/subghz/protocols/bett.h index 04bf46b56..c0ce0b7f4 100644 --- a/lib/subghz/protocols/bett.h +++ b/lib/subghz/protocols/bett.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderBETT. * @param context Pointer to a SubGhzProtocolDecoderBETT instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_bett_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderBETT. diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 7c037bd12..1ac4ec053 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -16,6 +16,8 @@ #define CAME_24_COUNT_BIT 24 #define PRASTEL_COUNT_BIT 25 #define PRASTEL_NAME "Prastel" +#define AIRFORCE_COUNT_BIT 18 +#define AIRFORCE_NAME "Airforce" static const SubGhzBlockConst subghz_protocol_came_const = { .te_short = 320, @@ -86,7 +88,7 @@ void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 10; - instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.size_upload = 128; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; @@ -151,10 +153,7 @@ bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flip FURI_LOG_E(TAG, "Deserialize error"); break; } - if((instance->generic.data_count_bit != - subghz_protocol_came_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != CAME_24_COUNT_BIT) && - (instance->generic.data_count_bit != PRASTEL_COUNT_BIT)) { + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } @@ -296,7 +295,7 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { bool subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCame* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); @@ -310,10 +309,7 @@ bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flip if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if((instance->generic.data_count_bit != - subghz_protocol_came_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != CAME_24_COUNT_BIT) && - (instance->generic.data_count_bit != PRASTEL_COUNT_BIT)) { + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } @@ -338,8 +334,11 @@ void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) "%s %dbit\r\n" "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n", - (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? PRASTEL_NAME : - instance->generic.protocol_name), + (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? + PRASTEL_NAME : + (instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ? + AIRFORCE_NAME : + instance->generic.protocol_name)), instance->generic.data_count_bit, code_found_lo, code_found_reverse_lo); diff --git a/lib/subghz/protocols/came.h b/lib/subghz/protocols/came.h index d1ac76286..253c93aae 100644 --- a/lib/subghz/protocols/came.h +++ b/lib/subghz/protocols/came.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCame. * @param context Pointer to a SubGhzProtocolDecoderCame instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderCame. diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index bd5104285..747d0d074 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -544,7 +544,7 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { bool subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/came_atomo.h b/lib/subghz/protocols/came_atomo.h index e9285bd3c..736aee850 100644 --- a/lib/subghz/protocols/came_atomo.h +++ b/lib/subghz/protocols/came_atomo.h @@ -86,13 +86,13 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCameAtomo. * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderCameAtomo. diff --git a/lib/subghz/protocols/came_twee.c b/lib/subghz/protocols/came_twee.c index 3d5029ec9..e7eb33c42 100644 --- a/lib/subghz/protocols/came_twee.c +++ b/lib/subghz/protocols/came_twee.c @@ -422,7 +422,7 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { bool subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCameTwee* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/came_twee.h b/lib/subghz/protocols/came_twee.h index aa1f0e0db..359b964da 100644 --- a/lib/subghz/protocols/came_twee.h +++ b/lib/subghz/protocols/came_twee.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCameTwee. * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderCameTwee. diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index d2d19af25..3650a9867 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -427,7 +427,7 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { bool subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderChamb_Code* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/chamberlain_code.h b/lib/subghz/protocols/chamberlain_code.h index 923a3151b..f87b64d90 100644 --- a/lib/subghz/protocols/chamberlain_code.h +++ b/lib/subghz/protocols/chamberlain_code.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderChamb_Code. * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderChamb_Code. diff --git a/lib/subghz/protocols/clemsa.c b/lib/subghz/protocols/clemsa.c index dbee0ac94..a2cb7a18b 100644 --- a/lib/subghz/protocols/clemsa.c +++ b/lib/subghz/protocols/clemsa.c @@ -319,7 +319,7 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { bool subghz_protocol_decoder_clemsa_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderClemsa* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/clemsa.h b/lib/subghz/protocols/clemsa.h index 8287af234..8858c1a3b 100644 --- a/lib/subghz/protocols/clemsa.h +++ b/lib/subghz/protocols/clemsa.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderClemsa. * @param context Pointer to a SubGhzProtocolDecoderClemsa instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_clemsa_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderClemsa. diff --git a/lib/subghz/protocols/doitrand.c b/lib/subghz/protocols/doitrand.c index 5c3402144..6b31d4f27 100644 --- a/lib/subghz/protocols/doitrand.c +++ b/lib/subghz/protocols/doitrand.c @@ -313,7 +313,7 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { bool subghz_protocol_decoder_doitrand_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderDoitrand* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/doitrand.h b/lib/subghz/protocols/doitrand.h index c96946a13..30f1fffd0 100644 --- a/lib/subghz/protocols/doitrand.h +++ b/lib/subghz/protocols/doitrand.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderDoitrand. * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_doitrand_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderDoitrand. diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index 117ab60d5..7572bd8ab 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -157,7 +157,7 @@ bool subghz_protocol_faac_slh_create_data( uint32_t cnt, uint32_t seed, const char* manufacture_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); // roguemaster don't steal!!! SubGhzProtocolEncoderFaacSLH* instance = context; @@ -425,7 +425,7 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { bool subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; @@ -462,10 +462,7 @@ bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } + uint8_t seed_data[sizeof(uint32_t)] = {0}; for(size_t i = 0; i < sizeof(uint32_t); i++) { seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; @@ -476,6 +473,11 @@ bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* } instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | seed_data[3]; + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } res = true; } while(false); diff --git a/lib/subghz/protocols/faac_slh.h b/lib/subghz/protocols/faac_slh.h index b8ad27700..9390da43a 100644 --- a/lib/subghz/protocols/faac_slh.h +++ b/lib/subghz/protocols/faac_slh.h @@ -33,7 +33,7 @@ void subghz_protocol_encoder_faac_slh_free(void* context); * @param cnt Counter value, 16 bit * @param seed Seed value, 32 bit * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition + * @param preset Modulation, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_faac_slh_create_data( @@ -44,7 +44,7 @@ bool subghz_protocol_faac_slh_create_data( uint32_t cnt, uint32_t seed, const char* manufacture_name, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize and generating an upload to send. @@ -105,13 +105,13 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderFaacSLH. * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderFaacSLH. diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c index 5cf3a8714..4c7c2d484 100644 --- a/lib/subghz/protocols/gate_tx.c +++ b/lib/subghz/protocols/gate_tx.c @@ -293,7 +293,7 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) { bool subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderGateTx* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/gate_tx.h b/lib/subghz/protocols/gate_tx.h index 36eeb9a99..4bfba3597 100644 --- a/lib/subghz/protocols/gate_tx.h +++ b/lib/subghz/protocols/gate_tx.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderGateTx. * @param context Pointer to a SubGhzProtocolDecoderGateTx instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderGateTx. diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c index 230f4cfef..39e27bbf8 100644 --- a/lib/subghz/protocols/holtek.c +++ b/lib/subghz/protocols/holtek.c @@ -326,7 +326,7 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { bool subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHoltek* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/holtek.h b/lib/subghz/protocols/holtek.h index 1dac783b1..252a26dc7 100644 --- a/lib/subghz/protocols/holtek.h +++ b/lib/subghz/protocols/holtek.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHoltek. * @param context Pointer to a SubGhzProtocolDecoderHoltek instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderHoltek. diff --git a/lib/subghz/protocols/honeywell_wdb.c b/lib/subghz/protocols/honeywell_wdb.c index 3cd62698d..3b940fc67 100644 --- a/lib/subghz/protocols/honeywell_wdb.c +++ b/lib/subghz/protocols/honeywell_wdb.c @@ -348,7 +348,7 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { bool subghz_protocol_decoder_honeywell_wdb_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHoneywell_WDB* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/honeywell_wdb.h b/lib/subghz/protocols/honeywell_wdb.h index c61aa822f..828631837 100644 --- a/lib/subghz/protocols/honeywell_wdb.h +++ b/lib/subghz/protocols/honeywell_wdb.h @@ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHoneywell_WDB. * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_honeywell_wdb_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderHoneywell_WDB. diff --git a/lib/subghz/protocols/hormann.c b/lib/subghz/protocols/hormann.c index d8438604f..cb6adaf62 100644 --- a/lib/subghz/protocols/hormann.c +++ b/lib/subghz/protocols/hormann.c @@ -314,7 +314,7 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { bool subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/hormann.h b/lib/subghz/protocols/hormann.h index 743ad0b38..857a50041 100644 --- a/lib/subghz/protocols/hormann.h +++ b/lib/subghz/protocols/hormann.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHormann. * @param context Pointer to a SubGhzProtocolDecoderHormann instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderHormann. diff --git a/lib/subghz/protocols/ido.c b/lib/subghz/protocols/ido.c index f37718b4e..dff9defc0 100644 --- a/lib/subghz/protocols/ido.c +++ b/lib/subghz/protocols/ido.c @@ -183,7 +183,7 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { bool subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderIDo* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/ido.h b/lib/subghz/protocols/ido.h index 67d2b4d8f..634f6ff89 100644 --- a/lib/subghz/protocols/ido.h +++ b/lib/subghz/protocols/ido.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderIDo. * @param context Pointer to a SubGhzProtocolDecoderIDo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderIDo. diff --git a/lib/subghz/protocols/intertechno_v3.c b/lib/subghz/protocols/intertechno_v3.c index 14e137cab..2c4e514cc 100644 --- a/lib/subghz/protocols/intertechno_v3.c +++ b/lib/subghz/protocols/intertechno_v3.c @@ -407,7 +407,7 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { bool subghz_protocol_decoder_intertechno_v3_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderIntertechno_V3* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/intertechno_v3.h b/lib/subghz/protocols/intertechno_v3.h index 078d03978..ffee14b04 100644 --- a/lib/subghz/protocols/intertechno_v3.h +++ b/lib/subghz/protocols/intertechno_v3.h @@ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderIntertechno_V3. * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_intertechno_v3_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderIntertechno_V3. diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 4df10aff2..46c86fee4 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -137,7 +137,11 @@ void subghz_protocol_encoder_keeloq_free(void* context) { * @param btn Button number, 4 bit */ static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { - instance->generic.cnt++; + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } uint32_t fix = btn << 28 | instance->generic.serial; uint32_t decrypt = btn << 28 | (instance->generic.serial & 0x3FF) @@ -151,15 +155,18 @@ static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instanc instance->manufacture_name = ""; } - // DTM Neo uses 12bit, simple learning - if((strcmp(instance->manufacture_name, "DTM_Neo") == 0)) { + // DTM Neo uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode -> 12bit normal learning + if((strcmp(instance->manufacture_name, "DTM_Neo") == 0) || + (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || + (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0)) { decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; } - // Nice Smilo, MHouse, JCM has 8bit serial - simple learning + // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning if((strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || - (strcmp(instance->manufacture_name, "JCM_Tech") == 0)) { + (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || + (strcmp(instance->manufacture_name, "Normstahl") == 0)) { decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; } @@ -247,7 +254,7 @@ bool subghz_protocol_keeloq_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolEncoderKeeloq* instance = context; instance->generic.serial = serial; @@ -269,7 +276,7 @@ bool subghz_protocol_keeloq_bft_create_data( uint16_t cnt, uint32_t seed, const char* manufacture_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolEncoderKeeloq* instance = context; instance->generic.serial = serial; @@ -368,15 +375,6 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl break; } - // Read manufacturer from file - if(flipper_format_read_string( - flipper_format, "Manufacture", instance->manufacture_from_file)) { - instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); - mfname = furi_string_get_cstr(instance->manufacture_from_file); - } else { - FURI_LOG_D(TAG, "ENCODER: Missing Manufacture"); - } - uint8_t seed_data[sizeof(uint32_t)] = {0}; for(size_t i = 0; i < sizeof(uint32_t); i++) { seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; @@ -387,6 +385,20 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | seed_data[3]; + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "ENCODER: Missing Manufacture"); + } + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); @@ -657,6 +669,26 @@ 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); + mfname = *manufacture_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); + mfname = *manufacture_name; + return 1; + } + break; case KEELOQ_LEARNING_UNKNOWN: // Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); @@ -947,7 +979,7 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { bool subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; @@ -956,9 +988,22 @@ bool subghz_protocol_decoder_keeloq_serialize( subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); + if(strcmp(instance->manufacture_name, "BFT") == 0) { + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "DECODER Serialize: Unable to add Seed"); + res = false; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + } + if(res && !flipper_format_write_string_cstr( flipper_format, "Manufacture", instance->manufacture_name)) { - FURI_LOG_E(TAG, "Unable to add manufacture name"); + FURI_LOG_E(TAG, "DECODER Serialize: Unable to add manufacture name"); res = false; } return res; @@ -978,19 +1023,6 @@ bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* fl FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - - // Read manufacturer from file - if(flipper_format_read_string( - flipper_format, "Manufacture", instance->manufacture_from_file)) { - instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); - mfname = furi_string_get_cstr(instance->manufacture_from_file); - } else { - FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); - } uint8_t seed_data[sizeof(uint32_t)] = {0}; for(size_t i = 0; i < sizeof(uint32_t); i++) { @@ -1001,6 +1033,21 @@ bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* fl } instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | seed_data[3]; + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); + } + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + res = true; } while(false); diff --git a/lib/subghz/protocols/keeloq.h b/lib/subghz/protocols/keeloq.h index f87b5c1c3..7b0cfc3bd 100644 --- a/lib/subghz/protocols/keeloq.h +++ b/lib/subghz/protocols/keeloq.h @@ -36,7 +36,7 @@ void subghz_protocol_encoder_keeloq_free(void* context); * @param btn Button number, 4 bit * @param cnt Counter value, 16 bit * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition + * @param preset Modulation, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_keeloq_create_data( @@ -46,7 +46,7 @@ bool subghz_protocol_keeloq_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Key generation for BFT. @@ -57,7 +57,7 @@ bool subghz_protocol_keeloq_create_data( * @param cnt Counter value, 16 bit * @param seed Seed value, 32 bit * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition + * @param preset Modulation, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_keeloq_bft_create_data( @@ -68,7 +68,7 @@ bool subghz_protocol_keeloq_bft_create_data( uint16_t cnt, uint32_t seed, const char* manufacture_name, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize and generating an upload to send. @@ -129,13 +129,13 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderKeeloq. * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderKeeloq. diff --git a/lib/subghz/protocols/keeloq_common.c b/lib/subghz/protocols/keeloq_common.c index 7203256f2..1d2d04457 100644 --- a/lib/subghz/protocols/keeloq_common.c +++ b/lib/subghz/protocols/keeloq_common.c @@ -109,6 +109,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 5d813de0f..a6c0d346e 100644 --- a/lib/subghz/protocols/keeloq_common.h +++ b/lib/subghz/protocols/keeloq_common.h @@ -23,6 +23,8 @@ #define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u #define KEELOQ_LEARNING_FAAC 5u #define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 6u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 7u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 8u /** * Simple Learning Encrypt @@ -80,3 +82,19 @@ uint64_t subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const */ 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); diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index d46838661..997f8e1de 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -233,7 +233,7 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { bool subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/kia.h b/lib/subghz/protocols/kia.h index cf1869219..a9afcf149 100644 --- a/lib/subghz/protocols/kia.h +++ b/lib/subghz/protocols/kia.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderKIA. * @param context Pointer to a SubGhzProtocolDecoderKIA instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderKIA. diff --git a/lib/subghz/protocols/linear.c b/lib/subghz/protocols/linear.c index 582c4aaf3..2fc8b20c8 100644 --- a/lib/subghz/protocols/linear.c +++ b/lib/subghz/protocols/linear.c @@ -303,7 +303,7 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { bool subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderLinear* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/linear.h b/lib/subghz/protocols/linear.h index 6111ad85c..923337ac2 100644 --- a/lib/subghz/protocols/linear.h +++ b/lib/subghz/protocols/linear.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderLinear. * @param context Pointer to a SubGhzProtocolDecoderLinear instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderLinear. diff --git a/lib/subghz/protocols/magellan.c b/lib/subghz/protocols/magellan.c new file mode 100644 index 000000000..67d3fe3d3 --- /dev/null +++ b/lib/subghz/protocols/magellan.c @@ -0,0 +1,445 @@ +#include "magellan.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMagellan" + +static const SubGhzBlockConst subghz_protocol_magellan_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 100, + .min_count_bit_for_found = 32, +}; + +struct SubGhzProtocolDecoderMagellan { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderMagellan { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MagellanDecoderStepReset = 0, + MagellanDecoderStepCheckPreambula, + MagellanDecoderStepFoundPreambula, + MagellanDecoderStepSaveDuration, + MagellanDecoderStepCheckDuration, +} MagellanDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = { + .alloc = subghz_protocol_decoder_magellan_alloc, + .free = subghz_protocol_decoder_magellan_free, + + .feed = subghz_protocol_decoder_magellan_feed, + .reset = subghz_protocol_decoder_magellan_reset, + + .get_hash_data = subghz_protocol_decoder_magellan_get_hash_data, + .serialize = subghz_protocol_decoder_magellan_serialize, + .deserialize = subghz_protocol_decoder_magellan_deserialize, + .get_string = subghz_protocol_decoder_magellan_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = { + .alloc = subghz_protocol_encoder_magellan_alloc, + .free = subghz_protocol_encoder_magellan_free, + + .deserialize = subghz_protocol_encoder_magellan_deserialize, + .stop = subghz_protocol_encoder_magellan_stop, + .yield = subghz_protocol_encoder_magellan_yield, +}; + +const SubGhzProtocol subghz_protocol_magellan = { + .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_magellan_decoder, + .encoder = &subghz_protocol_magellan_encoder, +}; + +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan)); + + instance->base.protocol = &subghz_protocol_magellan; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_magellan_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMagellan* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMagellan instance + * @return true On success + */ +static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) { + furi_assert(instance); + + size_t index = 0; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + for(uint8_t i = 0; i < 12; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + } + } + + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100); + + instance->encoder.size_upload = index; + return true; +} + +bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMagellan* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_magellan_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_magellan_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_magellan_stop(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_magellan_yield(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan)); + instance->base.protocol = &subghz_protocol_magellan; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_magellan_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + free(instance); +} + +void subghz_protocol_decoder_magellan_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + instance->decoder.parser_step = MagellanDecoderStepReset; +} + +uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x00; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) { + uint8_t data[3] = { + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + return (instance->decoder.decode_data & 0xFF) == + subghz_protocol_magellan_crc8(data, sizeof(data)); +} + +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + + switch(instance->decoder.parser_step) { + case MagellanDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + instance->decoder.parser_step = MagellanDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case MagellanDecoderStepCheckPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + // Found header + instance->header_count++; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2) && + (instance->header_count > 10)) { + instance->decoder.parser_step = MagellanDecoderStepFoundPreambula; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } + break; + + case MagellanDecoderStepFoundPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) < + subghz_protocol_magellan_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2)) { + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } + break; + + case MagellanDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = MagellanDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + break; + + case MagellanDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) { + //Found stop bit + if((instance->decoder.decode_count_bit == + subghz_protocol_magellan_const.min_count_bit_for_found) && + subghz_protocol_magellan_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = MagellanDecoderStepReset; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) { + /* +* package 32b data 24b CRC8 +* 0x037AE4828 => 001101111010111001001000 00101000 +* +* 0x037AE48 (flipped in reverse bit sequence) => 0x1275EC +* +* 0x1275EC => 0x12-event codes, 0x75EC-serial (dec 117236) +* +* event codes +* bit_0: 1-Open/Motion, 0-close/ok +* bit_1: 1-Tamper On (alarm), 0-Tamper Off (ok) +* bit_2: ? +* bit_3: 1-power on +* bit_4: model type - wireless reed +* bit_5: model type - motion sensor +* bit_6: ? +* bit_7: ? +* +*/ + uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24); + instance->serial = data_rev & 0xFFFF; + instance->btn = (data_rev >> 16) & 0xFF; +} + +static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s%s%s%s%s", + ((event >> 4) & 0x1 ? (event & 0x1 ? " Open" : " Close") : + (event & 0x1 ? " Motion" : " Ok")), + ((event >> 1) & 0x1 ? ", Tamper On\n(Alarm)" : ""), + ((event >> 2) & 0x1 ? ", ?" : ""), + ((event >> 3) & 0x1 ? ", Power On" : ""), + ((event >> 4) & 0x1 ? ", MT:Wireless_Reed" : ""), + ((event >> 5) & 0x1 ? ", MT:Motion_\nSensor" : ""), + ((event >> 6) & 0x1 ? ", ?" : ""), + ((event >> 7) & 0x1 ? ", ?" : "")); +} + +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_magellan_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + subghz_protocol_magellan_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Sn:%03ld%03ld, Event:0x%02X\r\n" + "Stat:", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + (instance->generic.serial >> 8) & 0xFF, + instance->generic.serial & 0xFF, + instance->generic.btn); + + subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output); +} diff --git a/lib/subghz/protocols/magellan.h b/lib/subghz/protocols/magellan.h new file mode 100644 index 000000000..a179c9cb4 --- /dev/null +++ b/lib/subghz/protocols/magellan.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan" + +typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan; +typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan; + +extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder; +extern const SubGhzProtocol subghz_protocol_magellan; + +/** + * Allocate SubGhzProtocolEncoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance + */ +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMagellan. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_magellan_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance + */ +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan 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 subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param output Resulting text + */ +void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/magellen.c b/lib/subghz/protocols/magellen.c deleted file mode 100644 index 160ec4a2c..000000000 --- a/lib/subghz/protocols/magellen.c +++ /dev/null @@ -1,445 +0,0 @@ -#include "magellen.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolMagellen" - -static const SubGhzBlockConst subghz_protocol_magellen_const = { - .te_short = 200, - .te_long = 400, - .te_delta = 100, - .min_count_bit_for_found = 32, -}; - -struct SubGhzProtocolDecoderMagellen { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - uint16_t header_count; -}; - -struct SubGhzProtocolEncoderMagellen { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - MagellenDecoderStepReset = 0, - MagellenDecoderStepCheckPreambula, - MagellenDecoderStepFoundPreambula, - MagellenDecoderStepSaveDuration, - MagellenDecoderStepCheckDuration, -} MagellenDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_magellen_decoder = { - .alloc = subghz_protocol_decoder_magellen_alloc, - .free = subghz_protocol_decoder_magellen_free, - - .feed = subghz_protocol_decoder_magellen_feed, - .reset = subghz_protocol_decoder_magellen_reset, - - .get_hash_data = subghz_protocol_decoder_magellen_get_hash_data, - .serialize = subghz_protocol_decoder_magellen_serialize, - .deserialize = subghz_protocol_decoder_magellen_deserialize, - .get_string = subghz_protocol_decoder_magellen_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_magellen_encoder = { - .alloc = subghz_protocol_encoder_magellen_alloc, - .free = subghz_protocol_encoder_magellen_free, - - .deserialize = subghz_protocol_encoder_magellen_deserialize, - .stop = subghz_protocol_encoder_magellen_stop, - .yield = subghz_protocol_encoder_magellen_yield, -}; - -const SubGhzProtocol subghz_protocol_magellen = { - .name = SUBGHZ_PROTOCOL_MAGELLEN_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_magellen_decoder, - .encoder = &subghz_protocol_magellen_encoder, -}; - -void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderMagellen* instance = malloc(sizeof(SubGhzProtocolEncoderMagellen)); - - instance->base.protocol = &subghz_protocol_magellen; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_magellen_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderMagellen* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderMagellen instance - * @return true On success - */ -static bool subghz_protocol_encoder_magellen_get_upload(SubGhzProtocolEncoderMagellen* instance) { - furi_assert(instance); - - size_t index = 0; - - //Send header - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short * 4); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); - for(uint8_t i = 0; i < 12; i++) { - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); - } - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); - - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long * 3); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_short); - } - } - - //Send stop bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellen_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellen_const.te_long * 100); - - instance->encoder.size_upload = index; - return true; -} - -bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderMagellen* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_magellen_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - if(!subghz_protocol_encoder_magellen_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_magellen_stop(void* context) { - SubGhzProtocolEncoderMagellen* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_magellen_yield(void* context) { - SubGhzProtocolEncoderMagellen* instance = context; - - if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { - instance->encoder.is_running = false; - return level_duration_reset(); - } - - LevelDuration ret = instance->encoder.upload[instance->encoder.front]; - - if(++instance->encoder.front == instance->encoder.size_upload) { - instance->encoder.repeat--; - instance->encoder.front = 0; - } - - return ret; -} - -void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderMagellen* instance = malloc(sizeof(SubGhzProtocolDecoderMagellen)); - instance->base.protocol = &subghz_protocol_magellen; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_magellen_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - free(instance); -} - -void subghz_protocol_decoder_magellen_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - instance->decoder.parser_step = MagellenDecoderStepReset; -} - -uint8_t subghz_protocol_magellen_crc8(uint8_t* data, size_t len) { - uint8_t crc = 0x00; - size_t i, j; - for(i = 0; i < len; i++) { - crc ^= data[i]; - for(j = 0; j < 8; j++) { - if((crc & 0x80) != 0) - crc = (uint8_t)((crc << 1) ^ 0x31); - else - crc <<= 1; - } - } - return crc; -} - -static bool subghz_protocol_magellen_check_crc(SubGhzProtocolDecoderMagellen* instance) { - uint8_t data[3] = { - instance->decoder.decode_data >> 24, - instance->decoder.decode_data >> 16, - instance->decoder.decode_data >> 8}; - return (instance->decoder.decode_data & 0xFF) == - subghz_protocol_magellen_crc8(data, sizeof(data)); -} - -void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - - switch(instance->decoder.parser_step) { - case MagellenDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta)) { - instance->decoder.parser_step = MagellenDecoderStepCheckPreambula; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case MagellenDecoderStepCheckPreambula: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta)) { - // Found header - instance->header_count++; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < - subghz_protocol_magellen_const.te_delta * 2) && - (instance->header_count > 10)) { - instance->decoder.parser_step = MagellenDecoderStepFoundPreambula; - } else { - instance->decoder.parser_step = MagellenDecoderStepReset; - } - } - break; - - case MagellenDecoderStepFoundPreambula: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_magellen_const.te_short * 6) < - subghz_protocol_magellen_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < - subghz_protocol_magellen_const.te_delta * 2)) { - instance->decoder.parser_step = MagellenDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = MagellenDecoderStepReset; - } - } - break; - - case MagellenDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = MagellenDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = MagellenDecoderStepReset; - } - break; - - case MagellenDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_long) < - subghz_protocol_magellen_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = MagellenDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellen_const.te_long) < - subghz_protocol_magellen_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellen_const.te_short) < - subghz_protocol_magellen_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = MagellenDecoderStepSaveDuration; - } else if(duration >= (subghz_protocol_magellen_const.te_long * 3)) { - //Found stop bit - if((instance->decoder.decode_count_bit == - subghz_protocol_magellen_const.min_count_bit_for_found) && - subghz_protocol_magellen_check_crc(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = MagellenDecoderStepReset; - } else { - instance->decoder.parser_step = MagellenDecoderStepReset; - } - } else { - instance->decoder.parser_step = MagellenDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_magellen_check_remote_controller(SubGhzBlockGeneric* instance) { - /* -* package 32b data 24b CRC8 -* 0x037AE4828 => 001101111010111001001000 00101000 -* -* 0x037AE48 (flipped in reverse bit sequence) => 0x1275EC -* -* 0x1275EC => 0x12-event codes, 0x75EC-serial (dec 117236) -* -* event codes -* bit_0: 1-Open/Motion, 0-close/ok -* bit_1: 1-Tamper On (alarm), 0-Tamper Off (ok) -* bit_2: ? -* bit_3: 1-power on -* bit_4: model type - wireless reed -* bit_5: model type - motion sensor -* bit_6: ? -* bit_7: ? -* -*/ - uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24); - instance->serial = data_rev & 0xFFFF; - instance->btn = (data_rev >> 16) & 0xFF; -} - -static void subghz_protocol_magellen_get_event_serialize(uint8_t event, FuriString* output) { - furi_string_cat_printf( - output, - "%s%s%s%s%s%s%s%s", - ((event >> 4) & 0x1 ? (event & 0x1 ? " Open" : " Close") : - (event & 0x1 ? " Motion" : " Ok")), - ((event >> 1) & 0x1 ? ", Tamper On\n(Alarm)" : ""), - ((event >> 2) & 0x1 ? ", ?" : ""), - ((event >> 3) & 0x1 ? ", Power On" : ""), - ((event >> 4) & 0x1 ? ", MT:Wireless_Reed" : ""), - ((event >> 5) & 0x1 ? ", MT:Motion_Sensor" : ""), - ((event >> 6) & 0x1 ? ", ?" : ""), - ((event >> 7) & 0x1 ? ", ?" : "")); -} - -uint8_t subghz_protocol_decoder_magellen_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_magellen_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { - furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_magellen_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; -} - -void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderMagellen* instance = context; - subghz_protocol_magellen_check_remote_controller(&instance->generic); - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%08lX\r\n" - "Sn:%03ld%03ld, Event:0x%02X\r\n" - "Stat:", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - (instance->generic.serial >> 8) & 0xFF, - instance->generic.serial & 0xFF, - instance->generic.btn); - - subghz_protocol_magellen_get_event_serialize(instance->generic.btn, output); -} diff --git a/lib/subghz/protocols/magellen.h b/lib/subghz/protocols/magellen.h deleted file mode 100644 index 226919c8d..000000000 --- a/lib/subghz/protocols/magellen.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_MAGELLEN_NAME "Magellen" - -typedef struct SubGhzProtocolDecoderMagellen SubGhzProtocolDecoderMagellen; -typedef struct SubGhzProtocolEncoderMagellen SubGhzProtocolEncoderMagellen; - -extern const SubGhzProtocolDecoder subghz_protocol_magellen_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_magellen_encoder; -extern const SubGhzProtocol subghz_protocol_magellen; - -/** - * Allocate SubGhzProtocolEncoderMagellen. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderMagellen* pointer to a SubGhzProtocolEncoderMagellen instance - */ -void* subghz_protocol_encoder_magellen_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderMagellen. - * @param context Pointer to a SubGhzProtocolEncoderMagellen instance - */ -void subghz_protocol_encoder_magellen_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderMagellen instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_magellen_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderMagellen instance - */ -void subghz_protocol_encoder_magellen_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderMagellen instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_magellen_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderMagellen. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderMagellen* pointer to a SubGhzProtocolDecoderMagellen instance - */ -void* subghz_protocol_decoder_magellen_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderMagellen. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - */ -void subghz_protocol_decoder_magellen_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderMagellen. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - */ -void subghz_protocol_decoder_magellen_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_magellen_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_magellen_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderMagellen. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition - * @return true On success - */ -bool subghz_protocol_decoder_magellen_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); - -/** - * Deserialize data SubGhzProtocolDecoderMagellen. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_magellen_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderMagellen instance - * @param output Resulting text - */ -void subghz_protocol_decoder_magellen_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/marantec.c b/lib/subghz/protocols/marantec.c index a72238a41..d557c29b0 100644 --- a/lib/subghz/protocols/marantec.c +++ b/lib/subghz/protocols/marantec.c @@ -349,7 +349,7 @@ uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { bool subghz_protocol_decoder_marantec_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderMarantec* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/marantec.h b/lib/subghz/protocols/marantec.h index 5fc042e77..e330ccf16 100644 --- a/lib/subghz/protocols/marantec.h +++ b/lib/subghz/protocols/marantec.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderMarantec. * @param context Pointer to a SubGhzProtocolDecoderMarantec instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_marantec_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderMarantec. diff --git a/lib/subghz/protocols/megacode.c b/lib/subghz/protocols/megacode.c index baa8188d3..1b871a0c6 100644 --- a/lib/subghz/protocols/megacode.c +++ b/lib/subghz/protocols/megacode.c @@ -384,7 +384,7 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { bool subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderMegaCode* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/megacode.h b/lib/subghz/protocols/megacode.h index 0fafddb6b..e31434fa3 100644 --- a/lib/subghz/protocols/megacode.h +++ b/lib/subghz/protocols/megacode.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderMegaCode. * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderMegaCode. diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index d6925d485..5fffaa19d 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -346,7 +346,7 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { bool subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/nero_radio.h b/lib/subghz/protocols/nero_radio.h index ef270342c..361da6173 100644 --- a/lib/subghz/protocols/nero_radio.h +++ b/lib/subghz/protocols/nero_radio.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNeroRadio. * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderNeroRadio. diff --git a/lib/subghz/protocols/nero_sketch.c b/lib/subghz/protocols/nero_sketch.c index 8080bf9e4..b124b717b 100644 --- a/lib/subghz/protocols/nero_sketch.c +++ b/lib/subghz/protocols/nero_sketch.c @@ -331,7 +331,7 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { bool subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNeroSketch* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/nero_sketch.h b/lib/subghz/protocols/nero_sketch.h index 4536e7043..ac87fb00a 100644 --- a/lib/subghz/protocols/nero_sketch.h +++ b/lib/subghz/protocols/nero_sketch.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNeroSketch. * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderNeroSketch. diff --git a/lib/subghz/protocols/nice_flo.c b/lib/subghz/protocols/nice_flo.c index 06538db10..a57d5f4da 100644 --- a/lib/subghz/protocols/nice_flo.c +++ b/lib/subghz/protocols/nice_flo.c @@ -138,9 +138,9 @@ bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* FURI_LOG_E(TAG, "Deserialize error"); break; } - if((instance->generic.data_count_bit != - subghz_protocol_nice_flo_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (instance->generic.data_count_bit > 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; @@ -283,7 +283,7 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { bool subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlo* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); @@ -297,9 +297,9 @@ bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if((instance->generic.data_count_bit != - subghz_protocol_nice_flo_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (instance->generic.data_count_bit > 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; diff --git a/lib/subghz/protocols/nice_flo.h b/lib/subghz/protocols/nice_flo.h index dd374006b..e382e6146 100644 --- a/lib/subghz/protocols/nice_flo.h +++ b/lib/subghz/protocols/nice_flo.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNiceFlo. * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderNiceFlo. diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 766b5f401..bcc5c7466 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -92,7 +92,7 @@ void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment) instance->nice_flor_s_rainbow_table_file_name = subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment); if(instance->nice_flor_s_rainbow_table_file_name) { - FURI_LOG_I( + FURI_LOG_D( TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); } instance->encoder.repeat = 10; @@ -343,7 +343,7 @@ void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment) instance->nice_flor_s_rainbow_table_file_name = subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment); if(instance->nice_flor_s_rainbow_table_file_name) { - FURI_LOG_I( + FURI_LOG_D( TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); } return instance; @@ -493,7 +493,7 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { bool subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/nice_flor_s.h b/lib/subghz/protocols/nice_flor_s.h index e65ec90a0..bd93c985c 100644 --- a/lib/subghz/protocols/nice_flor_s.h +++ b/lib/subghz/protocols/nice_flor_s.h @@ -85,13 +85,13 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNiceFlorS. * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderNiceFlorS. diff --git a/lib/subghz/protocols/oregon2.c b/lib/subghz/protocols/oregon2.c deleted file mode 100644 index 561df819f..000000000 --- a/lib/subghz/protocols/oregon2.c +++ /dev/null @@ -1,323 +0,0 @@ -#include "oregon2.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" -#include -#include - -#define TAG "SubGhzProtocolOregon2" - -static const SubGhzBlockConst oregon2_const = { - .te_long = 1000, - .te_short = 500, - .te_delta = 200, - .min_count_bit_for_found = 32, -}; - -#define OREGON2_PREAMBLE_BITS 19 -#define OREGON2_PREAMBLE_MASK ((1 << (OREGON2_PREAMBLE_BITS + 1)) - 1) -#define OREGON2_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) -#define OREGON2_CHECKSUM_BITS 8 - -// 15 ones + 0101 (inverted A) -#define OREGON2_PREAMBLE 0b1111111111111110101 - -// bit indicating the low battery -#define OREGON2_FLAG_BAT_LOW 0x4 - -struct SubGhzProtocolDecoderOregon2 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - ManchesterState manchester_state; - bool prev_bit; - bool have_bit; - - uint8_t var_bits; - uint32_t var_data; -}; - -typedef struct SubGhzProtocolDecoderOregon2 SubGhzProtocolDecoderOregon2; - -typedef enum { - Oregon2DecoderStepReset = 0, - Oregon2DecoderStepFoundPreamble, - Oregon2DecoderStepVarData, -} Oregon2DecoderStep; - -void* subghz_protocol_decoder_oregon2_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderOregon2* instance = malloc(sizeof(SubGhzProtocolDecoderOregon2)); - instance->base.protocol = &subghz_protocol_oregon2; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_oregon2_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - free(instance); -} - -void subghz_protocol_decoder_oregon2_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - instance->decoder.parser_step = Oregon2DecoderStepReset; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); - instance->have_bit = false; - instance->var_data = 0; - instance->var_bits = 0; -} - -static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { - bool is_long = false; - - if(DURATION_DIFF(duration, oregon2_const.te_long) < oregon2_const.te_delta) { - is_long = true; - } else if(DURATION_DIFF(duration, oregon2_const.te_short) < oregon2_const.te_delta) { - is_long = false; - } else { - return ManchesterEventReset; - } - - if(level) - return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh; - else - return is_long ? ManchesterEventLongLow : ManchesterEventShortLow; -} - -// 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; - return 0; -} - -void subghz_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - // oregon v2.1 signal is inverted - ManchesterEvent event = level_and_duration_to_event(!level, duration); - bool data; - - // low-level bit sequence decoding - if(event == ManchesterEventReset) { - instance->decoder.parser_step = Oregon2DecoderStepReset; - instance->have_bit = false; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - } - if(manchester_advance(instance->manchester_state, event, &instance->manchester_state, &data)) { - if(instance->have_bit) { - if(!instance->prev_bit && data) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } else if(instance->prev_bit && !data) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } else { - subghz_protocol_decoder_oregon2_reset(context); - } - instance->have_bit = false; - } else { - instance->prev_bit = data; - instance->have_bit = true; - } - } - - switch(instance->decoder.parser_step) { - case Oregon2DecoderStepReset: - // waiting for fixed oregon2 preamble - if(instance->decoder.decode_count_bit >= OREGON2_PREAMBLE_BITS && - ((instance->decoder.decode_data & OREGON2_PREAMBLE_MASK) == OREGON2_PREAMBLE)) { - instance->decoder.parser_step = Oregon2DecoderStepFoundPreamble; - instance->decoder.decode_count_bit = 0; - instance->decoder.decode_data = 0UL; - } - break; - case Oregon2DecoderStepFoundPreamble: - // waiting for fixed oregon2 data - if(instance->decoder.decode_count_bit == 32) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - - // reverse nibbles in decoded data - instance->generic.data = (instance->generic.data & 0x55555555) << 1 | - (instance->generic.data & 0xAAAAAAAA) >> 1; - instance->generic.data = (instance->generic.data & 0x33333333) << 2 | - (instance->generic.data & 0xCCCCCCCC) >> 2; - - instance->var_bits = - oregon2_sensor_id_var_bits(OREGON2_SENSOR_ID(instance->generic.data)); - - if(!instance->var_bits) { - // sensor is not supported, stop decoding, but showing the decoded fixed part - instance->decoder.parser_step = Oregon2DecoderStepReset; - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } else { - instance->decoder.parser_step = Oregon2DecoderStepVarData; - } - } - break; - case Oregon2DecoderStepVarData: - // waiting for variable (sensor-specific data) - if(instance->decoder.decode_count_bit == instance->var_bits + OREGON2_CHECKSUM_BITS) { - instance->var_data = instance->decoder.decode_data & 0xFFFFFFFF; - - // reverse nibbles in var data - instance->var_data = (instance->var_data & 0x55555555) << 1 | - (instance->var_data & 0xAAAAAAAA) >> 1; - instance->var_data = (instance->var_data & 0x33333333) << 2 | - (instance->var_data & 0xCCCCCCCC) >> 2; - - instance->decoder.parser_step = Oregon2DecoderStepReset; - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - break; - } -} - -uint8_t subghz_protocol_decoder_oregon2_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_oregon2_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { - furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - if(!subghz_block_generic_serialize(&instance->generic, flipper_format, preset)) return false; - uint32_t temp = instance->var_bits; - if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { - FURI_LOG_E(TAG, "Error adding VarBits"); - return false; - } - if(!flipper_format_write_hex( - flipper_format, - "VarData", - (const uint8_t*)&instance->var_data, - sizeof(instance->var_data))) { - FURI_LOG_E(TAG, "Error adding VarData"); - return false; - } - return true; -} - -bool subghz_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - bool ret = false; - uint32_t temp_data; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { - FURI_LOG_E(TAG, "Missing VarLen"); - break; - } - instance->var_bits = (uint8_t)temp_data; - if(!flipper_format_read_hex( - flipper_format, - "VarData", - (uint8_t*)&instance->var_data, - sizeof(instance->var_data))) { - FURI_LOG_E(TAG, "Missing VarData"); - break; - } - if(instance->generic.data_count_bit != oregon2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); - break; - } - ret = true; - } while(false); - return ret; -} - -// append string of the variable data -static void - oregon2_var_data_append_string(uint16_t sensor_id, uint32_t var_data, FuriString* output) { - uint32_t val; - - if(sensor_id == 0xEC40) { - val = ((var_data >> 4) & 0xF) * 10 + ((var_data >> 8) & 0xF); - furi_string_cat_printf( - output, - "Temp: %s%ld.%ld C\r\n", - (var_data & 0xF) ? "-" : "+", - val, - (uint32_t)(var_data >> 12) & 0xF); - } -} - -static void oregon2_append_check_sum(uint32_t fix_data, uint32_t var_data, FuriString* output) { - uint8_t sum = fix_data & 0xF; - uint8_t ref_sum = var_data & 0xFF; - var_data >>= 8; - - for(uint8_t i = 1; i < 8; i++) { - fix_data >>= 4; - var_data >>= 4; - sum += (fix_data & 0xF) + (var_data & 0xF); - } - - // swap calculated sum nibbles - sum = (((sum >> 4) & 0xF) | (sum << 4)) & 0xFF; - if(sum == ref_sum) - furi_string_cat_printf(output, "Sum ok: 0x%hhX", ref_sum); - else - furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); -} - -void subghz_protocol_decoder_oregon2_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderOregon2* instance = context; - uint16_t sensor_id = OREGON2_SENSOR_ID(instance->generic.data); - furi_string_cat_printf( - output, - "%s\r\n" - "ID: 0x%04lX, ch: %ld%s, rc: 0x%02lX\r\n", - instance->generic.protocol_name, - (uint32_t)sensor_id, - (uint32_t)(instance->generic.data >> 12) & 0xF, - ((instance->generic.data & OREGON2_FLAG_BAT_LOW) ? ", low bat" : ""), - (uint32_t)(instance->generic.data >> 4) & 0xFF); - - if(instance->var_bits > 0) { - oregon2_var_data_append_string( - sensor_id, instance->var_data >> OREGON2_CHECKSUM_BITS, output); - oregon2_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); - } -} - -const SubGhzProtocolDecoder subghz_protocol_oregon2_decoder = { - .alloc = subghz_protocol_decoder_oregon2_alloc, - .free = subghz_protocol_decoder_oregon2_free, - - .feed = subghz_protocol_decoder_oregon2_feed, - .reset = subghz_protocol_decoder_oregon2_reset, - - .get_hash_data = subghz_protocol_decoder_oregon2_get_hash_data, - .serialize = subghz_protocol_decoder_oregon2_serialize, - .deserialize = subghz_protocol_decoder_oregon2_deserialize, - .get_string = subghz_protocol_decoder_oregon2_get_string, -}; - -const SubGhzProtocol subghz_protocol_oregon2 = { - .name = SUBGHZ_PROTOCOL_OREGON2_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save, - - .decoder = &subghz_protocol_oregon2_decoder, -}; diff --git a/lib/subghz/protocols/oregon2.h b/lib/subghz/protocols/oregon2.h deleted file mode 100644 index 981b25999..000000000 --- a/lib/subghz/protocols/oregon2.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "base.h" -#define SUBGHZ_PROTOCOL_OREGON2_NAME "Oregon2" -extern const SubGhzProtocol subghz_protocol_oregon2; diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index 019c3ec25..b3d6f1e98 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -296,7 +296,7 @@ uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { bool subghz_protocol_decoder_phoenix_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPhoenix_V2* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/phoenix_v2.h b/lib/subghz/protocols/phoenix_v2.h index b2f182796..48487535e 100644 --- a/lib/subghz/protocols/phoenix_v2.h +++ b/lib/subghz/protocols/phoenix_v2.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPhoenix_V2. * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_phoenix_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderPhoenix_V2. diff --git a/lib/subghz/protocols/power_smart.c b/lib/subghz/protocols/power_smart.c index 3ec35c74f..1e8d10e95 100644 --- a/lib/subghz/protocols/power_smart.c +++ b/lib/subghz/protocols/power_smart.c @@ -312,7 +312,6 @@ void subghz_protocol_decoder_power_smart_feed( if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) == POWER_SMART_PACKET_HEADER) { if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) { - instance->decoder.decode_data = instance->decoder.decode_data; instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = subghz_protocol_power_smart_const.min_count_bit_for_found; @@ -349,7 +348,7 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { bool subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPowerSmart* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/power_smart.h b/lib/subghz/protocols/power_smart.h index 21e5dcf4a..806729f8e 100644 --- a/lib/subghz/protocols/power_smart.h +++ b/lib/subghz/protocols/power_smart.h @@ -83,13 +83,13 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPowerSmart. * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderPowerSmart. diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index 370f33fb0..a4a0921ac 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -312,7 +312,7 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { bool subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/princeton.h b/lib/subghz/protocols/princeton.h index 4e482f747..a2a11292e 100644 --- a/lib/subghz/protocols/princeton.h +++ b/lib/subghz/protocols/princeton.h @@ -4,6 +4,10 @@ #define SUBGHZ_PROTOCOL_PRINCETON_NAME "Princeton" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzProtocolDecoderPrinceton SubGhzProtocolDecoderPrinceton; typedef struct SubGhzProtocolEncoderPrinceton SubGhzProtocolEncoderPrinceton; @@ -83,13 +87,13 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPrinceton. * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderPrinceton. @@ -105,3 +109,7 @@ bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* * @param output Resulting text */ void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c new file mode 100644 index 000000000..24aaae8df --- /dev/null +++ b/lib/subghz/protocols/protocol_items.c @@ -0,0 +1,20 @@ +#include "protocol_items.h" + +const SubGhzProtocol* subghz_protocol_registry_items[] = { + &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, + &subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh, + &subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo, + &subghz_protocol_nero_sketch, &subghz_protocol_ido, &subghz_protocol_kia, + &subghz_protocol_hormann, &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, + &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_princeton, + &subghz_protocol_raw, &subghz_protocol_linear, &subghz_protocol_secplus_v2, + &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, + &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, + &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, + &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa, &subghz_protocol_ansonic, +}; + +const SubGhzProtocolRegistry subghz_protocol_registry = { + .items = subghz_protocol_registry_items, + .size = COUNT_OF(subghz_protocol_registry_items)}; \ No newline at end of file diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h new file mode 100644 index 000000000..e732f2659 --- /dev/null +++ b/lib/subghz/protocols/protocol_items.h @@ -0,0 +1,48 @@ +#pragma once +#include "../registry.h" + +#include "princeton.h" +#include "keeloq.h" +#include "star_line.h" +#include "nice_flo.h" +#include "came.h" +#include "faac_slh.h" +#include "nice_flor_s.h" +#include "came_twee.h" +#include "came_atomo.h" +#include "nero_sketch.h" +#include "ido.h" +#include "kia.h" +#include "hormann.h" +#include "nero_radio.h" +#include "somfy_telis.h" +#include "somfy_keytis.h" +#include "scher_khan.h" +#include "gate_tx.h" +#include "raw.h" +#include "linear.h" +#include "secplus_v2.h" +#include "secplus_v1.h" +#include "megacode.h" +#include "holtek.h" +#include "chamberlain_code.h" +#include "power_smart.h" +#include "marantec.h" +#include "bett.h" +#include "doitrand.h" +#include "phoenix_v2.h" +#include "honeywell_wdb.h" +#include "magellan.h" +#include "intertechno_v3.h" +#include "clemsa.h" +#include "ansonic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzProtocolRegistry subghz_protocol_registry; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index fda466334..61a7442aa 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -17,7 +17,7 @@ #define SUBGHZ_DOWNLOAD_MAX_SIZE 512 #define SUBGHZ_AUTO_DETECT_DOWNLOAD_MAX_SIZE 2048 #define SUBGHZ_AUTO_DETECT_RAW_THRESHOLD -72.0f -#define SUBGHZ_AUTO_DETECT_RAW_POSTROLL_FRAMES 30 +#define SUBGHZ_AUTO_DETECT_RAW_POSTROLL_FRAMES 40 static const SubGhzBlockConst subghz_protocol_raw_const = { .te_short = 50, @@ -43,6 +43,7 @@ struct SubGhzProtocolDecoderRAW { bool has_rssi_above_threshold; int rssi_threshold; uint8_t postroll_frames; + bool pause; }; struct SubGhzProtocolEncoderRAW { @@ -95,7 +96,7 @@ const SubGhzProtocol subghz_protocol_raw = { bool subghz_protocol_raw_save_to_file_init( SubGhzProtocolDecoderRAW* instance, const char* dev_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(instance); instance->storage = furi_record_open(RECORD_STORAGE); @@ -169,6 +170,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); @@ -214,7 +216,7 @@ void subghz_protocol_decoder_raw_set_rssi_threshold(void* context, int rssi_thre furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - FURI_LOG_E(TAG, "RSSI set: (%d)", rssi_threshold); + FURI_LOG_D(TAG, "RSSI set: (%d)", rssi_threshold); instance->rssi_threshold = rssi_threshold; @@ -240,6 +242,14 @@ void subghz_protocol_decoder_raw_set_auto_mode(void* context, bool auto_mode) { subghz_protocol_decoder_raw_reset(context); } +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; } @@ -306,7 +316,8 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - if(instance->upload_raw != NULL && duration > subghz_protocol_raw_const.te_short) { + if(instance->upload_raw != NULL && !instance->pause && + duration > subghz_protocol_raw_const.te_short) { if(instance->auto_mode) { float rssi = furi_hal_subghz_get_rssi(); @@ -437,7 +448,7 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* bool subghz_protocol_decoder_raw_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; if(instance->auto_mode) { diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index fa025589e..08efff50e 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -23,13 +23,13 @@ extern const SubGhzProtocol subghz_protocol_raw; * Open file for writing * @param instance Pointer to a SubGhzProtocolDecoderRAW instance * @param dev_name File name - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_raw_save_to_file_init( SubGhzProtocolDecoderRAW* instance, const char* dev_name, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Set SubGhzProtocolDecoderRAW to auto mode, which allows subghz_scene_receiver to capture RAW. @@ -143,13 +143,13 @@ void subghz_protocol_encoder_raw_free(void* context); void subghz_protocol_encoder_raw_stop(void* context); /** - * Π‘allback on completion of file transfer. + * pause writing to flash. * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @param pause pause writing */ -// void subghz_protocol_raw_file_encoder_worker_callback_end(void* context); +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 * @param callback_end Callback, SubGhzProtocolEncoderRAWCallbackEnd @@ -178,7 +178,7 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* bool subghz_protocol_decoder_raw_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize and generating an upload to send. diff --git a/lib/subghz/protocols/registry.c b/lib/subghz/protocols/registry.c deleted file mode 100644 index 453fa747a..000000000 --- a/lib/subghz/protocols/registry.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "registry.h" - -const SubGhzProtocol* subghz_protocol_registry[] = { - &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, - &subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh, - &subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo, - &subghz_protocol_nero_sketch, &subghz_protocol_ido, &subghz_protocol_kia, - &subghz_protocol_hormann, &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, - &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_princeton, - &subghz_protocol_raw, &subghz_protocol_linear, &subghz_protocol_secplus_v2, - &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, - &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, - &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, - &subghz_protocol_honeywell_wdb, &subghz_protocol_magellen, &subghz_protocol_intertechno_v3, - &subghz_protocol_clemsa, &subghz_protocol_oregon2}; - -const SubGhzProtocol* subghz_protocol_registry_get_by_name(const char* name) { - for(size_t i = 0; i < subghz_protocol_registry_count(); i++) { - if(strcmp(name, subghz_protocol_registry[i]->name) == 0) { - return subghz_protocol_registry[i]; - } - } - return NULL; -} - -const SubGhzProtocol* subghz_protocol_registry_get_by_index(size_t index) { - if(index < subghz_protocol_registry_count()) { - return subghz_protocol_registry[index]; - } else { - return NULL; - } -} - -size_t subghz_protocol_registry_count() { - return COUNT_OF(subghz_protocol_registry); -} diff --git a/lib/subghz/protocols/registry.h b/lib/subghz/protocols/registry.h deleted file mode 100644 index 44f54b6ca..000000000 --- a/lib/subghz/protocols/registry.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "../types.h" - -#include "princeton.h" -#include "keeloq.h" -#include "star_line.h" -#include "nice_flo.h" -#include "came.h" -#include "faac_slh.h" -#include "nice_flor_s.h" -#include "came_twee.h" -#include "came_atomo.h" -#include "nero_sketch.h" -#include "ido.h" -#include "kia.h" -#include "hormann.h" -#include "nero_radio.h" -#include "somfy_telis.h" -#include "somfy_keytis.h" -#include "scher_khan.h" -#include "gate_tx.h" -#include "raw.h" -#include "linear.h" -#include "secplus_v2.h" -#include "secplus_v1.h" -#include "megacode.h" -#include "holtek.h" -#include "chamberlain_code.h" -#include "power_smart.h" -#include "marantec.h" -#include "bett.h" -#include "doitrand.h" -#include "phoenix_v2.h" -#include "honeywell_wdb.h" -#include "magellen.h" -#include "intertechno_v3.h" -#include "clemsa.h" -#include "oregon2.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Registration by name SubGhzProtocol. - * @param name Protocol name - * @return SubGhzProtocol* pointer to a SubGhzProtocol instance - */ -const SubGhzProtocol* subghz_protocol_registry_get_by_name(const char* name); - -/** - * Registration protocol by index in array SubGhzProtocol. - * @param index Protocol by index in array - * @return SubGhzProtocol* pointer to a SubGhzProtocol instance - */ -const SubGhzProtocol* subghz_protocol_registry_get_by_index(size_t index); - -/** - * Getting the number of registered protocols. - * @return Number of protocols - */ -size_t subghz_protocol_registry_count(); - -#ifdef __cplusplus -} -#endif diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index 0e1b7de45..c3eaf6509 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -252,7 +252,7 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { bool subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderScherKhan* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/scher_khan.h b/lib/subghz/protocols/scher_khan.h index f0fe595b4..b7e84ea1f 100644 --- a/lib/subghz/protocols/scher_khan.h +++ b/lib/subghz/protocols/scher_khan.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderScherKhan. * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderScherKhan. diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index a0d167dab..68f1443f4 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -519,7 +519,7 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { bool subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/secplus_v1.h b/lib/subghz/protocols/secplus_v1.h index ba1762f55..99480b62b 100644 --- a/lib/subghz/protocols/secplus_v1.h +++ b/lib/subghz/protocols/secplus_v1.h @@ -82,13 +82,13 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSecPlus_v1. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSecPlus_v1. diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 0a1c5cf6e..a844e20cb 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -592,7 +592,7 @@ bool subghz_protocol_secplus_v2_create_data( uint32_t serial, uint8_t btn, uint32_t cnt, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolEncoderSecPlus_v2* instance = context; instance->generic.serial = serial; @@ -759,7 +759,7 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { bool subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v2* instance = context; bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/secplus_v2.h b/lib/subghz/protocols/secplus_v2.h index 007609fc1..bea8cdb5d 100644 --- a/lib/subghz/protocols/secplus_v2.h +++ b/lib/subghz/protocols/secplus_v2.h @@ -52,7 +52,7 @@ LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context); * @param btn Button number, 8 bit * @param cnt Container value, 28 bit * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition + * @param preset Modulation, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_secplus_v2_create_data( @@ -61,7 +61,7 @@ bool subghz_protocol_secplus_v2_create_data( uint32_t serial, uint8_t btn, uint32_t cnt, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Allocate SubGhzProtocolDecoderSecPlus_v2. @@ -101,13 +101,13 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSecPlus_v2. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSecPlus_v2. diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index b78ac2f13..63a3e26d2 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -382,7 +382,7 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { bool subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/somfy_keytis.h b/lib/subghz/protocols/somfy_keytis.h index af1df91d5..3b5950611 100644 --- a/lib/subghz/protocols/somfy_keytis.h +++ b/lib/subghz/protocols/somfy_keytis.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSomfyKeytis. * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSomfyKeytis. diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index b02f8e72e..945a88405 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -339,7 +339,7 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { bool subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/somfy_telis.h b/lib/subghz/protocols/somfy_telis.h index b6e58e34c..a6a9fa5b2 100644 --- a/lib/subghz/protocols/somfy_telis.h +++ b/lib/subghz/protocols/somfy_telis.h @@ -49,13 +49,13 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSomfyTelis. * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSomfyTelis. diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index f91e9ad18..3066c6e2b 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -77,7 +77,7 @@ const SubGhzProtocol subghz_protocol_star_line = { .name = SUBGHZ_PROTOCOL_STAR_LINE_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_star_line_decoder, .encoder = &subghz_protocol_star_line_encoder, @@ -138,7 +138,11 @@ void subghz_protocol_encoder_star_line_free(void* context) { */ static bool subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) { - instance->generic.cnt++; + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } uint32_t fix = btn << 24 | instance->generic.serial; uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; uint32_t hop = 0; @@ -204,7 +208,7 @@ bool subghz_protocol_star_line_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolEncoderStarLine* instance = context; instance->generic.serial = serial; @@ -236,7 +240,7 @@ static bool subghz_protocol_encoder_star_line_get_upload( } size_t index = 0; - size_t size_upload = 6 * 2 + (instance->generic.data_count_bit * 2) + 4; + size_t size_upload = 6 * 2 + (instance->generic.data_count_bit * 2); if(size_upload > instance->encoder.size_upload) { FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); return false; @@ -697,7 +701,7 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { bool subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderStarLine* instance = context; subghz_protocol_star_line_check_remote_controller( @@ -763,8 +767,7 @@ void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* out "Key:%08lX%08lX\r\n" "Fix:0x%08lX Cnt:%04lX\r\n" "Hop:0x%08lX Btn:%02X\r\n" - "MF:%s\r\n" - "Sn:0x%07lX \r\n", + "MF:%s\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, code_found_hi, @@ -773,6 +776,5 @@ void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* out instance->generic.cnt, code_found_reverse_lo, instance->generic.btn, - instance->manufacture_name, - instance->generic.serial); + instance->manufacture_name); } diff --git a/lib/subghz/protocols/star_line.h b/lib/subghz/protocols/star_line.h index 396d1228f..e8873d41a 100644 --- a/lib/subghz/protocols/star_line.h +++ b/lib/subghz/protocols/star_line.h @@ -36,7 +36,7 @@ void subghz_protocol_encoder_star_line_free(void* context); * @param btn Button number, 8 bit * @param cnt Counter value, 16 bit * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition + * @param preset Modulation, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_star_line_create_data( @@ -46,7 +46,7 @@ bool subghz_protocol_star_line_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize and generating an upload to send. @@ -107,13 +107,13 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderStarLine. * @param context Pointer to a SubGhzProtocolDecoderStarLine instance * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderStarLine. diff --git a/lib/subghz/receiver.c b/lib/subghz/receiver.c index 129e0091f..d7b422170 100644 --- a/lib/subghz/receiver.c +++ b/lib/subghz/receiver.c @@ -1,6 +1,7 @@ #include "receiver.h" -#include "protocols/registry.h" +#include "registry.h" +#include "protocols/protocol_items.h" #include @@ -22,9 +23,12 @@ struct SubGhzReceiver { SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { SubGhzReceiver* instance = malloc(sizeof(SubGhzReceiver)); SubGhzReceiverSlotArray_init(instance->slots); + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); - for(size_t i = 0; i < subghz_protocol_registry_count(); ++i) { - const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_index(i); + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry_items); ++i) { + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(protocol_registry_items, i); if(protocol->decoder && protocol->decoder->alloc) { SubGhzReceiverSlot* slot = SubGhzReceiverSlotArray_push_new(instance->slots); @@ -34,7 +38,6 @@ SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { instance->callback = NULL; instance->context = NULL; - return instance; } diff --git a/lib/subghz/registry.c b/lib/subghz/registry.c new file mode 100644 index 000000000..d0c22ea8c --- /dev/null +++ b/lib/subghz/registry.c @@ -0,0 +1,30 @@ +#include "registry.h" + +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name) { + furi_assert(protocol_registry); + + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry); i++) { + if(strcmp(name, protocol_registry->items[i]->name) == 0) { + return protocol_registry->items[i]; + } + } + return NULL; +} + +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index) { + furi_assert(protocol_registry); + if(index < subghz_protocol_registry_count(protocol_registry)) { + return protocol_registry->items[index]; + } else { + return NULL; + } +} + +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry) { + furi_assert(protocol_registry); + return protocol_registry->size; +} diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h new file mode 100644 index 000000000..91027807e --- /dev/null +++ b/lib/subghz/registry.h @@ -0,0 +1,47 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzEnvironment SubGhzEnvironment; + +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; + +struct SubGhzProtocolRegistry { + const SubGhzProtocol** items; + const size_t size; +}; + +/** + * Registration by name SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param name Protocol name + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name); + +/** + * Registration protocol by index in array SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param index Protocol by index in array + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index); + +/** + * Getting the number of registered protocols. + * @param protocol_registry SubGhzProtocolRegistry + * @return Number of protocols + */ +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index c37b17f91..e25eaf6c1 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -186,11 +186,8 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { SubGhzFileEncoderWorker* instance = malloc(sizeof(SubGhzFileEncoderWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzFEWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_file_encoder_worker_thread); + instance->thread = + furi_thread_alloc_ex("SubGhzFEWorker", 2048, subghz_file_encoder_worker_thread, instance); instance->stream = furi_stream_buffer_alloc(sizeof(int32_t) * 2048, sizeof(int32_t)); instance->storage = furi_record_open(RECORD_STORAGE); diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index 1b4b3b716..e06bd9796 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -464,7 +464,7 @@ bool subghz_keystore_raw_encrypted_save( } stream_write_cstring(output_stream, encrypted_line); - } while(ret > 0 && result); + } while(result); flipper_format_free(output_flipper_format); diff --git a/applications/main/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c similarity index 95% rename from applications/main/subghz/subghz_setting.c rename to lib/subghz/subghz_setting.c index 322e80e7d..574365efa 100644 --- a/applications/main/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -1,5 +1,6 @@ #include "subghz_setting.h" -#include "subghz_i.h" +#include "types.h" +//#include "subghz_i.h" #include #include @@ -197,7 +198,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path, bool no if(file_path) { do { if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); + FURI_LOG_I(TAG, "File is not used %s", file_path); break; } @@ -264,17 +265,17 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path, bool no fff_data_file, "Default_frequency", &temp_data32, 1)) { subghz_setting_set_default_frequency(instance, temp_data32); } - } - // custom preset (optional) - if(!flipper_format_rewind(fff_data_file)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) { - FURI_LOG_I(TAG, "Custom preset loaded %s", furi_string_get_cstr(temp_str)); - subghz_setting_load_custom_preset( - instance, furi_string_get_cstr(temp_str), fff_data_file); + // custom preset (optional) + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) { + FURI_LOG_I(TAG, "Custom preset loaded %s", furi_string_get_cstr(temp_str)); + subghz_setting_load_custom_preset( + instance, furi_string_get_cstr(temp_str), fff_data_file); + } } } while(false); diff --git a/applications/main/subghz/subghz_setting.h b/lib/subghz/subghz_setting.h similarity index 96% rename from applications/main/subghz/subghz_setting.h rename to lib/subghz/subghz_setting.h index 3b3b1131a..c72c6f784 100644 --- a/applications/main/subghz/subghz_setting.h +++ b/lib/subghz/subghz_setting.h @@ -6,6 +6,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 typedef struct SubGhzSetting SubGhzSetting; @@ -48,3 +52,7 @@ uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance); uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 37c0bfc5e..42124bebc 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -201,11 +201,8 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { SubGhzTxRxWorker* instance = malloc(sizeof(SubGhzTxRxWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzTxRxWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_tx_rx_worker_thread); + instance->thread = + furi_thread_alloc_ex("SubGhzTxRxWorker", 2048, subghz_tx_rx_worker_thread, instance); instance->stream_tx = furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); instance->stream_rx = diff --git a/lib/subghz/subghz_worker.c b/lib/subghz/subghz_worker.c index 61146c168..35e399885 100644 --- a/lib/subghz/subghz_worker.c +++ b/lib/subghz/subghz_worker.c @@ -88,11 +88,8 @@ static int32_t subghz_worker_thread_callback(void* context) { SubGhzWorker* subghz_worker_alloc() { SubGhzWorker* instance = malloc(sizeof(SubGhzWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_worker_thread_callback); + instance->thread = + furi_thread_alloc_ex("SubGhzWorker", 2048, subghz_worker_thread_callback, instance); instance->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); diff --git a/lib/subghz/transmitter.c b/lib/subghz/transmitter.c index 0dc2bacde..8507ee054 100644 --- a/lib/subghz/transmitter.c +++ b/lib/subghz/transmitter.c @@ -1,7 +1,8 @@ #include "transmitter.h" #include "protocols/base.h" -#include "protocols/registry.h" +#include "registry.h" +#include "protocols/protocol_items.h" struct SubGhzTransmitter { const SubGhzProtocol* protocol; @@ -11,14 +12,17 @@ struct SubGhzTransmitter { SubGhzTransmitter* subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { SubGhzTransmitter* instance = NULL; - const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_name(protocol_name); + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); + + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_name(protocol_registry_items, protocol_name); if(protocol && protocol->encoder && protocol->encoder->alloc) { instance = malloc(sizeof(SubGhzTransmitter)); instance->protocol = protocol; instance->protocol_instance = instance->protocol->encoder->alloc(environment); } - return instance; } diff --git a/lib/subghz/types.h b/lib/subghz/types.h index dd9cefb8b..6c34dc728 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -10,7 +10,6 @@ #include "environment.h" #include #include -#include #define SUBGHZ_APP_FOLDER ANY_PATH("subghz") #define SUBGHZ_RAW_FOLDER EXT_PATH("subghz") @@ -22,19 +21,21 @@ #define SUBGHZ_RAW_FILE_VERSION 1 #define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" -// -// Abstract method types -// +// Radio Preset +typedef struct { + FuriString* name; + uint32_t frequency; + uint8_t* data; + size_t data_size; +} SubGhzRadioPreset; // Allocator and Deallocator typedef void* (*SubGhzAlloc)(SubGhzEnvironment* environment); typedef void (*SubGhzFree)(void* context); // Serialize and Deserialize -typedef bool (*SubGhzSerialize)( - void* context, - FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); +typedef bool ( + *SubGhzSerialize)(void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); typedef bool (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); // Decoder specific @@ -74,6 +75,8 @@ typedef enum { SubGhzProtocolTypeStatic, SubGhzProtocolTypeDynamic, SubGhzProtocolTypeRAW, + SubGhzProtocolWeatherStation, + SubGhzProtocolCustom, } SubGhzProtocolType; typedef enum { diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index d631431ee..d51e31826 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -8,23 +8,24 @@ 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("value_index.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/lib/toolbox/path.c b/lib/toolbox/path.c index 53e9fc092..ce65aca4f 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -38,7 +38,7 @@ void path_extract_extension(FuriString* path, char* ext, size_t ext_len_max) { size_t dot = furi_string_search_rchar(path, '.'); size_t filename_start = furi_string_search_rchar(path, '/'); - if((dot > 0) && (filename_start < dot)) { + if((dot != FURI_STRING_FAILURE) && (filename_start < dot)) { strlcpy(ext, &(furi_string_get_cstr(path))[dot], ext_len_max); } } diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index d19585cb8..d60e8d8c4 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -53,6 +53,8 @@ class FlipperApplication: order: int = 0 link: Optional[str] = "" sdk_headers: List[str] = field(default_factory=list) + targets: List[str] = field(default_factory=lambda: ["all"]) + # .fap-specific sources: List[str] = field(default_factory=lambda: ["*.c*"]) fap_version: Tuple[int] = field(default_factory=lambda: (0, 1)) @@ -136,8 +138,8 @@ class AppManager: raise FlipperManifestException(f"Duplicate app declaration: {app.appid}") self.known_apps[app.appid] = app - def filter_apps(self, applist: List[str]): - return AppBuildset(self, applist) + def filter_apps(self, applist: List[str], hw_target: str): + return AppBuildset(self, applist, hw_target) class AppBuilderException(Exception): @@ -156,11 +158,13 @@ class AppBuildset: FlipperAppType.STARTUP, ) - def __init__(self, appmgr: AppManager, appnames: List[str]): + def __init__(self, appmgr: AppManager, appnames: List[str], hw_target: str): self.appmgr = appmgr self.appnames = set(appnames) + self.hw_target = hw_target self._orig_appnames = appnames self._process_deps() + self._filter_by_target() self._check_conflicts() self._check_unsatisfied() # unneeded? self.apps = sorted( @@ -171,6 +175,16 @@ class AppBuildset: def _is_missing_dep(self, dep_name: str): return dep_name not in self.appnames + def _filter_by_target(self): + for appname in self.appnames.copy(): + app = self.appmgr.get(appname) + # if app.apptype not in self.BUILTIN_APP_TYPES: + if not any(map(lambda t: t in app.targets, ["all", self.hw_target])): + print( + f"Removing {appname} due to target mismatch (building for {self.hw_target}, app supports {app.targets}" + ) + self.appnames.remove(appname) + def _process_deps(self): while True: provided = [] diff --git a/scripts/fbt/elfmanifest.py b/scripts/fbt/elfmanifest.py index 313a64c09..17bceddf4 100644 --- a/scripts/fbt/elfmanifest.py +++ b/scripts/fbt/elfmanifest.py @@ -5,6 +5,7 @@ import struct from dataclasses import dataclass, field from .appmanifest import FlipperApplication +from flipper.assets.icon import file2image _MANIFEST_MAGIC = 0x52474448 @@ -53,8 +54,6 @@ def assemble_manifest_data( ): image_data = b"" if app_manifest.fap_icon: - from flipper.assets.icon import file2image - image = file2image(os.path.join(app_manifest._apppath, app_manifest.fap_icon)) if (image.width, image.height) != (10, 10): raise ValueError( diff --git a/scripts/fbt/sdk.py b/scripts/fbt/sdk.py deleted file mode 100644 index 3d4a17b2f..000000000 --- a/scripts/fbt/sdk.py +++ /dev/null @@ -1,519 +0,0 @@ -import operator -import os -import csv -import operator - -from enum import Enum, auto -from typing import List, Set, ClassVar, Any -from dataclasses import dataclass, field - -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 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: - major: int = 0 - minor: int = 0 - - csv_type: ClassVar[str] = "Version" - - def __str__(self) -> str: - return f"{self.major}.{self.minor}" - - def as_int(self) -> int: - return ((self.major & 0xFFFF) << 16) | (self.minor & 0xFFFF) - - @staticmethod - def from_str(s: str) -> "SdkVersion": - major, minor = s.split(".") - return SdkVersion(int(major), int(minor)) - - def dictify(self) -> dict: - return dict(name=str(self), type=None, params=None) - - -class VersionBump(Enum): - NONE = auto() - MAJOR = auto() - MINOR = auto() - - -class ApiEntryState(Enum): - PENDING = "?" - APPROVED = "+" - DISABLED = "-" - # Special value for API version entry so users have less incentive to edit it - VERSION_PENDING = "v" - - -# Class that stores all known API entries, both enabled and disabled. -# Also keeps track of API versioning -# Allows comparison and update from newly-generated API -class SdkCache: - CSV_FIELD_NAMES = ("entry", "status", "name", "type", "params") - - def __init__(self, cache_file: str, load_version_only=False): - self.cache_file_name = cache_file - self.version = SdkVersion(0, 0) - self.sdk = ApiEntries() - self.disabled_entries = set() - self.new_entries = set() - self.loaded_dirty_version = False - - self.version_action = VersionBump.NONE - self._load_version_only = load_version_only - self.load_cache() - - def is_buildable(self) -> bool: - return ( - self.version != SdkVersion(0, 0) - and self.version_action == VersionBump.NONE - and not self._have_pending_entries() - ) - - def _filter_enabled(self, sdk_entries): - return sorted( - filter(lambda e: e not in self.disabled_entries, sdk_entries), - key=operator.attrgetter("name"), - ) - - def get_valid_names(self): - syms = set(map(lambda e: e.name, self.get_functions())) - syms.update(map(lambda e: e.name, self.get_variables())) - return syms - - def get_functions(self): - return self._filter_enabled(self.sdk.functions) - - def get_variables(self): - return self._filter_enabled(self.sdk.variables) - - def get_headers(self): - return self._filter_enabled(self.sdk.headers) - - def _get_entry_status(self, entry) -> str: - if entry in self.disabled_entries: - return ApiEntryState.DISABLED - elif entry in self.new_entries: - if isinstance(entry, SdkVersion): - return ApiEntryState.VERSION_PENDING - return ApiEntryState.PENDING - else: - return ApiEntryState.APPROVED - - def _format_entry(self, obj): - obj_dict = obj.dictify() - obj_dict.update( - dict( - entry=obj.csv_type, - status=self._get_entry_status(obj).value, - ) - ) - return obj_dict - - def save(self) -> None: - if self._load_version_only: - raise Exception("Only SDK version was loaded, cannot save") - - if self.version_action == VersionBump.MINOR: - self.version = SdkVersion(self.version.major, self.version.minor + 1) - elif self.version_action == VersionBump.MAJOR: - self.version = SdkVersion(self.version.major + 1, 0) - - if self._have_pending_entries(): - self.new_entries.add(self.version) - print( - f"API version is still WIP: {self.version}. Review the changes and re-run command." - ) - print(f"Entries to review:") - print( - "\n".join( - map( - str, - filter( - lambda e: not isinstance(e, SdkVersion), self.new_entries - ), - ) - ) - ) - else: - print(f"API version {self.version} is up to date") - - regenerate_csv = ( - self.loaded_dirty_version - or self._have_pending_entries() - or self.version_action != VersionBump.NONE - ) - - if regenerate_csv: - str_cache_entries = [self.version] - name_getter = operator.attrgetter("name") - str_cache_entries.extend(sorted(self.sdk.headers, key=name_getter)) - str_cache_entries.extend(sorted(self.sdk.functions, key=name_getter)) - str_cache_entries.extend(sorted(self.sdk.variables, key=name_getter)) - - with open(self.cache_file_name, "wt", newline="") as f: - writer = csv.DictWriter(f, fieldnames=SdkCache.CSV_FIELD_NAMES) - writer.writeheader() - - for entry in str_cache_entries: - writer.writerow(self._format_entry(entry)) - - def _process_entry(self, entry_dict: dict) -> None: - entry_class = entry_dict["entry"] - entry_status = entry_dict["status"] - entry_name = entry_dict["name"] - - entry = None - if entry_class == SdkVersion.csv_type: - self.version = SdkVersion.from_str(entry_name) - if entry_status == ApiEntryState.VERSION_PENDING.value: - self.loaded_dirty_version = True - elif entry_class == ApiHeader.csv_type: - self.sdk.headers.add(entry := ApiHeader(entry_name)) - elif entry_class == ApiEntryFunction.csv_type: - self.sdk.functions.add( - entry := ApiEntryFunction( - entry_name, - entry_dict["type"], - entry_dict["params"], - ) - ) - elif entry_class == ApiEntryVariable.csv_type: - self.sdk.variables.add( - entry := ApiEntryVariable(entry_name, entry_dict["type"]) - ) - else: - print(entry_dict) - raise Exception("Unknown entry type: %s" % entry_class) - - if entry is None: - return - - if entry_status == ApiEntryState.DISABLED.value: - self.disabled_entries.add(entry) - elif entry_status == ApiEntryState.PENDING.value: - self.new_entries.add(entry) - - def load_cache(self) -> None: - if not os.path.exists(self.cache_file_name): - raise Exception( - f"Cannot load symbol cache '{self.cache_file_name}'! File does not exist" - ) - - with open(self.cache_file_name, "rt") as f: - reader = csv.DictReader(f) - for row in reader: - self._process_entry(row) - if self._load_version_only and row.get("entry") == SdkVersion.csv_type: - break - - def _have_pending_entries(self) -> bool: - return any( - filter( - lambda e: not isinstance(e, SdkVersion), - self.new_entries, - ) - ) - - def sync_sets( - self, known_set: Set[Any], new_set: Set[Any], update_version: bool = True - ): - new_entries = new_set - known_set - if new_entries: - print(f"New: {new_entries}") - known_set |= new_entries - self.new_entries |= new_entries - if update_version and self.version_action == VersionBump.NONE: - self.version_action = VersionBump.MINOR - removed_entries = known_set - new_set - if removed_entries: - print(f"Removed: {removed_entries}") - known_set -= removed_entries - # If any of removed entries was a part of active API, that's a major bump - if update_version and any( - filter( - lambda e: e not in self.disabled_entries - and e not in self.new_entries, - removed_entries, - ) - ): - self.version_action = VersionBump.MAJOR - self.disabled_entries -= removed_entries - self.new_entries -= removed_entries - - def validate_api(self, api: ApiEntries) -> None: - self.sync_sets(self.sdk.headers, api.headers, False) - self.sync_sets(self.sdk.functions, api.functions) - self.sync_sets(self.sdk.variables, api.variables) 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/cache.py b/scripts/fbt/sdk/cache.py new file mode 100644 index 000000000..756c37827 --- /dev/null +++ b/scripts/fbt/sdk/cache.py @@ -0,0 +1,263 @@ +import operator +import os +import csv +import operator + +from enum import Enum, auto +from typing import Set, ClassVar, Any +from dataclasses import dataclass + +from ansi.color import fg + +from . import ( + ApiEntries, + ApiEntryFunction, + ApiEntryVariable, + ApiHeader, +) + + +@dataclass(frozen=True) +class SdkVersion: + major: int = 0 + minor: int = 0 + + csv_type: ClassVar[str] = "Version" + + def __str__(self) -> str: + return f"{self.major}.{self.minor}" + + def as_int(self) -> int: + return ((self.major & 0xFFFF) << 16) | (self.minor & 0xFFFF) + + @staticmethod + def from_str(s: str) -> "SdkVersion": + major, minor = s.split(".") + return SdkVersion(int(major), int(minor)) + + def dictify(self) -> dict: + return dict(name=str(self), type=None, params=None) + + +class VersionBump(Enum): + NONE = auto() + MAJOR = auto() + MINOR = auto() + + +class ApiEntryState(Enum): + PENDING = "?" + APPROVED = "+" + DISABLED = "-" + # Special value for API version entry so users have less incentive to edit it + VERSION_PENDING = "v" + + +# Class that stores all known API entries, both enabled and disabled. +# Also keeps track of API versioning +# Allows comparison and update from newly-generated API +class SdkCache: + CSV_FIELD_NAMES = ("entry", "status", "name", "type", "params") + + def __init__(self, cache_file: str, load_version_only=False): + self.cache_file_name = cache_file + self.version = SdkVersion(0, 0) + self.sdk = ApiEntries() + self.disabled_entries = set() + self.new_entries = set() + self.loaded_dirty_version = False + + self.version_action = VersionBump.NONE + self._load_version_only = load_version_only + self.load_cache() + + def is_buildable(self) -> bool: + return ( + self.version != SdkVersion(0, 0) + and self.version_action == VersionBump.NONE + and not self._have_pending_entries() + ) + + def _filter_enabled(self, sdk_entries): + return sorted( + filter(lambda e: e not in self.disabled_entries, sdk_entries), + key=operator.attrgetter("name"), + ) + + def get_valid_names(self): + syms = set(map(lambda e: e.name, self.get_functions())) + syms.update(map(lambda e: e.name, self.get_variables())) + return syms + + def get_disabled_names(self): + return set(map(lambda e: e.name, self.disabled_entries)) + + def get_functions(self): + return self._filter_enabled(self.sdk.functions) + + def get_variables(self): + return self._filter_enabled(self.sdk.variables) + + def get_headers(self): + return self._filter_enabled(self.sdk.headers) + + def _get_entry_status(self, entry) -> str: + if entry in self.disabled_entries: + return ApiEntryState.DISABLED + elif entry in self.new_entries: + if isinstance(entry, SdkVersion): + return ApiEntryState.VERSION_PENDING + return ApiEntryState.PENDING + else: + return ApiEntryState.APPROVED + + def _format_entry(self, obj): + obj_dict = obj.dictify() + obj_dict.update( + dict( + entry=obj.csv_type, + status=self._get_entry_status(obj).value, + ) + ) + return obj_dict + + def save(self) -> None: + if self._load_version_only: + raise Exception("Only SDK version was loaded, cannot save") + + if self.version_action == VersionBump.MINOR: + self.version = SdkVersion(self.version.major, self.version.minor + 1) + elif self.version_action == VersionBump.MAJOR: + self.version = SdkVersion(self.version.major + 1, 0) + + if self._have_pending_entries(): + self.new_entries.add(self.version) + print( + fg.red( + f"API version is still WIP: {self.version}. Review the changes and re-run command." + ) + ) + print(f"CSV file entries to mark up:") + print( + fg.yellow( + "\n".join( + map( + str, + filter( + lambda e: not isinstance(e, SdkVersion), + self.new_entries, + ), + ) + ) + ) + ) + else: + print(fg.green(f"API version {self.version} is up to date")) + + regenerate_csv = ( + self.loaded_dirty_version + or self._have_pending_entries() + or self.version_action != VersionBump.NONE + ) + + if regenerate_csv: + str_cache_entries = [self.version] + name_getter = operator.attrgetter("name") + str_cache_entries.extend(sorted(self.sdk.headers, key=name_getter)) + str_cache_entries.extend(sorted(self.sdk.functions, key=name_getter)) + str_cache_entries.extend(sorted(self.sdk.variables, key=name_getter)) + + with open(self.cache_file_name, "wt", newline="") as f: + writer = csv.DictWriter(f, fieldnames=SdkCache.CSV_FIELD_NAMES) + writer.writeheader() + + for entry in str_cache_entries: + writer.writerow(self._format_entry(entry)) + + def _process_entry(self, entry_dict: dict) -> None: + entry_class = entry_dict["entry"] + entry_status = entry_dict["status"] + entry_name = entry_dict["name"] + + entry = None + if entry_class == SdkVersion.csv_type: + self.version = SdkVersion.from_str(entry_name) + if entry_status == ApiEntryState.VERSION_PENDING.value: + self.loaded_dirty_version = True + elif entry_class == ApiHeader.csv_type: + self.sdk.headers.add(entry := ApiHeader(entry_name)) + elif entry_class == ApiEntryFunction.csv_type: + self.sdk.functions.add( + entry := ApiEntryFunction( + entry_name, + entry_dict["type"], + entry_dict["params"], + ) + ) + elif entry_class == ApiEntryVariable.csv_type: + self.sdk.variables.add( + entry := ApiEntryVariable(entry_name, entry_dict["type"]) + ) + else: + print(entry_dict) + raise Exception("Unknown entry type: %s" % entry_class) + + if entry is None: + return + + if entry_status == ApiEntryState.DISABLED.value: + self.disabled_entries.add(entry) + elif entry_status == ApiEntryState.PENDING.value: + self.new_entries.add(entry) + + def load_cache(self) -> None: + if not os.path.exists(self.cache_file_name): + raise Exception( + f"Cannot load symbol cache '{self.cache_file_name}'! File does not exist" + ) + + with open(self.cache_file_name, "rt") as f: + reader = csv.DictReader(f) + for row in reader: + self._process_entry(row) + if self._load_version_only and row.get("entry") == SdkVersion.csv_type: + break + + def _have_pending_entries(self) -> bool: + return any( + filter( + lambda e: not isinstance(e, SdkVersion), + self.new_entries, + ) + ) + + def sync_sets( + self, known_set: Set[Any], new_set: Set[Any], update_version: bool = True + ): + new_entries = new_set - known_set + if new_entries: + print(f"New: {new_entries}") + known_set |= new_entries + self.new_entries |= new_entries + if update_version and self.version_action == VersionBump.NONE: + self.version_action = VersionBump.MINOR + removed_entries = known_set - new_set + if removed_entries: + print(f"Removed: {removed_entries}") + known_set -= removed_entries + # If any of removed entries was a part of active API, that's a major bump + if update_version and any( + filter( + lambda e: e not in self.disabled_entries + and e not in self.new_entries, + removed_entries, + ) + ): + self.version_action = VersionBump.MAJOR + self.disabled_entries -= removed_entries + self.new_entries -= removed_entries + + def validate_api(self, api: ApiEntries) -> None: + self.sync_sets(self.sdk.headers, api.headers, False) + self.sync_sets(self.sdk.functions, api.functions) + self.sync_sets(self.sdk.variables, api.variables) 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/util.py b/scripts/fbt/util.py index baa4ddfee..ee7562058 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -4,8 +4,7 @@ from SCons.Errors import StopError import re import os -import random -import string + WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)") @@ -41,3 +40,26 @@ 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(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 + + +def extract_abs_dir_path(node): + abs_dir_node = extract_abs_dir(node) + if abs_dir_node is None: + raise StopError(f"Can't find absolute path for {node.name}") + + return abs_dir_node.abspath + + +def path_as_posix(path): + if SCons.Platform.platform_default() == "win32": + return path.replace(os.path.sep, os.path.altsep) + return path diff --git a/scripts/fbt_tools/compilation_db.py b/scripts/fbt_tools/compilation_db.py new file mode 100644 index 000000000..17ff6aaa3 --- /dev/null +++ b/scripts/fbt_tools/compilation_db.py @@ -0,0 +1,278 @@ +""" +Implements the ability for SCons to emit a compilation database for the MongoDB project. See +http://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation +database is, and why you might want one. The only user visible entry point here is +'env.CompilationDatabase'. This method takes an optional 'target' to name the file that +should hold the compilation database, otherwise, the file defaults to compile_commands.json, +which is the name that most clang tools search for by default. +""" + +# Copyright 2020 MongoDB Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import json +import itertools +import fnmatch +import SCons + +from SCons.Tool.cxx import CXXSuffixes +from SCons.Tool.cc import CSuffixes +from SCons.Tool.asm import ASSuffixes, ASPPSuffixes + +# TODO: Is there a better way to do this than this global? Right now this exists so that the +# emitter we add can record all of the things it emits, so that the scanner for the top level +# compilation database can access the complete list, and also so that the writer has easy +# access to write all of the files. But it seems clunky. How can the emitter and the scanner +# communicate more gracefully? +__COMPILATION_DB_ENTRIES = [] + + +# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even +# integrate with the cache, but there doesn't seem to be much call for it. +class __CompilationDbNode(SCons.Node.Python.Value): + def __init__(self, value): + SCons.Node.Python.Value.__init__(self, value) + self.Decider(changed_since_last_build_node) + + +def changed_since_last_build_node(child, target, prev_ni, node): + """Dummy decider to force always building""" + return True + + +def make_emit_compilation_DB_entry(comstr): + """ + Effectively this creates a lambda function to capture: + * command line + * source + * target + :param comstr: unevaluated command line + :return: an emitter which has captured the above + """ + user_action = SCons.Action.Action(comstr) + + def emit_compilation_db_entry(target, source, env): + """ + This emitter will be added to each c/c++ object build to capture the info needed + for clang tools + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :return: target(s), source(s) + """ + + dbtarget = __CompilationDbNode(source) + + entry = env.__COMPILATIONDB_Entry( + target=dbtarget, + source=[], + __COMPILATIONDB_UOUTPUT=target, + __COMPILATIONDB_USOURCE=source, + __COMPILATIONDB_UACTION=user_action, + __COMPILATIONDB_ENV=env, + ) + + # TODO: Technically, these next two lines should not be required: it should be fine to + # cache the entries. However, they don't seem to update properly. Since they are quick + # to re-generate disable caching and sidestep this problem. + env.AlwaysBuild(entry) + env.NoCache(entry) + + __COMPILATION_DB_ENTRIES.append(dbtarget) + + return target, source + + return emit_compilation_db_entry + + +def compilation_db_entry_action(target, source, env, **kw): + """ + Create a dictionary with evaluated command line, target, source + and store that info as an attribute on the target + (Which has been stored in __COMPILATION_DB_ENTRIES array + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :param kw: + :return: None + """ + + command = env["__COMPILATIONDB_UACTION"].strfunction( + target=env["__COMPILATIONDB_UOUTPUT"], + source=env["__COMPILATIONDB_USOURCE"], + env=env["__COMPILATIONDB_ENV"], + ) + + entry = { + "directory": env.Dir("#").abspath, + "command": command, + "file": env["__COMPILATIONDB_USOURCE"][0], + "output": env["__COMPILATIONDB_UOUTPUT"][0], + } + + target[0].write(entry) + + +def write_compilation_db(target, source, env): + entries = [] + + use_abspath = env["COMPILATIONDB_USE_ABSPATH"] in [True, 1, "True", "true"] + use_path_filter = env.subst("$COMPILATIONDB_PATH_FILTER") + use_srcpath_filter = env.subst("$COMPILATIONDB_SRCPATH_FILTER") + + for s in __COMPILATION_DB_ENTRIES: + entry = s.read() + source_file = entry["file"] + output_file = entry["output"] + + if source_file.rfile().srcnode().exists(): + source_file = source_file.rfile().srcnode() + + if use_abspath: + source_file = source_file.abspath + output_file = output_file.abspath + else: + source_file = source_file.path + output_file = output_file.path + + # print("output_file, path_filter", output_file, use_path_filter) + if use_path_filter and not fnmatch.fnmatch(output_file, use_path_filter): + continue + + if use_srcpath_filter and not fnmatch.fnmatch(source_file, use_srcpath_filter): + continue + + path_entry = { + "directory": entry["directory"], + "command": entry["command"], + "file": source_file, + "output": output_file, + } + + entries.append(path_entry) + + with open(target[0].path, "w") as output_file: + json.dump( + entries, output_file, sort_keys=True, indent=4, separators=(",", ": ") + ) + + +def scan_compilation_db(node, env, path): + return __COMPILATION_DB_ENTRIES + + +def compilation_db_emitter(target, source, env): + """fix up the source/targets""" + + # Someone called env.CompilationDatabase('my_targetname.json') + if not target and len(source) == 1: + target = source + + # Default target name is compilation_db.json + if not target: + target = [ + "compile_commands.json", + ] + + # No source should have been passed. Drop it. + if source: + source = [] + + return target, source + + +def generate(env, **kwargs): + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + env.SetDefault( + COMPILATIONDB_COMSTR=kwargs.get( + "COMPILATIONDB_COMSTR", "Building compilation database $TARGET" + ), + COMPILATIONDB_USE_ABSPATH=False, + COMPILATIONDB_PATH_FILTER="", + COMPILATIONDB_SRCPATH_FILTER="", + ) + + components_by_suffix = itertools.chain( + itertools.product( + CSuffixes, + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CCCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCCCOM"), + ], + ), + itertools.product( + CXXSuffixes, + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CXXCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCXXCOM"), + ], + ), + itertools.product( + ASSuffixes, + [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASCOM")], + [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASCOM")], + ), + itertools.product( + ASPPSuffixes, + [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASPPCOM")], + [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASPPCOM")], + ), + ) + + for entry in components_by_suffix: + suffix = entry[0] + builder, base_emitter, command = entry[1] + + # Assumes a dictionary emitter + emitter = builder.emitter.get(suffix, False) + if emitter: + # We may not have tools installed which initialize all or any of + # cxx, cc, or assembly. If not skip resetting the respective emitter. + builder.emitter[suffix] = SCons.Builder.ListEmitter( + [ + emitter, + make_emit_compilation_DB_entry(command), + ] + ) + + env.Append( + BUILDERS={ + "__COMPILATIONDB_Entry": SCons.Builder.Builder( + action=SCons.Action.Action(compilation_db_entry_action, None), + ), + "CompilationDatabase": SCons.Builder.Builder( + action=SCons.Action.Action( + write_compilation_db, "$COMPILATIONDB_COMSTR" + ), + target_scanner=SCons.Scanner.Scanner( + function=scan_compilation_db, node_class=None + ), + emitter=compilation_db_emitter, + suffix="json", + ), + } + ) + + +def exists(env): + return True 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..96528f4e5 100644 --- a/scripts/fbt_tools/fbt_apps.py +++ b/scripts/fbt_tools/fbt_apps.py @@ -1,8 +1,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,42 +15,39 @@ 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): - appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps(env["APPS"]) + appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps( + env["APPS"], env.subst("f${TARGET_HW}") + ) env.Append( SDK_HEADERS=appbuild.get_sdk_headers(), ) - env["APPBUILD_DUMP"] = env.Action( - DumpApplicationConfig, - "\tINFO\t", - ) 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 +61,15 @@ def build_apps_c(target, source, env): def generate(env): - env.AddMethod(LoadApplicationManifests) + env.AddMethod(LoadAppManifest) env.AddMethod(PrepareApplicationsBuild) + env.SetDefault( + APPMGR=AppManager(), + APPBUILD_DUMP=env.Action( + DumpApplicationConfig, + "\tINFO\t", + ), + ) env.Append( BUILDERS={ diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index f058d15f9..e17487358 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 @@ -30,22 +28,25 @@ def dolphin_emitter(target, source, env): res_root_dir = source[0].Dir(env["DOLPHIN_RES_TYPE"]) source = [res_root_dir] source.extend( - env.GlobRecursive("*.*", res_root_dir), + env.GlobRecursive("*.*", res_root_dir.srcnode()), ) target_base_dir = target[0] env.Replace(_DOLPHIN_OUT_DIR=target[0]) if env["DOLPHIN_RES_TYPE"] == "external": - target = [] - target.extend( - map( - lambda node: target_base_dir.File( - res_root_dir.rel_path(node).replace(".png", ".bm") - ), - filter(lambda node: isinstance(node, SCons.Node.FS.File), source), - ) - ) + target = [target_base_dir.File("manifest.txt")] + ## A detailed list of files to be generated + ## works better if we just leave target the folder + # target = [] + # target.extend( + # map( + # lambda node: target_base_dir.File( + # res_root_dir.rel_path(node).replace(".png", ".bm") + # ), + # filter(lambda node: isinstance(node, SCons.Node.FS.File), source), + # ) + # ) else: asset_basename = f"assets_dolphin_{env['DOLPHIN_RES_TYPE']}" target = [ @@ -53,6 +54,13 @@ def dolphin_emitter(target, source, env): target_base_dir.File(asset_basename + ".h"), ] + # Debug output + # print( + # f"Dolphin res type: {env['DOLPHIN_RES_TYPE']},\ntarget files:", + # list(f.path for f in target), + # f"\nsource files:", + # list(f.path for f in source), + # ) return target, source @@ -76,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( @@ -84,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", @@ -106,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) @@ -115,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) @@ -133,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_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index 3e7b07010..9ff05cb73 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -1,15 +1,52 @@ +from re import search + +from SCons.Errors import UserError + + +def _get_device_serials(search_str="STLink"): + import serial.tools.list_ports as list_ports + + return set([device.serial_number for device in list_ports.grep(search_str)]) + + +def GetDevices(env): + serials = _get_device_serials() + if len(serials) == 0: + raise UserError("No devices found") + + print("\n".join(serials)) + + def generate(env, **kw): + env.AddMethod(GetDevices) + env.SetDefault( + FBT_DEBUG_DIR="${ROOT_DIR}/debug", + ) + + if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto": + env.Append( + OPENOCD_OPTS=[ + "-c", + f"adapter serial {adapter_serial}", + ] + ) + + # Final command is "init", always explicitly added + env.Append( + OPENOCD_OPTS=["-c", "init"], + ) + env.SetDefault( OPENOCD_GDB_PIPE=[ - "|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" + "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" ], GDBOPTS_BASE=[ + "-ex", + "set pagination off", "-ex", "target extended-remote ${GDBREMOTE}", "-ex", "set confirm off", - "-ex", - "set pagination off", ], GDBOPTS_BLACKMAGIC=[ "-ex", @@ -23,17 +60,19 @@ def generate(env, **kw): ], GDBPYOPTS=[ "-ex", - "source debug/FreeRTOS/FreeRTOS.py", + "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", "-ex", - "source debug/flipperapps.py", + "source ${FBT_DEBUG_DIR}/flipperapps.py", "-ex", - "source debug/PyCortexMDebug/PyCortexMDebug.py", + "fap-set-debug-elf-root ${FBT_FAP_DEBUG_ELF_ROOT}", + "-ex", + "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", "-ex", "svd_load ${SVD_FILE}", "-ex", "compare-sections", ], - JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash", + JFLASHPROJECT="${FBT_DEBUG_DIR}/fw.jflash", ) diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index 853013e9f..f0b443486 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -22,7 +22,7 @@ def GetProjetDirName(env, project=None): def create_fw_build_targets(env, configuration_name): flavor = GetProjetDirName(env, configuration_name) - build_dir = env.Dir("build").Dir(flavor).abspath + build_dir = env.Dir("build").Dir(flavor) return env.SConscript( "firmware.scons", variant_dir=build_dir, @@ -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}" ${UPDATE_BUNDLE_DIR}/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 34fb942ab..a4116e513 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -1,14 +1,31 @@ +from dataclasses import dataclass, field +from typing import Optional from SCons.Builder import Builder from SCons.Action import Action from SCons.Errors import UserError +from SCons.Node import NodeList import SCons.Warnings +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 -from fbt.elfmanifest import assemble_manifest_data -from fbt.appmanifest import FlipperApplication, FlipperManifestException -from fbt.sdk import SdkCache import itertools +import shutil + +from ansi.color import fg + + +@dataclass +class FlipperExternalAppInfo: + app: FlipperApplication + compact: NodeList = field(default_factory=NodeList) + debug: NodeList = field(default_factory=NodeList) + validator: NodeList = field(default_factory=NodeList) + installer: NodeList = field(default_factory=NodeList) def BuildAppElf(env, app): @@ -21,15 +38,7 @@ def BuildAppElf(env, app): app_alias = f"fap_{app.appid}" - # Deprecation stub - legacy_app_taget_name = f"{app_env['FIRMWARE_BUILD_CFG']}_{app.appid}" - - def legacy_app_build_stub(**kw): - raise UserError( - f"Target name '{legacy_app_taget_name}' is deprecated, use '{app_alias}' instead" - ) - - app_env.PhonyTarget(legacy_app_taget_name, Action(legacy_app_build_stub, None)) + app_artifacts = FlipperExternalAppInfo(app) externally_built_files = [] if app.fap_extbuild: @@ -48,11 +57,12 @@ def BuildAppElf(env, app): ) if app.fap_icon_assets: - app_env.CompileIcons( + fap_icons = app_env.CompileIcons( app_env.Dir(app_work_dir), app._appdir.Dir(app.fap_icon_assets), icon_bundle_name=f"{app.appid}_icons", ) + app_env.Alias("_fap_icons", fap_icons) private_libs = [] @@ -60,7 +70,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 ), ) @@ -81,8 +91,10 @@ def BuildAppElf(env, app): ], CPPDEFINES=lib_def.cdefines, CPPPATH=list( - os.path.join(app._appdir.path, cinclude) - for cinclude in lib_def.cincludes + map( + lambda cpath: extract_abs_dir_path(app._appdir.Dir(cpath)), + lib_def.cincludes, + ) ), ) @@ -108,20 +120,22 @@ def BuildAppElf(env, app): CPPPATH=env.Dir(app_work_dir), ) - app_elf_raw = app_env.Program( + app_artifacts.debug = app_env.Program( os.path.join(ext_apps_work_dir, f"{app.appid}_d"), app_sources, APP_ENTRY=app.entry_point, ) - app_env.Clean(app_elf_raw, [*externally_built_files, app_env.Dir(app_work_dir)]) + app_env.Clean( + app_artifacts.debug, [*externally_built_files, app_env.Dir(app_work_dir)] + ) - app_elf_dump = app_env.ObjDump(app_elf_raw) + app_elf_dump = app_env.ObjDump(app_artifacts.debug) app_env.Alias(f"{app_alias}_list", app_elf_dump) - app_elf_augmented = app_env.EmbedAppMetadata( + app_artifacts.compact = app_env.EmbedAppMetadata( os.path.join(ext_apps_work_dir, app.appid), - app_elf_raw, + app_artifacts.debug, APP=app, ) @@ -132,23 +146,25 @@ def BuildAppElf(env, app): } app_env.Depends( - app_elf_augmented, + app_artifacts.compact, [app_env["SDK_DEFINITION"], app_env.Value(manifest_vals)], ) if app.fap_icon: app_env.Depends( - app_elf_augmented, + app_artifacts.compact, app_env.File(f"{app._apppath}/{app.fap_icon}"), ) - app_elf_import_validator = app_env.ValidateAppImports(app_elf_augmented) - app_env.AlwaysBuild(app_elf_import_validator) - app_env.Alias(app_alias, app_elf_import_validator) - return (app_elf_augmented, app_elf_raw, app_elf_import_validator) + app_artifacts.validator = app_env.ValidateAppImports(app_artifacts.compact) + app_env.AlwaysBuild(app_artifacts.validator) + app_env.Alias(app_alias, app_artifacts.validator) + + env["EXT_APPS"][app.appid] = app_artifacts + return app_artifacts def prepare_app_metadata(target, source, env): - sdk_cache = SdkCache(env.subst("$SDK_DEFINITION"), load_version_only=True) + sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=True) if not sdk_cache.is_buildable(): raise UserError( @@ -158,7 +174,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, @@ -169,17 +184,24 @@ def prepare_app_metadata(target, source, env): def validate_app_imports(target, source, env): - sdk_cache = SdkCache(env.subst("$SDK_DEFINITION"), load_version_only=False) + sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=False) app_syms = set() with open(target[0].path, "rt") as f: for line in f: app_syms.add(line.split()[0]) unresolved_syms = app_syms - sdk_cache.get_valid_names() if unresolved_syms: - SCons.Warnings.warn( - SCons.Warnings.LinkWarning, - f"\033[93m{source[0].path}: app won't run. Unresolved symbols: \033[95m{unresolved_syms}\033[0m", - ) + warning_msg = fg.brightyellow( + f"{source[0].path}: app won't run. Unresolved symbols: " + ) + fg.brightmagenta(f"{unresolved_syms}") + disabled_api_syms = unresolved_syms.intersection(sdk_cache.get_disabled_names()) + if disabled_api_syms: + warning_msg += ( + fg.brightyellow(" (in API, but disabled: ") + + fg.brightmagenta(f"{disabled_api_syms}") + + fg.brightyellow(")") + ) + SCons.Warnings.warn(SCons.Warnings.LinkWarning, warning_msg), def GetExtAppFromPath(env, app_dir): @@ -201,25 +223,68 @@ def GetExtAppFromPath(env, app_dir): if not app: raise UserError(f"Failed to resolve application for given APPSRC={app_dir}") - app_elf = env["_extapps"]["compact"].get(app.appid, None) - if not app_elf: + app_artifacts = env["EXT_APPS"].get(app.appid, None) + if not app_artifacts: raise UserError( f"Application {app.appid} is not configured for building as external" ) - app_validator = env["_extapps"]["validators"].get(app.appid, None) + return app_artifacts - return (app, app_elf[0], app_validator[0]) + +def fap_dist_emitter(target, source, env): + target_dir = target[0] + + target = [] + for _, app_artifacts in env["EXT_APPS"].items(): + source.extend(app_artifacts.compact) + target.append( + target_dir.Dir(app_artifacts.app.fap_category).File( + app_artifacts.compact[0].name + ) + ) + + return (target, source) + + +def fap_dist_action(target, source, env): + # FIXME + target_dir = env.Dir("#/assets/resources/apps") + + shutil.rmtree(target_dir.path, ignore_errors=True) + for src, target in zip(source, target): + os.makedirs(os.path.dirname(target.path), exist_ok=True) + shutil.copy(src.path, target.path) def generate(env, **kw): - env.SetDefault(EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR")) - # env.VariantDir(env.subst("$EXT_APPS_WORK_DIR"), env.Dir("#"), duplicate=False) + env.SetDefault( + EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", + APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", + ) + if not env["VERBOSE"]: + env.SetDefault( + FAPDISTCOMSTR="\tFAPDIST\t${TARGET}", + APPMETA_COMSTR="\tAPPMETA\t${TARGET}", + APPMETAEMBED_COMSTR="\tFAP\t${TARGET}", + APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", + ) + + env.SetDefault( + EXT_APPS={}, # appid -> FlipperExternalAppInfo + ) env.AddMethod(BuildAppElf) env.AddMethod(GetExtAppFromPath) env.Append( BUILDERS={ + "FapDist": Builder( + action=Action( + fap_dist_action, + "$FAPDISTCOMSTR", + ), + emitter=fap_dist_emitter, + ), "EmbedAppMetadata": Builder( action=[ Action(prepare_app_metadata, "$APPMETA_COMSTR"), diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index ed0abdff6..3a37eacc9 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -1,9 +1,10 @@ +import shutil from SCons.Builder import Builder 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 @@ -11,7 +12,9 @@ import posixpath import pathlib import json -from fbt.sdk import SdkCollector, SdkCache +from fbt.sdk.collector import SdkCollector +from fbt.sdk.cache import SdkCache +from fbt.util import path_as_posix def ProcessSdkDepends(env, filename): @@ -44,29 +47,47 @@ 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: - def __init__(self, env): + MAP_FILE_SUBST = "SDK_MAP_FILE_SUBST" + + 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_libs": self.env.subst("${LIBS}"), + "app_ep_subst": self.env.subst("${APP_ENTRY}"), + "sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"), + "map_file_subst": self.MAP_FILE_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")) - return expanded_vars.replace("\\", "/") + expanded_vars = self.env.subst( + vars, + target=Entry(self.MAP_FILE_SUBST), + ) + return path_as_posix(expanded_vars) 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 self.target = target @@ -79,6 +100,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: @@ -87,72 +113,76 @@ class SdkTreeBuilder: self.header_depends = list( filter(lambda fname: fname.endswith(".h"), depends.split()), ) + 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) - 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( + 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(CPPPATH=filtered_paths) - meta = SdkMeta(sdk_env) + self.sdk_env.Replace( + CPPPATH=filtered_paths, + ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"], + LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"), + ) + meta = SdkMeta(self.sdk_env, self) meta.save_to(self.target[0].path) + def build_sdk_file_path(self, orig_path: str) -> str: + return path_as_posix( + posixpath.normpath( + posixpath.join( + self.SDK_DIR_SUBST, + self.target_sdk_dir_name, + orig_path, + ) + ) + ) + def emitter(self, target, source, env): target_folder = target[0] target = [target_folder.File("sdk.opts")] return target, source - def _create_deploy_commands(self): + def _run_deploy_commands(self): dirs_to_create = set( - self.sdk_deploy_dir.Dir(dirpath) for dirpath in self.header_dirs + self.sdk_deploy_dir.Dir(dirpath).path for dirpath in self.header_dirs ) - actions = [ - Delete(self.sdk_deploy_dir), - Mkdir(self.sdk_deploy_dir), - Copy( - self.sdk_root_dir, - self.env["SDK_DEFINITION"], - ), - ] - actions += [Mkdir(d) for d in dirs_to_create] - actions += [ - Action( - Copy(self.sdk_deploy_dir.File(h).path, h), - # f"Copy {h} to {self.sdk_deploy_dir}", - ) - for h in self.header_depends - ] - return actions + shutil.rmtree(self.sdk_root_dir.path, ignore_errors=False) - def generate_actions(self): + for sdkdir in dirs_to_create: + os.makedirs(sdkdir, exist_ok=True) + + for header in self.header_depends: + shutil.copy2(header, self.sdk_deploy_dir.File(header).path) + + def deploy_action(self): self._parse_sdk_depends() + self._run_deploy_commands() self._generate_sdk_meta() - return self._create_deploy_commands() - - -def deploy_sdk_tree(target, source, env, for_signature): - if for_signature: - return [] +def deploy_sdk_tree_action(target, source, env): sdk_tree = SdkTreeBuilder(env, target, source) - return sdk_tree.generate_actions() + return sdk_tree.deploy_action() def deploy_sdk_tree_emitter(target, source, env): @@ -199,7 +229,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()) @@ -217,6 +247,30 @@ def generate_sdk_symbols(source, target, env): def generate(env, **kw): + if not env["VERBOSE"]: + env.SetDefault( + SDK_PREGEN_COMSTR="\tPREGEN\t${TARGET}", + SDK_COMSTR="\tSDKSRC\t${TARGET}", + SDKSYM_UPDATER_COMSTR="\tSDKCHK\t${TARGET}", + SDKSYM_GENERATOR_COMSTR="\tSDKSYM\t${TARGET}", + SDKDEPLOY_COMSTR="\tSDKTREE\t${TARGET}", + ) + + # Filtering out things cxxheaderparser cannot handle + env.SetDefault( + SDK_PP_FLAGS=[ + '-D"_Static_assert(x,y)="', + '-D"__asm__(x)="', + '-D"__attribute__(x)="', + "-Drestrict=", + "-D_Noreturn=", + "-D__restrict=", + "-D__extension__=", + "-D__inline=inline", + "-D__inline__=inline", + ] + ) + env.AddMethod(ProcessSdkDepends) env.Append( BUILDERS={ @@ -235,7 +289,10 @@ def generate(env, **kw): suffix=".i", ), "SDKTree": Builder( - generator=deploy_sdk_tree, + action=Action( + deploy_sdk_tree_action, + "$SDKDEPLOY_COMSTR", + ), emitter=deploy_sdk_tree_emitter, src_suffix=".d", ), diff --git a/scripts/fbt_tools/fbt_tweaks.py b/scripts/fbt_tools/fbt_tweaks.py new file mode 100644 index 000000000..a903d4033 --- /dev/null +++ b/scripts/fbt_tools/fbt_tweaks.py @@ -0,0 +1,43 @@ +import SCons.Warnings as Warnings + +# from SCons.Script.Main import find_deepest_user_frame + +from ansi.color import fg, bg, fx + +import traceback +import sys +import os + + +def find_deepest_user_frame(tb): + tb.reverse() + + # find the deepest traceback frame that is not part + # of SCons: + for frame in tb: + filename = frame[0] + if filename.find("fbt_tweaks") != -1: + continue + if filename.find(os.sep + "SCons" + os.sep) == -1: + return frame + return tb[0] + + +def fbt_warning(e): + filename, lineno, routine, dummy = find_deepest_user_frame( + traceback.extract_stack() + ) + fbt_line = "\nfbt: warning: %s\n" % e.args[0] + sys.stderr.write(fg.boldmagenta(fbt_line)) + fbt_line = ( + fg.yellow("%s, line %d, " % (routine, lineno)) + 'in file "%s"\n' % filename + ) + sys.stderr.write(fg.yellow(fbt_line)) + + +def generate(env): + Warnings._warningOut = fbt_warning + + +def exists(): + return True 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 678b04998..f510c2a60 100644 --- a/scripts/fbt_tools/fwbin.py +++ b/scripts/fbt_tools/fwbin.py @@ -8,10 +8,19 @@ __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 ) + + if not env["VERBOSE"]: + env.SetDefault( + HEXCOMSTR="\tHEX\t${TARGET}", + BINCOMSTR="\tBIN\t${TARGET}", + DFUCOMSTR="\tDFU\t${TARGET}", + ) + env.Append( BUILDERS={ "HEXBuilder": Builder( diff --git a/scripts/fbt_tools/gdb.py b/scripts/fbt_tools/gdb.py index 94ea9fbe9..38256a0f8 100644 --- a/scripts/fbt_tools/gdb.py +++ b/scripts/fbt_tools/gdb.py @@ -6,8 +6,6 @@ def generate(env): env.SetDefault( GDB="gdb", GDBPY="gdb-py", - GDBOPTS="", - GDBPYOPTS="", GDBCOM="$GDB $GDBOPTS $SOURCES", # no $TARGET GDBPYCOM="$GDBPY $GDBOPTS $GDBPYOPTS $SOURCES", # no $TARGET ) 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/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index d39f30333..b61ac0329 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -3,6 +3,8 @@ import json from io import BytesIO import tarfile import xml.etree.ElementTree as ET +import posixpath +import os from flipper.utils import * from flipper.assets.coprobin import CoproBinary, get_stack_type @@ -23,6 +25,8 @@ MANIFEST_TEMPLATE = { class Copro: + COPRO_TAR_DIR = "core2_firmware" + def __init__(self, mcu): self.mcu = mcu self.version = None @@ -50,9 +54,8 @@ class Copro: raise Exception(f"Unsupported cube version") self.version = cube_version - @staticmethod - def _getFileName(name): - return os.path.join("core2_firmware", name) + def _getFileName(self, name): + return posixpath.join(self.COPRO_TAR_DIR, name) def addFile(self, array, filename, **kwargs): source_file = os.path.join(self.mcu_copro, filename) @@ -61,6 +64,9 @@ class Copro: def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None): self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT) + fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR) + fw_directory.type = tarfile.DIRTYPE + self.output_tar.addfile(fw_directory) stack_file = os.path.join(self.mcu_copro, stack_file_name) # Form Manifest 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/fwsize.py b/scripts/fwsize.py index b381f6e9a..445c29049 100644 --- a/scripts/fwsize.py +++ b/scripts/fwsize.py @@ -4,6 +4,7 @@ from flipper.app import App import subprocess import os import math +from ansi.color import fg class Main(App): @@ -43,7 +44,9 @@ class Main(App): pages = math.ceil(binsize / PAGE_SIZE) last_page_state = (binsize % PAGE_SIZE) * 100 / PAGE_SIZE print( - f"{os.path.basename(self.args.binname):<11}: {pages:>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/lint.py b/scripts/lint.py index c178c8763..58f2d69f5 100755 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -35,11 +35,23 @@ class Main(App): ) self.parser_format.set_defaults(func=self.format) + @staticmethod + def _filter_lint_directories(dirnames: list[str]): + # Skipping 3rd-party code - usually resides in subfolder "lib" + if "lib" in dirnames: + dirnames.remove("lib") + # Skipping hidden folders + for dirname in dirnames.copy(): + if dirname.startswith("."): + dirnames.remove(dirname) + def _check_folders(self, folders: list): show_message = False pattern = re.compile(SOURCE_CODE_DIR_PATTERN) for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): + self._filter_lint_directories(dirnames) + for dirname in dirnames: if not pattern.match(dirname): to_fix = os.path.join(dirpath, dirname) @@ -54,9 +66,7 @@ class Main(App): output = [] for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): - # Skipping 3rd-party code - usually resides in subfolder "lib" - if "lib" in dirnames: - dirnames.remove("lib") + self._filter_lint_directories(dirnames) for filename in filenames: ext = os.path.splitext(filename.lower())[1] diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 35cac7742..5b6fac5f7 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,7 +1,9 @@ -pyserial==3.5 +ansi==0.3.6 +black==22.6.0 +colorlog==6.7.0 heatshrink2==0.11.0 Pillow==9.1.1 -grpcio==1.47.0 -grpcio-tools==1.47.0 -protobuf==3.20.2 +protobuf==3.20.1 +pyserial==3.5 python3-protobuf==2.5.0 +SCons==4.4.0 diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 5f38ec980..0fa120d87 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: @@ -47,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), @@ -126,7 +131,9 @@ 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.boldgreen( + f"Firmware binaries can be found at:\n\t{self.output_dir_path}" + ) ) if self.args.version: @@ -143,12 +150,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: @@ -170,7 +177,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.boldgreen( + f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" + ) ) # Create tgz archive 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 100755 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 100755 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() diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 5eaf16f94..9fbd8fd9b 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -1,33 +1,38 @@ @echo off -if not [%FBT_ROOT%] == [] ( +if not ["%FBT_ROOT%"] == [""] ( goto already_set ) set "FBT_ROOT=%~dp0\..\..\" -pushd %FBT_ROOT% +pushd "%FBT_ROOT%" set "FBT_ROOT=%cd%" popd -if not [%FBT_NOENV%] == [] ( +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=19" +if ["%FBT_TOOLCHAIN_ROOT%"] == [""] ( + set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows" +) + +set "FBT_TOOLCHAIN_VERSION_FILE=%FBT_TOOLCHAIN_ROOT%\VERSION" if not exist "%FBT_TOOLCHAIN_ROOT%" ( - powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" -) -if not exist "%FBT_TOOLCHAIN_ROOT%\VERSION" ( - powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" -) -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%" ) +if not exist "%FBT_TOOLCHAIN_VERSION_FILE%" ( + 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_VERSION_FILE%" +if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( + powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" %flipper_toolchain_version% "%FBT_TOOLCHAIN_ROOT%" +) set "HOME=%USERPROFILE%" set "PYTHONHOME=%FBT_TOOLCHAIN_ROOT%\python" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 98d496810..a607280c1 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:-"19"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_show_usage() @@ -13,7 +13,7 @@ fbtenv_show_usage() echo "Running this script manually is wrong, please source it"; echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh\n"; - echo "To restore your enviroment source fbtenv.sh with '--restore'." + echo "To restore your environment source fbtenv.sh with '--restore'." echo "Example:"; printf "\tsource scripts/toolchain/fbtenv.sh --restore\n"; } @@ -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 @@ -129,10 +129,10 @@ fbtenv_get_kernel_type() TOOLCHAIN_URL=$FBT_TOOLS_CUSTOM_LINK; fi 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; @@ -152,7 +152,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; @@ -163,7 +163,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; @@ -214,7 +214,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; @@ -237,7 +237,7 @@ fbtenv_curl_wget_check() echo; echo "$TOOLCHAIN_URL"; echo; - echo "And place in $FBT_TOOLCHAIN_PATH/toolchain/ dir mannualy"; + echo "And place in $FBT_TOOLCHAIN_PATH/toolchain/ dir manually"; return 1; fi echo "yes" diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index bcb8f998f..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-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_target_path = $args[1] -if (Test-Path -LiteralPath "$repo_root\toolchain\i686-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\i686-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 "Unziping Windows toolchain.." +Write-Host -NoNewline "Extracting Windows toolchain.." +# This is faster than Expand-Archive 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" +[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 "Clearing temporary files.." -Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force +Write-Host -NoNewline "Cleaning up temporary files.." +Remove-Item -LiteralPath "$toolchain_zip_temp_path" -Force Write-Host "done!" + +# dasdasd \ No newline at end of file diff --git a/scripts/update.py b/scripts/update.py index 1ff818c46..3259c5b09 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -78,9 +78,12 @@ class Main(App): def generate(self): stage_basename = "updater.bin" # used to be basename(self.args.stage) - dfu_basename = "firmware.dfu" # used to be basename(self.args.dfu) - radiobin_basename = "radio.bin" # used to be basename(self.args.radiobin) - radiobin_basename_arg = basename(self.args.radiobin) + dfu_basename = ( + "firmware.dfu" if self.args.dfu else "" + ) # used to be basename(self.args.dfu) + radiobin_basename = ( + "radio.bin" if self.args.radiobin else "" + ) # used to be basename(self.args.radiobin) resources_basename = "" radio_version = 0 @@ -116,7 +119,7 @@ class Main(App): if self.args.dfu: dfu_size = os.stat(self.args.dfu).st_size shutil.copyfile(self.args.dfu, join(self.args.directory, dfu_basename)) - if radiobin_basename_arg: + if radiobin_basename: shutil.copyfile( self.args.radiobin, join(self.args.directory, radiobin_basename) ) @@ -156,13 +159,10 @@ class Main(App): file.writeComment("little-endian hex!") file.writeKey("Loader CRC", self.int2ffhex(self.crc(self.args.stage))) file.writeKey("Firmware", dfu_basename) - if radiobin_basename_arg: - file.writeKey("Radio", radiobin_basename) - else: - file.writeKey("Radio", "") + file.writeKey("Radio", radiobin_basename or "") file.writeKey("Radio address", self.int2ffhex(radio_addr)) file.writeKey("Radio version", self.int2ffhex(radio_version, 12)) - if radiobin_basename_arg: + if radiobin_basename: file.writeKey("Radio CRC", self.int2ffhex(self.crc(self.args.radiobin))) else: file.writeKey("Radio CRC", self.int2ffhex(0)) diff --git a/scripts/version.py b/scripts/version.py index a7ab438ce..f4dd127f5 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -48,7 +48,7 @@ class GitVersion: "GIT_BRANCH": "dev", "GIT_BRANCH_NUM": branch_num, "FURI_CUSTOM_FLIPPER_NAME": custom_fz_name, - "VERSION": "0.68.2", + "VERSION": "0.71.2", "BUILD_DIRTY": 0, } else: @@ -56,7 +56,7 @@ class GitVersion: "GIT_COMMIT": commit, "GIT_BRANCH": "dev", "GIT_BRANCH_NUM": branch_num, - "VERSION": "0.68.2", + "VERSION": "0.71.2", "BUILD_DIRTY": 0, } diff --git a/site_scons/cc.scons b/site_scons/cc.scons index 507ec04a4..5c7df2575 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -33,7 +33,8 @@ ENV.AppendUnique( "-ffunction-sections", "-fsingle-precision-constant", "-fno-math-errno", - "-fstack-usage", + # Generates .su files with stack usage information + # "-fstack-usage", "-g", "-Wno-unused-parameter", "-Wno-type-limits", diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 6167f4f44..3e6859b4e 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -15,7 +15,7 @@ AddOption( nargs=1, action="store", default="fbt_options.py", - help="Enviroment option file", + help="Environment option file", ) AddOption( @@ -23,7 +23,7 @@ AddOption( action="store", dest="extra_int_apps", default="", - help="List of applications to add to firmare's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", + help="List of applications to add to firmware's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", ) AddOption( @@ -63,6 +63,11 @@ vars.AddVariables( help="Enable debug build", default=True, ), + BoolVariable( + "LIB_DEBUG", + help="Enable debug build for libraries", + default=False, + ), BoolVariable( "COMPACT", help="Optimize for size", @@ -152,7 +157,7 @@ vars.AddVariables( PathVariable( "SVD_FILE", help="Path to SVD file", - validator=PathVariable.PathIsFile, + validator=PathVariable.PathAccept, default="", ), PathVariable( @@ -176,6 +181,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 12b166480..128a667f1 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 @@ -36,6 +41,7 @@ for env_value_name in variables_to_forward: coreenv = VAR_ENV.Clone( tools=[ + "fbt_tweaks", ( "crosscc", { @@ -52,6 +58,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, + ROOT_DIR=Dir("#"), + FBT_SCRIPT_DIR="${ROOT_DIR}/scripts", ) # If DIST_SUFFIX is set in environment, is has precedence (set by CI) @@ -60,41 +72,19 @@ 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()) +## 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) # 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 9edc6b5c1..b8f210563 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,27 +1,22 @@ -from SCons.Errors import UserError +from dataclasses import dataclass, field +from SCons.Node import NodeList +from SCons.Warnings import warn, WarningOnByDefault Import("ENV") - from fbt.appmanifest import FlipperAppType -appenv = ENV.Clone( +appenv = ENV["APPENV"] = ENV.Clone( tools=[ - ( - "fbt_extapps", - { - "EXT_APPS_WORK_DIR": ENV.subst( - "${BUILD_DIR}/.extapps", - ) - }, - ), + "fbt_extapps", "fbt_assets", + "fbt_sdk", ] ) appenv.Replace( - LINKER_SCRIPT="application_ext", + LINKER_SCRIPT=appenv.subst("$APP_LINKER_SCRIPT"), ) appenv.AppendUnique( @@ -60,20 +55,11 @@ appenv.AppendUnique( ) -extapps = appenv["_extapps"] = { - "compact": {}, - "debug": {}, - "validators": {}, - "dist": {}, -} - - -def build_app_as_external(env, appdef): - compact_elf, debug_elf, validator = env.BuildAppElf(appdef) - extapps["compact"][appdef.appid] = compact_elf - extapps["debug"][appdef.appid] = debug_elf - extapps["validators"][appdef.appid] = validator - extapps["dist"][appdef.appid] = (appdef.fap_category, compact_elf) +@dataclass +class FlipperExtAppBuildArtifacts: + applications: dict = field(default_factory=dict) + resources_dist: NodeList = field(default_factory=NodeList) + sdk_tree: NodeList = field(default_factory=NodeList) apps_to_build_as_faps = [ @@ -83,39 +69,83 @@ apps_to_build_as_faps = [ if appenv["DEBUG_TOOLS"]: apps_to_build_as_faps.append(FlipperAppType.DEBUG) -for apptype in apps_to_build_as_faps: - for app in appenv["APPBUILD"].get_apps_of_type(apptype, True): - build_app_as_external(appenv, app) +known_extapps = [ + app + for apptype in apps_to_build_as_faps + for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) +] # Ugly access to global option if extra_app_list := GetOption("extra_ext_apps"): - for extra_app in extra_app_list.split(","): - build_app_as_external(appenv, appenv["APPMGR"].get(extra_app)) + known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) + +for app in known_extapps: + if not any(map(lambda t: t in app.targets, ["all", appenv.subst("f${TARGET_HW}")])): + warn( + WarningOnByDefault, + f"Can't build '{app.name}' (id '{app.appid}'): target mismatch" + f" (building for {appenv.subst('f${TARGET_HW}')}, app supports {app.targets}", + ) + continue + + appenv.BuildAppElf(app) if appenv["FORCE"]: - appenv.AlwaysBuild(extapps["compact"].values()) + appenv.AlwaysBuild( + list(app_artifact.compact for app_artifact in appenv["EXT_APPS"].values()) + ) -# Deprecation stub -def legacy_app_build_stub(**kw): - raise UserError(f"Target name 'firmware_extapps' is deprecated, use 'faps' instead") +Alias( + "faps", list(app_artifact.validator for app_artifact in appenv["EXT_APPS"].values()) +) - -appenv.PhonyTarget("firmware_extapps", appenv.Action(legacy_app_build_stub, None)) - - -Alias("faps", extapps["compact"].values()) -Alias("faps", extapps["validators"].values()) +extapps = FlipperExtAppBuildArtifacts() +extapps.applications = appenv["EXT_APPS"] +extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) if appsrc := appenv.subst("$APPSRC"): - app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) + app_artifacts = appenv.GetExtAppFromPath(appsrc) appenv.PhonyTarget( "launch_app", - '${PYTHON3} scripts/runfap.py ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', - source=fap_file, - FAP_CATEGORY=app_manifest.fap_category, + '${PYTHON3} "${APP_RUN_SCRIPT}" "${SOURCE}" --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', + source=app_artifacts.compact, + FAP_CATEGORY=app_artifacts.app.fap_category, ) - appenv.Alias("launch_app", app_validator) + appenv.Alias("launch_app", app_artifacts.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 +# Available on second and subsequent builds +Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) + +appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk") +sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) +# AlwaysBuild(sdk_tree) +Alias("sdk_tree", sdk_tree) +extapps.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") 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/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", ) 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) diff --git a/test_iso7816_helpers.c b/test_iso7816_helpers.c new file mode 100644 index 000000000..91d420dd1 --- /dev/null +++ b/test_iso7816_helpers.c @@ -0,0 +1,243 @@ +#include +#include + +#include "lib/nfc/helpers/iso7816.h" + +#define COLOR_RED "\033[0;31m" +#define COLOR_GREEN "\033[0;32m" +#define COLOR_RESET "\033[0;0m" + +#define num_elements(A) (sizeof(A)/sizeof(A[0])) + +//TODO: do something with ISO7816-4 Table 9 β€” Interindustry data objects for tag allocation authority +//0x06 Object identifier (encoding specified in ISO/IEC 8825-1, see examples in annex A) +//0x41 Country code (encoding specified in ISO 3166-1 [1] ) and optional national data +//0x42 Issuer identification number (encoding and registration specified in ISO/IEC 7812-1 [3] ) and optional issuer data +//0x4F Application identifier (AID, encoding specified in 8.2.1.2) + +void print_hex(const uint8_t* data, size_t length) { + for(size_t i=0; i +#include +#include +#include + +#include "applications/main/nfc/test_bac_creds.h" //TODO: remove + +#include "lib/nfc/protocols/mrtd_helpers.h" + +// gcc -o test_mrtd_helpers -Wall -Ilib/mbedtls/include -Itoolchain/x86_64-linux/arm-none-eabi/include/ lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/des.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c + +#define COLOR_RED "\033[0;31m" +#define COLOR_GREEN "\033[0;32m" +#define COLOR_RESET "\033[0;0m" + +void print_hex(const uint8_t* data, size_t length) { + for(uint8_t i=0; i