diff --git a/.github/assets/dark_theme_banner.png b/.github/assets/dark_theme_banner.png deleted file mode 100644 index 10a4e8c30..000000000 Binary files a/.github/assets/dark_theme_banner.png and /dev/null differ diff --git a/.github/assets/light_theme_banner.png b/.github/assets/light_theme_banner.png deleted file mode 100644 index 6c6b3b708..000000000 Binary files a/.github/assets/light_theme_banner.png and /dev/null differ diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md new file mode 100644 index 000000000..1dcbf31ee --- /dev/null +++ b/.github/workflow_data/release.md @@ -0,0 +1,22 @@ +## ⬇️ Download +>### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{release_tag}/{release_tag}.tgz) [recommended] + +>### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{release_tag}/{release_tag}.zip) + +>### [🖥️ Web Updater (chrome)](https://lab.flipper.net/?url={webupdater_url}&channel=XFW-Updater&version={release_tag}) [not recommended] + +**Remember to delete your `apps` (and `update` if using "Install from file...") folders before updating!**\ +**Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions!** + +## 🚀 Changelog +{changelog} + +## ❤️ Support +If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! + +- **[Direct transfer to my bank](https://bunq.me/ClaraK)**: No account needed, they have a convenient online pay thingy (that hates americans, sowwy) +- **[Patreon](https://patreon.com/CynthiaLabs)** +- **[Paypal](https://paypal.me/RdX2020)** +- **Monero**: 41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T + +**Thanks for all your support <3** diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b7eee574a..715070b83 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,28 +1,22 @@ -name: 'Build' +name: 'Build artifact' on: push: branches: - dev - - "release*" + - main tags: - '*' pull_request: env: - TARGETS: f7 f18 + TARGETS: f7 DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /home/runner/work jobs: - main: + build: runs-on: ubuntu-latest steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - name: 'Checkout code' uses: actions/checkout@v3 @@ -30,70 +24,24 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: 'Get commit details' - id: names - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - echo random_hash=$(openssl rand -base64 40 | shasum -a 256 | awk '{print $1}') >> $GITHUB_OUTPUT - echo "event_type=$TYPE" >> $GITHUB_OUTPUT - - - name: 'Make artifacts directory' - run: | - rm -rf artifacts - mkdir artifacts - - - name: 'Bundle scripts' - run: | - tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug - - name: 'Build the firmware' run: | set -e for TARGET in ${TARGETS}; do TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET copro_dist updater_package \ - ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} - done - - - name: 'Move upload files' - run: | - set -e - for TARGET in ${TARGETS}; do - mv dist/${TARGET}-*/* artifacts/ + ./fbt TARGET_HW=$TARGET updater_package done - name: "Check for uncommitted changes" run: | git diff --exit-code - - name: 'Bundle resources' - run: | - tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources - - - name: 'Bundle core2 firmware' - run: | - cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - - name: 'Updater artifact' uses: actions/upload-artifact@v3 with: name: updater path: | - artifacts/f7-* - - - name: 'Firmware artifact' - uses: actions/upload-artifact@v3 - with: - name: firmware - path: | - artifacts + dist/${{ env.DEFAULT_TARGET }}-* # - name: 'Find Previous Comment' # if: ${{ github.event.pull_request }} @@ -102,7 +50,7 @@ jobs: # with: # issue-number: ${{ github.event.pull_request.number }} # comment-author: 'github-actions[bot]' - # body-includes: 'Compiled firmware for commit' + # body-includes: 'Compiled firmware:' # - name: Artifact info # id: artifact-info @@ -117,44 +65,6 @@ jobs: # comment-id: ${{ steps.fc.outputs.comment-id }} # issue-number: ${{ github.event.pull_request.number }} # body: | - # **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** + # **Compiled firmware:** # - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}}) # edit-mode: replace - - compact: - if: ${{ !startsWith(github.ref, 'refs/tags') }} - runs-on: ubuntu-latest - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ] - then - git submodule status \ - || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: true - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get commit details' - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - - name: 'Build the firmware' - run: | - set -e - for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package - done diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml index d1a1a64c3..ad63f711b 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/check_submodules.yml @@ -1,10 +1,10 @@ -name: 'Check submodules branch' +name: 'Check submodules' on: push: branches: - dev - - "release*" + - main tags: - '*' pull_request: @@ -13,11 +13,6 @@ jobs: check_protobuf: runs-on: ubuntu-latest steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - name: 'Checkout code' uses: actions/checkout@v3 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..818aa7d84 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,27 @@ +name: 'Lint formatting' + +on: + push: + branches: + - dev + - main + tags: + - '*' + pull_request: + +env: + SET_GH_OUTPUT: 1 + +jobs: + lint: + runs-on: ubuntu-latest + steps: + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Check code formatting' + run: ./fbt lint lint_py diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml deleted file mode 100644 index 232e3c689..000000000 --- a/.github/workflows/lint_c.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: 'Lint C/C++ with clang-format' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - SET_GH_OUTPUT: 1 - -jobs: - lint_c_cpp: - runs-on: ubuntu-latest - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Check code formatting' - id: syntax_check - run: ./fbt lint - - - name: Report code formatting errors - if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request - uses: peter-evans/create-or-update-comment@v1 - with: - issue-number: ${{ github.event.pull_request.number }} - body: | - Please fix following code formatting errors: - ``` - ${{ steps.syntax_check.outputs.errors }} - ``` - You might want to run `./fbt format` for an auto-fix. diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml deleted file mode 100644 index 65a8b6150..000000000 --- a/.github/workflows/pvs_studio.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: 'Static C/C++ analysis with PVS-Studio' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /runner/_work - -jobs: - analyse_c_cpp: - if: ${{ !github.event.pull_request.head.repo.fork }} - runs-on: [self-hosted, FlipperZeroShell] - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get commit details' - id: names - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - - name: 'Supply PVS credentials' - run: | - pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} - - - name: 'Convert PVS-Studio output to html and detect warnings' - id: pvs-warn - run: | - WARNINGS=0 - ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 - echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT - - - name: 'Upload report' - if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} - uses: prewk/s3-cp-action@v2 - with: - aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}" - aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}" - aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}" - source: "./build/f7-firmware-DC/pvsreport" - dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/" - flags: "--recursive --acl public-read" - - - name: 'Find Previous Comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} - uses: peter-evans/find-comment@v2 - id: fc - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: 'PVS-Studio report for commit' - - - name: 'Create or update comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} - uses: peter-evans/create-or-update-comment@v1 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** - - [Report](https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) - edit-mode: replace - - - name: 'Raise exception' - if: ${{ steps.pvs-warn.outputs.warnings != 0 }} - run: | - echo "Please fix all PVS warnings before merge" - exit 1 - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..dc6ead78d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,98 @@ +name: "Release integration" + +on: + pull_request_review: + types: [submitted] + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + +jobs: + release: + if: | + github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name && + endsWith(github.event.pull_request.title, ' Release Candidate Changes') && + github.event.review.author_association == 'OWNER' && + startsWith(github.event.pull_request.title, 'V') && + github.event.pull_request.base.ref == 'main' && + github.event.pull_request.head.ref == 'dev' && + github.event.pull_request.state == 'open' && + github.event.pull_request.draft == false && + github.event.review.state == 'APPROVED' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + + - name: "Update version number" + run: | + VERSION="$(python -c "print('%04d' % int('${{ github.event.pull_request.title }}'.removeprefix('V').removesuffix(' Release Candidate Changes')), end='')")" + DATE="$(python -c "from datetime import date;print(date.today().strftime('%d%m%Y'), end='')")" + RELEASE_NAME="XFW-${VERSION}" + RELEASE_TAG="${RELEASE_NAME}_${DATE}" + echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV + + sed "s/VERSION = \"XFW-[[:digit:]]\{4\}\"/VERSION = \"${RELEASE_NAME}\"/g" ./scripts/version.py > ./scripts/version.py.new + rm ./scripts/version.py + mv ./scripts/version.py.new ./scripts/version.py + + sed "s/DIST_SUFFIX = \"XFW-[[:digit:]]\{4\}_[[:digit:]]\{8\}\"/DIST_SUFFIX = \"${RELEASE_TAG}\"/g" ./fbt_options.py > ./fbt_options.py.new + rm ./fbt_options.py + mv ./fbt_options.py.new ./fbt_options.py + + - name: 'Build the firmware' + run: | + set -e + for TARGET in ${TARGETS}; do + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET updater_package + done + + - name: "Make tgz, zip and webupdater" + run: | + cd ./dist/${DEFAULT_TARGET}-* + mv flipper-z-${DEFAULT_TARGET}-update-*.tgz ${RELEASE_TAG}.tgz + cd ./${DEFAULT_TARGET}-update-* + 7z a ../${RELEASE_TAG}.zip . + cd .. + python -m pip install pyncclient + WEBUPDATER_URL="$(python -c "import nextcloud_client as n;c = n.Client('https://cloud.cynthialabs.net/');c.login('${{ secrets.NC_USER }}', '${{ secrets.NC_PASS }}');c.put_file('XFW-Updater/${RELEASE_TAG}.tgz', '${RELEASE_TAG}.tgz');print(c.share_file_with_link('XFW-Updater/${RELEASE_TAG}.tgz').get_link().rstrip('/') + '/download/${RELEASE_TAG}.tgz', end='')")" + echo "WEBUPDATER_URL=${WEBUPDATER_URL}" >> $GITHUB_ENV + + - name: "Update release notes" + run: | + python -c "import json, os;f = '.github/workflow_data/release.md';c = json.load(open(os.environ['GITHUB_EVENT_PATH']))['pull_request']['body'];f_ = open(f);n = f_.read().format(release_tag='${RELEASE_TAG}', webupdater_url='${WEBUPDATER_URL}', changelog=c);f_.close();f_ = open(f, 'w');f_.write(n);f_.close()" + + - name: "Commit new version number" + uses: EndBug/add-and-commit@v9 + with: + default_author: github_actions + message: Update version number + add: './fbt_options.py ./scripts/version.py' + + - name: "Merge pull request" + uses: "pascalgn/automerge-action@v0.15.6" + env: + MERGE_LABELS: "" + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + - name: "Make release" + uses: softprops/action-gh-release@v1 + with: + body_path: ".github/workflow_data/release.md" + draft: false + prerelease: false + files: | + ${{ env.RELEASE_TAG }}.tgz + ${{ env.RELEASE_TAG }}.zip + name: "${{ env.RELEASE_TAG }}" + tag_name: "${{ env.RELEASE_TAG }}" + target_commitish: main diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarqube.yaml index 5d0ee1eef..7f3d311b0 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarqube.yaml @@ -1,33 +1,36 @@ -name: SonarCloud +name: 'SonarCloud analysis' + on: workflow_dispatch: pull_request: types: [opened, synchronize, reopened] + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + jobs: - build: - name: Build and analyze + analyze: runs-on: ubuntu-latest env: SONAR_SCANNER_VERSION: 4.7.0.2747 SONAR_SERVER_URL: "https://sonarcloud.io" BUILD_WRAPPER_OUT_DIR: "$HOME/.sonar/build_wrapper_output" # Directory where build-wrapper output will be placed FBT_NO_SYNC: "true" - TARGETS: f7 - DEFAULT_TARGET: f7 steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - uses: actions/checkout@v2 + + - name: 'Checkout code' + uses: actions/checkout@v3 with: + submodules: 'recursive' # FBT_NO_SYNC is on, get submodules now fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - submodules: 'recursive' + ref: ${{ github.event.pull_request.head.sha }} + - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 + - name: Download and set up sonar-scanner env: SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip @@ -36,6 +39,7 @@ jobs: curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }} unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH + - name: Download and set up build-wrapper env: BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip @@ -43,10 +47,16 @@ jobs: curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }} unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/ echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH + - name: Run build-wrapper run: | - mkdir $HOME/.sonar/build_wrapper_output - build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build + mkdir ${{ env.BUILD_WRAPPER_OUT_DIR }} + set -e + for TARGET in ${TARGETS}; do + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build "./fbt TARGET_HW=$TARGET updater_package" + done + - name: Run sonar-scanner env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 025246faa..f816372fa 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,10 @@ Brewfile.lock.json # Visual Studio .vs/ +# Kate +.kateproject +.kateconfig + # legendary cmake's build CMakeLists.txt @@ -68,9 +72,8 @@ PVS-Studio.log # Automate files, etc automate.py deployments/ -fbt_options.py commitnotes.md -lib/STM32CubeWB +fbt_options.py # Asset packs assets/dolphin/custom/* diff --git a/.gitmodules b/.gitmodules index a97e0933a..de580c3c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,6 +28,9 @@ [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git +[submodule "applications/plugins/subbrute"] + path = applications/plugins/subbrute + url = https://github.com/derskythe/flipperzero-subbrute.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 a9ab9c9f6..49c63ad73 100644 --- a/.pvsconfig +++ b/.pvsconfig @@ -44,3 +44,6 @@ # Functions that always return the same error code //-V:picopass_device_decrypt:1048 + +# Examples +//V_EXCLUDE_PATH applications/examples/ \ No newline at end of file diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index 5c46d3979..f7a9f8269 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -11,9 +11,10 @@ "args": { "useSingleResult": true, "env": { - "PATH": "${workspaceFolder};${env:PATH}" + "PATH": "${workspaceFolder};${env:PATH}", + "FBT_QUIET": 1 }, - "command": "./fbt get_blackmagic", + "command": "fbt get_blackmagic", "description": "Get Blackmagic device", } } diff --git a/ReadMe.md b/ReadMe.md index 6ff014479..5481ad1a3 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,10 +1,11 @@
Xtreme Firmware for the Flipper Zero
+
-Base Graphics: Change the fallback assets used. Its either SFW (default) or NSFW
-Asset Pack: Allows you to easily customize your firmware, more on that below
-Anim Speed: Speed in which the animations play
-Cycle Anims: Duration of how long animations are played before switching to next
-Unlock Anims: Custom setting just for NSFW fallback animations. Figure it out ;)
-Battery Icon: Classic Firmware battery style toggle, just at a more convenient place
-XP Level: Changes your Flippers level
-SubGhz Extend: Allows you to extend the subghz range beyond what FZ devs planned
-SubGhz Bypass: Allows you to bypass the subghz region locks of the Flipper
+Graphics:Statusbar:Protocols:Dolphin:Misc:Anims & Icons folders, inside each Asset Pack.
+You can easily create your own pack, or find some user made ones in the discord channel. Check here for a tutorial on creating your own. Essentially, we got our own Anims & Icons folders, inside each Asset Pack.
SD/dolphin_custom
-After installing the packs to Flipper, hit the Arrow UP button on the main menu and go to Xtreme Settings. Here choose which pack you want and tweak the other settings how you prefer, then press back to reboot and enjoy your new assets & animations!
+After installing the packs to Flipper, hit the Arrow UP button on the main menu and go to Xtreme Settings. Here choose which pack you want and tweak the other settings how you prefer, then press back to reboot and enjoy your new assets for all apps (e.g. Subghz scanning asset) & animations!
+-----
+
+Bad Keyboard:
+
+
+BadUSB is a wonderful app, but it lacks bluetooth capabilities. Now some might argue that its useless as you will always need authentication from both sides, but what if I told you that we found a solution to this problem?
+
+Bad-KB allows you to toggle between USB and Bluetooth mode for your attacks. In Bluetooth mode it allows you to spoof the name & MAC of the device to whatever you want. Being a JBL speaker or a wireless razer keyboard is easily doable, allowing you to trick people so you can run your payloads without needing a cable at hand.
+
-----
Levels:
@@ -86,7 +90,7 @@ With this new system in place, it allows for some cool stuff like locking animat
Our example
-In our case, this is used with the NSFW animations. Dont worry, these are disabled by default because I know not everyone likes to see my / anime tits and thats fine. Anyways.. each level gives a brand new background animation, they also become more and more lewd over time. (Funfact for those reading.. thats why the repository has this warning. Github doesnt like my tits :c)
+In our example case, this is used with the NSFW animation pack you can select in the Xtreme app. Dont worry, this is not used by default because I know not everyone likes to see my / anime tits and thats fine. Anyways.. each level gives a brand new background animation, they also become more and more lewd over time.
| Level | Animations |
| ------------- | ------------- |
@@ -108,6 +112,7 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- Xtreme App
- Asset Packs
- More UI options
+- Bad-Keyboard App
- A new battery display-type
- Scrolling view for long file names in browser
- NSFW Animations tied to the level system. Read more above
@@ -117,7 +122,6 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- Multiple NFC protocols
- Multiple Sub-Ghz protocols | Merged from Unleashed, thanks @xMasterX
- Subghz and IR signal replication via gpio | Credits to @ankris812
-- Honda Keys (CVE-2022-27254) & Ford blockers
- New API Routes for Locale settings
```
@@ -129,12 +133,13 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
- Tons of apps
- File browser
- Massive compiler re-do
-- About 1k files to speed things up a lot
+- About 4k files to speed things up a lot
- Applications to now use the new Locale setting
```
```txt
[Fixed]
+- Keyboard issues on first char
- Passport crash on high level
- SFW / Dummy_mode getting you XP
- Leveling system
@@ -152,17 +157,21 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
Install:
-**NOTE: If you are coming from a different FW, it is recommended to delete / clear your "apps" folder on the SD card prior to updating. This folder houses all the .fap files, which do not update to the correct API versions by default if old ones are present (Thanks flipper devs). This does `NOT` remove any of your saved files!**
+**This is the recommended install procedure. Please follow these steps EXACTLY and CAREFULLY to ensure you install correctly.**
+**This process will NOT delete any saved files and simply ensures the install goes smoothly.**
-- Download the latest release (.zip) from [The releases tab](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/latest)
-- Extract the archive. This is now your new Firmware folder
-- Open [qFlipper](https://flipperzero.one/update), head to `SD/Update` and simply move the firmware folder there
-- On the Flipper, hit the `Arrow Down` button, this will get you to the file menu. Hit `Arrow Left` once, and then simply search for your updates folder
-- Inside that folder, select the Firmware you just moved onto it, and run the file thats simply called `Update`
+- Download the latest release (`.tgz`) from [the release page](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/latest)
+- Open [qFlipper](https://flipperzero.one/update) and connect your Flipper Zero
+- Go to the files tab, into SD Card, and DELETE THE `apps` AND `update` FOLDERS
+- Go back to the device tab
+- Click `Install from file...` and select the downloaded file (`.tgz`)
- Enjoy!
+**If you have issues or crashes with that process, you can try to use `Settings > Storage > Factory Reset` then retry the install.**
+**Doing that will NOT remove your saved files, it will only forget some settings and paired devices.**
+
----
Build it yourself:
diff --git a/SConstruct b/SConstruct
index 62e37dfdc..81ff67790 100644
--- a/SConstruct
+++ b/SConstruct
@@ -139,7 +139,7 @@ if GetOption("fullenv") or any(
basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
distenv.Default(basic_dist)
-dist_dir = distenv.GetProjetDirName()
+dist_dir = distenv.GetProjectDirName()
fap_dist = [
distenv.Install(
distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
diff --git a/applications/ReadMe.md b/applications/ReadMe.md
index efc9afd86..ddbe5d65b 100644
--- a/applications/ReadMe.md
+++ b/applications/ReadMe.md
@@ -86,4 +86,4 @@ Small applications providing configuration for basic firmware and its services.
Utility apps not visible in other menus.
- `storage_move_to_sd` - Data migration tool for internal storage
-- `updater` - updater service & application
+- `updater` - Update service & application
diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp
index 9d3708ebe..337437d0e 100644
--- a/applications/debug/accessor/accessor_app.cpp
+++ b/applications/debug/accessor/accessor_app.cpp
@@ -34,7 +34,7 @@ void AccessorApp::run(void) {
AccessorApp::AccessorApp()
: text_store{0} {
notification = static_cast(furi_record_open(RECORD_NOTIFICATION));
- onewire_host = onewire_host_alloc();
+ onewire_host = onewire_host_alloc(&ibutton_gpio);
furi_hal_power_enable_otg();
}
diff --git a/applications/debug/locale_test/locale_test.c b/applications/debug/locale_test/locale_test.c
index 46248cf3c..003df55dc 100644
--- a/applications/debug/locale_test/locale_test.c
+++ b/applications/debug/locale_test/locale_test.c
@@ -99,4 +99,4 @@ int32_t locale_test_app(void* p) {
view_dispatcher_run(app->view_dispatcher);
locale_test_free(app);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam
index a16c611bd..b9766254c 100644
--- a/applications/debug/uart_echo/application.fam
+++ b/applications/debug/uart_echo/application.fam
@@ -1,6 +1,6 @@
App(
appid="UART_Echo",
- name="UART Echo",
+ name="[GPIO] UART Echo",
apptype=FlipperAppType.PLUGIN,
entry_point="uart_echo_app",
cdefines=["APP_UART_ECHO"],
@@ -8,5 +8,5 @@ App(
stack_size=2 * 1024,
order=70,
fap_icon="uart_10px.png",
- fap_category="Misc",
+ fap_category="GPIO",
)
diff --git a/applications/debug/unit_tests/power/power_test.c b/applications/debug/unit_tests/power/power_test.c
index ce2c7aad7..a9b66b221 100644
--- a/applications/debug/unit_tests/power/power_test.c
+++ b/applications/debug/unit_tests/power/power_test.c
@@ -3,56 +3,63 @@
#include "../minunit.h"
static void power_test_deinit(void) {
- // Try to reset to default charging voltage
- furi_hal_power_set_battery_charging_voltage(4.208f);
+ // Try to reset to default charge voltage limit
+ furi_hal_power_set_battery_charge_voltage_limit(4.208f);
}
-MU_TEST(test_power_charge_voltage_exact) {
- // Power of 16mV charge voltages get applied exactly
+MU_TEST(test_power_charge_voltage_limit_exact) {
+ // Power of 16mV charge voltage limits get applied exactly
// (bq25896 charge controller works in 16mV increments)
//
// This test may need adapted if other charge controllers are used in the future.
for(uint16_t charge_mv = 3840; charge_mv <= 4208; charge_mv += 16) {
float charge_volt = (float)charge_mv / 1000.0f;
- furi_hal_power_set_battery_charging_voltage(charge_volt);
- mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(charge_volt);
+ mu_assert_double_eq(charge_volt, furi_hal_power_get_battery_charge_voltage_limit());
}
}
-MU_TEST(test_power_charge_voltage_floating_imprecision) {
+MU_TEST(test_power_charge_voltage_limit_floating_imprecision) {
// 4.016f should act as 4.016 V, even with floating point imprecision
- furi_hal_power_set_battery_charging_voltage(4.016f);
- mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(4.016f);
+ mu_assert_double_eq(4.016f, furi_hal_power_get_battery_charge_voltage_limit());
}
-MU_TEST(test_power_charge_voltage_inexact) {
- // Charge voltages that are not power of 16mV get truncated down
- furi_hal_power_set_battery_charging_voltage(3.841f);
- mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
+MU_TEST(test_power_charge_voltage_limit_inexact) {
+ // Charge voltage limits that are not power of 16mV get truncated down
+ furi_hal_power_set_battery_charge_voltage_limit(3.841f);
+ mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
- furi_hal_power_set_battery_charging_voltage(3.900f);
- mu_assert_double_eq(3.888, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(3.900f);
+ mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit());
- furi_hal_power_set_battery_charging_voltage(4.200f);
- mu_assert_double_eq(4.192, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(4.200f);
+ mu_assert_double_eq(4.192, furi_hal_power_get_battery_charge_voltage_limit());
}
-MU_TEST(test_power_charge_voltage_invalid_clamped) {
- // Out-of-range charge voltages get clamped to 3.840 V and 4.208 V
- furi_hal_power_set_battery_charging_voltage(3.808f);
- mu_assert_double_eq(3.840, furi_hal_power_get_battery_charging_voltage());
+MU_TEST(test_power_charge_voltage_limit_invalid_clamped) {
+ // Out-of-range charge voltage limits get clamped to 3.840 V and 4.208 V
+ furi_hal_power_set_battery_charge_voltage_limit(3.808f);
+ mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
+ furi_hal_power_set_battery_charge_voltage_limit(1.0f);
+ mu_assert_double_eq(3.840, furi_hal_power_get_battery_charge_voltage_limit());
// NOTE: Intentionally picking a small increment above 4.208 V to reduce the risk of an
// unhappy battery if this fails.
- furi_hal_power_set_battery_charging_voltage(4.240f);
- mu_assert_double_eq(4.208, furi_hal_power_get_battery_charging_voltage());
+ furi_hal_power_set_battery_charge_voltage_limit(4.240f);
+ mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit());
+ // Likewise, picking a number that the uint8_t wraparound in the driver would result in a
+ // VREG value under 23 if this test fails.
+ // E.g. (uint8_t)((8105-3840)/16) -> 10
+ furi_hal_power_set_battery_charge_voltage_limit(8.105f);
+ mu_assert_double_eq(4.208, furi_hal_power_get_battery_charge_voltage_limit());
}
MU_TEST_SUITE(test_power_suite) {
- MU_RUN_TEST(test_power_charge_voltage_exact);
- MU_RUN_TEST(test_power_charge_voltage_floating_imprecision);
- MU_RUN_TEST(test_power_charge_voltage_inexact);
- MU_RUN_TEST(test_power_charge_voltage_invalid_clamped);
+ MU_RUN_TEST(test_power_charge_voltage_limit_exact);
+ MU_RUN_TEST(test_power_charge_voltage_limit_floating_imprecision);
+ MU_RUN_TEST(test_power_charge_voltage_limit_inexact);
+ MU_RUN_TEST(test_power_charge_voltage_limit_invalid_clamped);
power_test_deinit();
}
diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c
index 5b52df2fa..329f3b741 100644
--- a/applications/debug/unit_tests/rpc/rpc_test.c
+++ b/applications/debug/unit_tests/rpc/rpc_test.c
@@ -89,7 +89,7 @@ static void test_rpc_setup(void) {
}
furi_check(rpc_session[0].session);
- rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1);
+ rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1);
rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary();
rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();
@@ -191,7 +191,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) {
size_t size = strlen(clean_dir) + strlen(name) + 1 + 1;
char* fullname = malloc(size);
snprintf(fullname, size, "%s/%s", clean_dir, name);
- if(fileinfo.flags & FSF_DIRECTORY) {
+ if(file_info_is_dir(&fileinfo)) {
clean_directory(fs_api, fullname);
}
FS_Error error = storage_common_remove(fs_api, fullname);
@@ -608,9 +608,8 @@ static void test_rpc_storage_list_create_expected_list(
}
if(path_contains_only_ascii(name)) {
- list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ?
- PB_Storage_File_FileType_DIR :
- PB_Storage_File_FileType_FILE;
+ list->file[i].type = file_info_is_dir(&fileinfo) ? PB_Storage_File_FileType_DIR :
+ PB_Storage_File_FileType_FILE;
list->file[i].size = fileinfo.size;
list->file[i].data = NULL;
/* memory free inside rpc_encode_and_send() -> pb_release() */
@@ -873,7 +872,7 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {
if(error == FSE_OK) {
response->which_content = PB_Main_storage_stat_response_tag;
response->content.storage_stat_response.has_file = true;
- response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ?
+ response->content.storage_stat_response.file.type = file_info_is_dir(&fileinfo) ?
PB_Storage_File_FileType_DIR :
PB_Storage_File_FileType_FILE;
response->content.storage_stat_response.file.size = fileinfo.size;
diff --git a/applications/debug/unit_tests/storage/dirwalk_test.c b/applications/debug/unit_tests/storage/dirwalk_test.c
index 97aaa3580..e0842a7a4 100644
--- a/applications/debug/unit_tests/storage/dirwalk_test.c
+++ b/applications/debug/unit_tests/storage/dirwalk_test.c
@@ -179,7 +179,7 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) {
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
- mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
+ mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
}
dir_walk_free(dir_walk);
@@ -204,7 +204,7 @@ MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) {
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
- mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
+ mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
}
dir_walk_free(dir_walk);
@@ -219,7 +219,7 @@ static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* filein
UNUSED(ctx);
// only files
- if(!(fileinfo->flags & FSF_DIRECTORY)) {
+ if(!file_info_is_dir(fileinfo)) {
// with ".test" in name
if(strstr(name, ".test") != NULL) {
return true;
@@ -243,7 +243,7 @@ MU_TEST_1(test_dirwalk_filter, Storage* storage) {
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
furi_string_right(path, strlen(EXT_PATH("dirwalk/")));
- mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
+ mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo)));
}
dir_walk_free(dir_walk);
diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c
index 115009701..582be7902 100644
--- a/applications/debug/unit_tests/storage/storage_test.c
+++ b/applications/debug/unit_tests/storage/storage_test.c
@@ -2,9 +2,40 @@
#include
#include
+// DO NOT USE THIS IN PRODUCTION CODE
+// This is a hack to access internal storage functions and definitions
+#include
+
+#define UNIT_TESTS_PATH(path) EXT_PATH("unit_tests/" path)
+
#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test")
#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX
+#define STORAGE_TEST_DIR UNIT_TESTS_PATH("test_dir")
+
+static bool storage_file_create(Storage* storage, const char* path, const char* data) {
+ File* file = storage_file_alloc(storage);
+ bool result = false;
+ do {
+ if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_NEW)) {
+ break;
+ }
+
+ if(storage_file_write(file, data, strlen(data)) != strlen(data)) {
+ break;
+ }
+
+ if(!storage_file_close(file)) {
+ break;
+ }
+
+ result = true;
+ } while(0);
+
+ storage_file_free(file);
+ return result;
+}
+
static void storage_file_open_lock_setup() {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
@@ -115,7 +146,7 @@ static int32_t storage_dir_locker(void* ctx) {
File* file = storage_file_alloc(storage);
furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
furi_semaphore_release(semaphore);
- furi_delay_ms(1000);
+ furi_delay_ms(100);
furi_check(storage_dir_close(file));
furi_record_close(RECORD_STORAGE);
@@ -152,9 +183,21 @@ MU_TEST(storage_dir_open_lock) {
mu_assert(result, "cannot open locked dir");
}
+MU_TEST(storage_dir_exists_test) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ mu_check(!storage_dir_exists(storage, STORAGE_TEST_DIR));
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, STORAGE_TEST_DIR));
+ mu_check(storage_dir_exists(storage, STORAGE_TEST_DIR));
+ mu_assert_int_eq(FSE_OK, storage_common_remove(storage, STORAGE_TEST_DIR));
+
+ furi_record_close(RECORD_STORAGE);
+}
+
MU_TEST_SUITE(storage_dir) {
MU_RUN_TEST(storage_dir_open_close);
MU_RUN_TEST(storage_dir_open_lock);
+ MU_RUN_TEST(storage_dir_exists_test);
}
static const char* const storage_copy_test_paths[] = {
@@ -303,9 +346,256 @@ MU_TEST_SUITE(storage_rename) {
furi_record_close(RECORD_STORAGE);
}
+#define APPSDATA_APP_PATH(path) APPS_DATA_PATH "/" path
+
+static const char* storage_test_apps[] = {
+ "-_twilight_-",
+ "-_rainbow_-",
+ "-_pinkie_-",
+ "-_apple_-",
+ "-_flutter_-",
+ "-_rare_-",
+};
+
+static size_t storage_test_apps_count = COUNT_OF(storage_test_apps);
+
+static int32_t storage_test_app(void* arg) {
+ UNUSED(arg);
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ storage_common_remove(storage, "/app/test");
+ int32_t ret = storage_file_create(storage, "/app/test", "test");
+ furi_record_close(RECORD_STORAGE);
+ return ret;
+}
+
+MU_TEST(test_storage_data_path_apps) {
+ for(size_t i = 0; i < storage_test_apps_count; i++) {
+ FuriThread* thread =
+ furi_thread_alloc_ex(storage_test_apps[i], 1024, storage_test_app, NULL);
+ furi_thread_set_appid(thread, storage_test_apps[i]);
+ furi_thread_start(thread);
+ furi_thread_join(thread);
+
+ mu_assert_int_eq(true, furi_thread_get_return_code(thread));
+
+ // Check if app data dir and file exists
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FuriString* expected = furi_string_alloc();
+ furi_string_printf(expected, APPSDATA_APP_PATH("%s"), storage_test_apps[i]);
+
+ mu_check(storage_dir_exists(storage, furi_string_get_cstr(expected)));
+ furi_string_cat(expected, "/test");
+ mu_check(storage_file_exists(storage, furi_string_get_cstr(expected)));
+
+ furi_string_printf(expected, APPSDATA_APP_PATH("%s"), storage_test_apps[i]);
+ storage_simply_remove_recursive(storage, furi_string_get_cstr(expected));
+
+ furi_record_close(RECORD_STORAGE);
+
+ furi_string_free(expected);
+ furi_thread_free(thread);
+ }
+}
+
+MU_TEST(test_storage_data_path) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ File* file = storage_file_alloc(storage);
+ mu_check(storage_dir_open(file, "/app"));
+ mu_check(storage_dir_close(file));
+ storage_file_free(file);
+
+ // check that appsdata folder exists
+ mu_check(storage_dir_exists(storage, APPS_DATA_PATH));
+
+ // check that cli folder exists
+ mu_check(storage_dir_exists(storage, APPSDATA_APP_PATH("cli")));
+
+ storage_simply_remove(storage, APPSDATA_APP_PATH("cli"));
+
+ furi_record_close(RECORD_STORAGE);
+}
+
+MU_TEST(test_storage_common_migrate) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ // Setup test folders
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from non existing
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ // Test migration from existing folder to non existing
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file1"), "test1"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file2.ext"), "test2"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file3.ext.ext"), "test3"));
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file1")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file2.ext")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext.ext")));
+ mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ // Test migration from existing folder to existing folder
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file1"), "test1"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file2.ext"), "test2"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old/file3.ext.ext"), "test3"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file1")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file2.ext")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext.ext")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file11")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file21.ext")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new/file3.ext1.ext")));
+ mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from empty folder to existing file
+ // Expected result: FSE_OK, folder removed, file untouched
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new"), "test1"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from empty folder to existing folder
+ // Expected result: FSE_OK, old folder removed, new folder untouched
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_new")));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from existing file to non existing, no extension
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+
+ // Test migration from existing file to non existing, with extension
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old.file"), "test1"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old.file"), UNIT_TESTS_PATH("migrate_new.file")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new.file")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old.file")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old.file"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new.file"));
+
+ // Test migration from existing file to existing file, no extension
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new"), "test2"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1"));
+
+ // Test migration from existing file to existing file, with extension
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old.file"), "test1"));
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_new.file"), "test2"));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old.file"), UNIT_TESTS_PATH("migrate_new.file")));
+
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new.file")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old.file")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1.file")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old.file"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new.file"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1.file"));
+
+ // Test migration from existing file to existing folder
+ mu_check(storage_file_create(storage, UNIT_TESTS_PATH("migrate_old"), "test1"));
+ mu_assert_int_eq(FSE_OK, storage_common_mkdir(storage, UNIT_TESTS_PATH("migrate_new")));
+
+ mu_assert_int_eq(
+ FSE_OK,
+ storage_common_migrate(
+ storage, UNIT_TESTS_PATH("migrate_old"), UNIT_TESTS_PATH("migrate_new")));
+
+ mu_check(storage_dir_exists(storage, UNIT_TESTS_PATH("migrate_new")));
+ mu_check(!storage_file_exists(storage, UNIT_TESTS_PATH("migrate_old")));
+ mu_check(storage_file_exists(storage, UNIT_TESTS_PATH("migrate_new1")));
+
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_old"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new"));
+ storage_simply_remove_recursive(storage, UNIT_TESTS_PATH("migrate_new1"));
+
+ furi_record_close(RECORD_STORAGE);
+}
+
+MU_TEST_SUITE(test_data_path) {
+ MU_RUN_TEST(test_storage_data_path);
+ MU_RUN_TEST(test_storage_data_path_apps);
+}
+
+MU_TEST_SUITE(test_storage_common) {
+ MU_RUN_TEST(test_storage_common_migrate);
+}
+
int run_minunit_test_storage() {
MU_RUN_SUITE(storage_file);
MU_RUN_SUITE(storage_dir);
MU_RUN_SUITE(storage_rename);
+ MU_RUN_SUITE(test_data_path);
+ MU_RUN_SUITE(test_storage_common);
return MU_EXIT_CODE;
}
diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c
index fcd3dbded..c7e9c96f1 100644
--- a/applications/debug/unit_tests/subghz/subghz_test.c
+++ b/applications/debug/unit_tests/subghz/subghz_test.c
@@ -12,8 +12,9 @@
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
+#define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
-#define TEST_RANDOM_COUNT_PARSE 273
+#define TEST_RANDOM_COUNT_PARSE 329
#define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler;
@@ -43,6 +44,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_alutech_at_4n_rainbow_table_file_name(
+ environment_handler, ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_protocol_registry(
environment_handler, (void*)&subghz_protocol_registry);
@@ -489,6 +492,14 @@ MU_TEST(subghz_decoder_linear_test) {
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
}
+MU_TEST(subghz_decoder_linear_delta3_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/linear_delta3_raw.sub"),
+ SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
+}
+
MU_TEST(subghz_decoder_megacode_test) {
mu_assert(
subghz_decoder_test(
@@ -590,12 +601,6 @@ MU_TEST(subghz_decoder_ansonic_test) {
"Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
}
-MU_TEST(subghz_decoder_pocsag_test) {
- mu_assert(
- subghz_decoder_test(EXT_PATH("unit_tests/subghz/pocsag.sub"), SUBGHZ_PROTOCOL_POCSAG_NAME),
- "Test decoder " SUBGHZ_PROTOCOL_POCSAG_NAME " error\r\n");
-}
-
MU_TEST(subghz_decoder_smc5326_test) {
mu_assert(
subghz_decoder_test(
@@ -610,6 +615,36 @@ MU_TEST(subghz_decoder_holtek_ht12x_test) {
"Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
}
+MU_TEST(subghz_decoder_dooya_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_alutech_at_4n_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/alutech_at_4n_raw.sub"),
+ SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_nice_one_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/nice_one_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n");
+}
+
+MU_TEST(subghz_decoder_kinggates_stylo4k_test) {
+ mu_assert(
+ subghz_decoder_test(
+ EXT_PATH("unit_tests/subghz/kinggates_stylo4k_raw.sub"),
+ SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME),
+ "Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n");
+}
+
//test encoders
MU_TEST(subghz_encoder_princeton_test) {
mu_assert(
@@ -653,6 +688,12 @@ MU_TEST(subghz_encoder_linear_test) {
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
}
+MU_TEST(subghz_encoder_linear_delta3_test) {
+ mu_assert(
+ subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear_delta3.sub")),
+ "Test encoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n");
+}
+
MU_TEST(subghz_encoder_megacode_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
@@ -749,6 +790,12 @@ MU_TEST(subghz_encoder_holtek_ht12x_test) {
"Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n");
}
+MU_TEST(subghz_encoder_dooya_test) {
+ mu_assert(
+ subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")),
+ "Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n");
+}
+
MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
}
@@ -778,6 +825,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_somfy_telis_test);
MU_RUN_TEST(subghz_decoder_star_line_test);
MU_RUN_TEST(subghz_decoder_linear_test);
+ MU_RUN_TEST(subghz_decoder_linear_delta3_test);
MU_RUN_TEST(subghz_decoder_megacode_test);
MU_RUN_TEST(subghz_decoder_secplus_v1_test);
MU_RUN_TEST(subghz_decoder_secplus_v2_test);
@@ -792,9 +840,12 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_decoder_ansonic_test);
- MU_RUN_TEST(subghz_decoder_pocsag_test);
MU_RUN_TEST(subghz_decoder_smc5326_test);
MU_RUN_TEST(subghz_decoder_holtek_ht12x_test);
+ MU_RUN_TEST(subghz_decoder_dooya_test);
+ MU_RUN_TEST(subghz_decoder_alutech_at_4n_test);
+ MU_RUN_TEST(subghz_decoder_nice_one_test);
+ MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test);
MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test);
@@ -803,6 +854,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_nice_flo_test);
MU_RUN_TEST(subghz_encoder_keelog_test);
MU_RUN_TEST(subghz_encoder_linear_test);
+ MU_RUN_TEST(subghz_encoder_linear_delta3_test);
MU_RUN_TEST(subghz_encoder_megacode_test);
MU_RUN_TEST(subghz_encoder_holtek_test);
MU_RUN_TEST(subghz_encoder_secplus_v1_test);
@@ -819,6 +871,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_ansonic_test);
MU_RUN_TEST(subghz_encoder_smc5326_test);
MU_RUN_TEST(subghz_encoder_holtek_ht12x_test);
+ MU_RUN_TEST(subghz_encoder_dooya_test);
MU_RUN_TEST(subghz_random_test);
subghz_test_deinit();
diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c
index 2bb9c423f..ac71ca397 100644
--- a/applications/debug/unit_tests/test_index.c
+++ b/applications/debug/unit_tests/test_index.c
@@ -70,7 +70,7 @@ void minunit_print_progress() {
}
void minunit_print_fail(const char* str) {
- printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str);
+ printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str);
}
void unit_tests_cli(Cli* cli, FuriString* args, void* context) {
diff --git a/applications/examples/example_apps_data/README.md b/applications/examples/example_apps_data/README.md
new file mode 100644
index 000000000..fd8666077
--- /dev/null
+++ b/applications/examples/example_apps_data/README.md
@@ -0,0 +1,18 @@
+# Apps Data folder Example
+
+This example demonstrates how to utilize the Apps Data folder to store data that is not part of the app itself, such as user data, configuration files, and so forth.
+
+## What is the Apps Data Folder?
+
+The **Apps Data** folder is a folder used to store data for external apps that are not part of the main firmware.
+
+The path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file.
+The Apps Data folder is located only on the external storage, the SD card.
+
+For example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `/app` alias instead.
+
+## How to get the path to the Apps Data folder?
+
+You can use `/app` alias to get the path to the current application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `/app/config.txt`. But this way is not recommended, because even the `/app` alias can change in the future.
+
+We recommend to use the `APP_DATA_PATH` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `APP_DATA_PATH("config.txt")`.
\ No newline at end of file
diff --git a/applications/examples/example_apps_data/application.fam b/applications/examples/example_apps_data/application.fam
new file mode 100644
index 000000000..f44dca97d
--- /dev/null
+++ b/applications/examples/example_apps_data/application.fam
@@ -0,0 +1,9 @@
+App(
+ appid="example_apps_data",
+ name="Example: Apps Data",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="example_apps_data_main",
+ requires=["gui"],
+ stack_size=1 * 1024,
+ fap_category="Examples",
+)
diff --git a/applications/examples/example_apps_data/example_apps_data.c b/applications/examples/example_apps_data/example_apps_data.c
new file mode 100644
index 000000000..d6104c137
--- /dev/null
+++ b/applications/examples/example_apps_data/example_apps_data.c
@@ -0,0 +1,40 @@
+#include
+#include
+
+// Define log tag
+#define TAG "example_apps_data"
+
+// Application entry point
+int32_t example_apps_data_main(void* p) {
+ // Mark argument as unused
+ UNUSED(p);
+
+ // Open storage
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ // Allocate file
+ File* file = storage_file_alloc(storage);
+
+ // Get the path to the current application data folder
+ // That is: /ext/apps_data/
+ // And it will create folders in the path if they don't exist
+ // In this example it will create /ext/apps_data/example_apps_data
+ // And file will be /ext/apps_data/example_apps_data/test.txt
+
+ // Open file, write data and close it
+ if(!storage_file_open(file, APP_DATA_PATH("test.txt"), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+ FURI_LOG_E(TAG, "Failed to open file");
+ }
+ if(!storage_file_write(file, "Hello World!", strlen("Hello World!"))) {
+ FURI_LOG_E(TAG, "Failed to write to file");
+ }
+ storage_file_close(file);
+
+ // Deallocate file
+ storage_file_free(file);
+
+ // Close storage
+ furi_record_close(RECORD_STORAGE);
+
+ return 0;
+}
diff --git a/applications/main/application.fam b/applications/main/application.fam
index eefb801b3..459a878ed 100644
--- a/applications/main/application.fam
+++ b/applications/main/application.fam
@@ -3,36 +3,16 @@ App(
name="Basic applications for main menu",
apptype=FlipperAppType.METAPACKAGE,
provides=[
+ "fap_loader",
+ "subghz",
+ "subghz_remote",
+ "lfrfid",
+ "nfc",
+ "infrared",
"gpio",
"ibutton",
- "infrared",
- "lfrfid",
- "nfc",
- "subghz",
"bad_kb",
"u2f",
- "fap_loader",
- "sub_playlist",
- "archive",
- "clock",
- "unirfremix",
- ],
-)
-
-App(
- appid="main_apps_default",
- name="Basic applications for main menu",
- apptype=FlipperAppType.METAPACKAGE,
- provides=[
- "gpio",
- # "ibutton",
- "infrared",
- "lfrfid",
- "nfc",
- "subghz",
- # "bad_kb",
- # "u2f",
- "fap_loader",
"archive",
],
)
diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c
index 78b010a78..f93d9694d 100644
--- a/applications/main/archive/helpers/archive_browser.c
+++ b/applications/main/archive/helpers/archive_browser.c
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
static void
archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) {
@@ -450,7 +451,7 @@ static bool archive_is_dir_exists(FuriString* path) {
FileInfo file_info;
Storage* storage = furi_record_open(RECORD_STORAGE);
if(storage_common_stat(storage, furi_string_get_cstr(path), &file_info) == FSE_OK) {
- if(file_info.flags & FSF_DIRECTORY) {
+ if(file_info_is_dir(&file_info)) {
state = true;
}
}
@@ -464,14 +465,17 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
browser->last_tab_switch_dir = key;
- for(int i = 0; i < 2; i++) {
+ if(key == InputKeyLeft) {
+ tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
+ } else {
+ tab = (tab + 1) % ArchiveTabTotal;
+ }
+ if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
if(key == InputKeyLeft) {
tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
} else {
tab = (tab + 1) % ArchiveTabTotal;
}
- if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue;
- break;
}
browser->is_root = true;
@@ -528,12 +532,16 @@ void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) {
browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false);
furi_string_set(browser->path, path);
+
file_browser_worker_folder_enter(browser->worker, path, idx_temp);
}
void archive_leave_dir(ArchiveBrowserView* browser) {
furi_assert(browser);
+ size_t dirname_start = furi_string_search_rchar(browser->path, '/');
+ furi_string_left(browser->path, dirname_start);
+
file_browser_worker_folder_exit(browser->worker);
}
diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c
index 8bbcb5213..f395ee5a1 100644
--- a/applications/main/archive/helpers/archive_favorites.c
+++ b/applications/main/archive/helpers/archive_favorites.c
@@ -160,7 +160,7 @@ bool archive_favorites_read(void* context) {
if(storage_file_exists(storage, furi_string_get_cstr(buffer))) {
storage_common_stat(storage, furi_string_get_cstr(buffer), &file_info);
archive_add_file_item(
- browser, (file_info.flags & FSF_DIRECTORY), furi_string_get_cstr(buffer));
+ browser, file_info_is_dir(&file_info), furi_string_get_cstr(buffer));
file_count++;
} else {
need_refresh = true;
diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c
index 83eb2a845..5f5d13fc3 100644
--- a/applications/main/archive/helpers/archive_files.c
+++ b/applications/main/archive/helpers/archive_files.c
@@ -96,7 +96,7 @@ void archive_delete_file(void* context, const char* format, ...) {
bool res = false;
- if(fileinfo.flags & FSF_DIRECTORY) {
+ if(file_info_is_dir(&fileinfo)) {
res = storage_simply_remove_recursive(fs_api, furi_string_get_cstr(filename));
} else {
res = (storage_common_remove(fs_api, furi_string_get_cstr(filename)) == FSE_OK);
diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h
index 863a8e7da..431c701b3 100644
--- a/applications/main/archive/helpers/archive_files.h
+++ b/applications/main/archive/helpers/archive_files.h
@@ -84,7 +84,7 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) {
}
static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) {
- if(!XTREME_SETTINGS()->sort_ignore_dirs) {
+ if(XTREME_SETTINGS()->sort_dirs_first) {
if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) {
return -1;
}
diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c
index dfce5acbf..9f2a3145a 100644
--- a/applications/main/bad_kb/bad_kb_app.c
+++ b/applications/main/bad_kb/bad_kb_app.c
@@ -37,9 +37,26 @@ static void bad_kb_load_settings(BadKbApp* app) {
!storage_file_eof(settings_file) && !isspace(chr)) {
furi_string_push_back(app->keyboard_layout, chr);
}
+ } else {
+ furi_string_reset(app->keyboard_layout);
}
storage_file_close(settings_file);
storage_file_free(settings_file);
+
+ if(!furi_string_empty(app->keyboard_layout)) {
+ Storage* fs_api = furi_record_open(RECORD_STORAGE);
+ FileInfo layout_file_info;
+ FS_Error file_check_err = storage_common_stat(
+ fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
+ furi_record_close(RECORD_STORAGE);
+ if(file_check_err != FSE_OK) {
+ furi_string_reset(app->keyboard_layout);
+ return;
+ }
+ if(layout_file_info.size != 256) {
+ furi_string_reset(app->keyboard_layout);
+ }
+ }
}
static void bad_kb_save_settings(BadKbApp* app) {
@@ -55,17 +72,6 @@ static void bad_kb_save_settings(BadKbApp* app) {
storage_file_free(settings_file);
}
-void bad_kb_set_name(BadKbApp* app, const char* fmt, ...) {
- furi_assert(app);
-
- va_list args;
- va_start(args, fmt);
-
- vsnprintf(app->name, BAD_KB_ADV_NAME_MAX_LEN, fmt, args);
-
- va_end(args);
-}
-
BadKbApp* bad_kb_app_alloc(char* arg) {
BadKbApp* app = malloc(sizeof(BadKbApp));
@@ -77,6 +83,18 @@ BadKbApp* bad_kb_app_alloc(char* arg) {
furi_string_set(app->file_path, arg);
}
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ // Remove old pre-included files to avoid duplicates on migrate
+ storage_simply_remove(storage, EXT_PATH("badusb/layouts"));
+ storage_simply_remove(storage, EXT_PATH("badusb/.badusb.settings"));
+ storage_simply_remove(storage, EXT_PATH("badusb/Kiosk-Evasion-Bruteforce.txt"));
+ storage_simply_remove(storage, EXT_PATH("badusb/Wifi-Stealer_ORG.txt"));
+ storage_simply_remove(storage, EXT_PATH("badusb/demo_macos.txt"));
+ storage_simply_remove(storage, EXT_PATH("badusb/demo_windows.txt"));
+ storage_common_migrate(storage, EXT_PATH("badusb"), BAD_KB_APP_BASE_FOLDER);
+ storage_simply_mkdir(storage, BAD_KB_APP_BASE_FOLDER);
+ furi_record_close(RECORD_STORAGE);
+
bad_kb_load_settings(app);
app->gui = furi_record_open(RECORD_GUI);
@@ -96,14 +114,19 @@ BadKbApp* bad_kb_app_alloc(char* arg) {
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, bad_kb_app_back_event_callback);
+ app->connection_init = false;
+
Bt* bt = furi_record_open(RECORD_BT);
app->bt = bt;
+ app->bt->suppress_pin_screen = true;
app->is_bt = XTREME_SETTINGS()->bad_bt;
- const char* adv_name = bt_get_profile_adv_name(bt);
+ app->bt_remember = XTREME_SETTINGS()->bad_bt_remember;
+ const char* adv_name = furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard);
memcpy(app->name, adv_name, BAD_KB_ADV_NAME_MAX_LEN);
memcpy(app->bt_old_config.name, adv_name, BAD_KB_ADV_NAME_MAX_LEN);
- const uint8_t* mac_addr = bt_get_profile_mac_address(bt);
+ // need to be done before bt init (where mac address get modified if bounding is activated)
+ const uint8_t* mac_addr = furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard);
memcpy(app->mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN);
memcpy(app->bt_old_config.mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN);
@@ -139,8 +162,12 @@ BadKbApp* bad_kb_app_alloc(char* arg) {
if(furi_hal_usb_is_locked()) {
app->error = BadKbAppErrorCloseRpc;
+ app->conn_init_thread = NULL;
scene_manager_next_scene(app->scene_manager, BadKbSceneError);
} else {
+ app->conn_init_thread = furi_thread_alloc_ex(
+ "BadKbConnInit", 512, (FuriThreadCallback)bad_kb_connection_init, app);
+ furi_thread_start(app->conn_init_thread);
if(!furi_string_empty(app->file_path)) {
app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL);
bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout);
@@ -188,16 +215,16 @@ void bad_kb_app_free(BadKbApp* app) {
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
- // restores bt config
- // BtProfile have already been switched to the previous one
- // so we directly modify the right profile
- bad_kb_connection_deinit(app->bt);
+ // Restore bt config
+ // BtProfile has already been switched to the previous one
+ // So we directly modify the right profile
if(strcmp(app->bt_old_config.name, app->name) != 0) {
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name);
}
if(memcmp(app->bt_old_config.mac, app->mac, BAD_KB_MAC_ADDRESS_LEN) != 0) {
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->bt_old_config.mac);
}
+ app->bt->suppress_pin_screen = false;
// Close records
furi_record_close(RECORD_GUI);
@@ -210,6 +237,12 @@ void bad_kb_app_free(BadKbApp* app) {
furi_string_free(app->file_path);
furi_string_free(app->keyboard_layout);
+ if(app->conn_init_thread) {
+ furi_thread_join(app->conn_init_thread);
+ furi_thread_free(app->conn_init_thread);
+ }
+ bad_kb_connection_deinit(app);
+
free(app);
}
diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h
index e75a94651..af64be253 100644
--- a/applications/main/bad_kb/bad_kb_app.h
+++ b/applications/main/bad_kb/bad_kb_app.h
@@ -6,8 +6,6 @@ extern "C" {
typedef struct BadKbApp BadKbApp;
-void bad_kb_set_name(BadKbApp* app, const char* fmt, ...);
-
#ifdef __cplusplus
}
#endif
diff --git a/applications/main/bad_kb/bad_kb_app_i.h b/applications/main/bad_kb/bad_kb_app_i.h
index 913830e72..a55875676 100644
--- a/applications/main/bad_kb/bad_kb_app_i.h
+++ b/applications/main/bad_kb/bad_kb_app_i.h
@@ -16,14 +16,19 @@
#include
#include "views/bad_kb_view.h"
-#define BAD_KB_APP_BASE_FOLDER ANY_PATH("badkb")
-#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/layouts"
+#define BAD_KB_APP_BASE_FOLDER EXT_PATH("badkb")
+#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/assets/layouts"
+#define BAD_KB_APP_PATH_BOUND_KEYS_FOLDER EXT_PATH("badkb/.bt_keys")
+#define BAD_KB_APP_PATH_BOUND_KEYS_FILE BAD_KB_APP_PATH_BOUND_KEYS_FOLDER "/.devices.keys"
#define BAD_KB_APP_SCRIPT_EXTENSION ".txt"
#define BAD_KB_APP_LAYOUT_EXTENSION ".kl"
#define BAD_KB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro
#define BAD_KB_ADV_NAME_MAX_LEN 18
+// this is the MAC address used when we do not forget paired device (BOUND STATE)
+#define BAD_KB_BOUND_MAC_ADDRESS {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4};
+
typedef enum {
BadKbAppErrorNoFiles,
BadKbAppErrorCloseRpc,
@@ -36,12 +41,9 @@ typedef enum BadKbCustomEvent {
} BadKbCustomEvent;
typedef struct {
+ //uint8_t bounded_mac[BAD_KB_MAC_ADDRESS_LEN];
uint8_t mac[BAD_KB_MAC_ADDRESS_LEN];
char name[BAD_KB_ADV_NAME_MAX_LEN + 1];
-
- // number of bt keys before starting the app (all keys added in
- // the bt keys file then will be removed)
- uint16_t n_keys;
} BadKbBtConfig;
struct BadKbApp {
@@ -59,6 +61,7 @@ struct BadKbApp {
ByteInput* byte_input;
uint8_t mac[BAD_KB_MAC_ADDRESS_LEN];
char name[BAD_KB_ADV_NAME_MAX_LEN + 1];
+ bool bt_remember; // weither we remember paired devices or not
BadKbBtConfig bt_old_config;
BadKbAppError error;
@@ -68,6 +71,12 @@ struct BadKbApp {
BadKbScript* bad_kb_script;
bool is_bt;
+
+ bool connection_init;
+ FuriHalUsbInterface* usb_prev_mode;
+ GapPairing bt_prev_mode;
+
+ FuriThread* conn_init_thread;
};
typedef enum {
diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c
index e2fd464ff..b91f36b9f 100644
--- a/applications/main/bad_kb/bad_kb_script.c
+++ b/applications/main/bad_kb/bad_kb_script.c
@@ -11,6 +11,8 @@
#include
+#include "bad_kb_app_i.h"
+
#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys")
#define TAG "BadKB"
@@ -41,12 +43,6 @@ typedef enum {
LevelRssiError = 0xFF,
} LevelRssiRange;
-typedef enum {
- BadKbConnectionModeNone,
- BadKbConnectionModeUsb,
- BadKbConnectionModeBt,
-} BadKbConnectionMode;
-
/**
* Delays for waiting between HID key press and key release
*/
@@ -65,6 +61,7 @@ struct BadKbScript {
FuriString* keyboard_layout;
uint32_t defdelay;
uint16_t layout[128];
+ uint32_t stringdelay;
FuriThread* thread;
uint8_t file_buf[FILE_BUFFER_LEN + 1];
uint8_t buf_start;
@@ -148,6 +145,8 @@ static const char ducky_cmd_delay[] = {"DELAY "};
static const char ducky_cmd_string[] = {"STRING "};
static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "};
static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "};
+static const char ducky_cmd_stringdelay_1[] = {"STRINGDELAY "};
+static const char ducky_cmd_stringdelay_2[] = {"STRING_DELAY "};
static const char ducky_cmd_repeat[] = {"REPEAT "};
static const char ducky_cmd_sysrq[] = {"SYSRQ "};
@@ -170,27 +169,22 @@ static const uint8_t numpad_keys[10] = {
HID_KEYPAD_9,
};
-BadKbConnectionMode connection_mode = BadKbConnectionModeNone;
-FuriHalUsbInterface* usb_mode_prev = NULL;
-GapPairing bt_mode_prev = GapPairingNone;
-bool bt_connected = false;
-bool usb_connected = false;
uint8_t bt_timeout = 0;
static LevelRssiRange bt_remote_rssi_range(Bt* bt) {
- BtRssi rssi_data = {0};
+ uint8_t rssi;
- if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError;
+ if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError;
- if(rssi_data.rssi <= 39)
+ if(rssi <= 39)
return LevelRssi39_0;
- else if(rssi_data.rssi <= 59)
+ else if(rssi <= 59)
return LevelRssi59_40;
- else if(rssi_data.rssi <= 79)
+ else if(rssi <= 79)
return LevelRssi79_60;
- else if(rssi_data.rssi <= 99)
+ else if(rssi <= 99)
return LevelRssi99_80;
- else if(rssi_data.rssi <= 122)
+ else if(rssi <= 122)
return LevelRssi122_100;
return LevelRssiError;
@@ -304,6 +298,7 @@ static bool ducky_altstring(BadKbScript* bad_kb, const char* param) {
static bool ducky_string(BadKbScript* bad_kb, const char* param) {
uint32_t i = 0;
+
while(param[i] != '\0') {
uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]);
if(keycode != HID_KEYBOARD_NONE) {
@@ -315,9 +310,13 @@ static bool ducky_string(BadKbScript* bad_kb, const char* param) {
furi_hal_hid_kb_press(keycode);
furi_hal_hid_kb_release(keycode);
}
+ if(bad_kb->stringdelay > 0) {
+ furi_delay_ms(bad_kb->stringdelay);
+ }
}
i++;
}
+ bad_kb->stringdelay = 0;
return true;
}
@@ -379,6 +378,19 @@ static int32_t
snprintf(error, error_len, "Invalid number %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
+ } else if(
+ (strncmp(line_tmp, ducky_cmd_stringdelay_1, strlen(ducky_cmd_stringdelay_1)) == 0) ||
+ (strncmp(line_tmp, ducky_cmd_stringdelay_2, strlen(ducky_cmd_stringdelay_2)) == 0)) {
+ //STRINGDELAY, finally it's here
+ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
+ state = ducky_get_number(line_tmp, &bad_kb->stringdelay);
+ if((state) && (bad_kb->stringdelay > 0)) {
+ return state;
+ }
+ if(error != NULL) {
+ snprintf(error, error_len, "Invalid number %s", line_tmp);
+ }
+ return SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
// STRING
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
@@ -607,10 +619,8 @@ static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) {
if(r != LevelRssiError) {
bt_timeout = bt_hid_delays[r];
}
- bt_connected = true;
furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect);
} else {
- bt_connected = false;
furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect);
}
}
@@ -620,83 +630,116 @@ static void bad_kb_usb_hid_state_callback(bool state, void* context) {
BadKbScript* bad_kb = context;
if(state == true) {
- usb_connected = true;
furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect);
} else {
- usb_connected = false;
furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect);
}
}
-void bad_kb_bt_init(Bt* bt) {
- bt_timeout = bt_hid_delays[LevelRssi39_0];
- bt_disconnect(bt);
- furi_delay_ms(200);
- bt_keys_storage_set_storage_path(bt, HID_BT_KEYS_STORAGE_PATH);
- furi_assert(bt_set_profile(bt, BtProfileHidKeyboard));
- bt_mode_prev = bt_get_profile_pairing_method(bt);
- bt_set_profile_pairing_method(bt, GapPairingNone);
- furi_hal_bt_start_advertising();
- // disable peer key adding to bt SRAM storage
- bt_disable_peer_key_update(bt);
-
- connection_mode = BadKbConnectionModeBt;
+void bad_kb_reload_worker(BadKbApp* app) {
+ bad_kb_script_close(app->bad_kb_script);
+ app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL);
+ bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout);
}
-void bad_kb_bt_deinit(Bt* bt) {
- // release all keys
- // bt_hid_hold_while_keyboard_buffer_full(6, 3000);
+void bad_kb_config_switch_mode(BadKbApp* app) {
+ scene_manager_previous_scene(app->scene_manager);
+ if(app->is_bt) {
+ furi_hal_bt_start_advertising();
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt);
+ } else {
+ furi_hal_bt_stop_advertising();
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb);
+ }
+ bad_kb_reload_worker(app);
+}
- // stop ble
- bt_disconnect(bt);
+void bad_kb_config_switch_remember_mode(BadKbApp* app) {
+ if(app->bt_remember) {
+ // set bouding mac
+ uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS;
+ furi_hal_bt_set_profile_pairing_method(
+ FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
+ bt_set_profile_mac_address(app->bt, mac); // this also restart bt
+ // enable keys storage
+ bt_enable_peer_key_update(app->bt);
+ } else {
+ // set back user defined mac address
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
+ bt_set_profile_mac_address(app->bt, app->mac);
+ // disable key storage
+ bt_disable_peer_key_update(app->bt);
+ }
+ bad_kb_reload_worker(app);
+}
- // Wait 2nd core to update nvm storage
- furi_delay_ms(200);
+int32_t bad_kb_connection_init(BadKbApp* app) {
+ app->usb_prev_mode = furi_hal_usb_get_config();
+ furi_hal_usb_set_config(NULL, NULL);
- bt_keys_storage_set_default_path(bt);
+ bt_timeout = bt_hid_delays[LevelRssi39_0];
+ bt_disconnect(app->bt);
+ // furi_delay_ms(200);
+ bt_keys_storage_set_storage_path(app->bt, BAD_KB_APP_PATH_BOUND_KEYS_FILE);
+ app->bt_prev_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard);
+ if(app->bt_remember) {
+ uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS;
+ furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac);
+ // using GapPairingNone breaks bounding between devices
+ furi_hal_bt_set_profile_pairing_method(
+ FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
+ } else {
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
+ }
- bt_set_profile_pairing_method(bt, bt_mode_prev);
+ bt_set_profile(app->bt, BtProfileHidKeyboard);
+ if(app->is_bt) {
+ furi_hal_bt_start_advertising();
+ if(app->bt_remember) {
+ bt_enable_peer_key_update(app->bt);
+ } else {
+ bt_disable_peer_key_update(app->bt); // disable peer key adding to bt SRAM storage
+ }
+ } else {
+ furi_hal_bt_stop_advertising();
+ }
+ app->connection_init = true;
+
+ return 0;
+}
+
+void bad_kb_connection_deinit(BadKbApp* app) {
+ if(!app->connection_init) return;
+
+ furi_hal_usb_set_config(app->usb_prev_mode, NULL);
+
+ // bt_hid_hold_while_keyboard_buffer_full(6, 3000); // release all keys
+ bt_disconnect(app->bt); // stop ble
+ // furi_delay_ms(200); // Wait 2nd core to update nvm storage
+ bt_keys_storage_set_default_path(app->bt);
+ if(app->bt_remember) {
+ // hal primitives doesn't restarts ble, that's what we want cuz we are shutting down
+ furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->mac);
+ }
+ bt_enable_peer_key_update(app->bt); // starts saving peer keys (bounded devices)
// fails if ble radio stack isn't ready when switching profile
// if it happens, maybe we should increase the delay after bt_disconnect
- bt_set_profile(bt, BtProfileSerial);
-
- // starts saving peer keys (bounded devices)
- bt_enable_peer_key_update(bt);
-
- connection_mode = BadKbConnectionModeNone;
+ bt_set_profile(app->bt, BtProfileSerial);
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->bt_prev_mode);
}
-void bad_kb_usb_init() {
- usb_mode_prev = furi_hal_usb_get_config();
-
- connection_mode = BadKbConnectionModeUsb;
-}
-
-void bad_kb_usb_deinit() {
- furi_hal_usb_set_config(usb_mode_prev, NULL);
-
- connection_mode = BadKbConnectionModeNone;
-}
-
-void bad_kb_connection_init(Bt* bt) {
- if(connection_mode != BadKbConnectionModeNone) return;
-
- if(bt) {
- bad_kb_bt_init(bt);
+static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) {
+ uint32_t flags = furi_thread_flags_get();
+ furi_check((flags & FuriFlagError) == 0);
+ if(flags == 0) {
+ flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout);
+ furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout));
} else {
- bad_kb_usb_init();
- }
-}
-
-void bad_kb_connection_deinit(Bt* bt) {
- if(connection_mode == BadKbConnectionModeNone) return;
-
- if(connection_mode == BadKbConnectionModeBt) {
- bad_kb_bt_deinit(bt);
- } else {
- bad_kb_usb_deinit();
+ uint32_t state = furi_thread_flags_clear(flags);
+ furi_check((state & FuriFlagError) == 0);
}
+ return flags;
}
static int32_t bad_kb_worker(void* context) {
@@ -705,8 +748,6 @@ static int32_t bad_kb_worker(void* context) {
BadKbWorkerState worker_state = BadKbStateInit;
int32_t delay_val = 0;
- bad_kb_connection_init(bad_kb->bt);
-
if(bad_kb->bt) {
bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb);
} else {
@@ -727,7 +768,11 @@ static int32_t bad_kb_worker(void* context) {
FSOM_OPEN_EXISTING)) {
if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) {
if(bad_kb->bt) {
- worker_state = BadKbStateNotConnected; // Ready to run
+ if(furi_hal_bt_is_connected()) {
+ worker_state = BadKbStateIdle; // Ready to run
+ } else {
+ worker_state = BadKbStateNotConnected; // Not connected
+ }
} else {
if(furi_hal_hid_is_connected()) {
worker_state = BadKbStateIdle; // Ready to run
@@ -745,30 +790,20 @@ static int32_t bad_kb_worker(void* context) {
bad_kb->st.state = worker_state;
} else if(worker_state == BadKbStateNotConnected) { // State: Not connected
- if((bad_kb->bt && bt_connected) || (!bad_kb->bt && usb_connected)) {
+ uint32_t flags = bad_kb_flags_get(
+ WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
+ if(flags & WorkerEvtEnd) {
+ break;
+ } else if(flags & WorkerEvtConnect) {
worker_state = BadKbStateIdle; // Ready to run
- } else {
- 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) {
- worker_state = BadKbStateIdle; // Ready to run
- } else if(flags & WorkerEvtToggle) {
- worker_state = BadKbStateWillRun; // Will run when connected
- }
+ } else if(flags & WorkerEvtToggle) {
+ worker_state = BadKbStateWillRun; // Will run when connected
}
bad_kb->st.state = worker_state;
} else if(worker_state == BadKbStateIdle) { // State: ready to start
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect,
- FuriFlagWaitAny,
- FuriWaitForever);
- furi_check((flags & FuriFlagError) == 0);
+ uint32_t flags = bad_kb_flags_get(
+ WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtToggle) { // Start executing script
@@ -777,6 +812,7 @@ static int32_t bad_kb_worker(void* context) {
bad_kb->buf_len = 0;
bad_kb->st.line_cur = 0;
bad_kb->defdelay = 0;
+ bad_kb->stringdelay = 0;
bad_kb->repeat_cnt = 0;
bad_kb->file_end = false;
storage_file_seek(script_file, 0, true);
@@ -788,11 +824,8 @@ static int32_t bad_kb_worker(void* context) {
bad_kb->st.state = worker_state;
} else if(worker_state == BadKbStateWillRun) { // State: start on connection
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle,
- FuriFlagWaitAny,
- FuriWaitForever);
- furi_check((flags & FuriFlagError) == 0);
+ uint32_t flags = bad_kb_flags_get(
+ WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) { // Start executing script
@@ -801,16 +834,26 @@ static int32_t bad_kb_worker(void* context) {
bad_kb->buf_len = 0;
bad_kb->st.line_cur = 0;
bad_kb->defdelay = 0;
+ bad_kb->stringdelay = 0;
bad_kb->repeat_cnt = 0;
bad_kb->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);
+ flags = furi_thread_flags_wait(
+ WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle,
+ FuriFlagWaitAny | FuriFlagNoClear,
+ 1500);
+ if(flags == (unsigned)FuriFlagErrorTimeout) {
+ // If nothing happened - start script execution
+ worker_state = BadKbStateRunning;
+ } else if(flags & WorkerEvtToggle) {
+ worker_state = BadKbStateIdle;
+ furi_thread_flags_clear(WorkerEvtToggle);
+ }
if(bad_kb->bt) {
update_bt_timeout(bad_kb->bt);
}
bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout);
- worker_state = BadKbStateRunning;
} else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
worker_state = BadKbStateNotConnected;
}
@@ -875,9 +918,8 @@ static int32_t bad_kb_worker(void* context) {
} else if(
(worker_state == BadKbStateFileError) ||
(worker_state == BadKbStateScriptError)) { // State: error
- uint32_t flags = furi_thread_flags_wait(
- WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command
- furi_check((flags & FuriFlagError) == 0);
+ uint32_t flags =
+ bad_kb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command
if(flags & WorkerEvtEnd) {
break;
}
@@ -921,6 +963,7 @@ BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt) {
bad_kb->st.state = BadKbStateInit;
bad_kb->st.error[0] = '\0';
+ bad_kb->st.is_bt = !!bt;
bad_kb->bt = bt;
@@ -949,7 +992,7 @@ void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_p
}
File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
- if(!furi_string_empty(layout_path)) {
+ if(!furi_string_empty(layout_path)) { //-V1051
furi_string_set(bad_kb->keyboard_layout, layout_path);
if(storage_file_open(
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
diff --git a/applications/main/bad_kb/bad_kb_script.h b/applications/main/bad_kb/bad_kb_script.h
index 0ea701eb8..a724971ec 100644
--- a/applications/main/bad_kb/bad_kb_script.h
+++ b/applications/main/bad_kb/bad_kb_script.h
@@ -7,6 +7,8 @@ extern "C" {
#include
#include
+typedef struct BadKbApp BadKbApp;
+
typedef struct BadKbScript BadKbScript;
typedef enum {
@@ -23,6 +25,8 @@ typedef enum {
typedef struct {
BadKbWorkerState state;
+ bool is_bt;
+ uint32_t pin;
uint16_t line_cur;
uint16_t line_nb;
uint32_t delay_remain;
@@ -30,9 +34,13 @@ typedef struct {
char error[64];
} BadKbState;
-void bad_kb_connection_init(Bt* bt);
+void bad_kb_config_switch_mode(BadKbApp* app);
-void bad_kb_connection_deinit(Bt* bt);
+void bad_kb_config_switch_remember_mode(BadKbApp* app);
+
+int32_t bad_kb_connection_init(BadKbApp* app);
+
+void bad_kb_connection_deinit(BadKbApp* app);
BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt);
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c
index 4412f0796..70dde9e16 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c
@@ -5,6 +5,7 @@
enum VarItemListIndex {
VarItemListIndexConnection,
+ VarItemListIndexRemember,
VarItemListIndexKeyboardLayout,
VarItemListIndexAdvertisementName,
VarItemListIndexMacAddress,
@@ -19,6 +20,15 @@ void bad_kb_scene_config_bt_connection_callback(VariableItem* item) {
view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection);
}
+void bad_kb_scene_config_bt_bounding_callback(VariableItem* item) {
+ BadKbApp* bad_kb = variable_item_get_context(item);
+ bad_kb->bt_remember = variable_item_get_current_value_index(item);
+ XTREME_SETTINGS()->bad_bt_remember = bad_kb->bt_remember;
+ XTREME_SETTINGS_SAVE();
+ variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF");
+ view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexRemember);
+}
+
void bad_kb_scene_config_bt_var_item_list_callback(void* context, uint32_t index) {
BadKbApp* bad_kb = context;
view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index);
@@ -34,11 +44,19 @@ void bad_kb_scene_config_bt_on_enter(void* context) {
variable_item_set_current_value_index(item, bad_kb->is_bt);
variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB");
+ item = variable_item_list_add(
+ var_item_list, "Remember", 2, bad_kb_scene_config_bt_bounding_callback, bad_kb);
+ variable_item_set_current_value_index(item, bad_kb->bt_remember);
+ variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF");
+
item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb);
item = variable_item_list_add(var_item_list, "BT device name", 0, NULL, bad_kb);
- item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb);
+ // this doesn't update instantly when toggling between Bounding modes
+ if(!bad_kb->bt_remember) {
+ item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb);
+ }
variable_item_list_set_enter_callback(
var_item_list, bad_kb_scene_config_bt_var_item_list_callback, bad_kb);
@@ -56,23 +74,15 @@ bool bad_kb_scene_config_bt_on_event(void* context, SceneManagerEvent event) {
if(event.event == VarItemListIndexKeyboardLayout) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout);
} else if(event.event == VarItemListIndexConnection) {
- bad_kb_script_close(bad_kb->bad_kb_script);
- bad_kb_connection_deinit(bad_kb->bt);
- bad_kb->bad_kb_script =
- bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL);
- bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout);
+ bad_kb_config_switch_mode(bad_kb);
+ } else if(event.event == VarItemListIndexRemember) {
+ bad_kb_config_switch_remember_mode(bad_kb);
scene_manager_previous_scene(bad_kb->scene_manager);
- if(bad_kb->is_bt) {
- scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt);
- } else {
- scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb);
- }
+ scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt);
} else if(event.event == VarItemListIndexAdvertisementName) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName);
} else if(event.event == VarItemListIndexMacAddress) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac);
- // } else {
- // furi_crash("Unknown key type");
}
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c
index 3842c59fa..a73822fcc 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c
@@ -3,8 +3,6 @@
#include "furi_hal_usb.h"
#include
-#define KEYBOARD_FOLDER "/ext/badkb/layouts"
-
static bool bad_kb_layout_select(BadKbApp* bad_kb) {
furi_assert(bad_kb);
@@ -19,7 +17,8 @@ static bool bad_kb_layout_select(BadKbApp* bad_kb) {
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, BAD_KB_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
- browser_options.base_path = KEYBOARD_FOLDER;
+ browser_options.base_path = BAD_KB_APP_PATH_LAYOUT_FOLDER;
+ browser_options.skip_assets = false;
// Input events and views are managed by file_browser
bool res = dialog_file_browser_show(
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c
index 0dc4be10a..d7027537a 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c
@@ -2,14 +2,14 @@
#define TAG "BadKbConfigMac"
-static uint8_t* reverse_mac_addr(uint8_t* mac) {
+static uint8_t* reverse_mac_addr(uint8_t* mac, uint8_t* out) {
uint8_t tmp;
for(int i = 0; i < 3; i++) {
tmp = mac[i];
- mac[i] = mac[5 - i];
- mac[5 - i] = tmp;
+ out[i] = mac[5 - i];
+ out[5 - i] = tmp;
}
- return mac;
+ return out;
}
void bad_kb_scene_config_mac_byte_input_callback(void* context) {
@@ -23,13 +23,13 @@ void bad_kb_scene_config_mac_on_enter(void* context) {
// Setup view
ByteInput* byte_input = bad_kb->byte_input;
- byte_input_set_header_text(byte_input, "Enter new MAC address");
+ byte_input_set_header_text(byte_input, "Set BT MAC address");
byte_input_set_result_callback(
byte_input,
bad_kb_scene_config_mac_byte_input_callback,
NULL,
bad_kb,
- reverse_mac_addr(bad_kb->mac),
+ reverse_mac_addr(bad_kb->mac, bad_kb->mac),
GAP_MAC_ADDR_SIZE);
view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigMac);
}
@@ -40,7 +40,8 @@ bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == BadKbAppCustomEventByteInputDone) {
- bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac));
+ uint8_t mac[GAP_MAC_ADDR_SIZE];
+ bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac, mac));
scene_manager_previous_scene(bad_kb->scene_manager);
consumed = true;
}
@@ -54,4 +55,7 @@ void bad_kb_scene_config_mac_on_exit(void* context) {
// Clear view
byte_input_set_result_callback(bad_kb->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(bad_kb->byte_input, "");
+
+ // reverse back addr (in case it didn't get modified)
+ reverse_mac_addr(bad_kb->mac, bad_kb->mac);
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c
index 82dd7a850..59574c334 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c
@@ -10,7 +10,7 @@ void bad_kb_scene_config_name_on_enter(void* context) {
BadKbApp* bad_kb = context;
TextInput* text_input = bad_kb->text_input;
- text_input_set_header_text(text_input, "Set BLE adv name");
+ text_input_set_header_text(text_input, "Set BT device name");
text_input_set_result_callback(
text_input,
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c
index 232ef8796..d3ff35fc8 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c
@@ -50,19 +50,7 @@ bool bad_kb_scene_config_usb_on_event(void* context, SceneManagerEvent event) {
if(event.event == VarItemListIndexKeyboardLayout) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout);
} else if(event.event == VarItemListIndexConnection) {
- bad_kb_script_close(bad_kb->bad_kb_script);
- bad_kb_connection_deinit(bad_kb->bt);
- bad_kb->bad_kb_script =
- bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL);
- bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout);
- scene_manager_previous_scene(bad_kb->scene_manager);
- if(bad_kb->is_bt) {
- scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt);
- } else {
- scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb);
- }
- // } else {
- // furi_crash("Unknown key type");
+ bad_kb_config_switch_mode(bad_kb);
}
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c
index bb2ddf6ca..ba238f390 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c
@@ -22,7 +22,6 @@ static bool bad_kb_file_select(BadKbApp* bad_kb) {
void bad_kb_scene_file_select_on_enter(void* context) {
BadKbApp* bad_kb = context;
- furi_hal_usb_disable();
if(bad_kb->bad_kb_script) {
bad_kb_script_close(bad_kb->bad_kb_script);
bad_kb->bad_kb_script = NULL;
@@ -35,7 +34,6 @@ void bad_kb_scene_file_select_on_enter(void* context) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork);
} else {
- furi_hal_usb_enable();
view_dispatcher_stop(bad_kb->view_dispatcher);
}
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_work.c b/applications/main/bad_kb/scenes/bad_kb_scene_work.c
index 165ee0289..e962f9132 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_work.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_work.c
@@ -16,10 +16,12 @@ bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InputKeyLeft) {
- if(app->is_bt) {
- scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt);
- } else {
- scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb);
+ if(bad_kb_is_idle_state(app->bad_kb_view)) {
+ if(app->is_bt) {
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt);
+ } else {
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb);
+ }
}
consumed = true;
} else if(event.event == InputKeyOk) {
diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c
index 1ffe58e9c..26ec90809 100644
--- a/applications/main/bad_kb/views/bad_kb_view.c
+++ b/applications/main/bad_kb/views/bad_kb_view.c
@@ -1,5 +1,6 @@
#include "bad_kb_view.h"
#include "../bad_kb_script.h"
+#include "../bad_kb_app_i.h"
#include
#include
#include
@@ -24,7 +25,8 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) {
BadKbModel* model = _model;
FuriString* disp_str;
- disp_str = furi_string_alloc_set(model->file_name);
+ disp_str = furi_string_alloc_set(model->state.is_bt ? "(BT) " : "(USB) ");
+ furi_string_cat_str(disp_str, model->file_name);
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
@@ -38,6 +40,9 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) {
furi_string_push_back(disp_str, model->layout[i]);
furi_string_push_back(disp_str, ')');
}
+ if(model->state.pin) {
+ furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin);
+ }
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_draw_str(
canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
@@ -51,19 +56,15 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) {
if(XTREME_ASSETS()->is_nsfw) {
elements_button_center(canvas, "Cum");
} else {
- elements_button_center(canvas, "Start");
+ elements_button_center(canvas, "Run");
}
+ elements_button_left(canvas, "Config");
} else if((model->state.state == BadKbStateRunning) || (model->state.state == BadKbStateDelay)) {
elements_button_center(canvas, "Stop");
} else if(model->state.state == BadKbStateWillRun) {
elements_button_center(canvas, "Cancel");
}
- if((model->state.state == BadKbStateNotConnected) || (model->state.state == BadKbStateIdle) ||
- (model->state.state == BadKbStateDone)) {
- elements_button_left(canvas, "Config");
- }
-
if(model->state.state == BadKbStateNotConnected) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
@@ -214,6 +215,14 @@ void bad_kb_set_layout(BadKb* bad_kb, const char* layout) {
void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) {
furi_assert(st);
+ uint32_t pin = 0;
+ if(bad_kb->context != NULL) {
+ BadKbApp* app = bad_kb->context;
+ if(app->bt != NULL) {
+ pin = app->bt->pin;
+ }
+ }
+ st->pin = pin;
with_view_model(
bad_kb->view,
BadKbModel * model,
@@ -223,3 +232,18 @@ void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) {
},
true);
}
+
+bool bad_kb_is_idle_state(BadKb* bad_kb) {
+ bool is_idle = false;
+ with_view_model(
+ bad_kb->view,
+ BadKbModel * model,
+ {
+ if((model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone) ||
+ (model->state.state == BadKbStateNotConnected)) {
+ is_idle = true;
+ }
+ },
+ false);
+ return is_idle;
+}
diff --git a/applications/main/bad_kb/views/bad_kb_view.h b/applications/main/bad_kb/views/bad_kb_view.h
index 24fdf4792..f16814bb4 100644
--- a/applications/main/bad_kb/views/bad_kb_view.h
+++ b/applications/main/bad_kb/views/bad_kb_view.h
@@ -19,3 +19,5 @@ void bad_kb_set_file_name(BadKb* bad_kb, const char* name);
void bad_kb_set_layout(BadKb* bad_kb, const char* layout);
void bad_kb_set_state(BadKb* bad_kb, BadKbState* st);
+
+bool bad_kb_is_idle_state(BadKb* bad_kb);
diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c
index 7911aa068..559463988 100644
--- a/applications/main/fap_loader/fap_loader_app.c
+++ b/applications/main/fap_loader/fap_loader_app.c
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include "elf_cpp/elf_hashtable.h"
#include "fap_loader_app.h"
@@ -35,7 +36,7 @@ bool fap_loader_load_name_and_icon(
if(preload_res == FlipperApplicationPreloadStatusSuccess) {
const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
- if(manifest->has_icon) {
+ if(manifest->has_icon && icon_ptr != NULL) {
memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE);
}
furi_string_set(item_name, manifest->name);
@@ -105,6 +106,12 @@ static bool fap_loader_run_selected_app(FapLoader* loader) {
FURI_LOG_I(TAG, "FAP Loader is starting app");
FuriThread* thread = flipper_application_spawn(loader->app, NULL);
+
+ FuriString* app_name = furi_string_alloc();
+ path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name);
+ furi_thread_set_appid(thread, furi_string_get_cstr(app_name));
+ furi_string_free(app_name);
+
furi_thread_start(thread);
furi_thread_join(thread);
@@ -146,6 +153,7 @@ static bool fap_loader_select_app(FapLoader* loader) {
.skip_assets = true,
.icon = &I_unknown_10px,
.hide_ext = true,
+ .hide_dot_files = true,
.item_loader_callback = fap_loader_item_callback,
.item_loader_context = loader,
.base_path = EXT_PATH("apps"),
diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c
index 1c1a665c7..2d8f7adbc 100644
--- a/applications/main/gpio/scenes/gpio_scene_start.c
+++ b/applications/main/gpio/scenes/gpio_scene_start.c
@@ -107,6 +107,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
} 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/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c
index 85212f42b..79999adb2 100644
--- a/applications/main/ibutton/ibutton.c
+++ b/applications/main/ibutton/ibutton.c
@@ -1,10 +1,6 @@
-#include "ibutton.h"
-#include "assets_icons.h"
#include "ibutton_i.h"
-#include "ibutton/scenes/ibutton_scene.h"
+
#include
-#include
-#include
#include
#define TAG "iButtonApp"
@@ -34,50 +30,13 @@ static const NotificationSequence* ibutton_notification_sequences[] = {
};
static void ibutton_make_app_folder(iButton* ibutton) {
- if(!storage_simply_mkdir(ibutton->storage, IBUTTON_APP_FOLDER)) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ if(!storage_simply_mkdir(storage, IBUTTON_APP_FOLDER)) {
dialog_message_show_storage_error(ibutton->dialogs, "Cannot create\napp folder");
}
-}
-bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog) {
- FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
- bool result = false;
- FuriString* data;
- data = furi_string_alloc();
-
- do {
- if(!flipper_format_file_open_existing(file, furi_string_get_cstr(key_path))) break;
-
- // header
- uint32_t version;
- if(!flipper_format_read_header(file, data, &version)) break;
- if(furi_string_cmp_str(data, IBUTTON_APP_FILE_TYPE) != 0) break;
- if(version != 1) break;
-
- // key type
- iButtonKeyType type;
- if(!flipper_format_read_string(file, "Key type", data)) break;
- if(!ibutton_key_get_type_by_string(furi_string_get_cstr(data), &type)) break;
-
- // key data
- uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0};
- if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type)))
- break;
-
- ibutton_key_set_type(ibutton->key, type);
- ibutton_key_set_data(ibutton->key, key_data, IBUTTON_KEY_DATA_SIZE);
-
- result = true;
- } while(false);
-
- flipper_format_free(file);
- furi_string_free(data);
-
- if((!result) && (show_dialog)) {
- dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
- }
-
- return result;
+ furi_record_close(RECORD_STORAGE);
}
static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) {
@@ -87,14 +46,14 @@ static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context)
if(event == RpcAppEventSessionClose) {
view_dispatcher_send_custom_event(
ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);
- rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
- ibutton->rpc_ctx = NULL;
+ rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
+ ibutton->rpc = NULL;
} else if(event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
} else if(event == RpcAppEventLoadFile) {
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
} else {
- rpc_system_app_confirm(ibutton->rpc_ctx, event, false);
+ rpc_system_app_confirm(ibutton->rpc, event, false);
}
}
@@ -135,13 +94,13 @@ iButton* ibutton_alloc() {
ibutton->gui = furi_record_open(RECORD_GUI);
- ibutton->storage = furi_record_open(RECORD_STORAGE);
ibutton->dialogs = furi_record_open(RECORD_DIALOGS);
ibutton->notifications = furi_record_open(RECORD_NOTIFICATION);
- ibutton->key = ibutton_key_alloc();
- ibutton->key_worker = ibutton_worker_alloc();
- ibutton_worker_start_thread(ibutton->key_worker);
+ ibutton->protocols = ibutton_protocols_alloc();
+ ibutton->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(ibutton->protocols));
+ ibutton->worker = ibutton_worker_alloc(ibutton->protocols);
+ ibutton_worker_start_thread(ibutton->worker);
ibutton->submenu = submenu_alloc();
view_dispatcher_add_view(
@@ -163,9 +122,9 @@ iButton* ibutton_alloc() {
view_dispatcher_add_view(
ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget));
- ibutton->dialog_ex = dialog_ex_alloc();
+ ibutton->loading = loading_alloc();
view_dispatcher_add_view(
- ibutton->view_dispatcher, iButtonViewDialogEx, dialog_ex_get_view(ibutton->dialog_ex));
+ ibutton->view_dispatcher, iButtonViewLoading, loading_get_view(ibutton->loading));
return ibutton;
}
@@ -173,8 +132,8 @@ iButton* ibutton_alloc() {
void ibutton_free(iButton* ibutton) {
furi_assert(ibutton);
- view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewDialogEx);
- dialog_ex_free(ibutton->dialog_ex);
+ view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewLoading);
+ loading_free(ibutton->loading);
view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewWidget);
widget_free(ibutton->widget);
@@ -194,9 +153,6 @@ void ibutton_free(iButton* ibutton) {
view_dispatcher_free(ibutton->view_dispatcher);
scene_manager_free(ibutton->scene_manager);
- furi_record_close(RECORD_STORAGE);
- ibutton->storage = NULL;
-
furi_record_close(RECORD_NOTIFICATION);
ibutton->notifications = NULL;
@@ -206,103 +162,83 @@ void ibutton_free(iButton* ibutton) {
furi_record_close(RECORD_GUI);
ibutton->gui = NULL;
- ibutton_worker_stop_thread(ibutton->key_worker);
- ibutton_worker_free(ibutton->key_worker);
+ ibutton_worker_stop_thread(ibutton->worker);
+ ibutton_worker_free(ibutton->worker);
ibutton_key_free(ibutton->key);
+ ibutton_protocols_free(ibutton->protocols);
furi_string_free(ibutton->file_path);
free(ibutton);
}
-bool ibutton_file_select(iButton* ibutton) {
- DialogsFileBrowserOptions browser_options;
- dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
- browser_options.base_path = IBUTTON_APP_FOLDER;
+bool ibutton_load_key(iButton* ibutton) {
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);
- bool success = dialog_file_browser_show(
- ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options);
+ const bool success = ibutton_protocols_load(
+ ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path));
- if(success) {
- success = ibutton_load_key_data(ibutton, ibutton->file_path, true);
+ if(!success) {
+ dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
+
+ } else {
+ FuriString* tmp = furi_string_alloc();
+
+ path_extract_filename(ibutton->file_path, tmp, true);
+ strncpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE);
+
+ furi_string_free(tmp);
}
return success;
}
-bool ibutton_save_key(iButton* ibutton, const char* key_name) {
- // Create ibutton directory if necessary
+bool ibutton_select_and_load_key(iButton* ibutton) {
+ DialogsFileBrowserOptions browser_options;
+ dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
+ browser_options.base_path = IBUTTON_APP_FOLDER;
+
+ if(furi_string_empty(ibutton->file_path)) {
+ furi_string_set(ibutton->file_path, browser_options.base_path);
+ }
+
+ return dialog_file_browser_show(
+ ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options) &&
+ ibutton_load_key(ibutton);
+}
+
+bool ibutton_save_key(iButton* ibutton) {
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading);
+
ibutton_make_app_folder(ibutton);
- FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
iButtonKey* key = ibutton->key;
+ const bool success =
+ ibutton_protocols_save(ibutton->protocols, key, furi_string_get_cstr(ibutton->file_path));
- bool result = false;
-
- do {
- // Check if we has old key
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- // First remove old key
- ibutton_delete_key(ibutton);
-
- // Remove old key name from path
- size_t filename_start = furi_string_search_rchar(ibutton->file_path, '/');
- furi_string_left(ibutton->file_path, filename_start);
- }
-
- furi_string_cat_printf(ibutton->file_path, "/%s%s", key_name, IBUTTON_APP_EXTENSION);
-
- // Open file for write
- if(!flipper_format_file_open_always(file, furi_string_get_cstr(ibutton->file_path))) break;
-
- // Write header
- if(!flipper_format_write_header_cstr(file, IBUTTON_APP_FILE_TYPE, 1)) break;
-
- // Write key type
- if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom"))
- break;
- const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key));
- if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break;
-
- // Write data
- if(!flipper_format_write_comment_cstr(
- file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8"))
- break;
-
- if(!flipper_format_write_hex(
- file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)))
- break;
- result = true;
-
- } while(false);
-
- flipper_format_free(file);
-
- if(!result) { //-V547
+ if(!success) {
dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file");
}
- return result;
+ return success;
}
bool ibutton_delete_key(iButton* ibutton) {
bool result = false;
- result = storage_simply_remove(ibutton->storage, furi_string_get_cstr(ibutton->file_path));
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ result = storage_simply_remove(storage, furi_string_get_cstr(ibutton->file_path));
+ furi_record_close(RECORD_STORAGE);
+
+ ibutton_reset_key(ibutton);
return result;
}
-void ibutton_text_store_set(iButton* ibutton, const char* text, ...) {
- va_list args;
- va_start(args, text);
-
- vsnprintf(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE, text, args);
-
- va_end(args);
-}
-
-void ibutton_text_store_clear(iButton* ibutton) {
- memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE + 1);
+void ibutton_reset_key(iButton* ibutton) {
+ memset(ibutton->key_name, 0, IBUTTON_KEY_NAME_SIZE + 1);
+ furi_string_reset(ibutton->file_path);
+ ibutton_key_reset(ibutton->key);
}
void ibutton_notification_message(iButton* ibutton, uint32_t message) {
@@ -310,36 +246,44 @@ void ibutton_notification_message(iButton* ibutton, uint32_t message) {
notification_message(ibutton->notifications, ibutton_notification_sequences[message]);
}
-int32_t ibutton_app(void* p) {
+void ibutton_submenu_callback(void* context, uint32_t index) {
+ iButton* ibutton = context;
+ view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
+}
+
+void ibutton_widget_callback(GuiButtonType result, InputType type, void* context) {
+ iButton* ibutton = context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+ }
+}
+
+int32_t ibutton_app(void* arg) {
iButton* ibutton = ibutton_alloc();
ibutton_make_app_folder(ibutton);
bool key_loaded = false;
- bool rpc_mode = false;
- if(p && strlen(p)) {
- uint32_t rpc_ctx = 0;
- if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) {
+ if((arg != NULL) && (strlen(arg) != 0)) {
+ if(sscanf(arg, "RPC %lX", (uint32_t*)&ibutton->rpc) == 1) {
FURI_LOG_D(TAG, "Running in RPC mode");
- ibutton->rpc_ctx = (void*)rpc_ctx;
- rpc_mode = true;
- rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton);
- rpc_system_app_send_started(ibutton->rpc_ctx);
+
+ rpc_system_app_set_callback(ibutton->rpc, ibutton_rpc_command_callback, ibutton);
+ rpc_system_app_send_started(ibutton->rpc);
+
} else {
- furi_string_set(ibutton->file_path, (const char*)p);
- if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) {
- key_loaded = true;
- // TODO: Display an error if the key from p could not be loaded
- }
+ furi_string_set(ibutton->file_path, (const char*)arg);
+ key_loaded = ibutton_load_key(ibutton);
}
}
- if(rpc_mode) {
+ if(ibutton->rpc != NULL) {
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);
@@ -353,9 +297,9 @@ int32_t ibutton_app(void* p) {
view_dispatcher_run(ibutton->view_dispatcher);
- if(ibutton->rpc_ctx) {
- rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL);
- rpc_system_app_send_exited(ibutton->rpc_ctx);
+ if(ibutton->rpc) {
+ rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
+ rpc_system_app_send_exited(ibutton->rpc);
}
ibutton_free(ibutton);
return 0;
diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c
index fab1ccf05..2b88b2007 100644
--- a/applications/main/ibutton/ibutton_cli.c
+++ b/applications/main/ibutton/ibutton_cli.c
@@ -1,11 +1,15 @@
#include
#include
-#include
+
#include
-#include
-#include
+#include
+
#include
+#include
+#include
+#include
+
static void ibutton_cli(Cli* cli, FuriString* args, void* context);
static void onewire_cli(Cli* cli, FuriString* args, void* context);
@@ -22,7 +26,7 @@ void ibutton_on_system_start() {
#endif
}
-void ibutton_cli_print_usage() {
+static void ibutton_cli_print_usage() {
printf("Usage:\r\n");
printf("ikey read\r\n");
printf("ikey emulate \r\n");
@@ -34,30 +38,52 @@ void ibutton_cli_print_usage() {
printf("\t are hex-formatted\r\n");
};
-bool ibutton_cli_get_key_type(FuriString* data, iButtonKeyType* type) {
+static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) {
bool result = false;
+ FuriString* name = furi_string_alloc();
- if(furi_string_cmp_str(data, "Dallas") == 0 || furi_string_cmp_str(data, "dallas") == 0) {
- result = true;
- *type = iButtonKeyDS1990;
- } else if(furi_string_cmp_str(data, "Cyfral") == 0 || furi_string_cmp_str(data, "cyfral") == 0) {
- result = true;
- *type = iButtonKeyCyfral;
- } else if(furi_string_cmp_str(data, "Metakom") == 0 || furi_string_cmp_str(data, "metakom") == 0) {
- result = true;
- *type = iButtonKeyMetakom;
- }
+ do {
+ // Read protocol name
+ if(!args_read_string_and_trim(args, name)) break;
+ // Make the protocol name uppercase
+ const char first = furi_string_get_char(name, 0);
+ furi_string_set_char(name, 0, toupper((int)first));
+
+ const iButtonProtocolId id =
+ ibutton_protocols_get_id_by_name(protocols, furi_string_get_cstr(name));
+ if(id == iButtonProtocolIdInvalid) break;
+
+ ibutton_key_set_protocol_id(key, id);
+
+ // Get the data pointer
+ iButtonEditableData data;
+ ibutton_protocols_get_editable_data(protocols, key, &data);
+
+ // Read data
+ if(!args_read_hex_bytes(args, data.ptr, data.size)) break;
+
+ result = true;
+ } while(false);
+
+ furi_string_free(name);
return result;
}
-void ibutton_cli_print_key_data(iButtonKey* key) {
- const uint8_t* key_data = ibutton_key_get_data_p(key);
- iButtonKeyType type = ibutton_key_get_type(key);
+static void ibutton_cli_print_key(iButtonProtocols* protocols, iButtonKey* key) {
+ const char* name = ibutton_protocols_get_name(protocols, ibutton_key_get_protocol_id(key));
- printf("%s ", ibutton_key_get_string_by_type(type));
- for(size_t i = 0; i < ibutton_key_get_size_by_type(type); i++) {
- printf("%02X", key_data[i]);
+ if(strncmp(name, "DS", 2) == 0) {
+ name = "Dallas";
+ }
+
+ printf("%s ", name);
+
+ iButtonEditableData data;
+ ibutton_protocols_get_editable_data(protocols, key, &data);
+
+ for(size_t i = 0; i < data.size; i++) {
+ printf("%02X", data.ptr[i]);
}
printf("\r\n");
@@ -71,9 +97,10 @@ static void ibutton_cli_worker_read_cb(void* context) {
furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE);
}
-void ibutton_cli_read(Cli* cli) {
- iButtonKey* key = ibutton_key_alloc();
- iButtonWorker* worker = ibutton_worker_alloc();
+static void ibutton_cli_read(Cli* cli) {
+ iButtonProtocols* protocols = ibutton_protocols_alloc();
+ iButtonWorker* worker = ibutton_worker_alloc(protocols);
+ iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
FuriEventFlag* event = furi_event_flag_alloc();
ibutton_worker_start_thread(worker);
@@ -81,32 +108,25 @@ void ibutton_cli_read(Cli* cli) {
printf("Reading iButton...\r\nPress Ctrl+C to abort\r\n");
ibutton_worker_read_start(worker, key);
+
while(true) {
uint32_t flags =
furi_event_flag_wait(event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);
if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {
- ibutton_cli_print_key_data(key);
-
- if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
- if(!ibutton_key_dallas_crc_is_valid(key)) {
- printf("Warning: invalid CRC\r\n");
- }
-
- if(!ibutton_key_dallas_is_1990_key(key)) {
- printf("Warning: not a key\r\n");
- }
- }
+ ibutton_cli_print_key(protocols, key);
break;
}
if(cli_cmd_interrupt_received(cli)) break;
}
- ibutton_worker_stop(worker);
+ ibutton_worker_stop(worker);
ibutton_worker_stop_thread(worker);
- ibutton_worker_free(worker);
+
ibutton_key_free(key);
+ ibutton_worker_free(worker);
+ ibutton_protocols_free(protocols);
furi_event_flag_free(event);
};
@@ -124,48 +144,33 @@ static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult
}
void ibutton_cli_write(Cli* cli, FuriString* args) {
- iButtonKey* key = ibutton_key_alloc();
- iButtonWorker* worker = ibutton_worker_alloc();
- iButtonKeyType type;
- iButtonWriteContext write_context;
- uint8_t key_data[IBUTTON_KEY_DATA_SIZE];
- FuriString* data;
+ iButtonProtocols* protocols = ibutton_protocols_alloc();
+ iButtonWorker* worker = ibutton_worker_alloc(protocols);
+ iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
+ iButtonWriteContext write_context;
write_context.event = furi_event_flag_alloc();
- data = furi_string_alloc();
ibutton_worker_start_thread(worker);
ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context);
do {
- if(!args_read_string_and_trim(args, data)) {
+ if(!ibutton_cli_parse_key(protocols, key, args)) {
ibutton_cli_print_usage();
break;
}
- if(!ibutton_cli_get_key_type(data, &type)) {
+ if(!(ibutton_protocols_get_features(protocols, ibutton_key_get_protocol_id(key)) &
+ iButtonProtocolFeatureWriteBlank)) {
ibutton_cli_print_usage();
break;
}
- if(type != iButtonKeyDS1990) {
- ibutton_cli_print_usage();
- break;
- }
-
- if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) {
- ibutton_cli_print_usage();
- break;
- }
-
- ibutton_key_set_type(key, type);
- ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type));
-
printf("Writing key ");
- ibutton_cli_print_key_data(key);
+ ibutton_cli_print_key(protocols, key);
printf("Press Ctrl+C to abort\r\n");
- ibutton_worker_write_start(worker, key);
+ ibutton_worker_write_blank_start(worker, key);
while(true) {
uint32_t flags = furi_event_flag_wait(
write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100);
@@ -183,64 +188,53 @@ void ibutton_cli_write(Cli* cli, FuriString* args) {
if(cli_cmd_interrupt_received(cli)) break;
}
- ibutton_worker_stop(worker);
} while(false);
- furi_string_free(data);
+ ibutton_worker_stop(worker);
ibutton_worker_stop_thread(worker);
- ibutton_worker_free(worker);
+
ibutton_key_free(key);
+ ibutton_worker_free(worker);
+ ibutton_protocols_free(protocols);
furi_event_flag_free(write_context.event);
-};
+}
void ibutton_cli_emulate(Cli* cli, FuriString* args) {
- iButtonKey* key = ibutton_key_alloc();
- iButtonWorker* worker = ibutton_worker_alloc();
- iButtonKeyType type;
- uint8_t key_data[IBUTTON_KEY_DATA_SIZE];
- FuriString* data;
+ iButtonProtocols* protocols = ibutton_protocols_alloc();
+ iButtonWorker* worker = ibutton_worker_alloc(protocols);
+ iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols));
- data = furi_string_alloc();
ibutton_worker_start_thread(worker);
do {
- if(!args_read_string_and_trim(args, data)) {
+ if(!ibutton_cli_parse_key(protocols, key, args)) {
ibutton_cli_print_usage();
break;
}
- if(!ibutton_cli_get_key_type(data, &type)) {
- ibutton_cli_print_usage();
- break;
- }
-
- if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) {
- ibutton_cli_print_usage();
- break;
- }
-
- ibutton_key_set_type(key, type);
- ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type));
-
printf("Emulating key ");
- ibutton_cli_print_key_data(key);
+ ibutton_cli_print_key(protocols, key);
printf("Press Ctrl+C to abort\r\n");
ibutton_worker_emulate_start(worker, key);
+
while(!cli_cmd_interrupt_received(cli)) {
furi_delay_ms(100);
};
- ibutton_worker_stop(worker);
+
} while(false);
- furi_string_free(data);
+ ibutton_worker_stop(worker);
ibutton_worker_stop_thread(worker);
- ibutton_worker_free(worker);
+
ibutton_key_free(key);
+ ibutton_worker_free(worker);
+ ibutton_protocols_free(protocols);
};
-static void ibutton_cli(Cli* cli, FuriString* args, void* context) {
+void ibutton_cli(Cli* cli, FuriString* args, void* context) {
+ UNUSED(cli);
UNUSED(context);
FuriString* cmd;
cmd = furi_string_alloc();
@@ -264,14 +258,14 @@ static void ibutton_cli(Cli* cli, FuriString* args, void* context) {
furi_string_free(cmd);
}
-void onewire_cli_print_usage() {
+static void onewire_cli_print_usage() {
printf("Usage:\r\n");
printf("onewire search\r\n");
};
static void onewire_cli_search(Cli* cli) {
UNUSED(cli);
- OneWireHost* onewire = onewire_host_alloc();
+ OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio);
uint8_t address[8];
bool done = false;
@@ -281,7 +275,7 @@ static void onewire_cli_search(Cli* cli) {
furi_hal_power_enable_otg();
while(!done) {
- if(onewire_host_search(onewire, address, NORMAL_SEARCH) != 1) {
+ if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) {
printf("Search finished\r\n");
onewire_host_reset_search(onewire);
done = true;
diff --git a/applications/main/ibutton/ibutton_custom_event.h b/applications/main/ibutton/ibutton_custom_event.h
index 1e2f0300d..28bcb94a0 100644
--- a/applications/main/ibutton/ibutton_custom_event.h
+++ b/applications/main/ibutton/ibutton_custom_event.h
@@ -6,6 +6,7 @@ enum iButtonCustomEvent {
iButtonCustomEventBack,
iButtonCustomEventTextEditResult,
+ iButtonCustomEventByteEditChanged,
iButtonCustomEventByteEditResult,
iButtonCustomEventWorkerEmulated,
iButtonCustomEventWorkerRead,
diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h
index 0a8099351..8ad0b90e4 100644
--- a/applications/main/ibutton/ibutton_i.h
+++ b/applications/main/ibutton/ibutton_i.h
@@ -4,31 +4,40 @@
#include
#include
-#include
-#include
#include
-#include
+#include
#include
+#include
+
+#include
#include
#include
+#include
+#include
#include
#include
-#include
#include
#include
#include
+#include
+
+#include
#include "ibutton_custom_event.h"
#include "scenes/ibutton_scene.h"
-#define IBUTTON_FILE_NAME_SIZE 100
-#define IBUTTON_TEXT_STORE_SIZE 128
-
#define IBUTTON_APP_FOLDER ANY_PATH("ibutton")
#define IBUTTON_APP_EXTENSION ".ibtn"
-#define IBUTTON_APP_FILE_TYPE "Flipper iButton key"
+
+#define IBUTTON_KEY_NAME_SIZE 22
+
+typedef enum {
+ iButtonWriteModeInvalid,
+ iButtonWriteModeBlank,
+ iButtonWriteModeCopy,
+} iButtonWriteMode;
struct iButton {
SceneManager* scene_manager;
@@ -38,21 +47,22 @@ struct iButton {
Storage* storage;
DialogsApp* dialogs;
NotificationApp* notifications;
+ RpcAppSystem* rpc;
- iButtonWorker* key_worker;
iButtonKey* key;
+ iButtonWorker* worker;
+ iButtonProtocols* protocols;
+ iButtonWriteMode write_mode;
FuriString* file_path;
- char text_store[IBUTTON_TEXT_STORE_SIZE + 1];
+ char key_name[IBUTTON_KEY_NAME_SIZE + 1];
Submenu* submenu;
ByteInput* byte_input;
TextInput* text_input;
Popup* popup;
Widget* widget;
- DialogEx* dialog_ex;
-
- void* rpc_ctx;
+ Loading* loading;
};
typedef enum {
@@ -61,7 +71,7 @@ typedef enum {
iButtonViewTextInput,
iButtonViewPopup,
iButtonViewWidget,
- iButtonViewDialogEx,
+ iButtonViewLoading,
} iButtonView;
typedef enum {
@@ -78,10 +88,12 @@ typedef enum {
iButtonNotificationMessageBlinkStop,
} iButtonNotificationMessage;
-bool ibutton_file_select(iButton* ibutton);
-bool ibutton_load_key_data(iButton* ibutton, FuriString* key_path, bool show_dialog);
-bool ibutton_save_key(iButton* ibutton, const char* key_name);
+bool ibutton_select_and_load_key(iButton* ibutton);
+bool ibutton_load_key(iButton* ibutton);
+bool ibutton_save_key(iButton* ibutton);
bool ibutton_delete_key(iButton* ibutton);
-void ibutton_text_store_set(iButton* ibutton, const char* text, ...);
-void ibutton_text_store_clear(iButton* ibutton);
+void ibutton_reset_key(iButton* ibutton);
void ibutton_notification_message(iButton* ibutton, uint32_t message);
+
+void ibutton_submenu_callback(void* context, uint32_t index);
+void ibutton_widget_callback(GuiButtonType result, InputType type, void* context);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_type.c b/applications/main/ibutton/scenes/ibutton_scene_add_type.c
index 38373999c..55e05f20d 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_add_type.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_add_type.c
@@ -1,54 +1,57 @@
#include "../ibutton_i.h"
-enum SubmenuIndex {
- SubmenuIndexCyfral,
- SubmenuIndexDallas,
- SubmenuIndexMetakom,
-};
-
-void ibutton_scene_add_type_submenu_callback(void* context, uint32_t index) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
-}
-
void ibutton_scene_add_type_on_enter(void* context) {
iButton* ibutton = context;
Submenu* submenu = ibutton->submenu;
- submenu_add_item(
- submenu, "Cyfral", SubmenuIndexCyfral, ibutton_scene_add_type_submenu_callback, ibutton);
- submenu_add_item(
- submenu, "Dallas", SubmenuIndexDallas, ibutton_scene_add_type_submenu_callback, ibutton);
- submenu_add_item(
- submenu, "Metakom", SubmenuIndexMetakom, ibutton_scene_add_type_submenu_callback, ibutton);
+ FuriString* tmp = furi_string_alloc();
- submenu_set_selected_item(
- submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType));
+ for(uint32_t protocol_id = 0; protocol_id < ibutton_protocols_get_protocol_count();
+ ++protocol_id) {
+ if((strcmp(
+ ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id)) != 0) &&
+ (strcmp(ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), "N/A") !=
+ 0)) {
+ furi_string_printf(
+ tmp,
+ "%s %s",
+ ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id));
+ } else {
+ furi_string_printf(
+ tmp, "%s", ibutton_protocols_get_name(ibutton->protocols, protocol_id));
+ }
+
+ submenu_add_item(
+ submenu, furi_string_get_cstr(tmp), protocol_id, ibutton_submenu_callback, context);
+ }
+
+ const uint32_t prev_protocol_id =
+ scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType);
+ submenu_set_selected_item(submenu, prev_protocol_id);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
+ furi_string_free(tmp);
}
bool ibutton_scene_add_type_on_event(void* context, SceneManagerEvent event) {
iButton* ibutton = context;
iButtonKey* key = ibutton->key;
+
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, event.event);
- consumed = true;
- if(event.event == SubmenuIndexCyfral) {
- ibutton_key_set_type(key, iButtonKeyCyfral);
- } else if(event.event == SubmenuIndexDallas) {
- ibutton_key_set_type(key, iButtonKeyDS1990);
- } else if(event.event == SubmenuIndexMetakom) {
- ibutton_key_set_type(key, iButtonKeyMetakom);
- } else {
- furi_crash("Unknown key type");
- }
+ const iButtonProtocolId protocol_id = event.event;
- furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
- ibutton_key_clear_data(key);
+ ibutton_key_reset(key);
+ ibutton_key_set_protocol_id(key, protocol_id);
+ ibutton_protocols_apply_edits(ibutton->protocols, key);
+
+ scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneAddType, protocol_id);
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);
+
+ consumed = true;
}
return consumed;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c
index ccac76121..dc340771b 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_add_value.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c
@@ -1,42 +1,52 @@
#include "../ibutton_i.h"
-void ibutton_scene_add_type_byte_input_callback(void* context) {
+static void ibutton_scene_add_type_byte_input_callback(void* context) {
iButton* ibutton = context;
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult);
}
+static void ibutton_scene_add_type_byte_changed_callback(void* context) {
+ iButton* ibutton = context;
+ view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditChanged);
+}
+
void ibutton_scene_add_value_on_enter(void* context) {
iButton* ibutton = context;
- iButtonKey* key = ibutton->key;
- uint8_t* new_key_data = malloc(IBUTTON_KEY_DATA_SIZE);
+ byte_input_set_header_text(ibutton->byte_input, "Enter the key");
- scene_manager_set_scene_state(
- ibutton->scene_manager, iButtonSceneAddValue, (uint32_t)new_key_data);
- memcpy(new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key));
+ iButtonEditableData editable_data;
+ ibutton_protocols_get_editable_data(ibutton->protocols, ibutton->key, &editable_data);
byte_input_set_result_callback(
ibutton->byte_input,
ibutton_scene_add_type_byte_input_callback,
- NULL,
- ibutton,
- new_key_data,
- ibutton_key_get_data_size(key));
+ ibutton_scene_add_type_byte_changed_callback,
+ context,
+ editable_data.ptr,
+ editable_data.size);
- byte_input_set_header_text(ibutton->byte_input, "Enter the key");
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewByteInput);
}
bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
iButton* ibutton = context;
- uint8_t* new_key_data =
- (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue);
+ SceneManager* scene_manager = ibutton->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == iButtonCustomEventByteEditResult) {
- ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE);
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
+ scene_manager_next_scene(scene_manager, iButtonSceneSaveName);
+ } else if(event.event == iButtonCustomEventByteEditChanged) {
+ ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key);
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ // User cancelled editing, reload the key from storage
+ if(scene_manager_has_previous_scene(scene_manager, iButtonSceneSavedKeyMenu)) {
+ if(!ibutton_load_key(ibutton)) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ scene_manager, iButtonSceneStart);
+ }
}
}
@@ -45,10 +55,7 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
void ibutton_scene_add_value_on_exit(void* context) {
iButton* ibutton = context;
- uint8_t* new_key_data =
- (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue);
byte_input_set_result_callback(ibutton->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(ibutton->byte_input, NULL);
- free(new_key_data);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_config.h b/applications/main/ibutton/scenes/ibutton_scene_config.h
index 87fa1a036..79f6791b3 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_config.h
+++ b/applications/main/ibutton/scenes/ibutton_scene_config.h
@@ -6,8 +6,7 @@ ADD_SCENE(ibutton, info, Info)
ADD_SCENE(ibutton, read, Read)
ADD_SCENE(ibutton, read_key_menu, ReadKeyMenu)
ADD_SCENE(ibutton, read_success, ReadSuccess)
-ADD_SCENE(ibutton, read_crc_error, ReadCRCError)
-ADD_SCENE(ibutton, read_not_key_error, ReadNotKeyError)
+ADD_SCENE(ibutton, read_error, ReadError)
ADD_SCENE(ibutton, select_key, SelectKey)
ADD_SCENE(ibutton, add_type, AddType)
ADD_SCENE(ibutton, add_value, AddValue)
@@ -18,4 +17,5 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm)
ADD_SCENE(ibutton, delete_success, DeleteSuccess)
ADD_SCENE(ibutton, retry_confirm, RetryConfirm)
ADD_SCENE(ibutton, exit_confirm, ExitConfirm)
+ADD_SCENE(ibutton, view_data, ViewData)
ADD_SCENE(ibutton, rpc, Rpc)
diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c
index 3d609e833..587cb748c 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c
@@ -1,74 +1,29 @@
#include "../ibutton_i.h"
#include
-static void ibutton_scene_delete_confirm_widget_callback(
- GuiButtonType result,
- InputType type,
- void* context) {
- iButton* ibutton = context;
- if(type == InputTypeShort) {
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
- }
-}
-
void ibutton_scene_delete_confirm_on_enter(void* context) {
iButton* ibutton = context;
- Widget* widget = ibutton->widget;
iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ Widget* widget = ibutton->widget;
- FuriString* key_name;
- key_name = furi_string_alloc();
- path_extract_filename(ibutton->file_path, key_name, true);
+ FuriString* tmp = furi_string_alloc();
- ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", furi_string_get_cstr(key_name));
- widget_add_text_box_element(
- widget, 0, 0, 128, 27, AlignCenter, AlignCenter, ibutton->text_store, true);
+ widget_add_button_element(widget, GuiButtonTypeLeft, "Back", ibutton_widget_callback, context);
widget_add_button_element(
- widget, GuiButtonTypeLeft, "Cancel", ibutton_scene_delete_confirm_widget_callback, ibutton);
- widget_add_button_element(
- widget,
- GuiButtonTypeRight,
- "Delete",
- ibutton_scene_delete_confirm_widget_callback,
- ibutton);
+ widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context);
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X %02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- widget_add_string_element(
- widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Dallas");
- break;
-
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
- widget_add_string_element(
- widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
- break;
-
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
- widget_add_string_element(
- widget, 64, 34, AlignCenter, AlignBottom, FontSecondary, "Metakom");
- break;
- }
+ furi_string_printf(tmp, "Delete %s?", ibutton->key_name);
widget_add_string_element(
- widget, 64, 46, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store);
+ widget, 128 / 2, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
+
+ furi_string_reset(tmp);
+ ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
+
+ widget_add_string_multiline_element(
+ widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
-
- furi_string_free(key_name);
+ furi_string_free(tmp);
}
bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
@@ -81,8 +36,10 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even
if(event.event == GuiButtonTypeRight) {
if(ibutton_delete_key(ibutton)) {
scene_manager_next_scene(scene_manager, iButtonSceneDeleteSuccess);
+ } else {
+ dialog_message_show_storage_error(ibutton->dialogs, "Cannot delete\nkey file");
+ scene_manager_previous_scene(scene_manager);
}
- //TODO: What if the key could not be deleted?
} else if(event.event == GuiButtonTypeLeft) {
scene_manager_previous_scene(scene_manager);
}
@@ -93,6 +50,5 @@ bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent even
void ibutton_scene_delete_confirm_on_exit(void* context) {
iButton* ibutton = context;
- ibutton_text_store_clear(ibutton);
widget_reset(ibutton->widget);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_emulate.c b/applications/main/ibutton/scenes/ibutton_scene_emulate.c
index 6f6ffcf57..713b8331c 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_emulate.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_emulate.c
@@ -14,61 +14,32 @@ static void ibutton_scene_emulate_callback(void* context, bool emulated) {
void ibutton_scene_emulate_on_enter(void* context) {
iButton* ibutton = context;
- Widget* widget = ibutton->widget;
iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ Widget* widget = ibutton->widget;
+ FuriString* tmp = furi_string_alloc();
- FuriString* key_name;
- key_name = furi_string_alloc();
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- path_extract_filename(ibutton->file_path, key_name, true);
- }
+ widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
- // check that stored key has name
- if(!furi_string_empty(key_name)) {
- ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
- } else {
- // if not, show key data
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X\n%02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- break;
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
- break;
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
- break;
- }
- }
+ furi_string_printf(
+ tmp,
+ "%s\n[%s]",
+ furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name,
+ ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));
+
+ widget_add_text_box_element(
+ widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
widget_add_string_multiline_element(
- widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
- widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
- widget_add_text_box_element(
- widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true);
+ widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
-
- ibutton_worker_emulate_set_callback(
- ibutton->key_worker, ibutton_scene_emulate_callback, ibutton);
- ibutton_worker_emulate_start(ibutton->key_worker, key);
-
- furi_string_free(key_name);
+ ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton);
+ ibutton_worker_emulate_start(ibutton->worker, key);
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+
+ furi_string_free(tmp);
}
bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
@@ -78,8 +49,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeTick) {
uint32_t cnt = scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate);
if(cnt > 0) {
- cnt--;
- if(cnt == 0) {
+ if(--cnt == 0) {
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink);
}
scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneEmulate, cnt);
@@ -101,7 +71,7 @@ bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
void ibutton_scene_emulate_on_exit(void* context) {
iButton* ibutton = context;
- ibutton_worker_stop(ibutton->key_worker);
+ ibutton_worker_stop(ibutton->worker);
widget_reset(ibutton->widget);
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_info.c b/applications/main/ibutton/scenes/ibutton_scene_info.c
index 15648f6f2..cf44d6a86 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_info.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_info.c
@@ -1,66 +1,54 @@
#include "../ibutton_i.h"
-#include
void ibutton_scene_info_on_enter(void* context) {
iButton* ibutton = context;
- Widget* widget = ibutton->widget;
iButtonKey* key = ibutton->key;
+ Widget* widget = ibutton->widget;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
- FuriString* key_name;
- key_name = furi_string_alloc();
- path_extract_filename(ibutton->file_path, key_name, true);
+ FuriString* tmp = furi_string_alloc();
+
+ furi_string_printf(
+ tmp,
+ "\e#%s [%s]\e#",
+ ibutton->key_name,
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id));
- ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
widget_add_text_box_element(
- widget, 0, 0, 128, 23, AlignCenter, AlignCenter, ibutton->text_store, true);
+ widget, 0, 2, 128, 12, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true);
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X %02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Dallas");
- break;
+ furi_string_reset(tmp);
+ ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
- widget_add_string_element(
- widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Metakom");
- break;
+ widget_add_string_multiline_element(
+ widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
- widget_add_string_element(widget, 64, 36, AlignCenter, AlignBottom, FontPrimary, "Cyfral");
- break;
+ if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) &
+ iButtonProtocolFeatureExtData) {
+ widget_add_button_element(
+ widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
}
- widget_add_string_element(
- widget, 64, 50, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store);
-
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
-
- furi_string_free(key_name);
+ furi_string_free(tmp);
}
bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) {
- UNUSED(context);
- UNUSED(event);
- return false;
+ iButton* ibutton = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ if(event.event == GuiButtonTypeRight) {
+ scene_manager_next_scene(ibutton->scene_manager, iButtonSceneViewData);
+ }
+ }
+
+ return consumed;
}
void ibutton_scene_info_on_exit(void* context) {
iButton* ibutton = context;
- ibutton_text_store_clear(ibutton);
widget_reset(ibutton->widget);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c
index 1ccd2562b..96bee7961 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read.c
@@ -11,14 +11,13 @@ void ibutton_scene_read_on_enter(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
iButtonKey* key = ibutton->key;
- iButtonWorker* worker = ibutton->key_worker;
+ iButtonWorker* worker = ibutton->worker;
popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 5, XTREME_ASSETS()->I_DolphinWait_61x59);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
- furi_string_set(ibutton->file_path, IBUTTON_APP_FOLDER);
ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton);
ibutton_worker_read_start(worker, key);
@@ -36,25 +35,14 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == iButtonCustomEventWorkerRead) {
- bool success = false;
- iButtonKey* key = ibutton->key;
-
- if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
- if(!ibutton_key_dallas_crc_is_valid(key)) {
- scene_manager_next_scene(scene_manager, iButtonSceneReadCRCError);
- } else if(!ibutton_key_dallas_is_1990_key(key)) {
- scene_manager_next_scene(scene_manager, iButtonSceneReadNotKeyError);
- } else {
- success = true;
- }
- } else {
- success = true;
- }
-
- if(success) {
+ if(ibutton_protocols_is_valid(ibutton->protocols, ibutton->key)) {
ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);
scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess);
+
DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
+
+ } else {
+ scene_manager_next_scene(scene_manager, iButtonSceneReadError);
}
}
}
@@ -65,7 +53,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
void ibutton_scene_read_on_exit(void* context) {
iButton* ibutton = context;
Popup* popup = ibutton->popup;
- ibutton_worker_stop(ibutton->key_worker);
+ ibutton_worker_stop(ibutton->worker);
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 0, NULL);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c
deleted file mode 100644
index f822ff6a2..000000000
--- a/applications/main/ibutton/scenes/ibutton_scene_read_crc_error.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "../ibutton_i.h"
-#include
-
-static void ibutton_scene_read_crc_error_dialog_ex_callback(DialogExResult result, void* context) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
-}
-
-void ibutton_scene_read_crc_error_on_enter(void* context) {
- iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
- iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
-
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7],
- maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
-
- dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
- dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop);
- dialog_ex_set_left_button_text(dialog_ex, "Retry");
- dialog_ex_set_right_button_text(dialog_ex, "More");
- dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_crc_error_dialog_ex_callback);
- dialog_ex_set_context(dialog_ex, ibutton);
-
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
-
- ibutton_notification_message(ibutton, iButtonNotificationMessageError);
- ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
-}
-
-bool ibutton_scene_read_crc_error_on_event(void* context, SceneManagerEvent event) {
- iButton* ibutton = context;
- SceneManager* scene_manager = ibutton->scene_manager;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeBack) {
- consumed = true;
- scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
- } else if(event.type == SceneManagerEventTypeCustom) {
- consumed = true;
- if(event.event == DialogExResultRight) {
- scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
- } else if(event.event == DialogExResultLeft) {
- scene_manager_previous_scene(scene_manager);
- }
- }
-
- return consumed;
-}
-
-void ibutton_scene_read_crc_error_on_exit(void* context) {
- iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
-
- ibutton_text_store_clear(ibutton);
-
- dialog_ex_reset(dialog_ex);
-
- ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
-}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_error.c
new file mode 100644
index 000000000..e966384bf
--- /dev/null
+++ b/applications/main/ibutton/scenes/ibutton_scene_read_error.c
@@ -0,0 +1,58 @@
+#include "../ibutton_i.h"
+#include
+
+void ibutton_scene_read_error_on_enter(void* context) {
+ iButton* ibutton = context;
+ iButtonKey* key = ibutton->key;
+
+ Widget* widget = ibutton->widget;
+
+ FuriString* tmp = furi_string_alloc();
+
+ widget_add_button_element(
+ widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context);
+ widget_add_button_element(
+ widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
+
+ widget_add_string_element(
+ widget, 128 / 2, 2, AlignCenter, AlignTop, FontPrimary, "Read Error");
+
+ ibutton_protocols_render_error(ibutton->protocols, key, tmp);
+
+ widget_add_string_multiline_element(
+ widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
+
+ ibutton_notification_message(ibutton, iButtonNotificationMessageError);
+ ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
+
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+ furi_string_free(tmp);
+}
+
+bool ibutton_scene_read_error_on_event(void* context, SceneManagerEvent event) {
+ iButton* ibutton = context;
+ SceneManager* scene_manager = ibutton->scene_manager;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeBack) {
+ consumed = true;
+ scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
+
+ } else if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ if(event.event == GuiButtonTypeLeft) {
+ scene_manager_previous_scene(scene_manager);
+ } else if(event.event == GuiButtonTypeRight) {
+ scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
+ }
+ }
+
+ return consumed;
+}
+
+void ibutton_scene_read_error_on_exit(void* context) {
+ iButton* ibutton = context;
+
+ ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
+ widget_reset(ibutton->widget);
+}
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 0a8ecfa55..716f72c7d 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c
@@ -4,7 +4,9 @@
typedef enum {
SubmenuIndexSave,
SubmenuIndexEmulate,
- SubmenuIndexWrite,
+ SubmenuIndexViewData,
+ SubmenuIndexWriteBlank,
+ SubmenuIndexWriteCopy,
} SubmenuIndex;
void ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) {
@@ -16,6 +18,9 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
iButton* ibutton = context;
Submenu* submenu = ibutton->submenu;
+ const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(ibutton->key);
+ const uint32_t features = ibutton_protocols_get_features(ibutton->protocols, protocol_id);
+
submenu_add_item(
submenu, "Save", SubmenuIndexSave, ibutton_scene_read_key_menu_submenu_callback, ibutton);
submenu_add_item(
@@ -24,36 +29,66 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
SubmenuIndexEmulate,
ibutton_scene_read_key_menu_submenu_callback,
ibutton);
- if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) {
+
+ if(features & iButtonProtocolFeatureExtData) {
submenu_add_item(
submenu,
- "Write",
- SubmenuIndexWrite,
+ "View Data",
+ SubmenuIndexViewData,
ibutton_scene_read_key_menu_submenu_callback,
ibutton);
}
+
+ if(features & iButtonProtocolFeatureWriteBlank) {
+ submenu_add_item(
+ submenu,
+ "Write Blank",
+ SubmenuIndexWriteBlank,
+ ibutton_scene_read_key_menu_submenu_callback,
+ ibutton);
+ }
+
+ if(features & iButtonProtocolFeatureWriteCopy) {
+ submenu_add_item(
+ submenu,
+ "Write Copy",
+ SubmenuIndexWriteCopy,
+ ibutton_scene_read_key_menu_submenu_callback,
+ ibutton);
+ }
+
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneReadKeyMenu));
-
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
}
bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) {
iButton* ibutton = context;
+ SceneManager* scene_manager = ibutton->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- scene_manager_set_scene_state(
- ibutton->scene_manager, iButtonSceneReadKeyMenu, event.event);
+ scene_manager_set_scene_state(scene_manager, iButtonSceneReadKeyMenu, event.event);
consumed = true;
+
if(event.event == SubmenuIndexSave) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
+ scene_manager_next_scene(scene_manager, iButtonSceneSaveName);
} else if(event.event == SubmenuIndexEmulate) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+ scene_manager_next_scene(scene_manager, iButtonSceneEmulate);
DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
- } else if(event.event == SubmenuIndexWrite) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
+ } else if(event.event == SubmenuIndexViewData) {
+ scene_manager_next_scene(scene_manager, iButtonSceneViewData);
+ } else if(event.event == SubmenuIndexWriteBlank) {
+ ibutton->write_mode = iButtonWriteModeBlank;
+ scene_manager_next_scene(scene_manager, iButtonSceneWrite);
+ } else if(event.event == SubmenuIndexWriteCopy) {
+ ibutton->write_mode = iButtonWriteModeCopy;
+ scene_manager_next_scene(scene_manager, iButtonSceneWrite);
}
+ } else if(event.event == SceneManagerEventTypeBack) {
+ scene_manager_set_scene_state(
+ ibutton->scene_manager, iButtonSceneReadKeyMenu, SubmenuIndexSave);
+ // Event is not consumed
}
return consumed;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c b/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c
deleted file mode 100644
index 8a7528031..000000000
--- a/applications/main/ibutton/scenes/ibutton_scene_read_not_key_error.c
+++ /dev/null
@@ -1,71 +0,0 @@
-#include "../ibutton_i.h"
-#include
-
-static void
- ibutton_scene_read_not_key_error_dialog_ex_callback(DialogExResult result, void* context) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
-}
-
-void ibutton_scene_read_not_key_error_on_enter(void* context) {
- iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
- iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
-
- ibutton_text_store_set(
- ibutton,
- "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7],
- maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
-
- dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
- dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop);
- dialog_ex_set_left_button_text(dialog_ex, "Retry");
- dialog_ex_set_right_button_text(dialog_ex, "More");
- dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_not_key_error_dialog_ex_callback);
- dialog_ex_set_context(dialog_ex, ibutton);
-
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
-
- ibutton_notification_message(ibutton, iButtonNotificationMessageError);
- ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
-}
-
-bool ibutton_scene_read_not_key_error_on_event(void* context, SceneManagerEvent event) {
- iButton* ibutton = context;
- SceneManager* scene_manager = ibutton->scene_manager;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeBack) {
- consumed = true;
- scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
- } else if(event.type == SceneManagerEventTypeCustom) {
- consumed = true;
- if(event.event == DialogExResultRight) {
- scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
- } else if(event.event == DialogExResultLeft) {
- scene_manager_previous_scene(scene_manager);
- }
- }
-
- return consumed;
-}
-
-void ibutton_scene_read_not_key_error_on_exit(void* context) {
- iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
-
- ibutton_text_store_clear(ibutton);
-
- dialog_ex_reset(dialog_ex);
-
- ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
-}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_success.c b/applications/main/ibutton/scenes/ibutton_scene_read_success.c
index 749e7af37..2e50bc996 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_read_success.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_read_success.c
@@ -1,55 +1,40 @@
#include "../ibutton_i.h"
-#include
-static void ibutton_scene_read_success_dialog_ex_callback(DialogExResult result, void* context) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
-}
+#include
void ibutton_scene_read_success_on_enter(void* context) {
iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
iButtonKey* key = ibutton->key;
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ Widget* widget = ibutton->widget;
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- break;
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "Cyfral\n%02X %02X", key_data[0], key_data[1]);
- break;
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton,
- "Metakom\n%02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3]);
- break;
- }
+ FuriString* tmp = furi_string_alloc();
- dialog_ex_set_text(dialog_ex, ibutton->text_store, 95, 30, AlignCenter, AlignCenter);
- dialog_ex_set_left_button_text(dialog_ex, "Retry");
- dialog_ex_set_right_button_text(dialog_ex, "More");
- dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
- dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_success_dialog_ex_callback);
- dialog_ex_set_context(dialog_ex, ibutton);
+ const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
+ widget_add_button_element(
+ widget, GuiButtonTypeLeft, "Retry", ibutton_widget_callback, context);
+ widget_add_button_element(
+ widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
+ furi_string_printf(
+ tmp,
+ "%s[%s]",
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id),
+ ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id));
+
+ widget_add_string_element(
+ widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
+
+ furi_string_reset(tmp);
+ ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
+
+ widget_add_string_multiline_element(
+ widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
+
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);
+
+ furi_string_free(tmp);
}
bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) {
@@ -62,9 +47,9 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event)
scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
- if(event.event == DialogExResultRight) {
+ if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
- } else if(event.event == DialogExResultLeft) {
+ } else if(event.event == GuiButtonTypeLeft) {
scene_manager_next_scene(scene_manager, iButtonSceneRetryConfirm);
}
}
@@ -74,11 +59,8 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event)
void ibutton_scene_read_success_on_exit(void* context) {
iButton* ibutton = context;
- DialogEx* dialog_ex = ibutton->dialog_ex;
- ibutton_text_store_clear(ibutton);
-
- dialog_ex_reset(dialog_ex);
+ widget_reset(ibutton->widget);
ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff);
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_rpc.c b/applications/main/ibutton/scenes/ibutton_scene_rpc.c
index b25b1b8dd..9205eb4b4 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_rpc.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_rpc.c
@@ -1,6 +1,4 @@
#include "../ibutton_i.h"
-#include
-#include
void ibutton_scene_rpc_on_enter(void* context) {
iButton* ibutton = context;
@@ -17,8 +15,6 @@ void ibutton_scene_rpc_on_enter(void* context) {
}
bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
- UNUSED(context);
- UNUSED(event);
iButton* ibutton = context;
Popup* popup = ibutton->popup;
@@ -26,40 +22,32 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
- if(event.event == iButtonCustomEventRpcLoad) {
- const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx);
- bool result = false;
- if(arg && (furi_string_empty(ibutton->file_path))) {
- furi_string_set(ibutton->file_path, arg);
- if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) {
- ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key);
- FuriString* key_name;
- key_name = furi_string_alloc();
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- path_extract_filename(ibutton->file_path, key_name, true);
- }
- if(!furi_string_empty(key_name)) {
- ibutton_text_store_set(
- ibutton, "emulating\n%s", furi_string_get_cstr(key_name));
- } else {
- ibutton_text_store_set(ibutton, "emulating");
- }
- popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop);
+ if(event.event == iButtonCustomEventRpcLoad) {
+ bool result = false;
+ const char* file_path = rpc_system_app_get_data(ibutton->rpc);
+
+ if(file_path && (furi_string_empty(ibutton->file_path))) {
+ furi_string_set(ibutton->file_path, file_path);
+
+ if(ibutton_load_key(ibutton)) {
+ popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
+ ibutton_worker_emulate_start(ibutton->worker, ibutton->key);
- furi_string_free(key_name);
result = true;
- } else {
- furi_string_reset(ibutton->file_path);
}
}
- rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result);
+
+ rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result);
+
} else if(event.event == iButtonCustomEventRpcExit) {
- rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true);
+ rpc_system_app_confirm(ibutton->rpc, RpcAppEventAppExit, true);
scene_manager_stop(ibutton->scene_manager);
view_dispatcher_stop(ibutton->view_dispatcher);
+
} else if(event.event == iButtonCustomEventRpcSessionClose) {
scene_manager_stop(ibutton->scene_manager);
view_dispatcher_stop(ibutton->view_dispatcher);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c
index 5f25a0002..4ad0315e5 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c
@@ -1,6 +1,8 @@
#include "../ibutton_i.h"
-#include
+
+#include
#include
+
#include
static void ibutton_scene_save_name_text_input_callback(void* context) {
@@ -12,17 +14,10 @@ void ibutton_scene_save_name_on_enter(void* context) {
iButton* ibutton = context;
TextInput* text_input = ibutton->text_input;
- FuriString* key_name;
- key_name = furi_string_alloc();
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- path_extract_filename(ibutton->file_path, key_name, true);
- }
+ const bool is_new_file = furi_string_empty(ibutton->file_path);
- const bool key_name_is_empty = furi_string_empty(key_name);
- if(key_name_is_empty) {
- set_random_name(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE);
- } else {
- ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
+ if(is_new_file) {
+ set_random_name(ibutton->key_name, IBUTTON_KEY_NAME_SIZE);
}
text_input_set_header_text(text_input, "Name the key");
@@ -30,23 +25,15 @@ void ibutton_scene_save_name_on_enter(void* context) {
text_input,
ibutton_scene_save_name_text_input_callback,
ibutton,
- ibutton->text_store,
+ ibutton->key_name,
IBUTTON_KEY_NAME_SIZE,
- key_name_is_empty);
+ is_new_file);
- FuriString* folder_path;
- folder_path = furi_string_alloc();
-
- path_extract_dirname(furi_string_get_cstr(ibutton->file_path), folder_path);
-
- ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
- furi_string_get_cstr(folder_path), IBUTTON_APP_EXTENSION, furi_string_get_cstr(key_name));
+ ValidatorIsFile* validator_is_file =
+ validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, ibutton->key_name);
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput);
-
- furi_string_free(key_name);
- furi_string_free(folder_path);
}
bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
@@ -56,8 +43,16 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == iButtonCustomEventTextEditResult) {
- if(ibutton_save_key(ibutton, ibutton->text_store)) {
+ furi_string_printf(
+ ibutton->file_path,
+ "%s/%s%s",
+ IBUTTON_APP_FOLDER,
+ ibutton->key_name,
+ IBUTTON_APP_EXTENSION);
+
+ if(ibutton_save_key(ibutton)) {
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
@@ -67,6 +62,7 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
} else {
DOLPHIN_DEED(DolphinDeedIbuttonSave);
}
+
} else {
const uint32_t possible_scenes[] = {
iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType};
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 e4c9c350a..80fca28b5 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c
@@ -3,72 +3,70 @@
enum SubmenuIndex {
SubmenuIndexEmulate,
- SubmenuIndexWrite,
+ SubmenuIndexWriteBlank,
+ SubmenuIndexWriteCopy,
SubmenuIndexEdit,
SubmenuIndexDelete,
SubmenuIndexInfo,
};
-void ibutton_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
-}
-
void ibutton_scene_saved_key_menu_on_enter(void* context) {
iButton* ibutton = context;
Submenu* submenu = ibutton->submenu;
- submenu_add_item(
- submenu,
- "Emulate",
- SubmenuIndexEmulate,
- ibutton_scene_saved_key_menu_submenu_callback,
- ibutton);
- if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) {
+ const uint32_t features = ibutton_protocols_get_features(
+ ibutton->protocols, ibutton_key_get_protocol_id(ibutton->key));
+
+ submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, ibutton_submenu_callback, ibutton);
+
+ if(features & iButtonProtocolFeatureWriteBlank) {
submenu_add_item(
- submenu,
- "Write",
- SubmenuIndexWrite,
- ibutton_scene_saved_key_menu_submenu_callback,
- ibutton);
+ submenu, "Write Blank", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton);
}
- submenu_add_item(
- submenu, "Edit", SubmenuIndexEdit, ibutton_scene_saved_key_menu_submenu_callback, ibutton);
- submenu_add_item(
- submenu,
- "Delete",
- SubmenuIndexDelete,
- ibutton_scene_saved_key_menu_submenu_callback,
- ibutton);
- submenu_add_item(
- submenu, "Info", SubmenuIndexInfo, ibutton_scene_saved_key_menu_submenu_callback, ibutton);
+
+ if(features & iButtonProtocolFeatureWriteCopy) {
+ submenu_add_item(
+ submenu, "Write Copy", SubmenuIndexWriteCopy, ibutton_submenu_callback, ibutton);
+ }
+
+ submenu_add_item(submenu, "Edit", SubmenuIndexEdit, ibutton_submenu_callback, ibutton);
+ submenu_add_item(submenu, "Delete", SubmenuIndexDelete, ibutton_submenu_callback, ibutton);
+ submenu_add_item(submenu, "Info", SubmenuIndexInfo, ibutton_submenu_callback, ibutton);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneSavedKeyMenu));
-
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
}
bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) {
iButton* ibutton = context;
+ SceneManager* scene_manager = ibutton->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
- scene_manager_set_scene_state(
- ibutton->scene_manager, iButtonSceneSavedKeyMenu, event.event);
+ scene_manager_set_scene_state(scene_manager, iButtonSceneSavedKeyMenu, event.event);
consumed = true;
if(event.event == SubmenuIndexEmulate) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+ scene_manager_next_scene(scene_manager, iButtonSceneEmulate);
DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
- } else if(event.event == SubmenuIndexWrite) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
+ } else if(event.event == SubmenuIndexWriteBlank) {
+ ibutton->write_mode = iButtonWriteModeBlank;
+ scene_manager_next_scene(scene_manager, iButtonSceneWrite);
+ } else if(event.event == SubmenuIndexWriteCopy) {
+ ibutton->write_mode = iButtonWriteModeCopy;
+ scene_manager_next_scene(scene_manager, iButtonSceneWrite);
} else if(event.event == SubmenuIndexEdit) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);
+ scene_manager_next_scene(scene_manager, iButtonSceneAddValue);
} else if(event.event == SubmenuIndexDelete) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneDeleteConfirm);
+ scene_manager_next_scene(scene_manager, iButtonSceneDeleteConfirm);
} else if(event.event == SubmenuIndexInfo) {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneInfo);
+ scene_manager_next_scene(scene_manager, iButtonSceneInfo);
}
+
+ } else if(event.type == SceneManagerEventTypeBack) {
+ scene_manager_set_scene_state(
+ scene_manager, iButtonSceneSavedKeyMenu, SubmenuIndexEmulate);
+ // Event is not consumed
}
return consumed;
diff --git a/applications/main/ibutton/scenes/ibutton_scene_select_key.c b/applications/main/ibutton/scenes/ibutton_scene_select_key.c
index 32169a9c0..ebd504b1f 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_select_key.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_select_key.c
@@ -3,11 +3,11 @@
void ibutton_scene_select_key_on_enter(void* context) {
iButton* ibutton = context;
- if(!ibutton_file_select(ibutton)) {
+ if(ibutton_select_and_load_key(ibutton)) {
+ scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu);
+ } else {
scene_manager_search_and_switch_to_previous_scene(
ibutton->scene_manager, iButtonSceneStart);
- } else {
- scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu);
}
}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c
index 28807faa8..37bf96f39 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_start.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_start.c
@@ -8,21 +8,15 @@ enum SubmenuIndex {
SubmenuIndexAdd,
};
-void ibutton_scene_start_submenu_callback(void* context, uint32_t index) {
- iButton* ibutton = context;
- view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
-}
-
void ibutton_scene_start_on_enter(void* context) {
iButton* ibutton = context;
Submenu* submenu = ibutton->submenu;
- submenu_add_item(
- submenu, "Read", SubmenuIndexRead, ibutton_scene_start_submenu_callback, ibutton);
- submenu_add_item(
- submenu, "Saved", SubmenuIndexSaved, ibutton_scene_start_submenu_callback, ibutton);
- submenu_add_item(
- submenu, "Add Manually", SubmenuIndexAdd, ibutton_scene_start_submenu_callback, ibutton);
+ ibutton_reset_key(ibutton);
+
+ submenu_add_item(submenu, "Read", SubmenuIndexRead, ibutton_submenu_callback, ibutton);
+ submenu_add_item(submenu, "Saved", SubmenuIndexSaved, ibutton_submenu_callback, ibutton);
+ submenu_add_item(submenu, "Add Manually", SubmenuIndexAdd, ibutton_submenu_callback, ibutton);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneStart));
@@ -39,9 +33,8 @@ 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);
+ 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);
} else if(event.event == SubmenuIndexAdd) {
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType);
diff --git a/applications/main/ibutton/scenes/ibutton_scene_view_data.c b/applications/main/ibutton/scenes/ibutton_scene_view_data.c
new file mode 100644
index 000000000..7e063d7ec
--- /dev/null
+++ b/applications/main/ibutton/scenes/ibutton_scene_view_data.c
@@ -0,0 +1,26 @@
+#include "../ibutton_i.h"
+
+void ibutton_scene_view_data_on_enter(void* context) {
+ iButton* ibutton = context;
+ iButtonKey* key = ibutton->key;
+ Widget* widget = ibutton->widget;
+
+ FuriString* tmp = furi_string_alloc();
+ ibutton_protocols_render_data(ibutton->protocols, key, tmp);
+
+ widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(tmp));
+
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+ furi_string_free(tmp);
+}
+
+bool ibutton_scene_view_data_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void ibutton_scene_view_data_on_exit(void* context) {
+ iButton* ibutton = context;
+ widget_reset(ibutton->widget);
+}
diff --git a/applications/main/ibutton/scenes/ibutton_scene_write.c b/applications/main/ibutton/scenes/ibutton_scene_write.c
index cdea04db3..541aa1c52 100644
--- a/applications/main/ibutton/scenes/ibutton_scene_write.c
+++ b/applications/main/ibutton/scenes/ibutton_scene_write.c
@@ -1,5 +1,4 @@
#include "../ibutton_i.h"
-#include "toolbox/path.h"
typedef enum {
iButtonSceneWriteStateDefault,
@@ -13,61 +12,46 @@ static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult
void ibutton_scene_write_on_enter(void* context) {
iButton* ibutton = context;
+ furi_assert(ibutton->write_mode != iButtonWriteModeInvalid);
+
iButtonKey* key = ibutton->key;
+ iButtonWorker* worker = ibutton->worker;
+ const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
+
Widget* widget = ibutton->widget;
- iButtonWorker* worker = ibutton->key_worker;
+ FuriString* tmp = furi_string_alloc();
- const uint8_t* key_data = ibutton_key_get_data_p(key);
+ widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
- FuriString* key_name;
- key_name = furi_string_alloc();
- if(furi_string_end_with(ibutton->file_path, IBUTTON_APP_EXTENSION)) {
- path_extract_filename(ibutton->file_path, key_name, true);
- }
+ furi_string_printf(
+ tmp,
+ "%s\n[%s]",
+ ibutton->key_name,
+ ibutton_protocols_get_name(ibutton->protocols, protocol_id));
- // check that stored key has name
- if(!furi_string_empty(key_name)) {
- ibutton_text_store_set(ibutton, "%s", furi_string_get_cstr(key_name));
- } else {
- // if not, show key data
- switch(ibutton_key_get_type(key)) {
- case iButtonKeyDS1990:
- ibutton_text_store_set(
- ibutton,
- "%02X %02X %02X %02X\n%02X %02X %02X %02X",
- key_data[0],
- key_data[1],
- key_data[2],
- key_data[3],
- key_data[4],
- key_data[5],
- key_data[6],
- key_data[7]);
- break;
- case iButtonKeyCyfral:
- ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
- break;
- case iButtonKeyMetakom:
- ibutton_text_store_set(
- ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
- break;
- }
+ widget_add_text_box_element(
+ widget, 52, 38, 75, 26, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
+
+ ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
+
+ furi_string_set(tmp, "iButton\nwriting ");
+
+ if(ibutton->write_mode == iButtonWriteModeBlank) {
+ furi_string_cat(tmp, "Blank");
+ ibutton_worker_write_blank_start(worker, key);
+
+ } else if(ibutton->write_mode == iButtonWriteModeCopy) {
+ furi_string_cat(tmp, "Copy");
+ ibutton_worker_write_copy_start(worker, key);
}
widget_add_string_multiline_element(
- widget, 90, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nwriting");
- widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
- widget_add_text_box_element(
- widget, 54, 39, 75, 22, AlignCenter, AlignCenter, ibutton->text_store, true);
-
- view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
-
- ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
- ibutton_worker_write_start(worker, key);
-
- furi_string_free(key_name);
+ widget, 88, 10, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
+ view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+
+ furi_string_free(tmp);
}
bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {
@@ -94,7 +78,9 @@ bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {
void ibutton_scene_write_on_exit(void* context) {
iButton* ibutton = context;
- ibutton_worker_stop(ibutton->key_worker);
+ ibutton->write_mode = iButtonWriteModeInvalid;
+
+ ibutton_worker_stop(ibutton->worker);
widget_reset(ibutton->widget);
ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop);
diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c
index 9d78a09b6..a88306cc5 100644
--- a/applications/main/infrared/infrared.c
+++ b/applications/main/infrared/infrared.c
@@ -3,6 +3,8 @@
#include
#include
+#define INFRARED_TX_MIN_INTERVAL_MS 50U
+
static const NotificationSequence* infrared_notification_sequences[] = {
&sequence_success,
&sequence_set_only_green_255,
@@ -148,6 +150,12 @@ static Infrared* infrared_alloc() {
view_dispatcher_add_view(
view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input));
+ infrared->variable_item_list = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ infrared->view_dispatcher,
+ InfraredViewVariableItemList,
+ variable_item_list_get_view(infrared->variable_item_list));
+
infrared->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
@@ -195,6 +203,9 @@ static void infrared_free(Infrared* infrared) {
view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput);
text_input_free(infrared->text_input);
+ view_dispatcher_remove_view(infrared->view_dispatcher, InfraredViewVariableItemList);
+ variable_item_list_free(infrared->variable_item_list);
+
view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);
dialog_ex_free(infrared->dialog_ex);
@@ -299,10 +310,13 @@ bool infrared_rename_current_remote(Infrared* infrared, const char* name) {
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
if(infrared->app_state.is_transmitting) {
- FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already active");
return;
- } else {
- infrared->app_state.is_transmitting = true;
+ }
+
+ const uint32_t time_elapsed = furi_get_tick() - infrared->app_state.last_transmit_time;
+
+ if(time_elapsed < INFRARED_TX_MIN_INTERVAL_MS) {
+ return;
}
if(infrared_signal_is_raw(signal)) {
@@ -319,6 +333,8 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
infrared_worker_tx_set_get_signal_callback(
infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared);
infrared_worker_tx_start(infrared->worker);
+
+ infrared->app_state.is_transmitting = true;
}
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
@@ -328,26 +344,24 @@ void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
InfraredSignal* signal = infrared_remote_button_get_signal(button);
infrared_tx_start_signal(infrared, signal);
- infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
}
void infrared_tx_start_received(Infrared* infrared) {
infrared_tx_start_signal(infrared, infrared->received_signal);
- infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
}
void infrared_tx_stop(Infrared* infrared) {
if(!infrared->app_state.is_transmitting) {
- FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already stopped");
return;
- } else {
- infrared->app_state.is_transmitting = false;
}
infrared_worker_tx_stop(infrared->worker);
infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL);
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
+
+ infrared->app_state.is_transmitting = false;
+ infrared->app_state.last_transmit_time = furi_get_tick();
}
void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) {
diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c
index 5f5e2d4bb..3fa99cb02 100644
--- a/applications/main/infrared/infrared_cli.c
+++ b/applications/main/infrared/infrared_cli.c
@@ -86,7 +86,7 @@ static void infrared_cli_print_usage(void) {
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");
+ printf("\tAvailable universal remotes: tv audio ac projector\r\n");
}
static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) {
diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h
index 6972d53b4..55b70fc5e 100644
--- a/applications/main/infrared/infrared_i.h
+++ b/applications/main/infrared/infrared_i.h
@@ -2,6 +2,7 @@
#include
#include
+#include
#include
#include
#include
@@ -9,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -32,8 +34,6 @@
#include "rpc/rpc_app.h"
-#include
-
#define INFRARED_FILE_NAME_SIZE 100
#define INFRARED_TEXT_STORE_NUM 2
#define INFRARED_TEXT_STORE_SIZE 128
@@ -70,6 +70,7 @@ typedef struct {
InfraredEditTarget edit_target : 8;
InfraredEditMode edit_mode : 8;
int32_t current_button_index;
+ uint32_t last_transmit_time;
} InfraredAppState;
struct Infrared {
@@ -87,6 +88,7 @@ struct Infrared {
Submenu* submenu;
TextInput* text_input;
+ VariableItemList* variable_item_list;
DialogEx* dialog_ex;
ButtonMenu* button_menu;
Popup* popup;
@@ -108,6 +110,7 @@ struct Infrared {
typedef enum {
InfraredViewSubmenu,
InfraredViewTextInput,
+ InfraredViewVariableItemList,
InfraredViewDialogEx,
InfraredViewButtonMenu,
InfraredViewPopup,
diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h
index 551485295..b15974941 100644
--- a/applications/main/infrared/scenes/infrared_scene_config.h
+++ b/applications/main/infrared/scenes/infrared_scene_config.h
@@ -21,4 +21,5 @@ ADD_SCENE(infrared, universal_audio, UniversalAudio)
ADD_SCENE(infrared, universal_projector, UniversalProjector)
ADD_SCENE(infrared, debug, Debug)
ADD_SCENE(infrared, error_databases, ErrorDatabases)
+ADD_SCENE(infrared, debug_settings, DebugSettings)
ADD_SCENE(infrared, rpc, Rpc)
diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c
new file mode 100644
index 000000000..0bc830788
--- /dev/null
+++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c
@@ -0,0 +1,59 @@
+#include "../infrared_i.h"
+#include
+
+uint8_t value_index_ir;
+
+#define DEB_PINS_COUNT (sizeof(infrared_debug_cfg_variables_text) / sizeof(char* const))
+const char* const infrared_debug_cfg_variables_text[] = {
+ "Internal",
+ "2 (A7)",
+};
+
+static void infrared_scene_debug_settings_changed(VariableItem* item) {
+ Infrared* infrared = variable_item_get_context(item);
+ value_index_ir = variable_item_get_current_value_index(item);
+ UNUSED(infrared);
+
+ variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
+
+ furi_hal_infrared_set_debug_out(value_index_ir);
+}
+static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) {
+ Infrared* infrared = context;
+ view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
+}
+
+void infrared_scene_debug_settings_on_enter(void* context) {
+ Infrared* infrared = context;
+
+ VariableItemList* variable_item_list = infrared->variable_item_list;
+
+ value_index_ir = furi_hal_infrared_get_debug_out_status();
+ VariableItem* item = variable_item_list_add(
+ variable_item_list,
+ "Send signal to",
+ DEB_PINS_COUNT,
+ infrared_scene_debug_settings_changed,
+ infrared);
+
+ variable_item_list_set_enter_callback(
+ variable_item_list, infrared_debug_settings_start_var_list_enter_callback, infrared);
+
+ variable_item_set_current_value_index(item, value_index_ir);
+ variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]);
+
+ view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList);
+}
+
+bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent event) {
+ Infrared* infrared = context;
+ UNUSED(infrared);
+ UNUSED(event);
+
+ return false;
+}
+
+void infrared_scene_debug_settings_on_exit(void* context) {
+ Infrared* infrared = context;
+ variable_item_list_reset(infrared->variable_item_list);
+}
diff --git a/applications/main/infrared/scenes/infrared_scene_start.c b/applications/main/infrared/scenes/infrared_scene_start.c
index 4d13d6e0e..0403e40f8 100644
--- a/applications/main/infrared/scenes/infrared_scene_start.c
+++ b/applications/main/infrared/scenes/infrared_scene_start.c
@@ -5,7 +5,8 @@ enum SubmenuIndex {
SubmenuIndexLearnNewRemote,
SubmenuIndexLearnNewRemoteRaw,
SubmenuIndexSavedRemotes,
- SubmenuIndexDebug
+ SubmenuIndexDebug,
+ SubmenuIndexDebugSettings
};
static void infrared_scene_start_submenu_callback(void* context, uint32_t index) {
@@ -45,7 +46,17 @@ void infrared_scene_start_on_enter(void* context) {
infrared_scene_start_submenu_callback,
infrared);
submenu_add_item(
- submenu, "Debug", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared);
+ submenu,
+ "Debug RX",
+ SubmenuIndexDebug,
+ infrared_scene_start_submenu_callback,
+ infrared);
+ submenu_add_item(
+ submenu,
+ "Debug Settings",
+ SubmenuIndexDebugSettings,
+ infrared_scene_start_submenu_callback,
+ infrared);
}
const uint32_t submenu_index =
@@ -85,6 +96,9 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(submenu_index == SubmenuIndexDebug) {
scene_manager_next_scene(scene_manager, InfraredSceneDebug);
consumed = true;
+ } else if(submenu_index == SubmenuIndexDebugSettings) {
+ scene_manager_next_scene(scene_manager, InfraredSceneDebugSettings);
+ consumed = true;
}
}
diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c
index 09b77b1f7..b6c1ac983 100644
--- a/applications/main/infrared/scenes/infrared_scene_universal.c
+++ b/applications/main/infrared/scenes/infrared_scene_universal.c
@@ -52,7 +52,8 @@ void infrared_scene_universal_on_enter(void* context) {
infrared_scene_universal_submenu_callback,
context);
- submenu_set_selected_item(submenu, 0);
+ submenu_set_selected_item(
+ submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal));
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu);
}
@@ -79,6 +80,7 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC);
consumed = true;
}
+ scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event);
}
return consumed;
diff --git a/applications/main/infrared/views/infrared_progress_view.h b/applications/main/infrared/views/infrared_progress_view.h
index e8f76ba1f..4f3c3a875 100644
--- a/applications/main/infrared/views/infrared_progress_view.h
+++ b/applications/main/infrared/views/infrared_progress_view.h
@@ -10,7 +10,7 @@
extern "C" {
#endif
-/** Anonumous instance */
+/** Anonymous instance */
typedef struct InfraredProgressView InfraredProgressView;
/** Callback for back button handling */
diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h
index 201caa4f6..72b061930 100644
--- a/applications/main/lfrfid/lfrfid_i.h
+++ b/applications/main/lfrfid/lfrfid_i.h
@@ -5,8 +5,10 @@
#include
#include
+#include
#include
#include
+#include
#include
#include
@@ -32,8 +34,6 @@
#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_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
index 888288a31..a950de708 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c
@@ -16,7 +16,7 @@ static void lfrfid_clear_t5577_password_and_config_to_EM(LfRfid* app) {
Popup* popup = app->popup;
char curr_buf[32] = {};
- //TODO: use .txt file in resourses for passwords.
+ //TODO: use .txt file in resources for passwords.
const uint32_t default_passwords[] = {
0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F,
0x89A69E60, 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752,
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
index b1130d191..efa319c1e 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c
@@ -20,7 +20,7 @@ void lfrfid_scene_extra_actions_on_enter(void* context) {
submenu_add_item(
submenu,
- "Read ASK (Animal, FDX)",
+ "Read ASK (FDX,Regular)",
SubmenuIndexASK,
lfrfid_scene_extra_actions_submenu_callback,
app);
diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c
index 8e1c92dbb..2d83ba53b 100644
--- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c
+++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c
@@ -47,21 +47,28 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) {
+ scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead);
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
DOLPHIN_DEED(DolphinDeedRfidRead);
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
+ // Like in the other apps, explicitly save the scene state
+ // in each branch in case the user cancels loading a file.
+ scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexSaved);
furi_string_set(app->file_path, LFRFID_APP_FOLDER);
scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey);
consumed = true;
} else if(event.event == SubmenuIndexAddManually) {
+ scene_manager_set_scene_state(
+ app->scene_manager, LfRfidSceneStart, SubmenuIndexAddManually);
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType);
consumed = true;
} else if(event.event == SubmenuIndexExtraActions) {
+ scene_manager_set_scene_state(
+ app->scene_manager, LfRfidSceneStart, SubmenuIndexExtraActions);
scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions);
consumed = true;
}
- scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event);
}
return consumed;
diff --git a/applications/main/nfc/helpers/nfc_emv_parser.h b/applications/main/nfc/helpers/nfc_emv_parser.h
index abe57f470..c636ca77d 100644
--- a/applications/main/nfc/helpers/nfc_emv_parser.h
+++ b/applications/main/nfc/helpers/nfc_emv_parser.h
@@ -9,7 +9,7 @@
* @param aid - AID number array
* @param aid_len - AID length
* @param aid_name - string to keep AID name
- * @return - true if AID found, false otherwies
+ * @return - true if AID found, false otherwise
*/
bool nfc_emv_parser_get_aid_name(
Storage* storage,
@@ -21,7 +21,7 @@ bool nfc_emv_parser_get_aid_name(
* @param storage Storage instance
* @param country_code - ISO 3166 country code
* @param country_name - string to keep country name
- * @return - true if country found, false otherwies
+ * @return - true if country found, false otherwise
*/
bool nfc_emv_parser_get_country_name(
Storage* storage,
@@ -32,7 +32,7 @@ bool nfc_emv_parser_get_country_name(
* @param storage Storage instance
* @param currency_code - ISO 3166 currency code
* @param currency_name - string to keep currency name
- * @return - true if currency found, false otherwies
+ * @return - true if currency found, false otherwise
*/
bool nfc_emv_parser_get_currency_name(
Storage* storage,
diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c
index a6475ca68..23335e299 100644
--- a/applications/main/nfc/nfc_cli.c
+++ b/applications/main/nfc/nfc_cli.c
@@ -32,7 +32,7 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) {
while(!cmd_exit) {
cmd_exit |= cli_cmd_interrupt_received(cli);
if(furi_hal_nfc_detect(&dev_data, 400)) {
- printf("found: %s ", nfc_get_dev_type(dev_data.type));
+ printf("Found: %s ", nfc_get_dev_type(dev_data.type));
printf("UID length: %d, UID:", dev_data.uid_len);
for(size_t i = 0; i < dev_data.uid_len; i++) {
printf("%02X", dev_data.uid[i]);
diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h
index 045080fea..ef257b76e 100644
--- a/applications/main/nfc/scenes/nfc_scene_config.h
+++ b/applications/main/nfc/scenes/nfc_scene_config.h
@@ -34,6 +34,7 @@ ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu)
ADD_SCENE(nfc, mf_desfire_data, MfDesfireData)
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess)
+ADD_SCENE(nfc, mf_classic_data, MfClassicData)
ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu)
ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c
new file mode 100644
index 000000000..dcb02d364
--- /dev/null
+++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c
@@ -0,0 +1,106 @@
+#include "../nfc_i.h"
+
+void nfc_scene_mf_classic_data_on_enter(void* context) {
+ Nfc* nfc = context;
+ MfClassicType type = nfc->dev->dev_data.mf_classic_data.type;
+ MfClassicData* data = &nfc->dev->dev_data.mf_classic_data;
+ TextBox* text_box = nfc->text_box;
+
+ text_box_set_font(text_box, TextBoxFontHex);
+
+ int card_blocks = 0;
+ if(type == MfClassicType1k) {
+ card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4;
+ } else if(type == MfClassicType4k) {
+ // 16 sectors of 4 blocks each plus 8 sectors of 16 blocks each
+ card_blocks = MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4 + 8 * 16;
+ } else if(type == MfClassicTypeMini) {
+ card_blocks = MF_MINI_TOTAL_SECTORS_NUM * 4;
+ }
+
+ int bytes_written = 0;
+ for(int block_num = 0; block_num < card_blocks; block_num++) {
+ bool is_sec_trailer = mf_classic_is_sector_trailer(block_num);
+ if(is_sec_trailer) {
+ uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
+ MfClassicSectorTrailer* sec_tr =
+ mf_classic_get_sector_trailer_by_sector(data, sector_num);
+ // Key A
+ for(size_t i = 0; i < sizeof(sec_tr->key_a); i += 2) {
+ if((bytes_written % 8 == 0) && (bytes_written != 0)) {
+ furi_string_push_back(nfc->text_box_store, '\n');
+ }
+ if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) {
+ furi_string_cat_printf(
+ nfc->text_box_store, "%02X%02X ", sec_tr->key_a[i], sec_tr->key_a[i + 1]);
+ } else {
+ furi_string_cat_printf(nfc->text_box_store, "???? ");
+ }
+ bytes_written += 2;
+ }
+ // Access bytes
+ for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i += 2) {
+ if((bytes_written % 8 == 0) && (bytes_written != 0)) {
+ furi_string_push_back(nfc->text_box_store, '\n');
+ }
+ if(mf_classic_is_block_read(data, block_num)) {
+ furi_string_cat_printf(
+ nfc->text_box_store,
+ "%02X%02X ",
+ sec_tr->access_bits[i],
+ sec_tr->access_bits[i + 1]);
+ } else {
+ furi_string_cat_printf(nfc->text_box_store, "???? ");
+ }
+ bytes_written += 2;
+ }
+ // Key B
+ for(size_t i = 0; i < sizeof(sec_tr->key_b); i += 2) {
+ if((bytes_written % 8 == 0) && (bytes_written != 0)) {
+ furi_string_push_back(nfc->text_box_store, '\n');
+ }
+ if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) {
+ furi_string_cat_printf(
+ nfc->text_box_store, "%02X%02X ", sec_tr->key_b[i], sec_tr->key_b[i + 1]);
+ } else {
+ furi_string_cat_printf(nfc->text_box_store, "???? ");
+ }
+ bytes_written += 2;
+ }
+ } else {
+ // Write data block
+ for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i += 2) {
+ if((bytes_written % 8 == 0) && (bytes_written != 0)) {
+ furi_string_push_back(nfc->text_box_store, '\n');
+ }
+ if(mf_classic_is_block_read(data, block_num)) {
+ furi_string_cat_printf(
+ nfc->text_box_store,
+ "%02X%02X ",
+ data->block[block_num].value[i],
+ data->block[block_num].value[i + 1]);
+ } else {
+ furi_string_cat_printf(nfc->text_box_store, "???? ");
+ }
+ bytes_written += 2;
+ }
+ }
+ }
+ text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store));
+
+ view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
+}
+
+bool nfc_scene_mf_classic_data_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
+
+void nfc_scene_mf_classic_data_on_exit(void* context) {
+ Nfc* nfc = context;
+
+ // Clean view
+ text_box_reset(nfc->text_box);
+ furi_string_reset(nfc->text_box_store);
+}
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 372da8860..d6cb48047 100644
--- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
+++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c
@@ -26,7 +26,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
NfcProtocol protocol = dev_data->protocol;
uint8_t text_scroll_height = 0;
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) ||
- (protocol == NfcDeviceProtocolNfcV)) {
+ (protocol == NfcDeviceProtocolNfcV) || (protocol == NfcDeviceProtocolMifareClassic)) {
widget_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
text_scroll_height = 52;
@@ -290,6 +290,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
} else if(protocol == NfcDeviceProtocolNfcV) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
consumed = true;
+ } else if(protocol == NfcDeviceProtocolMifareClassic) {
+ scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData);
+ consumed = true;
}
}
}
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 9b2a2188e..ee80ee768 100644
--- a/applications/main/nfc/scenes/nfc_scene_read_card_success.c
+++ b/applications/main/nfc/scenes/nfc_scene_read_card_success.c
@@ -46,6 +46,9 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
}
+ } else if(event.type == SceneManagerEventTypeBack) {
+ consumed =
+ scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
}
return consumed;
}
diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c
index 616dc0ef7..2fb4dcec4 100644
--- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c
+++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c
@@ -153,6 +153,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
application_info_present = nfc_supported_card_verify_and_parse(dev_data);
}
+ FURI_LOG_I("nfc", "application_info_present: %d", application_info_present);
+
if(application_info_present) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
} else {
diff --git a/applications/main/sub_playlist/playlist_10px.png b/applications/main/sub_playlist/playlist_10px.png
deleted file mode 100644
index 94ad885c3..000000000
Binary files a/applications/main/sub_playlist/playlist_10px.png and /dev/null differ
diff --git a/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c
index dbf34c970..b589ba5d5 100644
--- a/applications/main/subghz/helpers/subghz_chat.c
+++ b/applications/main/subghz/helpers/subghz_chat.c
@@ -9,7 +9,7 @@ struct SubGhzChatWorker {
SubGhzTxRxWorker* subghz_txrx;
volatile bool worker_running;
- volatile bool worker_stoping;
+ volatile bool worker_stopping;
FuriMessageQueue* event_queue;
uint32_t last_time_rx_data;
diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h
index 350e68ee6..4475045ee 100644
--- a/applications/main/subghz/helpers/subghz_custom_event.h
+++ b/applications/main/subghz/helpers/subghz_custom_event.h
@@ -8,11 +8,14 @@ typedef enum {
//SubmenuIndex
SubmenuIndexFaacSLH_433,
SubmenuIndexFaacSLH_868,
- SubmenuIndexBFT,
+ SubmenuIndexBFTClone,
+ SubmenuIndexBFTMitto,
+ SubmenuIndexSomfyTelis,
SubmenuIndexPricenton,
SubmenuIndexNiceFlo12bit,
SubmenuIndexNiceFlo24bit,
SubmenuIndexNiceFlorS_433_92,
+ SubmenuIndexNiceOne_433_92,
SubmenuIndexNiceSmilo_433_92,
SubmenuIndexCAME12bit,
SubmenuIndexCAME24bit,
@@ -64,6 +67,7 @@ typedef enum {
SubGhzCustomEventViewReceiverBack,
SubGhzCustomEventViewReceiverOffDisplay,
SubGhzCustomEventViewReceiverUnlock,
+ SubGhzCustomEventViewReceiverDeleteItem,
SubGhzCustomEventViewReadRAWBack,
SubGhzCustomEventViewReadRAWIDLE,
diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
index 6452792a6..f632783f8 100644
--- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
+++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c
@@ -117,16 +117,16 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
// First stage: coarse scan
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
- if(furi_hal_subghz_is_frequency_valid(
- subghz_setting_get_frequency(instance->setting, i)) &&
+ uint32_t current_frequency = subghz_setting_get_frequency(instance->setting, i);
+ if(furi_hal_subghz_is_frequency_valid(current_frequency) &&
+ (current_frequency != 467750000) && (current_frequency != 464000000) &&
!((furi_hal_subghz.radio_type == SubGhzRadioExternal) &&
- (subghz_setting_get_frequency(instance->setting, i) >= 311900000 &&
- subghz_setting_get_frequency(instance->setting, i) <= 312200000))) {
+ (current_frequency != 390000000) &&
+ (current_frequency >= 311900000 && current_frequency <= 312200000))) {
furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle);
- frequency = cc1101_set_frequency(
- furi_hal_subghz.spi_bus_handle,
- subghz_setting_get_frequency(instance->setting, i));
+ frequency =
+ cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, current_frequency);
cc1101_calibrate(furi_hal_subghz.spi_bus_handle);
do {
@@ -331,4 +331,4 @@ void subghz_frequency_analyzer_worker_set_trigger_level(
float subghz_frequency_analyzer_worker_get_trigger_level(SubGhzFrequencyAnalyzerWorker* instance) {
return instance->trigger_level;
-}
\ No newline at end of file
+}
diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h
index 7d63ba6b9..3c5982427 100644
--- a/applications/main/subghz/helpers/subghz_types.h
+++ b/applications/main/subghz/helpers/subghz_types.h
@@ -23,7 +23,7 @@ typedef enum {
/** SubGhzHopperState state */
typedef enum {
SubGhzHopperStateOFF,
- SubGhzHopperStateRunnig,
+ SubGhzHopperStateRunning,
SubGhzHopperStatePause,
SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState;
@@ -54,6 +54,7 @@ typedef enum {
SubGhzLoadKeyStateOK,
SubGhzLoadKeyStateParseErr,
SubGhzLoadKeyStateOnlyRx,
+ SubGhzLoadKeyStateProtocolDescriptionErr,
} SubGhzLoadKeyState;
/** SubGhzLock */
diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c
index 7d7a505cb..df81eae29 100644
--- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c
+++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c
@@ -1,8 +1,9 @@
#include "../subghz_i.h"
#include "../helpers/subghz_custom_event.h"
-uint8_t value_index;
-uint8_t value_index2;
+uint8_t value_index_exm;
+uint8_t value_index_dpin;
+uint8_t value_index_cnt;
#define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const))
const char* const radio_modules_variables_text[] = {
@@ -13,15 +14,25 @@ const char* const radio_modules_variables_text[] = {
#define DEBUG_P_COUNT 2
const char* const debug_pin_text[DEBUG_P_COUNT] = {
"OFF",
- "A7",
+ "17(1W)",
+};
+
+#define DEBUG_COUNTER_COUNT 6
+const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = {
+ "+1",
+ "+2",
+ "+3",
+ "+4",
+ "+5",
+ "+10",
};
static void subghz_scene_ext_module_changed(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
- value_index = variable_item_get_current_value_index(item);
+ value_index_exm = variable_item_get_current_value_index(item);
UNUSED(subghz);
- variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]);
+ variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
}
static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) {
SubGhz* subghz = context;
@@ -37,20 +48,49 @@ static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) {
subghz->txrx->debug_pin_state = index == 1;
}
+static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) {
+ uint8_t index = variable_item_get_current_value_index(item);
+
+ variable_item_set_current_value_text(item, debug_counter_text[index]);
+
+ switch(index) {
+ case 0:
+ furi_hal_subghz_set_rolling_counter_mult(1);
+ break;
+ case 1:
+ furi_hal_subghz_set_rolling_counter_mult(2);
+ break;
+ case 2:
+ furi_hal_subghz_set_rolling_counter_mult(3);
+ break;
+ case 3:
+ furi_hal_subghz_set_rolling_counter_mult(4);
+ break;
+ case 4:
+ furi_hal_subghz_set_rolling_counter_mult(5);
+ break;
+ case 5:
+ furi_hal_subghz_set_rolling_counter_mult(10);
+ break;
+ default:
+ break;
+ }
+}
+
void subghz_scene_ext_module_settings_on_enter(void* context) {
SubGhz* subghz = context;
VariableItemList* variable_item_list = subghz->variable_item_list;
- value_index = furi_hal_subghz.radio_type;
+ value_index_exm = furi_hal_subghz.radio_type;
VariableItem* item = variable_item_list_add(
variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz);
variable_item_list_set_enter_callback(
variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz);
- variable_item_set_current_value_index(item, value_index);
- variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]);
+ variable_item_set_current_value_index(item, value_index_exm);
+ variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
item = variable_item_list_add(
@@ -59,9 +99,40 @@ void subghz_scene_ext_module_settings_on_enter(void* context) {
DEBUG_P_COUNT,
subghz_scene_receiver_config_set_debug_pin,
subghz);
- value_index2 = subghz->txrx->debug_pin_state;
- variable_item_set_current_value_index(item, value_index2);
- variable_item_set_current_value_text(item, debug_pin_text[value_index2]);
+ value_index_dpin = subghz->txrx->debug_pin_state;
+ variable_item_set_current_value_index(item, value_index_dpin);
+ variable_item_set_current_value_text(item, debug_pin_text[value_index_dpin]);
+
+ item = variable_item_list_add(
+ subghz->variable_item_list,
+ "Counter Mult:",
+ DEBUG_COUNTER_COUNT,
+ subghz_scene_receiver_config_set_debug_counter,
+ subghz);
+ switch(furi_hal_subghz_get_rolling_counter_mult()) {
+ case 1:
+ value_index_cnt = 0;
+ break;
+ case 2:
+ value_index_cnt = 1;
+ break;
+ case 3:
+ value_index_cnt = 2;
+ break;
+ case 4:
+ value_index_cnt = 3;
+ break;
+ case 5:
+ value_index_cnt = 4;
+ break;
+ case 10:
+ value_index_cnt = 5;
+ break;
+ default:
+ break;
+ }
+ variable_item_set_current_value_index(item, value_index_cnt);
+ variable_item_set_current_value_text(item, debug_counter_text[value_index_cnt]);
}
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
@@ -73,12 +144,12 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent
UNUSED(event);
// Set selected radio module
- furi_hal_subghz_set_radio_type(value_index);
+ furi_hal_subghz_set_radio_type(value_index_exm);
// Check if module is present, if no -> show error
if(!furi_hal_subghz_check_radio()) {
- value_index = 0;
- furi_hal_subghz_set_radio_type(value_index);
+ value_index_exm = 0;
+ furi_hal_subghz_set_radio_type(value_index_exm);
furi_string_set(subghz->error_str, "Please connect\nexternal radio");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
}
diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c
index e396527cc..1e3d6a9c0 100644
--- a/applications/main/subghz/scenes/subghz_scene_read_raw.c
+++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c
@@ -368,7 +368,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
float rssi = furi_hal_subghz_get_rssi();
- if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_TRESHOLD_MIN)) {
+ if(float_is_equal(subghz->txrx->raw_threshold_rssi, SUBGHZ_RAW_THRESHOLD_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);
@@ -421,4 +421,4 @@ void subghz_scene_read_raw_on_exit(void* context) {
//filter restoration
subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter);
-}
\ No newline at end of file
+}
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c
index e1ea08497..c0112199c 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver.c
@@ -204,6 +204,16 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo);
consumed = true;
break;
+ case SubGhzCustomEventViewReceiverDeleteItem:
+ subghz->txrx->idx_menu_chosen =
+ subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
+
+ subghz_history_delete_item(subghz->txrx->history, subghz->txrx->idx_menu_chosen);
+ subghz_view_receiver_delete_element_callback(subghz->subghz_receiver);
+
+ subghz_scene_receiver_update_statusbar(subghz);
+ consumed = true;
+ break;
case SubGhzCustomEventViewReceiverConfig:
subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz->txrx->idx_menu_chosen =
diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c
index af4c6aca3..ff51e2f4d 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c
@@ -8,11 +8,11 @@ enum SubGhzSettingIndex {
SubGhzSettingIndexBinRAW,
SubGhzSettingIndexSound,
SubGhzSettingIndexLock,
- SubGhzSettingIndexRAWThesholdRSSI,
+ SubGhzSettingIndexRAWThresholdRSSI,
};
#define RAW_THRESHOLD_RSSI_COUNT 11
-const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
+const char* const raw_threshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
"-----",
"-85.0",
"-80.0",
@@ -26,7 +26,7 @@ const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = {
"-40.0",
};
-const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
+const float raw_threshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = {
-90.0f,
-85.0f,
-80.0f,
@@ -47,7 +47,7 @@ const char* const hopping_text[HOPPING_COUNT] = {
};
const uint32_t hopping_value[HOPPING_COUNT] = {
SubGhzHopperStateOFF,
- SubGhzHopperStateRunnig,
+ SubGhzHopperStateRunning,
};
#define SPEAKER_COUNT 2
@@ -213,8 +213,8 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
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];
+ variable_item_set_current_value_text(item, raw_threshold_rssi_text[index]);
+ subghz->txrx->raw_threshold_rssi = raw_threshold_rssi_value[index];
}
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
@@ -320,9 +320,9 @@ void subghz_scene_receiver_config_on_enter(void* context) {
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);
+ subghz->txrx->raw_threshold_rssi, raw_threshold_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]);
+ variable_item_set_current_value_text(item, raw_threshold_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 0b265cca2..90046eb9d 100644
--- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c
+++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c
@@ -2,6 +2,9 @@
#include "../helpers/subghz_custom_event.h"
#include
#include
+#include
+#include
+#include
void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
@@ -24,8 +27,9 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
subghz->txrx->receiver,
subghz_history_get_protocol_name(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
+
if(subghz->txrx->decoder_result) {
- // In this case flipper format was changed to short file content
+ //todo we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
subghz_protocol_decoder_base_deserialize(
subghz->txrx->decoder_result,
subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen));
@@ -135,7 +139,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz,
subghz_history_get_raw_data(
subghz->txrx->history, subghz->txrx->idx_menu_chosen))) {
- scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
subghz_tx_stop(subghz);
}
@@ -148,7 +151,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
- subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
+ subghz->txrx->hopper_state = SubGhzHopperStateRunning;
}
subghz->state_notifications = SubGhzNotificationStateRx;
} else {
@@ -175,7 +178,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
subghz_rx(subghz, subghz->txrx->preset->frequency);
}
if(subghz->txrx->hopper_state == SubGhzHopperStatePause) {
- subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
+ subghz->txrx->hopper_state = SubGhzHopperStateRunning;
}
subghz->state_notifications = SubGhzNotificationStateRx;
}
@@ -233,6 +236,10 @@ void subghz_scene_receiver_info_on_exit(void* context) {
widget_reset(subghz->widget);
keeloq_reset_mfname();
keeloq_reset_kl_type();
+ keeloq_reset_original_btn();
+ alutech_reset_original_btn();
+ nice_flors_reset_original_btn();
+ somfy_telis_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c
index 609b4a71d..b9585ec36 100644
--- a/applications/main/subghz/scenes/subghz_scene_rpc.c
+++ b/applications/main/subghz/scenes/subghz_scene_rpc.c
@@ -1,6 +1,10 @@
#include "../subghz_i.h"
#include
#include
+#include
+#include
+#include
+
#include "xtreme/assets.h"
typedef enum {
@@ -110,6 +114,10 @@ void subghz_scene_rpc_on_exit(void* context) {
keeloq_reset_mfname();
keeloq_reset_kl_type();
+ keeloq_reset_original_btn();
+ alutech_reset_original_btn();
+ nice_flors_reset_original_btn();
+ somfy_telis_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
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 1d6f9d70b..2c48462ef 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c
@@ -14,7 +14,7 @@ void subghz_scene_set_seed_bft_on_enter(void* context) {
SubGhz* subghz = context;
// Setup view
- // roguemaster don't steal!!!
+ // RogueMaster don't steal!!!
ByteInput* byte_input = subghz->byte_input;
byte_input_set_header_text(byte_input, "Enter SEED in hex");
byte_input_set_result_callback(
diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c b/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c
index 8e955e4b7..55387a0a5 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c
@@ -65,7 +65,7 @@ bool subghz_scene_set_seed_faac_on_event(void* context, SceneManagerEvent event)
seed,
"FAAC_SLH",
subghz->txrx->preset);
- // rogueemaster dont steal!
+ // RogueMaster dont steal!
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] = (seed >> i * 8) & 0xFF;
diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c
index 95cb1ec03..bc76ee146 100644
--- a/applications/main/subghz/scenes/subghz_scene_set_type.c
+++ b/applications/main/subghz/scenes/subghz_scene_set_type.c
@@ -36,8 +36,9 @@ bool subghz_scene_set_type_submenu_gen_data_protocol(
do {
Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data);
stream_clean(fff_data_stream);
- if(!subghz_protocol_decoder_base_serialize(
- subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset)) {
+ if(subghz_protocol_decoder_base_serialize(
+ subghz->txrx->decoder_result, subghz->txrx->fff_data, subghz->txrx->preset) !=
+ SubGhzProtocolStatusOk) {
FURI_LOG_E(TAG, "Unable to serialize");
break;
}
@@ -79,10 +80,22 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexFaacSLH_433,
subghz_scene_set_type_submenu_callback,
subghz);
+ submenu_add_item(
+ subghz->submenu,
+ "BFT [Manual] 433MHz",
+ SubmenuIndexBFTClone,
+ subghz_scene_set_type_submenu_callback,
+ subghz);
submenu_add_item(
subghz->submenu,
"BFT Mitto 433MHz",
- SubmenuIndexBFT,
+ SubmenuIndexBFTMitto,
+ subghz_scene_set_type_submenu_callback,
+ subghz);
+ submenu_add_item(
+ subghz->submenu,
+ "Somfy Telis 433MHz",
+ SubmenuIndexSomfyTelis,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
@@ -115,6 +128,12 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexNiceFlorS_433_92,
subghz_scene_set_type_submenu_callback,
subghz);
+ submenu_add_item(
+ subghz->submenu,
+ "Nice One 433MHz",
+ SubmenuIndexNiceOne_433_92,
+ subghz_scene_set_type_submenu_callback,
+ subghz);
submenu_add_item(
subghz->submenu,
"CAME 12bit 433MHz",
@@ -230,7 +249,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case SubmenuIndexFaacSLH_433:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac);
break;
- case SubmenuIndexBFT:
+ case SubmenuIndexBFTClone:
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixBft);
break;
case SubmenuIndexPricenton:
@@ -306,11 +325,69 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
generated_protocol = true;
}
break;
+ case SubmenuIndexBFTMitto:
+ subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+ subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
+ if(subghz->txrx->transmitter) {
+ subghz_protocol_keeloq_bft_create_data(
+ subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+ subghz->txrx->fff_data,
+ key & 0x000FFFFF,
+ 0x2,
+ 0x0002,
+ key & 0x000FFFFF,
+ "BFT",
+ subghz->txrx->preset);
+
+ 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] = ((key & 0x000FFFFF) >> i * 8) & 0xFF;
+ }
+
+ 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;
+ }
+ subghz_transmitter_free(subghz->txrx->transmitter);
+ if(!generated_protocol) {
+ furi_string_set(
+ subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+ }
+ break;
+ case SubmenuIndexSomfyTelis:
+ subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+ subghz->txrx->environment, SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
+ if(subghz->txrx->transmitter) {
+ subghz_protocol_somfy_telis_create_data(
+ subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+ subghz->txrx->fff_data,
+ key & 0x00FFFFFF,
+ 0x2,
+ 0x0003,
+ subghz->txrx->preset);
+ generated_protocol = true;
+ } else {
+ generated_protocol = false;
+ }
+ subghz_transmitter_free(subghz->txrx->transmitter);
+ if(!generated_protocol) {
+ furi_string_set(
+ subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+ }
+ break;
case SubmenuIndexDoorHan_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
- subghz_preset_init(
- subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
@@ -358,8 +435,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case SubmenuIndexNiceFlorS_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME);
- subghz_preset_init(
- subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_nice_flor_s_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
@@ -367,7 +443,32 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
key & 0x0FFFFFFF,
0x1,
0x0003,
- subghz->txrx->preset);
+ subghz->txrx->preset,
+ false);
+ generated_protocol = true;
+ } else {
+ generated_protocol = false;
+ }
+ subghz_transmitter_free(subghz->txrx->transmitter);
+ if(!generated_protocol) {
+ furi_string_set(
+ subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+ }
+ break;
+ case SubmenuIndexNiceOne_433_92:
+ subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+ subghz->txrx->environment, SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
+ if(subghz->txrx->transmitter) {
+ subghz_protocol_nice_flor_s_create_data(
+ subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+ subghz->txrx->fff_data,
+ key & 0x0FFFFFFF,
+ 0x1,
+ 0x0003,
+ subghz->txrx->preset,
+ true);
generated_protocol = true;
} else {
generated_protocol = false;
@@ -382,8 +483,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
case SubmenuIndexNiceSmilo_433_92:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME);
- subghz_preset_init(
- subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0);
+ subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_keeloq_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c
index dff4f8384..34685a8e5 100644
--- a/applications/main/subghz/scenes/subghz_scene_transmitter.c
+++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c
@@ -2,7 +2,10 @@
#include "../views/transmitter.h"
#include
#include
+#include
#include
+#include
+#include
void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
furi_assert(context);
@@ -11,9 +14,8 @@ void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) {
}
bool subghz_scene_transmitter_update_data_show(void* context) {
- //ToDo Fix
SubGhz* subghz = context;
-
+ bool ret = false;
if(subghz->txrx->decoder_result) {
FuriString* key_str;
FuriString* frequency_str;
@@ -24,30 +26,29 @@ bool subghz_scene_transmitter_update_data_show(void* context) {
modulation_str = furi_string_alloc();
uint8_t show_button = 0;
- subghz_protocol_decoder_base_deserialize(
- subghz->txrx->decoder_result, subghz->txrx->fff_data);
- subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str);
+ if(subghz_protocol_decoder_base_deserialize(
+ subghz->txrx->decoder_result, subghz->txrx->fff_data) == SubGhzProtocolStatusOk) {
+ subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, key_str);
- if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) ==
- SubGhzProtocolFlag_Send) {
- show_button = 1;
+ if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) ==
+ SubGhzProtocolFlag_Send) {
+ show_button = 1;
+ }
+
+ subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
+ subghz_view_transmitter_add_data_to_show(
+ subghz->subghz_transmitter,
+ furi_string_get_cstr(key_str),
+ furi_string_get_cstr(frequency_str),
+ furi_string_get_cstr(modulation_str),
+ show_button);
+ ret = true;
}
-
- subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
- subghz_view_transmitter_add_data_to_show(
- subghz->subghz_transmitter,
- furi_string_get_cstr(key_str),
- furi_string_get_cstr(frequency_str),
- furi_string_get_cstr(modulation_str),
- show_button);
-
furi_string_free(frequency_str);
furi_string_free(modulation_str);
furi_string_free(key_str);
-
- return true;
}
- return false;
+ return ret;
}
void subghz_scene_transmitter_on_enter(void* context) {
@@ -89,6 +90,27 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
subghz_tx_stop(subghz);
subghz_sleep(subghz);
}
+ if(keeloq_get_custom_btn() != 0) {
+ keeloq_set_btn(0);
+ alutech_set_btn(0);
+ nice_flors_set_btn(0);
+ somfy_telis_set_btn(0);
+ uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
+ furi_hal_subghz_set_rolling_counter_mult(0);
+ // Calling restore!
+ if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
+ subghz_rx_end(subghz);
+ }
+ if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) ||
+ (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) {
+ if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) {
+ scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
+ }
+ }
+ subghz_tx_stop(subghz);
+ subghz_sleep(subghz);
+ furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
+ }
return true;
} else if(event.event == SubGhzCustomEventViewTransmitterBack) {
subghz->state_notifications = SubGhzNotificationStateIDLE;
@@ -113,6 +135,10 @@ void subghz_scene_transmitter_on_exit(void* context) {
subghz->state_notifications = SubGhzNotificationStateIDLE;
keeloq_reset_mfname();
keeloq_reset_kl_type();
+ keeloq_reset_original_btn();
+ alutech_reset_original_btn();
+ nice_flors_reset_original_btn();
+ somfy_telis_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c
index 78295f08c..ac9270114 100644
--- a/applications/main/subghz/subghz.c
+++ b/applications/main/subghz/subghz.c
@@ -275,7 +275,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->raw_threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN;
subghz->txrx->worker = subghz_worker_alloc();
subghz->txrx->fff_data = flipper_format_string_alloc();
diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c
index 184146698..f6530238e 100644
--- a/applications/main/subghz/subghz_history.c
+++ b/applications/main/subghz/subghz_history.c
@@ -88,6 +88,28 @@ void subghz_history_reset(SubGhzHistory* instance) {
instance->code_last_hash_data = 0;
}
+void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) {
+ furi_assert(instance);
+
+ SubGhzHistoryItemArray_it_t it;
+ //SubGhzHistoryItem* target_item = SubGhzHistoryItemArray_get(instance->history->data, item_id);
+ SubGhzHistoryItemArray_it_last(it, instance->history->data);
+ while(!SubGhzHistoryItemArray_end_p(it)) {
+ SubGhzHistoryItem* item = SubGhzHistoryItemArray_ref(it);
+
+ if(it->index == (size_t)(item_id)) {
+ furi_string_free(item->item_str);
+ furi_string_free(item->preset->name);
+ free(item->preset);
+ flipper_format_free(item->flipper_string);
+ item->type = 0;
+ SubGhzHistoryItemArray_remove(instance->history->data, it);
+ }
+ SubGhzHistoryItemArray_previous(it);
+ }
+ instance->last_index_write--;
+}
+
uint16_t subghz_history_get_item(SubGhzHistory* instance) {
furi_assert(instance);
return instance->last_index_write;
diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h
index 607dbeae2..1f2f8d246 100644
--- a/applications/main/subghz/subghz_history.h
+++ b/applications/main/subghz/subghz_history.h
@@ -27,6 +27,8 @@ void subghz_history_free(SubGhzHistory* instance);
*/
void subghz_history_reset(SubGhzHistory* instance);
+void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id);
+
/** Get frequency to history[idx]
*
* @param instance - SubGhzHistory instance
@@ -80,7 +82,7 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp
*
* @param instance - SubGhzHistory instance
* @param output - FuriString* output
- * @return bool - is FUUL
+ * @return bool - is FULL
*/
bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output);
diff --git a/applications/main/subghz/subghz_history_private.h b/applications/main/subghz/subghz_history_private.h
index 7c554621b..12ce28fff 100644
--- a/applications/main/subghz/subghz_history_private.h
+++ b/applications/main/subghz/subghz_history_private.h
@@ -92,7 +92,7 @@ uint32_t subghz_history_rand_range(uint32_t min, uint32_t max);
*
* @param file - Stream*
* @param is_negative_start - first value is negative or positive?
- * @param current_position - 0 if started from begining
+ * @param current_position - 0 if started from beginning
* @param empty_line - add RAW_Data to this line
* @return true
* @return false
@@ -160,4 +160,4 @@ bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool str
* @return true
* @return false
*/
-bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last);
\ No newline at end of file
+bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last);
diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c
index 1fbe662ed..579a16520 100644
--- a/applications/main/subghz/subghz_i.c
+++ b/applications/main/subghz/subghz_i.c
@@ -154,7 +154,6 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
FURI_LOG_E(TAG, "Missing Protocol");
break;
}
- //ToDo FIX
if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) {
FURI_LOG_E(TAG, "Unable Repeat");
break;
@@ -164,7 +163,8 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
subghz->txrx->environment, furi_string_get_cstr(temp_str));
if(subghz->txrx->transmitter) {
- if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format)) {
+ if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format) ==
+ SubGhzProtocolStatusOk) {
if(strcmp(furi_string_get_cstr(subghz->txrx->preset->name), "") != 0) {
subghz_begin(
subghz,
@@ -187,7 +187,12 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
//Start TX
furi_hal_subghz_start_async_tx(
subghz_transmitter_yield, subghz->txrx->transmitter);
+ } else {
+ subghz_dialog_message_show_only_rx(subghz);
}
+ } else {
+ dialog_message_show_storage_error(
+ subghz->dialogs, "Error in protocol\nparameters\ndescription");
}
}
if(!ret) {
@@ -335,8 +340,10 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name(
subghz->txrx->receiver, furi_string_get_cstr(temp_str));
if(subghz->txrx->decoder_result) {
- if(!subghz_protocol_decoder_base_deserialize(
- subghz->txrx->decoder_result, subghz->txrx->fff_data)) {
+ SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize(
+ subghz->txrx->decoder_result, subghz->txrx->fff_data);
+ if(status != SubGhzProtocolStatusOk) {
+ load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr;
break;
}
} else {
@@ -357,6 +364,12 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) {
dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile");
}
return false;
+ case SubGhzLoadKeyStateProtocolDescriptionErr:
+ if(show_dialog) {
+ dialog_message_show_storage_error(
+ subghz->dialogs, "Error in protocol\nparameters\ndescription");
+ }
+ return false;
case SubGhzLoadKeyStateOnlyRx:
if(show_dialog) {
@@ -575,7 +588,7 @@ void subghz_hopper_update(SubGhz* subghz) {
return;
}
} else {
- subghz->txrx->hopper_state = SubGhzHopperStateRunnig;
+ subghz->txrx->hopper_state = SubGhzHopperStateRunning;
}
// Select next frequency
if(subghz->txrx->hopper_idx_frequency <
@@ -598,7 +611,7 @@ void subghz_hopper_update(SubGhz* subghz) {
void subghz_speaker_on(SubGhz* subghz) {
if(subghz->txrx->debug_pin_state) {
- furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7);
+ furi_hal_subghz_set_async_mirror_pin(&ibutton_gpio);
}
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
@@ -643,7 +656,7 @@ void subghz_speaker_mute(SubGhz* subghz) {
void subghz_speaker_unmute(SubGhz* subghz) {
if(subghz->txrx->debug_pin_state) {
- furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7);
+ furi_hal_subghz_set_async_mirror_pin(&ibutton_gpio);
}
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
if(furi_hal_speaker_is_mine()) {
diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c
index fa3569245..2a432deb5 100644
--- a/applications/main/subghz/views/receiver.c
+++ b/applications/main/subghz/views/receiver.c
@@ -14,7 +14,7 @@
#define MENU_ITEMS 4u
#define UNLOCK_CNT 3
-#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
+#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct {
FuriString* item_str;
@@ -82,10 +82,10 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
instance->view,
SubGhzViewReceiverModel * model,
{
- if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
+ if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
model->u_rssi = 0;
} else {
- model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN);
+ model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN);
}
},
true);
@@ -261,13 +261,23 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
if(model->history_item == 0) {
if(model->mode == SubGhzViewReceiverModeLive) {
- canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52);
+ canvas_draw_icon(
+ canvas,
+ 0,
+ 0,
+ furi_hal_subghz_get_radio_type() ? XTREME_ASSETS()->I_Fishing_123x52 :
+ XTREME_ASSETS()->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);
} else {
- canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52);
+ canvas_draw_icon(
+ canvas,
+ 0,
+ 0,
+ furi_hal_subghz_get_radio_type() ? XTREME_ASSETS()->I_Fishing_123x52 :
+ XTREME_ASSETS()->I_Scanning_123x52);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 63, 46, "Decoding...");
canvas_set_font(canvas, FontSecondary);
@@ -421,6 +431,34 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) {
true);
} else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context);
+ } else if(event->key == InputKeyRight && event->type == InputTypeLong) {
+ with_view_model(
+ subghz_receiver->view,
+ SubGhzViewReceiverModel * model,
+ {
+ if(model->history_item != 0) {
+ SubGhzReceiverMenuItemArray_it_t it;
+ // SubGhzReceiverMenuItem* target_item =
+ // SubGhzReceiverMenuItemArray_get(model->history->data, model->idx);
+ SubGhzReceiverMenuItemArray_it_last(it, model->history->data);
+ while(!SubGhzReceiverMenuItemArray_end_p(it)) {
+ SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it);
+
+ if(it->index == (size_t)(model->idx)) {
+ furi_string_free(item->item_str);
+ item->type = 0;
+ SubGhzReceiverMenuItemArray_remove(model->history->data, it);
+ }
+
+ SubGhzReceiverMenuItemArray_previous(it);
+ }
+
+ // Callback
+ subghz_receiver->callback(
+ SubGhzCustomEventViewReceiverDeleteItem, subghz_receiver->context);
+ }
+ },
+ true);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
subghz_receiver->view,
@@ -534,12 +572,34 @@ View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) {
uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) {
furi_assert(subghz_receiver);
- uint32_t idx = 0;
+ uint16_t idx = 0;
with_view_model(
subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false);
return idx;
}
+void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver) {
+ furi_assert(subghz_receiver);
+
+ with_view_model(
+ subghz_receiver->view,
+ SubGhzViewReceiverModel * model,
+ {
+ if(model->history_item == 5) {
+ if(model->idx >= 2) {
+ model->idx = model->history_item - 1;
+ }
+ }
+ model->history_item--;
+
+ if(model->idx != 0) {
+ model->idx--;
+ }
+ },
+ true);
+ furi_delay_ms(200);
+}
+
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {
furi_assert(subghz_receiver);
with_view_model(
diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h
index 37eb473de..ad8c31eda 100644
--- a/applications/main/subghz/views/receiver.h
+++ b/applications/main/subghz/views/receiver.h
@@ -46,4 +46,6 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver);
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx);
+void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver);
+
void subghz_view_receiver_exit(void* context);
diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c
index ce2e34297..3b8c37d73 100644
--- a/applications/main/subghz/views/subghz_frequency_analyzer.c
+++ b/applications/main/subghz/views/subghz_frequency_analyzer.c
@@ -21,13 +21,12 @@
#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, 314980000, 315000000, 318000000, 330000000, 345000000, 348000000,
- 350000000, 387000000, 390000000, 418000000, 433075000, 433220000, 433420000,
- 433657070, 433889000, 433920000, 434075000, 434176948, 434390000, 434420000,
- 434775000, 438900000, 440175000, 464000000, 467750000, 779000000, 868350000,
- 868400000, 868800000, 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, 350000000, 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,
diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c
index 7ba2f4434..fcc077efa 100644
--- a/applications/main/subghz/views/subghz_read_raw.c
+++ b/applications/main/subghz/views/subghz_read_raw.c
@@ -23,7 +23,7 @@ typedef struct {
FuriString* sample_write;
FuriString* file_name;
uint8_t* rssi_history;
- uint8_t rssi_curret;
+ uint8_t rssi_current;
bool rssi_history_end;
uint8_t ind_write;
uint8_t ind_sin;
@@ -62,17 +62,17 @@ void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool tra
furi_assert(instance);
uint8_t u_rssi = 0;
- if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
+ if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) {
u_rssi = 0;
} else {
- u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7);
+ u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
}
with_view_model(
instance->view,
SubGhzReadRAWModel * model,
{
- model->rssi_curret = u_rssi;
+ model->rssi_current = u_rssi;
if(trace) {
model->rssi_history[model->ind_write++] = u_rssi;
} else {
@@ -206,10 +206,10 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
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);
+ canvas, model->ind_write + 1, 47, model->ind_write + 1, 47 - model->rssi_current);
if(model->ind_write > 3) {
canvas_draw_line(
- canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret);
+ canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_current);
for(uint8_t i = 13; i < 47; i += width * 2) {
canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width);
@@ -231,13 +231,13 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) {
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
47,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1,
- 47 - model->rssi_curret);
+ 47 - model->rssi_current);
canvas_draw_line(
canvas,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
47,
SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1,
- 47 - model->rssi_curret);
+ 47 - model->rssi_current);
for(uint8_t i = 13; i < 47; i += width * 2) {
canvas_draw_line(
@@ -266,9 +266,9 @@ void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* mod
uint8_t x = 118;
uint8_t y = 48;
- if(model->raw_threshold_rssi > SUBGHZ_RAW_TRESHOLD_MIN) {
+ if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) {
uint8_t x = 118;
- y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_TRESHOLD_MIN) / 2.7);
+ y -= (uint8_t)((model->raw_threshold_rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7);
uint8_t width = 3;
for(uint8_t i = 0; i < x; i += width * 2) {
diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h
index 43afac427..9d63870d5 100644
--- a/applications/main/subghz/views/subghz_read_raw.h
+++ b/applications/main/subghz/views/subghz_read_raw.h
@@ -3,7 +3,7 @@
#include
#include "../helpers/subghz_custom_event.h"
-#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
+#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f
typedef struct SubGhzReadRAW SubGhzReadRAW;
diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c
index 102639924..e6ed16d81 100644
--- a/applications/main/subghz/views/transmitter.c
+++ b/applications/main/subghz/views/transmitter.c
@@ -4,6 +4,11 @@
#include
#include
+#include
+#include
+#include
+#include
+
struct SubGhzViewTransmitter {
View* view;
SubGhzViewTransmitterCallback callback;
@@ -15,6 +20,8 @@ typedef struct {
FuriString* preset_str;
FuriString* key_str;
uint8_t show_button;
+ FuriString* temp_button_id;
+ bool draw_temp_button;
} SubGhzViewTransmitterModel;
void subghz_view_transmitter_set_callback(
@@ -89,6 +96,12 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo
canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str));
canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str));
+ if(model->draw_temp_button) {
+ canvas_set_font(canvas, FontBatteryPercent);
+ canvas_draw_str(canvas, 117, 40, furi_string_get_cstr(model->temp_button_id));
+ canvas_set_font(canvas, FontSecondary);
+ }
+
if(model->show_button) {
canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int");
subghz_view_transmitter_button_right(canvas, "Send");
@@ -108,7 +121,9 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
furi_string_reset(model->frequency_str);
furi_string_reset(model->preset_str);
furi_string_reset(model->key_str);
+ furi_string_reset(model->temp_button_id);
model->show_button = 0;
+ model->draw_temp_button = false;
},
false);
return false;
@@ -125,6 +140,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
true);
if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) {
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ model->draw_temp_button = false;
+ },
+ true);
subghz_transmitter->callback(
SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
return true;
@@ -134,6 +157,141 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) {
return true;
}
+ // Temp Buttons (UP)
+ if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) {
+ keeloq_set_btn(1);
+ alutech_set_btn(1);
+ nice_flors_set_btn(1);
+ somfy_telis_set_btn(1);
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ if(keeloq_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(alutech_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(nice_flors_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", nice_flors_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(somfy_telis_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", somfy_telis_get_original_btn());
+ model->draw_temp_button = true;
+ }
+ },
+ true);
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
+ return true;
+ } else if(can_be_sent && event->key == InputKeyUp && event->type == InputTypeRelease) {
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
+ return true;
+ }
+ // Down
+ if(can_be_sent && event->key == InputKeyDown && event->type == InputTypePress) {
+ keeloq_set_btn(2);
+ alutech_set_btn(2);
+ nice_flors_set_btn(2);
+ somfy_telis_set_btn(2);
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ if(keeloq_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(alutech_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(nice_flors_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", nice_flors_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(somfy_telis_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", somfy_telis_get_original_btn());
+ model->draw_temp_button = true;
+ }
+ },
+ true);
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
+ return true;
+ } else if(can_be_sent && event->key == InputKeyDown && event->type == InputTypeRelease) {
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
+ return true;
+ }
+ // Left
+ if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypePress) {
+ keeloq_set_btn(3);
+ alutech_set_btn(3);
+ nice_flors_set_btn(3);
+ somfy_telis_set_btn(3);
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ if(keeloq_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(alutech_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(nice_flors_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", nice_flors_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(somfy_telis_get_original_btn() != 0) {
+ furi_string_printf(
+ model->temp_button_id, "%01X", somfy_telis_get_original_btn());
+ model->draw_temp_button = true;
+ }
+ },
+ true);
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
+ return true;
+ } else if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypeRelease) {
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
+ return true;
+ }
+ // Right
+ if(can_be_sent && event->key == InputKeyRight && event->type == InputTypePress) {
+ keeloq_set_btn(4);
+ alutech_set_btn(4);
+ with_view_model(
+ subghz_transmitter->view,
+ SubGhzViewTransmitterModel * model,
+ {
+ furi_string_reset(model->temp_button_id);
+ if(keeloq_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", keeloq_get_original_btn());
+ model->draw_temp_button = true;
+ } else if(alutech_get_original_btn() != 0) {
+ furi_string_printf(model->temp_button_id, "%01X", alutech_get_original_btn());
+ model->draw_temp_button = true;
+ }
+ },
+ true);
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context);
+ return true;
+ } else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeRelease) {
+ subghz_transmitter->callback(
+ SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context);
+ return true;
+ }
+
return true;
}
@@ -166,6 +324,7 @@ SubGhzViewTransmitter* subghz_view_transmitter_alloc() {
model->frequency_str = furi_string_alloc();
model->preset_str = furi_string_alloc();
model->key_str = furi_string_alloc();
+ model->temp_button_id = furi_string_alloc();
},
true);
return subghz_transmitter;
@@ -181,6 +340,7 @@ void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) {
furi_string_free(model->frequency_str);
furi_string_free(model->preset_str);
furi_string_free(model->key_str);
+ furi_string_free(model->temp_button_id);
},
true);
view_free(subghz_transmitter->view);
diff --git a/applications/main/unirfremix/application.fam b/applications/main/subghz_remote/application.fam
similarity index 55%
rename from applications/main/unirfremix/application.fam
rename to applications/main/subghz_remote/application.fam
index fd3553947..e09f8500f 100644
--- a/applications/main/unirfremix/application.fam
+++ b/applications/main/subghz_remote/application.fam
@@ -1,14 +1,14 @@
App(
- appid="unirfremix",
+ appid="subghz_remote",
name="Sub-GHz Remote",
apptype=FlipperAppType.APP,
- entry_point="unirfremix_app",
- cdefines=["APP_UNIRFREMIX"],
+ entry_point="subghz_remote_app",
+ cdefines=["APP_SUBGHZREMOTE"],
requires=[
"gui",
"dialogs",
],
- icon="A_UniRFRemix_14",
+ icon="A_SubGHzRemote_14",
stack_size=4 * 1024,
order=11,
)
diff --git a/applications/main/unirfremix/unirfremix_app.c b/applications/main/subghz_remote/subghz_remote_app.c
similarity index 87%
rename from applications/main/unirfremix/unirfremix_app.c
rename to applications/main/subghz_remote/subghz_remote_app.c
index 2b12a12b2..941cdb889 100644
--- a/applications/main/unirfremix/unirfremix_app.c
+++ b/applications/main/subghz_remote/subghz_remote_app.c
@@ -19,11 +19,14 @@
#include
#include
#include
+#include
+#include
+#include
-#define UNIRFMAP_FOLDER "/ext/subghz/unirf"
-#define UNIRFMAP_EXTENSION ".txt"
+#define SUBREMOTEMAP_FOLDER "/ext/subghz/remote"
+#define SUBREMOTEMAP_EXTENSION ".txt"
-#define TAG "UniRF Remix"
+#define TAG "SubGHzRemote"
typedef struct {
uint32_t frequency;
@@ -36,7 +39,7 @@ typedef struct {
size_t data_size;
SubGhzProtocolDecoderBase* decoder;
-} UniRFPreset;
+} SubRemotePreset;
typedef struct {
FuriMutex* model_mutex;
@@ -50,7 +53,7 @@ typedef struct {
SubGhzEnvironment* environment;
SubGhzReceiver* subghz_receiver;
NotificationApp* notification;
- UniRFPreset* txpreset;
+ SubRemotePreset* txpreset;
FuriString* up_file;
FuriString* down_file;
@@ -85,17 +88,17 @@ typedef struct {
bool tx_not_allowed;
FuriString* signal;
-} UniRFRemix;
+} SubGHzRemote;
-UniRFPreset* unirfremix_preset_alloc(void) {
- UniRFPreset* preset = malloc(sizeof(UniRFPreset));
+SubRemotePreset* subghz_remote_preset_alloc(void) {
+ SubRemotePreset* preset = malloc(sizeof(SubRemotePreset));
preset->name = furi_string_alloc();
preset->protocol = furi_string_alloc();
preset->repeat = 200;
return preset;
}
-void unirfremix_preset_free(UniRFPreset* preset) {
+void subghz_remote_preset_free(SubRemotePreset* preset) {
furi_string_free(preset->name);
furi_string_free(preset->protocol);
free(preset);
@@ -167,7 +170,7 @@ static void cfg_read_file_label(
* set error flag if missing map file
*/
-void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) {
+void subghz_remote_cfg_set_check(SubGHzRemote* app, FuriString* file_name) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
@@ -310,11 +313,11 @@ void unirfremix_cfg_set_check(UniRFRemix* app, FuriString* file_name) {
}
}
-static void unirfremix_end_send(UniRFRemix* app) {
+static void subghz_remote_end_send(SubGHzRemote* app) {
app->processing = 0;
}
-bool unirfremix_set_preset(UniRFPreset* p, const char* preset) {
+bool subghz_remote_set_preset(SubRemotePreset* p, const char* preset) {
if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) {
furi_string_set(p->name, "AM270");
} else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) {
@@ -334,8 +337,8 @@ bool unirfremix_set_preset(UniRFPreset* p, const char* preset) {
return true;
}
-bool unirfremix_key_load(
- UniRFPreset* preset,
+bool subghz_remote_key_load(
+ SubRemotePreset* preset,
FlipperFormat* fff_file,
FlipperFormat* fff_data,
SubGhzSetting* setting,
@@ -364,7 +367,7 @@ bool unirfremix_key_load(
FURI_LOG_W(TAG, "Could not read Preset. Defaulting to Ook650Async");
furi_string_set(temp_str, "FuriHalSubGhzPresetOok650Async");
}
- if(!unirfremix_set_preset(preset, furi_string_get_cstr(temp_str))) {
+ if(!subghz_remote_set_preset(preset, furi_string_get_cstr(temp_str))) {
FURI_LOG_E(TAG, "Could not set preset");
break;
}
@@ -399,7 +402,10 @@ bool unirfremix_key_load(
preset->decoder = subghz_receiver_search_decoder_base_by_name(
receiver, furi_string_get_cstr(preset->protocol));
if(preset->decoder) {
- if(!subghz_protocol_decoder_base_deserialize(preset->decoder, fff_data)) {
+ SubGhzProtocolStatus status =
+ subghz_protocol_decoder_base_deserialize(preset->decoder, fff_data);
+ if(status != SubGhzProtocolStatusOk) {
+ FURI_LOG_E(TAG, "Protocol deserialize failed, status = %d", status);
break;
}
} else {
@@ -416,7 +422,7 @@ bool unirfremix_key_load(
// method modified from subghz_i.c
// https://github.com/flipperdevices/flipperzero-firmware/blob/b0daa601ad5b87427a45f9089c8b403a01f72c2a/applications/subghz/subghz_i.c#L417-L456
-bool unirfremix_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_file_name) {
+bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_file_name) {
furi_assert(fff_file);
furi_assert(dev_file_name);
@@ -453,7 +459,7 @@ bool unirfremix_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_f
return saved;
}
-void unirfremix_tx_stop(UniRFRemix* app) {
+void subghz_remote_tx_stop(SubGHzRemote* app) {
if(app->processing == 0) {
return;
}
@@ -477,10 +483,14 @@ void unirfremix_tx_stop(UniRFRemix* app) {
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);
+ subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path);
keeloq_reset_mfname();
keeloq_reset_kl_type();
+ keeloq_reset_original_btn();
+ alutech_reset_original_btn();
+ nice_flors_reset_original_btn();
+ somfy_telis_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
@@ -490,22 +500,22 @@ void unirfremix_tx_stop(UniRFRemix* app) {
notification_message(app->notification, &sequence_blink_stop);
- unirfremix_preset_free(app->txpreset);
+ subghz_remote_preset_free(app->txpreset);
flipper_format_free(app->tx_fff_data);
- unirfremix_end_send(app);
+ subghz_remote_end_send(app);
}
-static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) {
+static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) {
//
bool res = false;
do {
if(!furi_hal_subghz_is_tx_allowed(app->txpreset->frequency)) {
printf(
"In your settings, only reception on this frequency (%lu) is allowed,\r\n"
- "the actual operation of the unirf app is not possible\r\n ",
+ "the actual operation of the subghz remote app is not possible\r\n ",
app->txpreset->frequency);
app->tx_not_allowed = true;
- unirfremix_end_send(app);
+ subghz_remote_end_send(app);
break;
} else {
app->tx_not_allowed = false;
@@ -522,13 +532,14 @@ static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) {
furi_hal_subghz_reset();
furi_hal_subghz_idle();
furi_hal_subghz_load_custom_preset(app->txpreset->data);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, 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(furi_hal_subghz.cc1101_g0_pin, false);
+ furi_hal_gpio_init(
+ furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
if(!furi_hal_subghz_tx()) {
FURI_LOG_E(TAG, "Sending not allowed");
@@ -546,14 +557,14 @@ static bool unirfremix_send_sub(UniRFRemix* app, FlipperFormat* fff_data) {
return res;
}
-static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char* path) {
+static void subghz_remote_send_signal(SubGHzRemote* app, Storage* storage, const char* path) {
FURI_LOG_D(TAG, "Sending: %s", path);
app->tx_file_path = path;
app->tx_fff_data = flipper_format_string_alloc();
- app->txpreset = unirfremix_preset_alloc();
+ app->txpreset = subghz_remote_preset_alloc();
// load settings/stream from .sub file
FlipperFormat* fff_file = flipper_format_file_alloc(storage);
@@ -563,7 +574,7 @@ static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char
FURI_LOG_E(TAG, "Could not open file %s", path);
break;
}
- if(!unirfremix_key_load(
+ if(!subghz_remote_key_load(
app->txpreset,
fff_file,
app->tx_fff_data,
@@ -581,25 +592,25 @@ static void unirfremix_send_signal(UniRFRemix* app, Storage* storage, const char
return;
}
- unirfremix_send_sub(app, app->tx_fff_data);
+ subghz_remote_send_sub(app, app->tx_fff_data);
}
-static void unirfremix_process_signal(UniRFRemix* app, FuriString* signal) {
+static void subghz_remote_process_signal(SubGHzRemote* app, FuriString* signal) {
view_port_update(app->view_port);
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);
- unirfremix_send_signal(app, storage, furi_string_get_cstr(signal));
+ subghz_remote_send_signal(app, storage, furi_string_get_cstr(signal));
furi_record_close(RECORD_STORAGE);
} else if(strlen(furi_string_get_cstr(signal)) < 10) {
- unirfremix_end_send(app);
+ subghz_remote_end_send(app);
}
}
static void render_callback(Canvas* canvas, void* ctx) {
- UniRFRemix* app = ctx;
+ SubGHzRemote* app = ctx;
furi_check(furi_mutex_acquire(app->model_mutex, FuriWaitForever) == FuriStatusOk);
//setup different canvas settings
@@ -639,10 +650,10 @@ static void render_callback(Canvas* canvas, void* ctx) {
//canvas_draw_str(canvas, 0, 40, "D: ");
//canvas_draw_str(canvas, 0, 50, "Ok: ");
- //PNGs are located in assets/icons/UniRFRemix before compiliation
+ //PNGs are located in assets/icons/SubGHzRemote before compilation
//Icons for Labels
- //canvas_draw_icon(canvas, 0, 0, &I_UniRFRemix_LeftAlignedButtons_9x64);
+ //canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64);
canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4);
canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4);
canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7);
@@ -691,7 +702,7 @@ static void render_callback(Canvas* canvas, void* ctx) {
//Repeat indicator
//canvas_draw_str_aligned(canvas, 125, 40, AlignRight, AlignBottom, "Repeat:");
- //canvas_draw_icon(canvas, 115, 39, &I_UniRFRemix_Repeat_12x14);
+ //canvas_draw_icon(canvas, 115, 39, &I_SubGHzRemote_Repeat_12x14);
//canvas_draw_str_aligned(canvas, 125, 62, AlignRight, AlignBottom, int_to_char(app->repeat));
}
@@ -699,14 +710,14 @@ static void render_callback(Canvas* canvas, void* ctx) {
}
static void input_callback(InputEvent* input_event, void* ctx) {
- UniRFRemix* app = ctx;
+ SubGHzRemote* app = ctx;
furi_message_queue_put(app->input_queue, input_event, 0);
}
-void unirfremix_subghz_alloc(UniRFRemix* app) {
+void subghz_remote_subghz_alloc(SubGHzRemote* app) {
// load subghz presets
app->setting = subghz_setting_alloc();
- subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt"));
+ subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"));
// load mfcodes
app->environment = subghz_environment_alloc();
@@ -722,8 +733,11 @@ void unirfremix_subghz_alloc(UniRFRemix* app) {
app->subghz_receiver = subghz_receiver_alloc_init(app->environment);
}
-UniRFRemix* unirfremix_alloc(void) {
- UniRFRemix* app = malloc(sizeof(UniRFRemix));
+SubGHzRemote* subghz_remote_alloc(void) {
+ SubGHzRemote* app = malloc(sizeof(SubGHzRemote));
+
+ // Enable power for External CC1101 if it is connected
+ furi_hal_subghz_enable_ext_power();
furi_hal_power_suppress_charge_enter();
@@ -744,9 +758,12 @@ UniRFRemix* unirfremix_alloc(void) {
return app;
}
-void unirfremix_free(UniRFRemix* app, bool with_subghz) {
+void subghz_remote_free(SubGHzRemote* app, bool with_subghz) {
furi_hal_power_suppress_charge_exit();
+ // Disable power for External CC1101 if it was enabled and module is connected
+ furi_hal_subghz_disable_ext_power();
+
furi_string_free(app->up_file);
furi_string_free(app->down_file);
furi_string_free(app->left_file);
@@ -778,9 +795,9 @@ void unirfremix_free(UniRFRemix* app, bool with_subghz) {
free(app);
}
-int32_t unirfremix_app(void* p) {
+int32_t subghz_remote_app(void* p) {
UNUSED(p);
- UniRFRemix* app = unirfremix_alloc();
+ SubGHzRemote* app = subghz_remote_alloc();
app->file_path = furi_string_alloc();
app->signal = furi_string_alloc();
@@ -795,33 +812,40 @@ int32_t unirfremix_app(void* p) {
app->file_result = 3;
Storage* storage = furi_record_open(RECORD_STORAGE);
- if(!storage_simply_mkdir(storage, UNIRFMAP_FOLDER)) {
- FURI_LOG_E(TAG, "Could not create folder %s", UNIRFMAP_FOLDER);
+ // Remove old pre-included files to avoid duplicates on migrate
+ storage_simply_remove(storage, EXT_PATH("subghz/unirf/CVS_Walgreens.txt"));
+ storage_simply_remove(storage, EXT_PATH("subghz/unirf/Gas_Sign_Edit.txt"));
+ storage_simply_remove(storage, EXT_PATH("subghz/unirf/Ridin_Dirty.txt"));
+ storage_simply_remove(storage, EXT_PATH("subghz/unirf/Tesla_Handicap.txt"));
+ storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREMOTEMAP_FOLDER);
+
+ if(!storage_simply_mkdir(storage, SUBREMOTEMAP_FOLDER)) {
+ FURI_LOG_E(TAG, "Could not create folder %s", SUBREMOTEMAP_FOLDER);
}
furi_record_close(RECORD_STORAGE);
- furi_string_set(app->file_path, UNIRFMAP_FOLDER);
+ furi_string_set(app->file_path, SUBREMOTEMAP_FOLDER);
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogsFileBrowserOptions browser_options;
- dialog_file_browser_set_basic_options(&browser_options, UNIRFMAP_EXTENSION, &I_sub1_10px);
- browser_options.base_path = UNIRFMAP_FOLDER;
+ dialog_file_browser_set_basic_options(&browser_options, SUBREMOTEMAP_EXTENSION, &I_sub1_10px);
+ browser_options.base_path = SUBREMOTEMAP_FOLDER;
bool res = dialog_file_browser_show(dialogs, app->file_path, app->file_path, &browser_options);
furi_record_close(RECORD_DIALOGS);
if(!res) {
FURI_LOG_E(TAG, "No file selected");
- unirfremix_free(app, false);
+ subghz_remote_free(app, false);
return 255;
} else {
//check map and population variables
- unirfremix_cfg_set_check(app, app->file_path);
+ subghz_remote_cfg_set_check(app, app->file_path);
}
// init subghz stuff
- unirfremix_subghz_alloc(app);
+ subghz_remote_subghz_alloc(app);
bool exit_loop = false;
@@ -871,7 +895,7 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->up_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
@@ -889,7 +913,7 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->down_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
@@ -907,7 +931,7 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->right_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
@@ -925,7 +949,7 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->left_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
@@ -943,13 +967,13 @@ int32_t unirfremix_app(void* p) {
}
if(input.type == InputTypeRelease) {
if(app->ok_enabled) {
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
}
}
break;
case InputKeyBack:
- unirfremix_tx_stop(app);
+ subghz_remote_tx_stop(app);
exit_loop = true;
break;
default:
@@ -986,7 +1010,7 @@ int32_t unirfremix_app(void* p) {
app->processing = 2;
- unirfremix_process_signal(app, app->signal);
+ subghz_remote_process_signal(app, app->signal);
}
if(exit_loop == true) {
@@ -1042,7 +1066,7 @@ int32_t unirfremix_app(void* p) {
}
// remove & free all stuff created by app
- unirfremix_free(app, true);
+ subghz_remote_free(app, true);
return 0;
}
diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c
index 66604d166..217733c94 100644
--- a/applications/main/u2f/u2f_data.c
+++ b/applications/main/u2f/u2f_data.c
@@ -178,7 +178,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) {
uint8_t key_slot = 0;
uint32_t version = 0;
- // Check if unique key exists in secure eclave and generate it if missing
+ // Check if unique key exists in secure eclave(typo?) and generate it if missing
if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false;
FuriString* filetype;
diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam
new file mode 100644
index 000000000..266350521
--- /dev/null
+++ b/applications/main/xtreme_app/application.fam
@@ -0,0 +1,16 @@
+App(
+ appid="xtreme_app",
+ name="Xtreme Settings",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="xtreme_app",
+ cdefines=["APP_XTREME"],
+ requires=[
+ "gui",
+ "dolphin",
+ "xtreme",
+ ],
+ stack_size=2 * 1024,
+ # icon="A_Xtreme_14",
+ order=90,
+ fap_category=".Main",
+)
diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene.c b/applications/main/xtreme_app/scenes/xtreme_app_scene.c
similarity index 100%
rename from applications/settings/xtreme_app/scenes/xtreme_app_scene.c
rename to applications/main/xtreme_app/scenes/xtreme_app_scene.c
diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene.h b/applications/main/xtreme_app/scenes/xtreme_app_scene.h
similarity index 100%
rename from applications/settings/xtreme_app/scenes/xtreme_app_scene.h
rename to applications/main/xtreme_app/scenes/xtreme_app_scene.h
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h
new file mode 100644
index 000000000..a7370f9b4
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h
@@ -0,0 +1,11 @@
+ADD_SCENE(xtreme_app, start, Start)
+ADD_SCENE(xtreme_app, graphics, Graphics)
+ADD_SCENE(xtreme_app, statusbar, Statusbar)
+ADD_SCENE(xtreme_app, protocols, Protocols)
+ADD_SCENE(xtreme_app, protocols_frequencies, ProtocolsFrequencies)
+ADD_SCENE(xtreme_app, protocols_frequencies_static, ProtocolsFrequenciesStatic)
+ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper)
+ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd)
+ADD_SCENE(xtreme_app, dolphin, Dolphin)
+ADD_SCENE(xtreme_app, misc, Misc)
+ADD_SCENE(xtreme_app, misc_rename, MiscRename)
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c
new file mode 100644
index 000000000..a157b269a
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_dolphin.c
@@ -0,0 +1,92 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexXpLevel,
+ VarItemListIndexButthurtTimer,
+};
+
+void xtreme_app_scene_dolphin_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_dolphin_xp_level_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->dolphin_level = variable_item_get_current_value_index(item) + 1;
+ char level_str[4];
+ snprintf(level_str, 4, "%i", app->dolphin_level);
+ variable_item_set_current_value_text(item, level_str);
+ app->save_level = true;
+}
+
+const char* const butthurt_timer_names[] =
+ {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"};
+const int32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] =
+ {-1, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800};
+static void xtreme_app_scene_dolphin_butthurt_timer_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, butthurt_timer_names[index]);
+ XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index];
+ app->save_settings = true;
+ app->require_reboot = true;
+}
+
+void xtreme_app_scene_dolphin_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+ uint8_t value_index;
+
+ char level_str[4];
+ snprintf(level_str, 4, "%i", app->dolphin_level);
+ item = variable_item_list_add(
+ var_item_list,
+ "XP Level",
+ DOLPHIN_LEVEL_COUNT + 1,
+ xtreme_app_scene_dolphin_xp_level_changed,
+ app);
+ variable_item_set_current_value_index(item, app->dolphin_level - 1);
+ variable_item_set_current_value_text(item, level_str);
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Butthurt Timer",
+ COUNT_OF(butthurt_timer_names),
+ xtreme_app_scene_dolphin_butthurt_timer_changed,
+ app);
+ value_index = value_index_int32(
+ xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_names));
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, butthurt_timer_names[value_index]);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_dolphin_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneDolphin));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_dolphin_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneDolphin, event.event);
+ consumed = true;
+ switch(event.event) {
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_dolphin_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c
new file mode 100644
index 000000000..99a302a44
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_graphics.c
@@ -0,0 +1,146 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexAssetPack,
+ VarItemListIndexAnimSpeed,
+ VarItemListIndexCycleAnims,
+ VarItemListIndexUnlockAnims,
+};
+
+void xtreme_app_scene_graphics_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_graphics_asset_pack_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(
+ item, index == 0 ? "SFW" : *asset_packs_get(app->asset_packs, index - 1));
+ strlcpy(
+ XTREME_SETTINGS()->asset_pack,
+ index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1),
+ MAX_PACK_NAME_LEN);
+ app->asset_pack = index;
+ app->save_settings = true;
+ app->require_reboot = true;
+}
+
+const char* const anim_speed_names[] =
+ {"25%", "50%", "75%", "100%", "125%", "150%", "175%", "200%", "225%", "250%", "275%", "300%"};
+const int32_t anim_speed_values[COUNT_OF(anim_speed_names)] =
+ {25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300};
+static void xtreme_app_scene_graphics_anim_speed_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, anim_speed_names[index]);
+ XTREME_SETTINGS()->anim_speed = anim_speed_values[index];
+ app->save_settings = true;
+}
+
+const char* const cycle_anims_names[] = {
+ "OFF",
+ "Meta.txt",
+ "30 S",
+ "1 M",
+ "5 M",
+ "10 M",
+ "15 M",
+ "30 M",
+ "1 H",
+ "2 H",
+ "6 H",
+ "12 H",
+ "24 H"};
+const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] =
+ {-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400};
+static void xtreme_app_scene_graphics_cycle_anims_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, cycle_anims_names[index]);
+ XTREME_SETTINGS()->cycle_anims = cycle_anims_values[index];
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_graphics_unlock_anims_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->unlock_anims = value;
+ app->save_settings = true;
+}
+
+void xtreme_app_scene_graphics_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+ uint8_t value_index;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Asset Pack",
+ asset_packs_size(app->asset_packs) + 1,
+ xtreme_app_scene_graphics_asset_pack_changed,
+ app);
+ variable_item_set_current_value_index(item, app->asset_pack);
+ variable_item_set_current_value_text(
+ item,
+ app->asset_pack == 0 ? "SFW" : *asset_packs_get(app->asset_packs, app->asset_pack - 1));
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Anim Speed",
+ COUNT_OF(anim_speed_names),
+ xtreme_app_scene_graphics_anim_speed_changed,
+ app);
+ value_index = value_index_int32(
+ xtreme_settings->anim_speed, anim_speed_values, COUNT_OF(anim_speed_names));
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, anim_speed_names[value_index]);
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Cycle Anims",
+ COUNT_OF(cycle_anims_names),
+ xtreme_app_scene_graphics_cycle_anims_changed,
+ app);
+ value_index = value_index_int32(
+ xtreme_settings->cycle_anims, cycle_anims_values, COUNT_OF(cycle_anims_names));
+ variable_item_set_current_value_index(item, value_index);
+ variable_item_set_current_value_text(item, cycle_anims_names[value_index]);
+
+ item = variable_item_list_add(
+ var_item_list, "Unlock Anims", 2, xtreme_app_scene_graphics_unlock_anims_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->unlock_anims);
+ variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF");
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_graphics_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneGraphics));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_graphics_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneGraphics, event.event);
+ consumed = true;
+ switch(event.event) {
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_graphics_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c
new file mode 100644
index 000000000..030256be0
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c
@@ -0,0 +1,96 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexSortDirsFirst,
+ VarItemListIndexChangeDeviceName,
+ VarItemListIndexExperimentalOptions,
+ VarItemListIndexDarkMode,
+ VarItemListIndexLeftHanded,
+};
+
+void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_misc_sort_dirs_first_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->sort_dirs_first = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_misc_dark_mode_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->dark_mode = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_misc_left_handed_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->left_handed = value;
+ app->save_settings = true;
+}
+
+void xtreme_app_scene_misc_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list, "Sort Dirs First", 2, xtreme_app_scene_misc_sort_dirs_first_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first);
+ variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF");
+
+ variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, " Experimental Options:", 0, NULL, app);
+
+ item = variable_item_list_add(
+ var_item_list, "Dark Mode", 2, xtreme_app_scene_misc_dark_mode_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->dark_mode);
+ variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF");
+
+ item = variable_item_list_add(
+ var_item_list, "Left Handed", 2, xtreme_app_scene_misc_left_handed_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->left_handed);
+ variable_item_set_current_value_text(item, xtreme_settings->left_handed ? "ON" : "OFF");
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_misc_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMisc));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMisc, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexChangeDeviceName:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_misc_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c
new file mode 100644
index 000000000..a5f5dd8c5
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c
@@ -0,0 +1,53 @@
+#include "../xtreme_app.h"
+
+enum TextInputIndex {
+ TextInputResultOk,
+};
+
+static void xtreme_app_scene_misc_rename_text_input_callback(void* context) {
+ XtremeApp* app = context;
+
+ app->save_name = true;
+ app->require_reboot = true;
+ view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk);
+}
+
+void xtreme_app_scene_misc_rename_on_enter(void* context) {
+ XtremeApp* app = context;
+ TextInput* text_input = app->text_input;
+
+ text_input_set_header_text(text_input, "Leave empty for default");
+
+ text_input_set_result_callback(
+ text_input,
+ xtreme_app_scene_misc_rename_text_input_callback,
+ app,
+ app->device_name,
+ NAMECHANGER_TEXT_STORE_SIZE,
+ true);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewTextInput);
+}
+
+bool xtreme_app_scene_misc_rename_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ switch(event.event) {
+ case TextInputResultOk:
+ scene_manager_previous_scene(app->scene_manager);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_misc_rename_on_exit(void* context) {
+ XtremeApp* app = context;
+ text_input_reset(app->text_input);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c
new file mode 100644
index 000000000..eb8285e34
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c
@@ -0,0 +1,105 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexBadkbMode,
+ VarItemListIndexBadbtRemember,
+ VarItemListIndexSubghzFrequencies,
+ VarItemListIndexSubghzExtend,
+ VarItemListIndexSubghzBypass,
+};
+
+void xtreme_app_scene_protocols_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "BT" : "USB");
+ XTREME_SETTINGS()->bad_bt = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_protocols_badbt_remember_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->bad_bt_remember = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_protocols_subghz_extend_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->subghz_extend = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF");
+ app->save_subghz = true;
+}
+
+static void xtreme_app_scene_protocols_subghz_bypass_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->subghz_bypass = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF");
+ app->save_subghz = true;
+}
+
+void xtreme_app_scene_protocols_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_badkb_mode_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->bad_bt);
+ variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB");
+
+ item = variable_item_list_add(
+ var_item_list, "BadBT Rmembr", 2, xtreme_app_scene_protocols_badbt_remember_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember);
+ variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF");
+
+ variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app);
+
+ item = variable_item_list_add(
+ var_item_list, "SubGHz Extend", 2, xtreme_app_scene_protocols_subghz_extend_changed, app);
+ variable_item_set_current_value_index(item, app->subghz_extend);
+ variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF");
+
+ item = variable_item_list_add(
+ var_item_list, "SubGHz Bypass", 2, xtreme_app_scene_protocols_subghz_bypass_changed, app);
+ variable_item_set_current_value_index(item, app->subghz_bypass);
+ variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF");
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_protocols_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocols));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_protocols_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocols, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexSubghzFrequencies:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequencies);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c
new file mode 100644
index 000000000..6b92d1eb0
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c
@@ -0,0 +1,76 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexUseDefaults,
+ VarItemListIndexStaticFrequencies,
+ VarItemListIndexHopperFrequencies,
+};
+
+void xtreme_app_scene_protocols_frequencies_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_protocols_frequencies_use_defaults_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ app->subghz_use_defaults = value;
+ app->save_subghz_frequencies = true;
+}
+
+void xtreme_app_scene_protocols_frequencies_on_enter(void* context) {
+ XtremeApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Use Defaults",
+ 2,
+ xtreme_app_scene_protocols_frequencies_use_defaults_changed,
+ app);
+ variable_item_set_current_value_index(item, app->subghz_use_defaults);
+ variable_item_set_current_value_text(item, app->subghz_use_defaults ? "ON" : "OFF");
+
+ variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_protocols_frequencies_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list,
+ scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequencies));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequencies, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexStaticFrequencies:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic);
+ break;
+ case VarItemListIndexHopperFrequencies:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_frequencies_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c
new file mode 100644
index 000000000..21b29e295
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c
@@ -0,0 +1,83 @@
+#include "../xtreme_app.h"
+
+enum TextInputResult {
+ TextInputResultOk,
+ TextInputResultError,
+};
+
+static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* context) {
+ XtremeApp* app = context;
+
+ char* end;
+ uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 10000;
+ if(*end || !furi_hal_subghz_is_frequency_valid(value)) {
+ view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultError);
+ return;
+ }
+ bool is_hopper =
+ scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd);
+ if(is_hopper) {
+ FrequencyList_push_back(app->subghz_hopper_frequencies, value);
+ } else {
+ FrequencyList_push_back(app->subghz_static_frequencies, value);
+ }
+ app->save_subghz_frequencies = true;
+ view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk);
+}
+
+void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) {
+ XtremeApp* app = context;
+ TextInput* text_input = app->text_input;
+
+ text_input_set_header_text(text_input, "Format: 12356");
+
+ strlcpy(app->subghz_freq_buffer, "", XTREME_SUBGHZ_FREQ_BUFFER_SIZE);
+
+ text_input_set_result_callback(
+ text_input,
+ xtreme_app_scene_protocols_frequencies_add_text_input_callback,
+ app,
+ app->subghz_freq_buffer,
+ XTREME_SUBGHZ_FREQ_BUFFER_SIZE,
+ true);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewTextInput);
+}
+
+void callback_return(void* context) {
+ XtremeApp* app = context;
+ scene_manager_previous_scene(app->scene_manager);
+}
+
+bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ consumed = true;
+ switch(event.event) {
+ case TextInputResultOk:
+ scene_manager_previous_scene(app->scene_manager);
+ break;
+ case TextInputResultError:
+ popup_set_header(app->popup, "Invalid value!", 64, 26, AlignCenter, AlignCenter);
+ popup_set_text(
+ app->popup, "Frequency was not added...", 64, 40, AlignCenter, AlignCenter);
+ popup_set_callback(app->popup, callback_return);
+ popup_set_context(app->popup, app);
+ popup_set_timeout(app->popup, 1000);
+ popup_enable_timeout(app->popup);
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_frequencies_add_on_exit(void* context) {
+ XtremeApp* app = context;
+ text_input_reset(app->text_input);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c
new file mode 100644
index 000000000..24c7fe3cc
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c
@@ -0,0 +1,105 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexHopperFrequency,
+ VarItemListIndexDeleteHopperFreq,
+ VarItemListIndexAddHopperFreq,
+};
+
+void xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback(
+ void* context,
+ uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->subghz_hopper_index = variable_item_get_current_value_index(item);
+ uint32_t value = *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index);
+ char text[10] = {0};
+ snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000);
+ variable_item_set_current_value_text(item, text);
+}
+
+void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) {
+ XtremeApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Hopper Freq",
+ FrequencyList_size(app->subghz_hopper_frequencies),
+ xtreme_app_scene_protocols_frequencies_hopper_frequency_changed,
+ app);
+ app->subghz_hopper_index = 0;
+ variable_item_set_current_value_index(item, app->subghz_hopper_index);
+ if(FrequencyList_size(app->subghz_hopper_frequencies)) {
+ uint32_t value =
+ *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index);
+ char text[10] = {0};
+ snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000);
+ variable_item_set_current_value_text(item, text);
+ } else {
+ variable_item_set_current_value_text(item, "None");
+ }
+
+ variable_item_list_add(var_item_list, "Delete Hopper Freq", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, "Add Hopper Freq", 0, NULL, app);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list,
+ scene_manager_get_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexDeleteHopperFreq:
+ if(!FrequencyList_size(app->subghz_hopper_frequencies)) break;
+ uint32_t value =
+ *FrequencyList_get(app->subghz_hopper_frequencies, app->subghz_hopper_index);
+ FrequencyList_it_t it;
+ FrequencyList_it(it, app->subghz_hopper_frequencies);
+ while(!FrequencyList_end_p(it)) {
+ if(*FrequencyList_ref(it) == value) {
+ FrequencyList_remove(app->subghz_hopper_frequencies, it);
+ } else {
+ FrequencyList_next(it);
+ }
+ }
+ app->save_subghz_frequencies = true;
+ scene_manager_previous_scene(app->scene_manager);
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper);
+ break;
+ case VarItemListIndexAddHopperFreq:
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, true);
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_frequencies_hopper_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c
new file mode 100644
index 000000000..4d70a1a58
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c
@@ -0,0 +1,105 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexStaticFrequency,
+ VarItemListIndexDeleteStaticFreq,
+ VarItemListIndexAddStaticFreq,
+};
+
+void xtreme_app_scene_protocols_frequencies_static_var_item_list_callback(
+ void* context,
+ uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ app->subghz_static_index = variable_item_get_current_value_index(item);
+ uint32_t value = *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index);
+ char text[10] = {0};
+ snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000);
+ variable_item_set_current_value_text(item, text);
+}
+
+void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) {
+ XtremeApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Static Freq",
+ FrequencyList_size(app->subghz_static_frequencies),
+ xtreme_app_scene_protocols_frequencies_static_frequency_changed,
+ app);
+ app->subghz_static_index = 0;
+ variable_item_set_current_value_index(item, app->subghz_static_index);
+ if(FrequencyList_size(app->subghz_static_frequencies)) {
+ uint32_t value =
+ *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index);
+ char text[10] = {0};
+ snprintf(text, sizeof(text), "%lu.%02lu", value / 1000000, (value % 1000000) / 10000);
+ variable_item_set_current_value_text(item, text);
+ } else {
+ variable_item_set_current_value_text(item, "None");
+ }
+
+ variable_item_list_add(var_item_list, "Delete Static Freq", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, "Add Static Freq", 0, NULL, app);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_protocols_frequencies_static_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list,
+ scene_manager_get_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexDeleteStaticFreq:
+ if(!FrequencyList_size(app->subghz_static_frequencies)) break;
+ uint32_t value =
+ *FrequencyList_get(app->subghz_static_frequencies, app->subghz_static_index);
+ FrequencyList_it_t it;
+ FrequencyList_it(it, app->subghz_static_frequencies);
+ while(!FrequencyList_end_p(it)) {
+ if(*FrequencyList_ref(it) == value) {
+ FrequencyList_remove(app->subghz_static_frequencies, it);
+ } else {
+ FrequencyList_next(it);
+ }
+ }
+ app->save_subghz_frequencies = true;
+ scene_manager_previous_scene(app->scene_manager);
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic);
+ break;
+ case VarItemListIndexAddStaticFreq:
+ scene_manager_set_scene_state(
+ app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, false);
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_protocols_frequencies_static_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c
new file mode 100644
index 000000000..c1c7659fc
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c
@@ -0,0 +1,71 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexGraphics,
+ VarItemListIndexStatusbar,
+ VarItemListIndexProtocols,
+ VarItemListIndexDolphin,
+ VarItemListIndexMisc,
+};
+
+void xtreme_app_scene_start_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void xtreme_app_scene_start_on_enter(void* context) {
+ XtremeApp* app = context;
+ VariableItemList* var_item_list = app->var_item_list;
+
+ variable_item_list_add(var_item_list, "Graphics", 0, NULL, app);
+ variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app);
+ variable_item_list_add(var_item_list, "Protocols", 0, NULL, app);
+ variable_item_list_add(var_item_list, "Dolphin", 0, NULL, app);
+ variable_item_list_add(var_item_list, "Misc", 0, NULL, app);
+
+ variable_item_list_add(var_item_list, furi_string_get_cstr(app->version_tag), 0, NULL, app);
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_start_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneStart));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_start_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneStart, event.event);
+ consumed = true;
+ switch(event.event) {
+ case VarItemListIndexGraphics:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneGraphics);
+ break;
+ case VarItemListIndexStatusbar:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneStatusbar);
+ break;
+ case VarItemListIndexProtocols:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocols);
+ break;
+ case VarItemListIndexDolphin:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneDolphin);
+ break;
+ case VarItemListIndexMisc:
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_start_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c
new file mode 100644
index 000000000..48d7e6484
--- /dev/null
+++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_statusbar.c
@@ -0,0 +1,107 @@
+#include "../xtreme_app.h"
+
+enum VarItemListIndex {
+ VarItemListIndexBatteryIcon,
+ VarItemListIndexStatusIcons,
+ VarItemListIndexBarBorders,
+ VarItemListIndexbarBackground,
+};
+
+void xtreme_app_scene_statusbar_var_item_list_callback(void* context, uint32_t index) {
+ XtremeApp* app = context;
+ view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+const char* const battery_icon_names[] =
+ {"OFF", "Bar", "%", "Inv. %", "Retro 3", "Retro 5", "Bar %"};
+static void xtreme_app_scene_statusbar_battery_icon_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ uint8_t index = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, battery_icon_names[index]);
+ XTREME_SETTINGS()->battery_icon = index;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_statusbar_status_icons_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->status_icons = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_statusbar_bar_borders_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->bar_borders = value;
+ app->save_settings = true;
+}
+
+static void xtreme_app_scene_statusbar_bar_background_changed(VariableItem* item) {
+ XtremeApp* app = variable_item_get_context(item);
+ bool value = variable_item_get_current_value_index(item);
+ variable_item_set_current_value_text(item, value ? "ON" : "OFF");
+ XTREME_SETTINGS()->bar_background = value;
+ app->save_settings = true;
+}
+
+void xtreme_app_scene_statusbar_on_enter(void* context) {
+ XtremeApp* app = context;
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+ VariableItemList* var_item_list = app->var_item_list;
+ VariableItem* item;
+
+ item = variable_item_list_add(
+ var_item_list,
+ "Battery Icon",
+ BatteryIconCount,
+ xtreme_app_scene_statusbar_battery_icon_changed,
+ app);
+ variable_item_set_current_value_index(item, xtreme_settings->battery_icon);
+ variable_item_set_current_value_text(item, battery_icon_names[xtreme_settings->battery_icon]);
+
+ item = variable_item_list_add(
+ var_item_list, "Status Icons", 2, xtreme_app_scene_statusbar_status_icons_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->status_icons);
+ variable_item_set_current_value_text(item, xtreme_settings->status_icons ? "ON" : "OFF");
+
+ item = variable_item_list_add(
+ var_item_list, "Bar Borders", 2, xtreme_app_scene_statusbar_bar_borders_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->bar_borders);
+ variable_item_set_current_value_text(item, xtreme_settings->bar_borders ? "ON" : "OFF");
+
+ item = variable_item_list_add(
+ var_item_list, "Bar Background", 2, xtreme_app_scene_statusbar_bar_background_changed, app);
+ variable_item_set_current_value_index(item, xtreme_settings->bar_background);
+ variable_item_set_current_value_text(item, xtreme_settings->bar_background ? "ON" : "OFF");
+
+ variable_item_list_set_enter_callback(
+ var_item_list, xtreme_app_scene_statusbar_var_item_list_callback, app);
+
+ variable_item_list_set_selected_item(
+ var_item_list, scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneStatusbar));
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList);
+}
+
+bool xtreme_app_scene_statusbar_on_event(void* context, SceneManagerEvent event) {
+ XtremeApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneStatusbar, event.event);
+ consumed = true;
+ switch(event.event) {
+ default:
+ break;
+ }
+ }
+
+ return consumed;
+}
+
+void xtreme_app_scene_statusbar_on_exit(void* context) {
+ XtremeApp* app = context;
+ variable_item_list_reset(app->var_item_list);
+}
diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c
new file mode 100644
index 000000000..fa66a8d52
--- /dev/null
+++ b/applications/main/xtreme_app/xtreme_app.c
@@ -0,0 +1,274 @@
+#include "xtreme_app.h"
+
+static bool xtreme_app_custom_event_callback(void* context, uint32_t event) {
+ furi_assert(context);
+ XtremeApp* app = context;
+ return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+void callback_reboot(void* context) {
+ UNUSED(context);
+ power_reboot(PowerBootModeNormal);
+}
+
+static bool xtreme_app_back_event_callback(void* context) {
+ furi_assert(context);
+ XtremeApp* app = context;
+
+ if(!scene_manager_has_previous_scene(app->scene_manager, XtremeAppSceneStart)) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ if(app->save_subghz) {
+ furi_hal_subghz_set_extend_settings(app->subghz_extend, app->subghz_bypass);
+ }
+
+ if(app->save_subghz_frequencies) {
+ FlipperFormat* file = flipper_format_file_alloc(storage);
+ do {
+ FrequencyList_it_t it;
+ if(!flipper_format_file_open_always(file, EXT_PATH("subghz/assets/setting_user")))
+ break;
+
+ if(!flipper_format_write_header_cstr(
+ file, SUBGHZ_SETTING_FILE_TYPE, SUBGHZ_SETTING_FILE_VERSION))
+ break;
+
+ while(flipper_format_delete_key(file, "Add_standard_frequencies"))
+ ;
+ flipper_format_write_bool(
+ file, "Add_standard_frequencies", &app->subghz_use_defaults, 1);
+
+ if(!flipper_format_rewind(file)) break;
+ while(flipper_format_delete_key(file, "Frequency"))
+ ;
+ FrequencyList_it(it, app->subghz_static_frequencies);
+ for(uint i = 0; i < FrequencyList_size(app->subghz_static_frequencies); i++) {
+ flipper_format_write_uint32(
+ file, "Frequency", FrequencyList_get(app->subghz_static_frequencies, i), 1);
+ }
+
+ if(!flipper_format_rewind(file)) break;
+ while(flipper_format_delete_key(file, "Hopper_frequency"))
+ ;
+ for(uint i = 0; i < FrequencyList_size(app->subghz_hopper_frequencies); i++) {
+ flipper_format_write_uint32(
+ file,
+ "Hopper_frequency",
+ FrequencyList_get(app->subghz_hopper_frequencies, i),
+ 1);
+ }
+ } while(false);
+ flipper_format_free(file);
+ }
+
+ if(app->save_level) {
+ Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
+ int xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0;
+ dolphin->state->data.icounter = xp + 1;
+ dolphin->state->dirty = true;
+ dolphin_state_save(dolphin->state);
+ furi_record_close(RECORD_DOLPHIN);
+ }
+
+ if(app->save_name) {
+ if(strcmp(app->device_name, "") == 0) {
+ storage_simply_remove(storage, NAMECHANGER_PATH);
+ } else {
+ FlipperFormat* file = flipper_format_file_alloc(storage);
+
+ do {
+ if(!flipper_format_file_open_always(file, NAMECHANGER_PATH)) break;
+
+ if(!flipper_format_write_header_cstr(file, NAMECHANGER_HEADER, 1)) break;
+
+ 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(!flipper_format_write_string_cstr(file, "Name", app->device_name)) break;
+
+ } while(0);
+
+ flipper_format_free(file);
+ }
+ }
+
+ if(app->save_settings) {
+ XTREME_SETTINGS_SAVE();
+ }
+
+ if(app->require_reboot) {
+ popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter);
+ popup_set_text(app->popup, "Applying changes...", 64, 40, AlignCenter, AlignCenter);
+ popup_set_callback(app->popup, callback_reboot);
+ popup_set_context(app->popup, app);
+ popup_set_timeout(app->popup, 1000);
+ popup_enable_timeout(app->popup);
+ view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup);
+ return true;
+ }
+
+ furi_record_close(RECORD_STORAGE);
+ }
+
+ return scene_manager_handle_back_event(app->scene_manager);
+}
+
+XtremeApp* xtreme_app_alloc() {
+ XtremeApp* app = malloc(sizeof(XtremeApp));
+ app->gui = furi_record_open(RECORD_GUI);
+
+ // View Dispatcher and Scene Manager
+ app->view_dispatcher = view_dispatcher_alloc();
+ app->scene_manager = scene_manager_alloc(&xtreme_app_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, xtreme_app_custom_event_callback);
+ view_dispatcher_set_navigation_event_callback(
+ app->view_dispatcher, xtreme_app_back_event_callback);
+
+ view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+ // Gui Modules
+ app->var_item_list = variable_item_list_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher,
+ XtremeAppViewVarItemList,
+ variable_item_list_get_view(app->var_item_list));
+
+ app->text_input = text_input_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, XtremeAppViewTextInput, text_input_get_view(app->text_input));
+
+ app->popup = popup_alloc();
+ view_dispatcher_add_view(app->view_dispatcher, XtremeAppViewPopup, popup_get_view(app->popup));
+
+ // Settings init
+
+ XtremeSettings* xtreme_settings = XTREME_SETTINGS();
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* file = flipper_format_file_alloc(storage);
+ FrequencyList_init(app->subghz_static_frequencies);
+ FrequencyList_init(app->subghz_hopper_frequencies);
+ app->subghz_use_defaults = true;
+ do {
+ uint32_t temp;
+ if(!flipper_format_file_open_existing(file, EXT_PATH("subghz/assets/setting_user"))) break;
+
+ flipper_format_read_bool(file, "Add_standard_frequencies", &app->subghz_use_defaults, 1);
+
+ if(!flipper_format_rewind(file)) break;
+ while(flipper_format_read_uint32(file, "Frequency", &temp, 1)) {
+ if(furi_hal_subghz_is_frequency_valid(temp)) {
+ FrequencyList_push_back(app->subghz_static_frequencies, temp);
+ }
+ }
+
+ if(!flipper_format_rewind(file)) break;
+ while(flipper_format_read_uint32(file, "Hopper_frequency", &temp, 1)) {
+ if(furi_hal_subghz_is_frequency_valid(temp)) {
+ FrequencyList_push_back(app->subghz_hopper_frequencies, temp);
+ }
+ }
+ } while(false);
+ flipper_format_free(file);
+
+ furi_hal_subghz_get_extend_settings(&app->subghz_extend, &app->subghz_bypass);
+
+ Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
+ DolphinStats stats = dolphin_stats(dolphin);
+ app->dolphin_level = stats.level;
+ furi_record_close(RECORD_DOLPHIN);
+
+ strlcpy(app->device_name, furi_hal_version_get_name_ptr(), NAMECHANGER_TEXT_STORE_SIZE);
+
+ app->asset_pack = 0;
+ asset_packs_init(app->asset_packs);
+ File* folder = storage_file_alloc(storage);
+ FileInfo info;
+ char* name = malloc(MAX_PACK_NAME_LEN);
+ if(storage_dir_open(folder, PACKS_DIR)) {
+ while(storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) {
+ if(info.flags & FSF_DIRECTORY) {
+ char* copy = malloc(MAX_PACK_NAME_LEN);
+ strlcpy(copy, name, MAX_PACK_NAME_LEN);
+ uint idx = 0;
+ if(strcmp(copy, "NSFW") != 0) {
+ for(; idx < asset_packs_size(app->asset_packs); idx++) {
+ char* comp = *asset_packs_get(app->asset_packs, idx);
+ if(strcasecmp(copy, comp) < 0 && strcmp(comp, "NSFW") != 0) {
+ break;
+ }
+ }
+ }
+ asset_packs_push_at(app->asset_packs, idx, copy);
+ if(app->asset_pack != 0) {
+ if(idx < app->asset_pack) app->asset_pack++;
+ } else {
+ if(strcmp(copy, xtreme_settings->asset_pack) == 0) app->asset_pack = idx + 1;
+ }
+ }
+ }
+ }
+ free(name);
+ storage_file_free(folder);
+ furi_record_close(RECORD_STORAGE);
+
+ app->version_tag =
+ furi_string_alloc_printf("%s %s", version_get_version(NULL), version_get_builddate(NULL));
+
+ return app;
+}
+
+void xtreme_app_free(XtremeApp* app) {
+ furi_assert(app);
+
+ // Gui modules
+ view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewVarItemList);
+ variable_item_list_free(app->var_item_list);
+ view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewTextInput);
+ text_input_free(app->text_input);
+ view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewPopup);
+ popup_free(app->popup);
+
+ // View Dispatcher and Scene Manager
+ view_dispatcher_free(app->view_dispatcher);
+ scene_manager_free(app->scene_manager);
+
+ // Settings deinit
+
+ FrequencyList_clear(app->subghz_static_frequencies);
+ FrequencyList_clear(app->subghz_hopper_frequencies);
+
+ asset_packs_it_t it;
+ for(asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) {
+ free(*asset_packs_cref(it));
+ }
+ asset_packs_clear(app->asset_packs);
+
+ furi_string_free(app->version_tag);
+
+ // Records
+ furi_record_close(RECORD_GUI);
+ free(app);
+}
+
+extern int32_t xtreme_app(void* p) {
+ UNUSED(p);
+ XtremeApp* app = xtreme_app_alloc();
+ scene_manager_next_scene(app->scene_manager, XtremeAppSceneStart);
+ view_dispatcher_run(app->view_dispatcher);
+ xtreme_app_free(app);
+ return 0;
+}
diff --git a/applications/settings/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h
similarity index 54%
rename from applications/settings/xtreme_app/xtreme_app.h
rename to applications/main/xtreme_app/xtreme_app.h
index 23adbf5ac..d3af90b6e 100644
--- a/applications/settings/xtreme_app/xtreme_app.h
+++ b/applications/main/xtreme_app/xtreme_app.h
@@ -7,16 +7,22 @@
#include
#include
#include
+#include
#include
+#include
+#include
#include "scenes/xtreme_app_scene.h"
#include "dolphin/helpers/dolphin_state.h"
#include "dolphin/dolphin.h"
#include "dolphin/dolphin_i.h"
#include
+#include
#include
#include "xtreme/settings.h"
#include "xtreme/assets.h"
+#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 6
+
ARRAY_DEF(asset_packs, char*)
typedef struct {
@@ -24,18 +30,31 @@ typedef struct {
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
VariableItemList* var_item_list;
+ TextInput* text_input;
Popup* popup;
- int dolphin_level;
+ bool subghz_use_defaults;
+ FrequencyList_t subghz_static_frequencies;
+ uint8_t subghz_static_index;
+ FrequencyList_t subghz_hopper_frequencies;
+ uint8_t subghz_hopper_index;
+ char subghz_freq_buffer[XTREME_SUBGHZ_FREQ_BUFFER_SIZE];
bool subghz_extend;
bool subghz_bypass;
- bool settings_changed;
- bool assets_changed;
- bool subghz_changed;
- bool level_changed;
+ int dolphin_level;
+ char device_name[NAMECHANGER_TEXT_STORE_SIZE];
+ uint asset_pack;
asset_packs_t asset_packs;
+ FuriString* version_tag;
+ bool save_subghz;
+ bool save_subghz_frequencies;
+ bool save_level;
+ bool save_name;
+ bool save_settings;
+ bool require_reboot;
} XtremeApp;
typedef enum {
XtremeAppViewVarItemList,
+ XtremeAppViewTextInput,
XtremeAppViewPopup,
} XtremeAppView;
diff --git a/applications/plugins/barcode_generator/application.fam b/applications/plugins/barcode_generator/application.fam
index 97dc9acef..a02255334 100644
--- a/applications/plugins/barcode_generator/application.fam
+++ b/applications/plugins/barcode_generator/application.fam
@@ -9,7 +9,7 @@ App(
"dialogs",
],
stack_size=1 * 1024,
- order=250,
+ order=50,
fap_icon="barcode_10px.png",
fap_category="Misc",
)
diff --git a/applications/plugins/barcode_generator/barcode_generator.c b/applications/plugins/barcode_generator/barcode_generator.c
index 4aa54e7d4..cbbb41671 100644
--- a/applications/plugins/barcode_generator/barcode_generator.c
+++ b/applications/plugins/barcode_generator/barcode_generator.c
@@ -1,8 +1,3 @@
-#include
-#include
-#include
-#include
-
#include "barcode_generator.h"
static BarcodeType* barcodeTypes[NUMBER_OF_BARCODE_TYPES];
@@ -103,9 +98,9 @@ int get_menu_text_location(int index) {
}
int get_barcode_max_index(PluginState* plugin_state) {
- return plugin_state->doParityCalculation ?
- barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits - 1 :
- barcodeTypes[plugin_state->barcodeTypeIndex]->numberOfDigits;
+ return plugin_state->barcode_state.doParityCalculation ?
+ barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]->numberOfDigits - 1 :
+ barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]->numberOfDigits;
}
int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) {
@@ -114,12 +109,12 @@ int calculate_check_digit(PluginState* plugin_state, BarcodeType* type) {
int checkDigitEven = 0;
//add all odd positions. Confusing because 0index
for(int i = 0; i < type->numberOfDigits - 1; i += 2) {
- checkDigitOdd += plugin_state->barcodeNumeral[i];
+ checkDigitOdd += plugin_state->barcode_state.barcodeNumeral[i];
}
//add all even positions to above. Confusing because 0index
for(int i = 1; i < type->numberOfDigits - 1; i += 2) {
- checkDigitEven += plugin_state->barcodeNumeral[i];
+ checkDigitEven += plugin_state->barcode_state.barcodeNumeral[i];
}
if(type->bartype == BarTypeEAN13) {
@@ -152,7 +147,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas, 64, get_menu_text_location(2), AlignCenter, AlignCenter, "Parity?");
canvas_draw_frame(canvas, 83, get_menu_text_location(2) - 3, 6, 6);
- if(plugin_state->doParityCalculation == true) {
+ if(plugin_state->barcode_state.doParityCalculation == true) {
canvas_draw_box(canvas, 85, get_menu_text_location(2) - 1, 2, 2);
}
canvas_draw_str_aligned(
@@ -161,14 +156,14 @@ static void render_callback(Canvas* const canvas, void* ctx) {
get_menu_text_location(3),
AlignCenter,
AlignCenter,
- (barcodeTypes[plugin_state->barcodeTypeIndex])->name);
+ (barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex])->name);
canvas_draw_disc(
canvas,
40,
get_menu_text_location(plugin_state->menuIndex) - 1,
2); //draw menu cursor
} else {
- BarcodeType* type = barcodeTypes[plugin_state->barcodeTypeIndex];
+ BarcodeType* type = barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex];
//start saftey
canvas_set_color(canvas, ColorBlack);
@@ -181,13 +176,13 @@ static void render_callback(Canvas* const canvas, void* ctx) {
startpos++;
draw_digit(
canvas,
- plugin_state->barcodeNumeral[0],
+ plugin_state->barcode_state.barcodeNumeral[0],
BarEncodingTypeRight,
- get_digit_position(0, barcodeTypes[plugin_state->barcodeTypeIndex]),
+ get_digit_position(0, barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]),
false);
}
- if(plugin_state->doParityCalculation) { //calculate the check digit
- plugin_state->barcodeNumeral[type->numberOfDigits - 1] =
+ if(plugin_state->barcode_state.doParityCalculation) { //calculate the check digit
+ plugin_state->barcode_state.barcodeNumeral[type->numberOfDigits - 1] =
calculate_check_digit(plugin_state, type);
}
for(int index = startpos; index < endpos; index++) {
@@ -197,7 +192,9 @@ static void render_callback(Canvas* const canvas, void* ctx) {
barEncodingType = BarEncodingTypeRight;
} else {
barEncodingType =
- (FURI_BIT(EAN13ENCODE[plugin_state->barcodeNumeral[0]], index - 1)) ?
+ (FURI_BIT(
+ EAN13ENCODE[plugin_state->barcode_state.barcodeNumeral[0]],
+ index - 1)) ?
BarEncodingTypeG :
BarEncodingTypeLeft;
}
@@ -207,10 +204,14 @@ static void render_callback(Canvas* const canvas, void* ctx) {
}
}
- int digitPosition =
- get_digit_position(index, barcodeTypes[plugin_state->barcodeTypeIndex]);
+ int digitPosition = get_digit_position(
+ index, barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]);
draw_digit(
- canvas, plugin_state->barcodeNumeral[index], barEncodingType, digitPosition, true);
+ canvas,
+ plugin_state->barcode_state.barcodeNumeral[index],
+ barEncodingType,
+ digitPosition,
+ true);
}
//central separator
@@ -223,7 +224,8 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas_draw_box(
canvas,
get_digit_position(
- plugin_state->editingIndex, barcodeTypes[plugin_state->barcodeTypeIndex]) -
+ plugin_state->editingIndex,
+ barcodeTypes[plugin_state->barcode_state.barcodeTypeIndex]) -
1,
63,
7,
@@ -247,15 +249,17 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
-static void barcode_generator_state_init(PluginState* const plugin_state) {
- for(int i = 0; i < BARCODE_MAX_LENS; ++i) {
- plugin_state->barcodeNumeral[i] = i % 10;
- }
+static void barcode_generator_state_init(PluginState* plugin_state) {
plugin_state->editingIndex = 0;
plugin_state->mode = ViewMode;
- plugin_state->doParityCalculation = true;
plugin_state->menuIndex = MENU_INDEX_VIEW;
- plugin_state->barcodeTypeIndex = 0;
+ if(!LOAD_BARCODE_SETTINGS(&plugin_state->barcode_state)) {
+ for(int i = 0; i < BARCODE_MAX_LENS; ++i) {
+ plugin_state->barcode_state.barcodeNumeral[i] = i % 10;
+ }
+ plugin_state->barcode_state.doParityCalculation = true;
+ plugin_state->barcode_state.barcodeTypeIndex = 0;
+ }
}
static bool handle_key_press_view(InputKey key, PluginState* plugin_state) {
@@ -277,15 +281,15 @@ static bool handle_key_press_edit(InputKey key, PluginState* plugin_state) {
switch(key) {
case InputKeyUp:
- plugin_state->barcodeNumeral[plugin_state->editingIndex] =
- (plugin_state->barcodeNumeral[plugin_state->editingIndex] + 1) % 10;
+ plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] =
+ (plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] + 1) % 10;
break;
case InputKeyDown:
- plugin_state->barcodeNumeral[plugin_state->editingIndex] =
- (plugin_state->barcodeNumeral[plugin_state->editingIndex] == 0) ?
+ plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] =
+ (plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] == 0) ?
9 :
- plugin_state->barcodeNumeral[plugin_state->editingIndex] - 1;
+ plugin_state->barcode_state.barcodeNumeral[plugin_state->editingIndex] - 1;
break;
case InputKeyRight:
@@ -324,21 +328,24 @@ static bool handle_key_press_menu(InputKey key, PluginState* plugin_state) {
case InputKeyRight:
if(plugin_state->menuIndex == MENU_INDEX_TYPE) {
- plugin_state->barcodeTypeIndex =
- (plugin_state->barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ?
+ plugin_state->barcode_state.barcodeTypeIndex =
+ (plugin_state->barcode_state.barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ?
0 :
- plugin_state->barcodeTypeIndex + 1;
+ plugin_state->barcode_state.barcodeTypeIndex + 1;
} else if(plugin_state->menuIndex == MENU_INDEX_PARITY) {
- plugin_state->doParityCalculation = !plugin_state->doParityCalculation;
+ plugin_state->barcode_state.doParityCalculation =
+ !plugin_state->barcode_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;
+ plugin_state->barcode_state.barcodeTypeIndex =
+ (plugin_state->barcode_state.barcodeTypeIndex == 0) ?
+ NUMBER_OF_BARCODE_TYPES - 1 :
+ plugin_state->barcode_state.barcodeTypeIndex - 1;
} else if(plugin_state->menuIndex == MENU_INDEX_PARITY) {
- plugin_state->doParityCalculation = !plugin_state->doParityCalculation;
+ plugin_state->barcode_state.doParityCalculation =
+ !plugin_state->barcode_state.doParityCalculation;
}
break;
@@ -348,12 +355,13 @@ static bool handle_key_press_menu(InputKey key, PluginState* plugin_state) {
} 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;
+ plugin_state->barcode_state.doParityCalculation =
+ !plugin_state->barcode_state.doParityCalculation;
} else if(plugin_state->menuIndex == MENU_INDEX_TYPE) {
- plugin_state->barcodeTypeIndex =
- (plugin_state->barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ?
+ plugin_state->barcode_state.barcodeTypeIndex =
+ (plugin_state->barcode_state.barcodeTypeIndex == NUMBER_OF_BARCODE_TYPES - 1) ?
0 :
- plugin_state->barcodeTypeIndex + 1;
+ plugin_state->barcode_state.barcodeTypeIndex + 1;
}
break;
@@ -430,6 +438,9 @@ int32_t barcode_generator_app(void* p) {
furi_record_close(RECORD_GUI);
view_port_free(view_port);
furi_message_queue_free(event_queue);
+ // save settings
+ SAVE_BARCODE_SETTINGS(&plugin_state->barcode_state);
+ free(plugin_state);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/barcode_generator/barcode_generator.h b/applications/plugins/barcode_generator/barcode_generator.h
index 1b5ff9e15..b39e43320 100644
--- a/applications/plugins/barcode_generator/barcode_generator.h
+++ b/applications/plugins/barcode_generator/barcode_generator.h
@@ -1,3 +1,34 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define BARCODE_SETTINGS_FILE_NAME "apps/Misc/barcodegen.save"
+
+#define BARCODE_SETTINGS_VER (1)
+#define BARCODE_SETTINGS_PATH EXT_PATH(BARCODE_SETTINGS_FILE_NAME)
+#define BARCODE_SETTINGS_MAGIC (0xC2)
+
+#define SAVE_BARCODE_SETTINGS(x) \
+ saved_struct_save( \
+ BARCODE_SETTINGS_PATH, \
+ (x), \
+ sizeof(BarcodeState), \
+ BARCODE_SETTINGS_MAGIC, \
+ BARCODE_SETTINGS_VER)
+
+#define LOAD_BARCODE_SETTINGS(x) \
+ saved_struct_load( \
+ BARCODE_SETTINGS_PATH, \
+ (x), \
+ sizeof(BarcodeState), \
+ BARCODE_SETTINGS_MAGIC, \
+ BARCODE_SETTINGS_VER)
+
#define BARCODE_HEIGHT 50
#define BARCODE_Y_START 3
#define BARCODE_TEXT_OFFSET 9
@@ -45,11 +76,15 @@ typedef struct {
typedef struct {
int barcodeNumeral[BARCODE_MAX_LENS]; //The current barcode number
+ bool doParityCalculation; //Should do parity check?
+ int barcodeTypeIndex;
+} BarcodeState;
+
+typedef struct {
+ BarcodeState barcode_state;
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] = {
diff --git a/applications/plugins/blackjack/blackjack.c b/applications/plugins/blackjack/blackjack.c
index 73d393f8b..384104378 100644
--- a/applications/plugins/blackjack/blackjack.c
+++ b/applications/plugins/blackjack/blackjack.c
@@ -1,7 +1,6 @@
#include
#include
-#include
#include
#include
@@ -631,4 +630,4 @@ free_and_exit:
furi_message_queue_free(event_queue);
return return_code;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/blackjack/common/card.c b/applications/plugins/blackjack/common/card.c
index 199135bb5..88228fda4 100644
--- a/applications/plugins/blackjack/common/card.c
+++ b/applications/plugins/blackjack/common/card.c
@@ -173,7 +173,7 @@ uint8_t hand_count(const Card* cards, uint8_t count) {
}
for(uint8_t i = 0; i < aceCount; i++) {
- if((score + 11) <= 21)
+ if((score + 11 + (aceCount - 1)) <= 21)
score += 11;
else
score++;
@@ -350,4 +350,4 @@ void add_hand_region(Hand* to, Hand* from) {
add_to_hand(to, from->cards[i]);
}
}
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/clock_app/application.fam b/applications/plugins/clock_app/application.fam
deleted file mode 100644
index a6a2eff3e..000000000
--- a/applications/plugins/clock_app/application.fam
+++ /dev/null
@@ -1,10 +0,0 @@
-App(
- appid="clock",
- name="Clock",
- apptype=FlipperAppType.EXTERNAL,
- entry_point="clock_app",
- requires=["gui"],
- stack_size=2 * 1024,
- fap_icon="clock.png",
- fap_category="Tools",
-)
diff --git a/applications/plugins/clock_app/clock.png b/applications/plugins/clock_app/clock.png
deleted file mode 100644
index 0d96df102..000000000
Binary files a/applications/plugins/clock_app/clock.png and /dev/null differ
diff --git a/applications/plugins/clock_app/clock_app.c b/applications/plugins/clock_app/clock_app.c
deleted file mode 100644
index d2c178903..000000000
--- a/applications/plugins/clock_app/clock_app.c
+++ /dev/null
@@ -1,241 +0,0 @@
-#include
-#include
-
-#include
-#include
-
-#include "clock_app.h"
-
-static void clock_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 clock_render_callback(Canvas* const canvas, void* ctx) {
- //canvas_clear(canvas);
- //canvas_set_color(canvas, ColorBlack);
-
- ClockState* state = ctx;
- if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) {
- //FURI_LOG_D(TAG, "Can't obtain mutex, requeue render");
- PluginEvent event = {.type = EventTypeTick};
- furi_message_queue_put(state->event_queue, &event, 0);
- return;
- }
-
- FuriHalRtcDateTime curr_dt;
- furi_hal_rtc_get_datetime(&curr_dt);
- uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
-
- char time_string[TIME_LEN];
- char date_string[DATE_LEN];
- char meridian_string[MERIDIAN_LEN];
- char timer_string[20];
-
- if(state->time_format == LocaleTimeFormat24h) {
- snprintf(
- time_string, TIME_LEN, CLOCK_TIME_FORMAT, curr_dt.hour, curr_dt.minute, curr_dt.second);
- } else {
- bool pm = curr_dt.hour > 12;
- bool pm12 = curr_dt.hour >= 12;
- bool am12 = curr_dt.hour == 0;
- snprintf(
- time_string,
- TIME_LEN,
- CLOCK_TIME_FORMAT,
- pm ? curr_dt.hour - 12 : (am12 ? 12 : curr_dt.hour),
- curr_dt.minute,
- curr_dt.second);
-
- snprintf(
- meridian_string,
- MERIDIAN_LEN,
- MERIDIAN_FORMAT,
- pm12 ? MERIDIAN_STRING_PM : MERIDIAN_STRING_AM);
- }
-
- if(state->date_format == LocaleDateFormatYMD) {
- snprintf(
- date_string, DATE_LEN, CLOCK_ISO_DATE_FORMAT, curr_dt.year, curr_dt.month, curr_dt.day);
- } else if(state->date_format == LocaleDateFormatMDY) {
- snprintf(
- date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.month, curr_dt.day, curr_dt.year);
- } else {
- snprintf(
- date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.day, curr_dt.month, curr_dt.year);
- }
-
- bool timer_running = state->timer_running;
- uint32_t timer_start_timestamp = state->timer_start_timestamp;
- uint32_t timer_stopped_seconds = state->timer_stopped_seconds;
-
- furi_mutex_release(state->mutex);
-
- canvas_set_font(canvas, FontBigNumbers);
-
- if(timer_start_timestamp != 0) {
- int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) :
- timer_stopped_seconds;
- snprintf(timer_string, 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60);
- canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, time_string); // DRAW TIME
- canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, timer_string); // DRAW TIMER
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignTop, date_string); // DRAW DATE
- elements_button_left(canvas, "Reset");
- } else {
- canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, time_string);
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, date_string);
-
- if(state->time_format == LocaleTimeFormat12h)
- canvas_draw_str_aligned(canvas, 65, 12, AlignCenter, AlignCenter, meridian_string);
- }
- if(timer_running) {
- elements_button_center(canvas, "Stop");
- } else if(timer_start_timestamp != 0 && !timer_running) {
- elements_button_center(canvas, "Start");
- }
-}
-
-static void clock_state_init(ClockState* const state) {
- state->time_format = locale_get_time_format();
-
- state->date_format = locale_get_date_format();
-
- //FURI_LOG_D(TAG, "Time format: %s", state->settings.time_format == H12 ? "12h" : "24h");
- //FURI_LOG_D(TAG, "Date format: %s", state->settings.date_format == Iso ? "ISO 8601" : "RFC 5322");
- //furi_hal_rtc_get_datetime(&state->datetime);
-}
-
-// Runs every 1000ms by default
-static void clock_tick(void* ctx) {
- furi_assert(ctx);
- FuriMessageQueue* event_queue = ctx;
- PluginEvent event = {.type = EventTypeTick};
- // It's OK to loose this event if system overloaded
- furi_message_queue_put(event_queue, &event, 0);
-}
-
-int32_t clock_app(void* p) {
- UNUSED(p);
- ClockState* plugin_state = malloc(sizeof(ClockState));
-
- plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
- if(plugin_state->event_queue == NULL) {
- FURI_LOG_E(TAG, "Cannot create event queue");
- free(plugin_state);
- return 255;
- }
- //FURI_LOG_D(TAG, "Event queue created");
-
- plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- if(plugin_state->mutex == NULL) {
- FURI_LOG_E(TAG, "Cannot create mutex");
- furi_message_queue_free(plugin_state->event_queue);
- free(plugin_state);
- return 255;
- }
- //FURI_LOG_D(TAG, "Mutex created");
-
- clock_state_init(plugin_state);
-
- // Set system callbacks
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, clock_render_callback, plugin_state);
- view_port_input_callback_set(view_port, clock_input_callback, plugin_state->event_queue);
-
- FuriTimer* timer =
- furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, plugin_state->event_queue);
-
- if(timer == NULL) {
- FURI_LOG_E(TAG, "Cannot create timer");
- furi_mutex_free(plugin_state->mutex);
- furi_message_queue_free(plugin_state->event_queue);
- free(plugin_state);
- return 255;
- }
- //FURI_LOG_D(TAG, "Timer created");
-
- // Open GUI and register view_port
- Gui* gui = furi_record_open(RECORD_GUI);
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
- furi_timer_start(timer, furi_kernel_get_tick_frequency());
- //FURI_LOG_D(TAG, "Timer started");
-
- // Main loop
- PluginEvent event;
- for(bool processing = true; processing;) {
- FuriStatus event_status = furi_message_queue_get(plugin_state->event_queue, &event, 100);
-
- if(event_status != FuriStatusOk) continue;
-
- if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) != FuriStatusOk) continue;
- // press events
- if(event.type == EventTypeKey) {
- if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) {
- switch(event.input.key) {
- case InputKeyUp:
- case InputKeyDown:
- case InputKeyRight:
- break;
- case InputKeyLeft:
- if(plugin_state->timer_start_timestamp != 0) {
- // Reset seconds
- plugin_state->timer_running = false;
- plugin_state->timer_start_timestamp = 0;
- plugin_state->timer_stopped_seconds = 0;
- }
- break;
- case InputKeyOk:;
- // START/STOP TIMER
-
- FuriHalRtcDateTime curr_dt;
- furi_hal_rtc_get_datetime(&curr_dt);
- uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
-
- if(plugin_state->timer_running) {
- // Update stopped seconds
- plugin_state->timer_stopped_seconds =
- curr_ts - plugin_state->timer_start_timestamp;
- } else {
- if(plugin_state->timer_start_timestamp == 0) {
- // Set starting timestamp if this is first time
- plugin_state->timer_start_timestamp = curr_ts;
- } else {
- // Timer was already running, need to slightly readjust so we don't
- // count the intervening time
- plugin_state->timer_start_timestamp =
- curr_ts - plugin_state->timer_stopped_seconds;
- }
- }
- plugin_state->timer_running = !plugin_state->timer_running;
- break;
- case InputKeyBack:
- // Exit the plugin
- processing = false;
- break;
- default:
- break;
- }
- }
- } /*else if(event.type == EventTypeTick) {
- furi_hal_rtc_get_datetime(&plugin_state->datetime);
- }*/
-
- view_port_update(view_port);
- furi_mutex_release(plugin_state->mutex);
- }
-
- 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(plugin_state->event_queue);
- furi_mutex_free(plugin_state->mutex);
- free(plugin_state);
-
- return 0;
-}
diff --git a/applications/plugins/clock_app/clock_app.h b/applications/plugins/clock_app/clock_app.h
deleted file mode 100644
index 693bdfac0..000000000
--- a/applications/plugins/clock_app/clock_app.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include
-#include
-
-#define TAG "Clock"
-
-#define CLOCK_ISO_DATE_FORMAT "%.4d-%.2d-%.2d"
-#define CLOCK_RFC_DATE_FORMAT "%.2d-%.2d-%.4d"
-#define CLOCK_TIME_FORMAT "%.2d:%.2d:%.2d"
-
-#define MERIDIAN_FORMAT "%s"
-#define MERIDIAN_STRING_AM "AM"
-#define MERIDIAN_STRING_PM "PM"
-
-#define TIME_LEN 12
-#define DATE_LEN 14
-#define MERIDIAN_LEN 3
-
-typedef enum {
- EventTypeTick,
- EventTypeKey,
-} EventType;
-
-typedef struct {
- EventType type;
- InputEvent input;
-} PluginEvent;
-
-typedef struct {
- LocaleDateFormat date_format;
- LocaleTimeFormat time_format;
- FuriHalRtcDateTime datetime;
- FuriMutex* mutex;
- FuriMessageQueue* event_queue;
- uint32_t timer_start_timestamp;
- uint32_t timer_stopped_seconds;
- bool timer_running;
-} ClockState;
diff --git a/applications/plugins/cntdown_timer/application.fam b/applications/plugins/cntdown_timer/application.fam
index f40ccaa51..e2c950907 100644
--- a/applications/plugins/cntdown_timer/application.fam
+++ b/applications/plugins/cntdown_timer/application.fam
@@ -10,5 +10,5 @@ App(
stack_size=2 * 1024,
order=20,
fap_icon="cntdown_timer.png",
- fap_category="Tools",
+ fap_category="Misc",
)
diff --git a/applications/plugins/dice/dice.c b/applications/plugins/dice/dice.c
index dc748b68f..0bf90a1c8 100644
--- a/applications/plugins/dice/dice.c
+++ b/applications/plugins/dice/dice.c
@@ -290,10 +290,10 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) {
state->diceQty,
state->diceType[0],
state->rollTime[0]);
- if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect)
- DOLPHIN_DEED(getRandomDeed());
- if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect - 1)
- DOLPHIN_DEED(getRandomDeed());
+ // if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect)
+ // DOLPHIN_DEED(getRandomDeed());
+ // if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect - 1)
+ // DOLPHIN_DEED(getRandomDeed());
if(state->diceQty == 1) {
snprintf(state->strings[1], sizeof(state->strings[1]), "%d", state->diceRoll);
} else if(state->diceQty == 2) {
diff --git a/applications/plugins/flappy_bird/flappy_bird.c b/applications/plugins/flappy_bird/flappy_bird.c
index a86ddb2bb..2eeaf7816 100644
--- a/applications/plugins/flappy_bird/flappy_bird.c
+++ b/applications/plugins/flappy_bird/flappy_bird.c
@@ -1,5 +1,4 @@
#include
-#include
#include
#include
@@ -256,10 +255,6 @@ static void flappy_game_render_callback(Canvas* const canvas, void* ctx) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 37, 31, "Game Over");
- if(game_state->points != 0 && game_state->points % 5 == 0) {
- DOLPHIN_DEED(getRandomDeed());
- }
-
canvas_set_font(canvas, FontSecondary);
char buffer[12];
snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points);
diff --git a/applications/plugins/game15/game15.c b/applications/plugins/game15/game15.c
index d9b059466..8faea4380 100644
--- a/applications/plugins/game15/game15.c
+++ b/applications/plugins/game15/game15.c
@@ -461,6 +461,7 @@ int32_t game15_app() {
sandbox_init(
FPS, (SandboxRenderCallback)render_callback, (SandboxEventHandler)game_event_handler);
+
sandbox_loop();
sandbox_free();
game_free();
diff --git a/applications/plugins/game_2048/game_2048.c b/applications/plugins/game_2048/game_2048.c
index e0c4a4cec..097f0d3d5 100644
--- a/applications/plugins/game_2048/game_2048.c
+++ b/applications/plugins/game_2048/game_2048.c
@@ -382,7 +382,7 @@ int32_t game_2048_app() {
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) {
- FURI_LOG_E("SnakeGame", "cannot create mutex\r\n");
+ FURI_LOG_E("2048Game", "cannot create mutex\r\n");
free(game_state);
return 255;
}
diff --git a/applications/plugins/hc_sr04/README.md b/applications/plugins/hc_sr04/README.md
deleted file mode 100644
index 2fb3b8ce4..000000000
--- a/applications/plugins/hc_sr04/README.md
+++ /dev/null
@@ -1 +0,0 @@
-## (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo)
\ No newline at end of file
diff --git a/applications/plugins/hc_sr04/hc_sr04.c b/applications/plugins/hc_sr04/hc_sr04.c
index dbbf4f3ec..db075247a 100644
--- a/applications/plugins/hc_sr04/hc_sr04.c
+++ b/applications/plugins/hc_sr04/hc_sr04.c
@@ -3,6 +3,7 @@
// Ported and modified by @xMasterX
#include
+#include
#include
#include
#include
@@ -26,7 +27,7 @@ typedef struct {
NotificationApp* notification;
bool have_5v;
bool measurement_made;
- uint32_t echo; // ms
+ uint32_t echo; // us
float distance; // meters
} PluginState;
@@ -72,7 +73,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
FuriString* str_buf;
str_buf = furi_string_alloc();
- furi_string_printf(str_buf, "Echo: %ld ms", plugin_state->echo);
+ furi_string_printf(str_buf, "Echo: %ld us", plugin_state->echo);
canvas_draw_str_aligned(
canvas, 8, 38, AlignLeft, AlignTop, furi_string_get_cstr(str_buf));
@@ -110,9 +111,11 @@ static void hc_sr04_state_init(PluginState* const plugin_state) {
}
}
-float hc_sr04_ms_to_m(uint32_t ms) {
- const float speed_sound_m_per_s = 343.0f;
- const float time_s = ms / 1e3f;
+float hc_sr04_us_to_m(uint32_t us) {
+ //speed of sound for 20°C, 50% relative humidity
+ //331.3 + 20 * 0.606 + 50 * 0.0124 = 0.034404
+ const float speed_sound_m_per_s = 344.04f;
+ const float time_s = us / 1e6f;
const float total_dist = time_s * speed_sound_m_per_s;
return total_dist / 2.0f;
}
@@ -147,10 +150,6 @@ static void hc_sr04_measure(PluginState* const plugin_state) {
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))
@@ -158,16 +157,17 @@ static void hc_sr04_measure(PluginState* const plugin_state) {
while(furi_get_tick() - start < timeout_ms && !furi_hal_gpio_read(&gpio_usart_rx))
;
- const uint32_t pulse_start = furi_get_tick();
+ const uint32_t pulse_start = DWT->CYCCNT;
while(furi_get_tick() - start < timeout_ms && furi_hal_gpio_read(&gpio_usart_rx))
;
+ const uint32_t pulse_end = DWT->CYCCNT;
- 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->echo =
+ (pulse_end - pulse_start) / furi_hal_cortex_instructions_per_microsecond();
+ plugin_state->distance = hc_sr04_us_to_m(plugin_state->echo);
plugin_state->measurement_made = true;
//furi_hal_light_set(LightRed, 0x00);
@@ -270,4 +270,4 @@ int32_t hc_sr04_app() {
delete_mutex(&state_mutex);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/hex_viewer/README.md b/applications/plugins/hex_viewer/README.md
deleted file mode 100644
index e830eef8e..000000000
--- a/applications/plugins/hex_viewer/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# flipper-zero-hex-viewer
-
-Hex Viewer application for Flipper Zero!
-
-
-
-[Link to FAP](https://nightly.link/QtRoS/flipper-zero-hex-viewer/actions/artifacts/448677581.zip) (firmware version 0.72)
diff --git a/applications/plugins/hex_viewer/application.fam b/applications/plugins/hex_viewer/application.fam
index cfddabb0d..30c31ba76 100644
--- a/applications/plugins/hex_viewer/application.fam
+++ b/applications/plugins/hex_viewer/application.fam
@@ -1,6 +1,6 @@
App(
- appid="HEX_Viewer",
- name="HEX Viewer",
+ appid="hex_viewer",
+ name="Hex Viewer",
apptype=FlipperAppType.EXTERNAL,
entry_point="hex_viewer_app",
cdefines=["APP_HEX_VIEWER"],
@@ -13,5 +13,4 @@ App(
fap_icon="icons/hex_10px.png",
fap_category="Misc",
fap_icon_assets="icons",
- fap_icon_assets_symbol="hex_viewer",
)
diff --git a/applications/plugins/hid_app/application.fam b/applications/plugins/hid_app/application.fam
index 86c65c2dd..7e04f0780 100644
--- a/applications/plugins/hid_app/application.fam
+++ b/applications/plugins/hid_app/application.fam
@@ -10,6 +10,7 @@ App(
fap_icon_assets_symbol="hid",
)
+
App(
appid="Bluetooth_Remote",
name="Bluetooth Remote",
diff --git a/applications/plugins/hid_app/hid.c b/applications/plugins/hid_app/hid.c
index 7f63f0cc6..949ff63b3 100644
--- a/applications/plugins/hid_app/hid.c
+++ b/applications/plugins/hid_app/hid.c
@@ -376,7 +376,17 @@ int32_t hid_ble_app(void* p) {
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
- bt_keys_storage_set_storage_path(app->bt, HID_BT_KEYS_STORAGE_PATH);
+ // Migrate data from old sd-card folder
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ storage_common_migrate(
+ storage,
+ EXT_PATH("apps/Tools/" HID_BT_KEYS_STORAGE_NAME),
+ APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
+
+ bt_keys_storage_set_storage_path(app->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));
+
+ furi_record_close(RECORD_STORAGE);
if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) {
FURI_LOG_E(TAG, "Failed to switch to HID profile");
diff --git a/applications/plugins/hid_app/hid.h b/applications/plugins/hid_app/hid.h
index fe32a199b..8ed1664a3 100644
--- a/applications/plugins/hid_app/hid.h
+++ b/applications/plugins/hid_app/hid.h
@@ -23,7 +23,7 @@
#include "views/hid_mouse_jiggler.h"
#include "views/hid_tiktok.h"
-#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys")
+#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
typedef enum {
HidTransportUsb,
diff --git a/applications/plugins/hid_app/hid_usb_10px.png b/applications/plugins/hid_app/hid_usb_10px.png
index 415de7d23..7649138eb 100644
Binary files a/applications/plugins/hid_app/hid_usb_10px.png and b/applications/plugins/hid_app/hid_usb_10px.png differ
diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c
index 82e772b15..9d29ccf5e 100644
--- a/applications/plugins/hid_app/views/hid_keyboard.c
+++ b/applications/plugins/hid_app/views/hid_keyboard.c
@@ -140,18 +140,18 @@ const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
{.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW},
},
{
- {.width = 3, .icon = NULL, .key = "Ctrl", .value = HID_KEYBOARD_L_CTRL},
+ {.width = 2, .icon = NULL, .key = "Ctl", .value = HID_KEYBOARD_L_CTRL},
{.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL},
- {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL},
- {.width = 3, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT},
+ {.width = 2, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT},
{.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT},
- {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT},
- {.width = 3, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI},
+ {.width = 2, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI},
{.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI},
- {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI},
- {.width = 3, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB},
- {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB},
+ {.width = 2, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB},
{.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB},
+ {.width = 2, .icon = NULL, .key = "Esc", .value = HID_KEYBOARD_ESCAPE},
+ {.width = 0, .icon = NULL, .value = HID_KEYBOARD_ESCAPE},
+ {.width = 2, .icon = NULL, .key = "Del", .value = HID_KEYBOARD_DELETE_FORWARD},
+ {.width = 0, .icon = NULL, .value = HID_KEYBOARD_DELETE_FORWARD},
},
};
diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.c b/applications/plugins/hid_app/views/hid_mouse_jiggler.c
index d8f1f8928..120d5bc34 100644
--- a/applications/plugins/hid_app/views/hid_mouse_jiggler.c
+++ b/applications/plugins/hid_app/views/hid_mouse_jiggler.c
@@ -6,6 +6,8 @@
#define TAG "HidMouseJiggler"
+#define LENGTH(x) (int)(sizeof(x) / sizeof((x)[0]))
+
struct HidMouseJiggler {
View* view;
Hid* hid;
@@ -15,10 +17,13 @@ struct HidMouseJiggler {
typedef struct {
bool connected;
bool running;
+ int interval_idx;
uint8_t counter;
HidTransport transport;
} HidMouseJigglerModel;
+const int intervals[6] = {500, 2000, 5000, 10000, 30000, 60000};
+
static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidMouseJigglerModel* model = context;
@@ -33,29 +38,39 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
}
canvas_set_font(canvas, FontPrimary);
- elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler");
+ elements_multiline_text_aligned(canvas, 17, 2, AlignLeft, AlignTop, "Mouse Jiggler");
+
+ // Timeout
+ elements_multiline_text(canvas, AlignLeft, 26, "Interval (ms):");
+ canvas_set_font(canvas, FontSecondary);
+ if(model->interval_idx != 0) canvas_draw_icon(canvas, 74, 19, &I_ButtonLeft_4x7);
+ if(model->interval_idx != LENGTH(intervals) - 1)
+ canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7);
+ FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]);
+ elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str));
+ furi_string_free(interval_str);
canvas_set_font(canvas, FontPrimary);
- elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto jiggle");
+ elements_multiline_text(canvas, AlignLeft, 40, "Press Start\nto jiggle");
canvas_set_font(canvas, FontSecondary);
// Ok
- canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
+ canvas_draw_icon(canvas, 63, 30, &I_Space_65x18);
if(model->running) {
- elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
+ elements_slightly_rounded_box(canvas, 66, 32, 60, 13);
canvas_set_color(canvas, ColorWhite);
}
- canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
+ canvas_draw_icon(canvas, 74, 34, &I_Ok_btn_9x9);
if(model->running) {
- elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
+ elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Stop");
} else {
- elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
+ elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Start");
}
canvas_set_color(canvas, ColorBlack);
// Back
- canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
- elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
+ canvas_draw_icon(canvas, 74, 54, &I_Pin_back_arrow_10x8);
+ elements_multiline_text_aligned(canvas, 91, 62, AlignLeft, AlignBottom, "Quit");
}
static void hid_mouse_jiggler_timer_callback(void* context) {
@@ -76,13 +91,6 @@ static void hid_mouse_jiggler_timer_callback(void* context) {
false);
}
-static void hid_mouse_jiggler_enter_callback(void* context) {
- furi_assert(context);
- HidMouseJiggler* hid_mouse_jiggler = context;
-
- furi_timer_start(hid_mouse_jiggler->timer, 500);
-}
-
static void hid_mouse_jiggler_exit_callback(void* context) {
furi_assert(context);
HidMouseJiggler* hid_mouse_jiggler = context;
@@ -95,14 +103,30 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
bool consumed = false;
- if(event->key == InputKeyOk) {
- with_view_model(
- hid_mouse_jiggler->view,
- HidMouseJigglerModel * model,
- { model->running = !model->running; },
- true);
- consumed = true;
- }
+ with_view_model(
+ hid_mouse_jiggler->view,
+ HidMouseJigglerModel * model,
+ {
+ if(event->type == InputTypePress && event->key == InputKeyOk) {
+ model->running = !model->running;
+ if(model->running) {
+ furi_timer_stop(hid_mouse_jiggler->timer);
+ furi_timer_start(hid_mouse_jiggler->timer, intervals[model->interval_idx]);
+ };
+ consumed = true;
+ }
+ if(event->type == InputTypePress && event->key == InputKeyRight && !model->running &&
+ model->interval_idx < LENGTH(intervals) - 1) {
+ model->interval_idx++;
+ consumed = true;
+ }
+ if(event->type == InputTypePress && event->key == InputKeyLeft && !model->running &&
+ model->interval_idx > 0) {
+ model->interval_idx--;
+ consumed = true;
+ }
+ },
+ true);
return consumed;
}
@@ -116,7 +140,6 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel));
view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback);
view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback);
- view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback);
view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback);
hid_mouse_jiggler->hid = hid;
@@ -127,7 +150,10 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
with_view_model(
hid_mouse_jiggler->view,
HidMouseJigglerModel * model,
- { model->transport = hid->transport; },
+ {
+ model->transport = hid->transport;
+ model->interval_idx = 2;
+ },
true);
return hid_mouse_jiggler;
diff --git a/applications/plugins/htu21d_temp_sensor/Readme.md b/applications/plugins/htu21d_temp_sensor/Readme.md
deleted file mode 100644
index 45c332306..000000000
--- a/applications/plugins/htu21d_temp_sensor/Readme.md
+++ /dev/null
@@ -1,65 +0,0 @@
-[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.
-
-
-
-
-
-
-
-# How to Connect the HTU21D sensor
-
-
-
-# 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:
-
-
-
-## 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
deleted file mode 100644
index 724f55cd5..000000000
--- a/applications/plugins/htu21d_temp_sensor/application.fam
+++ /dev/null
@@ -1,14 +0,0 @@
-App(
- appid="HTU_Temperature_Sensor",
- name="[HTU/+] 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
deleted file mode 100644
index a0373bdbd..000000000
Binary files a/applications/plugins/htu21d_temp_sensor/docs/App.png and /dev/null differ
diff --git a/applications/plugins/htu21d_temp_sensor/docs/Connection.png b/applications/plugins/htu21d_temp_sensor/docs/Connection.png
deleted file mode 100644
index b38f5c250..000000000
Binary files a/applications/plugins/htu21d_temp_sensor/docs/Connection.png and /dev/null differ
diff --git a/applications/plugins/htu21d_temp_sensor/docs/Flipper.png b/applications/plugins/htu21d_temp_sensor/docs/Flipper.png
deleted file mode 100644
index c85319593..000000000
Binary files a/applications/plugins/htu21d_temp_sensor/docs/Flipper.png and /dev/null differ
diff --git a/applications/plugins/htu21d_temp_sensor/temperature_sensor.c b/applications/plugins/htu21d_temp_sensor/temperature_sensor.c
deleted file mode 100644
index b04e40a7d..000000000
--- a/applications/plugins/htu21d_temp_sensor/temperature_sensor.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/* 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
deleted file mode 100644
index b6fe6d7fe..000000000
Binary files a/applications/plugins/htu21d_temp_sensor/temperature_sensor.png and /dev/null differ
diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h
index 1db527f65..8e671fae0 100644
--- a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h
+++ b/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h
@@ -78,7 +78,8 @@ typedef struct {
uint8_t key_index;
iButtonWorker* worker;
iButtonKey* key;
- iButtonKeyType keytype;
+ iButtonProtocolId keytype;
+ iButtonProtocols* protocols;
bool workr_rund;
bool enter_rerun;
bool attack_stop_called;
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
index e45035d6b..1cab8b04e 100644
--- a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
+++ b/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
@@ -72,14 +72,15 @@ void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) {
context->time_between_cards = 8;
context->attack_step = 0;
context->attack_stop_called = false;
- context->key = ibutton_key_alloc();
- context->worker = ibutton_worker_alloc();
+ context->protocols = ibutton_protocols_alloc();
+ context->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(context->protocols));
+ context->worker = ibutton_worker_alloc(context->protocols);
if(context->proto == Metakom) {
- context->keytype = iButtonKeyMetakom;
+ context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Metakom");
} else if(context->proto == Cyfral) {
- context->keytype = iButtonKeyCyfral;
+ context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Cyfral");
} else {
- context->keytype = iButtonKeyDS1990;
+ context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "DS1990");
}
context->workr_rund = false;
}
@@ -90,8 +91,9 @@ void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context) {
ibutton_worker_stop_thread(context->worker);
context->workr_rund = false;
}
- ibutton_worker_free(context->worker);
ibutton_key_free(context->key);
+ ibutton_worker_free(context->worker);
+ ibutton_protocols_free(context->protocols);
notification_message(context->notify, &sequence_blink_stop);
}
@@ -99,9 +101,14 @@ 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_key_set_protocol_id(context->key, context->keytype);
+ iButtonEditableData data;
+ ibutton_protocols_get_editable_data(context->protocols, context->key, &data);
+ data.size = sizeof(context->payload);
+ for(size_t i = 0; i < data.size; i++) {
+ data.ptr[i] = context->payload[i];
+ }
+
ibutton_worker_emulate_start(context->worker, context->key);
context->workr_rund = true;
} else if(0 == counter) {
diff --git a/applications/plugins/ifttt/application.fam b/applications/plugins/ifttt/application.fam
index 75e5beb67..6a3c4986e 100644
--- a/applications/plugins/ifttt/application.fam
+++ b/applications/plugins/ifttt/application.fam
@@ -10,5 +10,5 @@ App(
stack_size=2 * 1024,
order=20,
fap_icon="icon.png",
- fap_category="GPIO",
+ fap_category="WiFi",
)
diff --git a/applications/plugins/lightmeter/.clang-format b/applications/plugins/lightmeter/.clang-format
deleted file mode 100644
index 4b76f7fa4..000000000
--- a/applications/plugins/lightmeter/.clang-format
+++ /dev/null
@@ -1,191 +0,0 @@
----
-Language: Cpp
-AccessModifierOffset: -4
-AlignAfterOpenBracket: AlwaysBreak
-AlignArrayOfStructures: None
-AlignConsecutiveMacros: None
-AlignConsecutiveAssignments: None
-AlignConsecutiveBitFields: None
-AlignConsecutiveDeclarations: None
-AlignEscapedNewlines: Left
-AlignOperands: Align
-AlignTrailingComments: false
-AllowAllArgumentsOnNextLine: true
-AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortEnumsOnASingleLine: true
-AllowShortBlocksOnASingleLine: Never
-AllowShortCaseLabelsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: None
-AllowShortLambdasOnASingleLine: All
-AllowShortIfStatementsOnASingleLine: WithoutElse
-AllowShortLoopsOnASingleLine: true
-AlwaysBreakAfterDefinitionReturnType: None
-AlwaysBreakAfterReturnType: None
-AlwaysBreakBeforeMultilineStrings: false
-AlwaysBreakTemplateDeclarations: Yes
-AttributeMacros:
- - __capability
-BinPackArguments: false
-BinPackParameters: false
-BraceWrapping:
- AfterCaseLabel: false
- AfterClass: false
- AfterControlStatement: Never
- AfterEnum: false
- AfterFunction: false
- AfterNamespace: false
- AfterObjCDeclaration: false
- AfterStruct: false
- AfterUnion: false
- AfterExternBlock: false
- BeforeCatch: false
- BeforeElse: false
- BeforeLambdaBody: false
- BeforeWhile: false
- IndentBraces: false
- SplitEmptyFunction: true
- SplitEmptyRecord: true
- SplitEmptyNamespace: true
-BreakBeforeBinaryOperators: None
-BreakBeforeConceptDeclarations: true
-BreakBeforeBraces: Attach
-BreakBeforeInheritanceComma: false
-BreakInheritanceList: BeforeColon
-BreakBeforeTernaryOperators: false
-BreakConstructorInitializersBeforeComma: false
-BreakConstructorInitializers: BeforeComma
-BreakAfterJavaFieldAnnotations: false
-BreakStringLiterals: false
-ColumnLimit: 99
-CommentPragmas: '^ IWYU pragma:'
-QualifierAlignment: Leave
-CompactNamespaces: false
-ConstructorInitializerIndentWidth: 4
-ContinuationIndentWidth: 4
-Cpp11BracedListStyle: true
-DeriveLineEnding: true
-DerivePointerAlignment: false
-DisableFormat: false
-EmptyLineAfterAccessModifier: Never
-EmptyLineBeforeAccessModifier: LogicalBlock
-ExperimentalAutoDetectBinPacking: false
-PackConstructorInitializers: BinPack
-BasedOnStyle: ''
-ConstructorInitializerAllOnOneLineOrOnePerLine: false
-AllowAllConstructorInitializersOnNextLine: true
-FixNamespaceComments: false
-ForEachMacros:
- - foreach
- - Q_FOREACH
- - BOOST_FOREACH
-IfMacros:
- - KJ_IF_MAYBE
-IncludeBlocks: Preserve
-IncludeCategories:
- - Regex: '.*'
- Priority: 1
- SortPriority: 0
- CaseSensitive: false
- - Regex: '^(<|"(gtest|gmock|isl|json)/)'
- Priority: 3
- SortPriority: 0
- CaseSensitive: false
- - Regex: '.*'
- Priority: 1
- SortPriority: 0
- CaseSensitive: false
-IncludeIsMainRegex: '(Test)?$'
-IncludeIsMainSourceRegex: ''
-IndentAccessModifiers: false
-IndentCaseLabels: false
-IndentCaseBlocks: false
-IndentGotoLabels: true
-IndentPPDirectives: None
-IndentExternBlock: AfterExternBlock
-IndentRequires: false
-IndentWidth: 4
-IndentWrappedFunctionNames: true
-InsertTrailingCommas: None
-JavaScriptQuotes: Leave
-JavaScriptWrapImports: true
-KeepEmptyLinesAtTheStartOfBlocks: false
-LambdaBodyIndentation: Signature
-MacroBlockBegin: ''
-MacroBlockEnd: ''
-MaxEmptyLinesToKeep: 1
-NamespaceIndentation: None
-ObjCBinPackProtocolList: Auto
-ObjCBlockIndentWidth: 4
-ObjCBreakBeforeNestedBlockParam: true
-ObjCSpaceAfterProperty: true
-ObjCSpaceBeforeProtocolList: true
-PenaltyBreakAssignment: 10
-PenaltyBreakBeforeFirstCallParameter: 30
-PenaltyBreakComment: 10
-PenaltyBreakFirstLessLess: 0
-PenaltyBreakOpenParenthesis: 0
-PenaltyBreakString: 10
-PenaltyBreakTemplateDeclaration: 10
-PenaltyExcessCharacter: 100
-PenaltyReturnTypeOnItsOwnLine: 60
-PenaltyIndentedWhitespace: 0
-PointerAlignment: Left
-PPIndentWidth: -1
-ReferenceAlignment: Pointer
-ReflowComments: false
-RemoveBracesLLVM: false
-SeparateDefinitionBlocks: Leave
-ShortNamespaceLines: 1
-SortIncludes: Never
-SortJavaStaticImport: Before
-SortUsingDeclarations: false
-SpaceAfterCStyleCast: false
-SpaceAfterLogicalNot: false
-SpaceAfterTemplateKeyword: true
-SpaceBeforeAssignmentOperators: true
-SpaceBeforeCaseColon: false
-SpaceBeforeCpp11BracedList: false
-SpaceBeforeCtorInitializerColon: true
-SpaceBeforeInheritanceColon: true
-SpaceBeforeParens: Never
-SpaceBeforeParensOptions:
- AfterControlStatements: false
- AfterForeachMacros: false
- AfterFunctionDefinitionName: false
- AfterFunctionDeclarationName: false
- AfterIfMacros: false
- AfterOverloadedOperator: false
- BeforeNonEmptyParentheses: false
-SpaceAroundPointerQualifiers: Default
-SpaceBeforeRangeBasedForLoopColon: true
-SpaceInEmptyBlock: false
-SpaceInEmptyParentheses: false
-SpacesBeforeTrailingComments: 1
-SpacesInAngles: Never
-SpacesInConditionalStatement: false
-SpacesInContainerLiterals: false
-SpacesInCStyleCastParentheses: false
-SpacesInLineCommentPrefix:
- Minimum: 1
- Maximum: -1
-SpacesInParentheses: false
-SpacesInSquareBrackets: false
-SpaceBeforeSquareBrackets: false
-BitFieldColonSpacing: Both
-Standard: c++03
-StatementAttributeLikeMacros:
- - Q_EMIT
-StatementMacros:
- - Q_UNUSED
- - QT_REQUIRE_VERSION
-TabWidth: 4
-UseCRLF: false
-UseTab: Never
-WhitespaceSensitiveMacros:
- - STRINGIZE
- - PP_STRINGIZE
- - BOOST_PP_STRINGIZE
- - NS_SWIFT_NAME
- - CF_SWIFT_NAME
-...
-
diff --git a/applications/plugins/lightmeter/README.md b/applications/plugins/lightmeter/README.md
index dc6c6ffd5..d9c071e67 100644
--- a/applications/plugins/lightmeter/README.md
+++ b/applications/plugins/lightmeter/README.md
@@ -1,10 +1,9 @@
-# flipperzero-lightmeter
+# flipperzero-lightmeter
-[](https://github.com/oleksiikutuzov/flipperzero-lightmeter/actions/workflows/build.yml)
+[Original link](https://github.com/oleksiikutuzov/flipperzero-lightmeter)
-
-
+
## Wiring
@@ -15,19 +14,6 @@ SCL -> C0
SDA -> C1
```
-## Sensor module
-
-
-
-### If you want to build this module, you'll need (it's quite over-engineered, sorry :D)
-1. [Module PCB](https://github.com/oleksiikutuzov/flipperzero-lightmeter/blob/main/module/module_v2_gerber.zip)
-2. [Enclosure](https://github.com/oleksiikutuzov/flipperzero-lightmeter/blob/main/module/module_v2_enclosure.stl)
-3. 4-pin female header
-4. 10-pin male header
-5. 2x M3 threaded inserts (max diameter 5.3 mm, max height 4 mm)
-6. 2x M3x5 screws
-
-
## TODO
- [ ] Save settings to sd card
diff --git a/applications/plugins/lightmeter/application.fam b/applications/plugins/lightmeter/application.fam
index b1de62641..b1ae6c6b2 100644
--- a/applications/plugins/lightmeter/application.fam
+++ b/applications/plugins/lightmeter/application.fam
@@ -9,7 +9,6 @@ App(
],
stack_size=1 * 1024,
order=90,
- fap_version=(0, 7),
fap_icon="lightmeter.png",
fap_category="GPIO",
fap_private_libs=[
@@ -22,7 +21,4 @@ App(
),
],
fap_icon_assets="icons",
- fap_description="Lightmeter app for photography based on BH1750 sensor",
- fap_author="Oleksii Kutuzov",
- fap_weburl="https://github.com/oleksiikutuzov/flipperzero-lightmeter",
)
diff --git a/applications/plugins/lightmeter/gui/views/main_view.c b/applications/plugins/lightmeter/gui/views/main_view.c
index fcbafbff4..8b3e2989f 100644
--- a/applications/plugins/lightmeter/gui/views/main_view.c
+++ b/applications/plugins/lightmeter/gui/views/main_view.c
@@ -62,11 +62,11 @@ const float aperture_numbers[] = {
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,
+ [SPEED_125] = 1.0 / 125, [SPEED_60] = 1.0 / 60, [SPEED_48] = 1.0 / 48,
+ [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 {
diff --git a/applications/plugins/lightmeter/images/framed_gui_config.png b/applications/plugins/lightmeter/images/framed_gui_config.png
deleted file mode 100644
index b87c3bd5c..000000000
Binary files a/applications/plugins/lightmeter/images/framed_gui_config.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/framed_gui_lux_meter.png b/applications/plugins/lightmeter/images/framed_gui_lux_meter.png
deleted file mode 100644
index 6ab0cf191..000000000
Binary files a/applications/plugins/lightmeter/images/framed_gui_lux_meter.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/framed_gui_main.png b/applications/plugins/lightmeter/images/framed_gui_main.png
deleted file mode 100644
index 23dc4a2cc..000000000
Binary files a/applications/plugins/lightmeter/images/framed_gui_main.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/gui_config.png b/applications/plugins/lightmeter/images/gui_config.png
deleted file mode 100644
index 95fdcb167..000000000
Binary files a/applications/plugins/lightmeter/images/gui_config.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/gui_lux_meter.png b/applications/plugins/lightmeter/images/gui_lux_meter.png
deleted file mode 100644
index f7159e671..000000000
Binary files a/applications/plugins/lightmeter/images/gui_lux_meter.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/images/gui_main.png b/applications/plugins/lightmeter/images/gui_main.png
deleted file mode 100644
index ba4c3f75f..000000000
Binary files a/applications/plugins/lightmeter/images/gui_main.png and /dev/null differ
diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.c b/applications/plugins/lightmeter/lib/BH1750/BH1750.c
index 28616e040..9e6a50758 100644
--- a/applications/plugins/lightmeter/lib/BH1750/BH1750.c
+++ b/applications/plugins/lightmeter/lib/BH1750/BH1750.c
@@ -15,6 +15,7 @@
BH1750_mode bh1750_mode = BH1750_DEFAULT_MODE; // Current sensor mode
uint8_t bh1750_mt_reg = BH1750_DEFAULT_MTREG; // Current MT register value
+uint8_t bh1750_addr = BH1750_ADDRESS;
BH1750_STATUS bh1750_init() {
if(BH1750_OK == bh1750_reset()) {
@@ -25,12 +26,17 @@ BH1750_STATUS bh1750_init() {
return BH1750_ERROR;
}
+BH1750_STATUS bh1750_init_with_addr(uint8_t addr) {
+ bh1750_addr = (addr << 1);
+ return bh1750_init();
+}
+
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);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &command, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
@@ -45,7 +51,7 @@ BH1750_STATUS bh1750_set_power_state(uint8_t PowerOn) {
bool status;
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &PowerOn, 1, I2C_TIMEOUT);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &PowerOn, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
@@ -69,7 +75,7 @@ BH1750_STATUS bh1750_set_mode(BH1750_mode mode) {
bh1750_mode = mode;
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_tx(I2C_BUS, BH1750_ADDRESS, &mode, 1, I2C_TIMEOUT);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &mode, 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
@@ -93,14 +99,14 @@ BH1750_STATUS bh1750_set_mt_reg(uint8_t mt_reg) {
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);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &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);
+ status = furi_hal_i2c_tx(I2C_BUS, bh1750_addr, &tmp[1], 1, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
return BH1750_OK;
@@ -122,7 +128,7 @@ BH1750_STATUS bh1750_read_light(float* result) {
bool status;
furi_hal_i2c_acquire(I2C_BUS);
- status = furi_hal_i2c_rx(I2C_BUS, BH1750_ADDRESS, rcv, 2, I2C_TIMEOUT);
+ status = furi_hal_i2c_rx(I2C_BUS, bh1750_addr, rcv, 2, I2C_TIMEOUT);
furi_hal_i2c_release(I2C_BUS);
if(status) {
diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.h b/applications/plugins/lightmeter/lib/BH1750/BH1750.h
index 991350f7f..46649c33e 100644
--- a/applications/plugins/lightmeter/lib/BH1750/BH1750.h
+++ b/applications/plugins/lightmeter/lib/BH1750/BH1750.h
@@ -49,6 +49,13 @@ typedef enum {
*/
BH1750_STATUS bh1750_init();
+/**
+ * @brief Change the I2C device address and then initialize the sensor.
+ *
+ * @return BH1750_STATUS
+ */
+BH1750_STATUS bh1750_init_with_addr(uint8_t addr);
+
/**
* @brief Reset all registers to the default value.
*
diff --git a/applications/plugins/lightmeter/lightmeter_config.h b/applications/plugins/lightmeter/lightmeter_config.h
index 4f7e11e0d..5edbdce0a 100644
--- a/applications/plugins/lightmeter/lightmeter_config.h
+++ b/applications/plugins/lightmeter/lightmeter_config.h
@@ -79,6 +79,7 @@ typedef enum {
SPEED_250,
SPEED_125,
SPEED_60,
+ SPEED_48,
SPEED_30,
SPEED_15,
SPEED_8,
diff --git a/applications/plugins/lightmeter/module/back.jpg b/applications/plugins/lightmeter/module/back.jpg
deleted file mode 100644
index 366d7a852..000000000
Binary files a/applications/plugins/lightmeter/module/back.jpg and /dev/null differ
diff --git a/applications/plugins/lightmeter/module/front.jpg b/applications/plugins/lightmeter/module/front.jpg
deleted file mode 100644
index b38fd4150..000000000
Binary files a/applications/plugins/lightmeter/module/front.jpg and /dev/null differ
diff --git a/applications/plugins/lightmeter/module/module.jpg b/applications/plugins/lightmeter/module/module.jpg
deleted file mode 100644
index 53f481338..000000000
Binary files a/applications/plugins/lightmeter/module/module.jpg and /dev/null differ
diff --git a/applications/plugins/lightmeter/module/module_v2_enclosure.stl b/applications/plugins/lightmeter/module/module_v2_enclosure.stl
deleted file mode 100644
index ee2be93fd..000000000
Binary files a/applications/plugins/lightmeter/module/module_v2_enclosure.stl and /dev/null differ
diff --git a/applications/plugins/lightmeter/module/module_v2_gerber.zip b/applications/plugins/lightmeter/module/module_v2_gerber.zip
deleted file mode 100644
index 02887f4bf..000000000
Binary files a/applications/plugins/lightmeter/module/module_v2_gerber.zip and /dev/null differ
diff --git a/applications/plugins/metronome/README.md b/applications/plugins/metronome/README.md
index 79134c350..4b6cd3122 100644
--- a/applications/plugins/metronome/README.md
+++ b/applications/plugins/metronome/README.md
@@ -1,5 +1,7 @@
# Metronome
+[Original link](https://github.com/panki27/Metronome)
+
A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along perfectly with my [BPM tapper](https://github.com/panki27/bpm-tapper).

@@ -17,5 +19,5 @@ A metronome for the [Flipper Zero](https://flipperzero.one/) device. Goes along
## Compiling
```
-./fbt fap_metronome
+./fbt firmware_metronome
```
diff --git a/applications/plugins/metronome/application.fam b/applications/plugins/metronome/application.fam
index b40809638..32588d06e 100644
--- a/applications/plugins/metronome/application.fam
+++ b/applications/plugins/metronome/application.fam
@@ -8,8 +8,8 @@ App(
"gui",
],
fap_icon="metronome_icon.png",
- fap_icon_assets="icons",
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 a7661ec28..458eb137b 100644
--- a/applications/plugins/metronome/gui_extensions.c
+++ b/applications/plugins/metronome/gui_extensions.c
@@ -1,6 +1,6 @@
#include
#include
-#include "Metronome_icons.h"
+#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/images/ButtonUp_7x4.png
similarity index 100%
rename from applications/plugins/metronome/icons/ButtonUp_7x4.png
rename to applications/plugins/metronome/images/ButtonUp_7x4.png
diff --git a/applications/plugins/metronome/metronome.c b/applications/plugins/metronome/metronome.c
index 4c374aa75..06bce1bff 100644
--- a/applications/plugins/metronome/metronome.c
+++ b/applications/plugins/metronome/metronome.c
@@ -287,7 +287,6 @@ int32_t metronome_app() {
metronome_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, &state_mutex);
// Open GUI and register view_port
- //
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
@@ -326,7 +325,7 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
- case InputKeyMAX:
+ default:
break;
}
} else if(event.input.type == InputTypeLong) {
@@ -348,7 +347,7 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
- case InputKeyMAX:
+ default:
break;
}
} else if(event.input.type == InputTypeRepeat) {
@@ -369,7 +368,7 @@ int32_t metronome_app() {
case InputKeyBack:
processing = false;
break;
- case InputKeyMAX:
+ default:
break;
}
}
diff --git a/applications/plugins/morse_code/application.fam b/applications/plugins/morse_code/application.fam
index 73ad2ba81..5801544a6 100644
--- a/applications/plugins/morse_code/application.fam
+++ b/applications/plugins/morse_code/application.fam
@@ -10,5 +10,5 @@ App(
stack_size=1 * 1024,
order=20,
fap_icon="morse_code_10px.png",
- fap_category="Music",
+ fap_category="Misc",
)
diff --git a/applications/plugins/morse_code/morse_code.c b/applications/plugins/morse_code/morse_code.c
index beb661222..3f96969e7 100644
--- a/applications/plugins/morse_code/morse_code.c
+++ b/applications/plugins/morse_code/morse_code.c
@@ -44,15 +44,10 @@ static void render_callback(Canvas* const canvas, void* ctx) {
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)));
+ //dit bpms
+ FuriString* ditbpm = furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta);
+ canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignCenter, furi_string_get_cstr(ditbpm));
+ furi_string_free(ditbpm);
//button info
elements_button_center(canvas, "Press/Hold");
@@ -67,7 +62,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
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_string_set(morse_code->model->words, words);
furi_mutex_release(morse_code->model_mutex);
view_port_update(morse_code->view_port);
}
@@ -109,6 +104,7 @@ void morse_code_free(MorseCode* instance) {
furi_mutex_free(instance->model_mutex);
+ furi_string_free(instance->model->words);
free(instance->model);
free(instance);
}
@@ -116,10 +112,12 @@ void morse_code_free(MorseCode* 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);
@@ -128,6 +126,7 @@ int32_t morse_code_app() {
break;
} else if(input.key == InputKeyBack && input.type == InputTypeShort) {
morse_code_worker_reset_text(morse_code->worker);
+ furi_string_reset(morse_code->model->words);
} else if(input.key == InputKeyOk) {
if(input.type == InputTypePress)
morse_code_worker_play(morse_code->worker, true);
@@ -160,6 +159,7 @@ int32_t morse_code_app() {
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;
diff --git a/applications/plugins/morse_code/morse_code_worker.c b/applications/plugins/morse_code/morse_code_worker.c
index 142b427b6..47896b91a 100644
--- a/applications/plugins/morse_code/morse_code_worker.c
+++ b/applications/plugins/morse_code/morse_code_worker.c
@@ -35,6 +35,8 @@ void morse_code_worker_fill_buffer(MorseCodeWorker* instance, uint32_t duration)
furi_string_push_back(instance->buffer, *DOT);
else if(duration <= (instance->dit_delta * 3))
furi_string_push_back(instance->buffer, *LINE);
+ else
+ furi_string_reset(instance->buffer);
if(furi_string_size(instance->buffer) > 5) furi_string_reset(instance->buffer);
FURI_LOG_D("MorseCode: Buffer", "%s", furi_string_get_cstr(instance->buffer));
}
@@ -87,9 +89,13 @@ static int32_t morse_code_worker_thread_callback(void* context) {
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);
+ if(!furi_string_empty(instance->buffer)) {
+ morse_code_worker_fill_letter(instance);
+ if(instance->callback)
+ instance->callback(instance->words, instance->callback_context);
+ } else {
+ spaced = true;
+ }
pushed = true;
}
}
@@ -170,6 +176,7 @@ void morse_code_worker_start(MorseCodeWorker* instance) {
void morse_code_worker_stop(MorseCodeWorker* instance) {
furi_assert(instance);
furi_assert(instance->is_running == true);
+ instance->play = false;
instance->is_running = false;
furi_thread_join(instance->thread);
FURI_LOG_D("MorseCode: Stop", "Stop");
diff --git a/applications/plugins/mouse_jiggler/application.fam b/applications/plugins/mouse_jiggler/application.fam
deleted file mode 100644
index 6115315f5..000000000
--- a/applications/plugins/mouse_jiggler/application.fam
+++ /dev/null
@@ -1,12 +0,0 @@
-App(
- appid="MouseJiggler",
- name="Mouse Jiggler",
- apptype=FlipperAppType.EXTERNAL,
- entry_point="mouse_jiggler_app",
- cdefines=["APP_MOUSE_JIGGLER"],
- requires=["gui"],
- stack_size=1 * 1024,
- order=150,
- fap_icon="mouse_10px.png",
- fap_category="Misc",
-)
diff --git a/applications/plugins/mouse_jiggler/mouse_10px.png b/applications/plugins/mouse_jiggler/mouse_10px.png
deleted file mode 100644
index 94c3a7a14..000000000
Binary files a/applications/plugins/mouse_jiggler/mouse_10px.png and /dev/null differ
diff --git a/applications/plugins/mouse_jiggler/mouse_jiggler.c b/applications/plugins/mouse_jiggler/mouse_jiggler.c
deleted file mode 100644
index 868082eea..000000000
--- a/applications/plugins/mouse_jiggler/mouse_jiggler.c
+++ /dev/null
@@ -1,141 +0,0 @@
-#include
-#include
-#include
-#include
-
-#define MOUSE_MOVE_SHORT 5
-#define MOUSE_MOVE_LONG 20
-
-typedef enum {
- EventTypeInput,
- EventTypeKey,
-} EventType;
-
-typedef struct {
- EventType type;
- InputEvent input;
-} UsbMouseEvent;
-
-typedef struct {
- bool running;
-} MouseJigglerState;
-
-static void mouse_jiggler_render_callback(Canvas* canvas, void* ctx) {
- const MouseJigglerState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
- if(plugin_state == NULL) {
- return;
- }
-
- canvas_set_font(canvas, FontPrimary);
- canvas_draw_str(canvas, 2, 12, "USB Mouse Jiggler");
- if(!plugin_state->running) {
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 2, 27, " -> STOPPED");
- canvas_draw_str(canvas, 2, 51, "Press [ok] to start");
- canvas_draw_str(canvas, 2, 63, "Press [back] to exit");
- } else {
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 2, 27, " -> RUNNING");
- canvas_draw_str(canvas, 2, 51, "Press [back] to stop");
- }
-
- release_mutex((ValueMutex*)ctx, plugin_state);
-}
-
-static void mouse_jiggler_input_callback(InputEvent* input_event, void* ctx) {
- FuriMessageQueue* event_queue = ctx;
- furi_assert(event_queue);
-
- UsbMouseEvent event = {.type = EventTypeKey, .input = *input_event};
- furi_message_queue_put(event_queue, &event, FuriWaitForever);
-}
-
-static void mouse_jiggler_state_init(MouseJigglerState* const plugin_state) {
- plugin_state->running = false;
-}
-
-int32_t mouse_jiggler_app(void* p) {
- UNUSED(p);
-
- FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent));
-
- MouseJigglerState* plugin_state = malloc(sizeof(MouseJigglerState));
- if(plugin_state == NULL) {
- FURI_LOG_E("MouseJiggler", "MouseJigglerState: malloc error\r\n");
- return 255;
- }
- mouse_jiggler_state_init(plugin_state);
-
- ValueMutex state_mutex;
- if(!init_mutex(&state_mutex, plugin_state, sizeof(MouseJigglerState))) {
- FURI_LOG_E("MouseJiggler", "cannot create mutex\r\n");
- furi_message_queue_free(event_queue);
- free(plugin_state);
- return 255;
- }
-
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, mouse_jiggler_render_callback, &state_mutex);
- view_port_input_callback_set(view_port, mouse_jiggler_input_callback, event_queue);
-
- FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
- furi_hal_usb_set_config(&usb_hid, NULL);
-
- // Open GUI and register view_port
- Gui* gui = furi_record_open(RECORD_GUI);
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
- UsbMouseEvent event;
- //bool status = 0;
-
- for(bool processing = true; processing;) {
- FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
-
- MouseJigglerState* plugin_state = (MouseJigglerState*)acquire_mutex_block(&state_mutex);
-
- if(event_status == FuriStatusOk) {
- if(event.type == EventTypeKey) {
- if(event.input.type == InputTypePress) {
- switch(event.input.key) {
- case InputKeyOk:
- if(!plugin_state->running) {
- plugin_state->running = true;
- }
- break;
- case InputKeyBack:
- if(!plugin_state->running) {
- processing = false;
- } else {
- plugin_state->running = false;
- }
- break;
- default:
- break;
- }
- }
- }
- }
-
- if(plugin_state->running) {
- furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0);
- furi_delay_ms(500);
- furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0);
- furi_delay_ms(500);
- }
-
- view_port_update(view_port);
- release_mutex(&state_mutex, plugin_state);
- }
-
- furi_hal_usb_set_config(usb_mode_prev, NULL);
-
- // remove & free all stuff created by app
- 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;
-}
diff --git a/applications/plugins/mousejacker/README.md b/applications/plugins/mousejacker/README.md
deleted file mode 100644
index ad5fdecdc..000000000
--- a/applications/plugins/mousejacker/README.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# flipperzero-nrf24
-
-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.
-
-## Warning
-This repo contains two Flipper Zero apps that utilize the NRF24 driver to sniff for NRF24 addresses and perform mousejack attacks. These apps are for **educational purposes** only. Please use this code responsibly and only use these apps on your own equipment.
-
-## Acknowledgments
-The NRF24 sniffing technique was discovered and shared by Travis Goodspeed in [his blog](http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html).
-
-The mousejack vulnerabilities were discovered and reported by Marc Newlin, see [the blog](https://www.bastille.net/research/vulnerabilities/mousejack/technical-details) for technical details.
-
-Much of the driver code was inspired by [RadioHead's Arduino library](https://www.airspayce.com/mikem/arduino/RadioHead/classRH__NRF24.html).
-Much of the mousejack code was inspired by the [Jackit project](https://github.com/insecurityofthings/jackit).
-
-
-## 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
-
-
-
-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. ¯\\\_(ツ)_/¯
-
-
-
-Selfmade NRF24 breakoutboard:
-
-
-
-## Practical hints from einstein2150
-If you are not successfull with the NRF Sniff Plugin you can try to get the MAC of the device with the crazyradio pa USB dongle. Have a look here: https://github.com/SySS-Research/nrf24-playset
-
-
-A sample output of the NRF-Research script could be like
-```
-[2022-08-05 13:28:56.366] Found nRF24 device with address 38:24:93:C0:07 on channel 75\
-[2022-08-05 13:28:56.371] Checking communication\
-[2022-08-05 13:28:59.088] Scan for nRF24 device\
-[2022-08-05 13:28:59.097] Actively searching for address 07:C0:93:24:38\
-```
-
-Be carefull with the byte-order using in mousejacker! The correct byte order is the reverse-byteorder. In my example the reversed one is ```07:C0:93:24:38``` for my Logitech R400 presenter.
-The next thing which could make trouble is the datarate. In my case the Logitech-device is only responding at 2Mbit. Commands at 1Mbit are not detected.
-
-Now its time to write all the relevant information in the config file. In my case you are creating a file ```addresses.txt``` in the SD directory of ```/nrfsniff```. The file content corresponding to the results of the research is ```07C0932438,2``` representing the reversed byte order of the MAC and the comma-separated datarate.
-
-Start the Mouse Jacker plugin and select the prepared ```addresses.txt```. If everything is alright you are starting with your MAC ready for attack:
-
-
-
-If you have troube you can check if a datarate of 1 Mbit will help. Change ```07C0932438,2``` to ```07C0932438,1``` in this case. Another problem in the practical use is electromagnetical noise. In my case the external USB-3.1-Hub creating massive noise in the 2,4 Ghz-Frequency around a distance of 5 cm. Try using a long USB connection-cable for the receiver. In my case the response of the attack raises significant because the signals of the NRF-breakoutboard is no more covered with noise.
-
diff --git a/applications/plugins/mousejacker/application.fam b/applications/plugins/mousejacker/application.fam
index 05835700b..725e81ae8 100644
--- a/applications/plugins/mousejacker/application.fam
+++ b/applications/plugins/mousejacker/application.fam
@@ -9,7 +9,7 @@ App(
"dialogs",
],
stack_size=2 * 1024,
- order=50,
+ order=60,
fap_icon="mouse_10px.png",
fap_category="GPIO",
fap_icon_assets="images",
diff --git a/applications/plugins/mousejacker/icons/badkb_10px.png b/applications/plugins/mousejacker/icons/badkb_10px.png
deleted file mode 100644
index 037474aa3..000000000
Binary files a/applications/plugins/mousejacker/icons/badkb_10px.png and /dev/null differ
diff --git a/applications/plugins/mousejacker/icons/sub1_10px.png b/applications/plugins/mousejacker/icons/sub1_10px.png
deleted file mode 100644
index 5a25fdf4e..000000000
Binary files a/applications/plugins/mousejacker/icons/sub1_10px.png and /dev/null differ
diff --git a/applications/plugins/mousejacker/mousejacker.c b/applications/plugins/mousejacker/mousejacker.c
index 3bd772889..8aaea0689 100644
--- a/applications/plugins/mousejacker/mousejacker.c
+++ b/applications/plugins/mousejacker/mousejacker.c
@@ -184,7 +184,7 @@ static bool process_ducky_file(
mj_process_ducky_script(
nrf24_HANDLE, addr, addr_size, rate, (char*)file_buf, plugin_state);
FURI_LOG_D(TAG, "finished execution");
- DOLPHIN_DEED(getRandomDeed());
+ // DOLPHIN_DEED(getRandomDeed());
loaded = true;
} else {
FURI_LOG_D(TAG, "load failed. file size: %d", file_size);
diff --git a/applications/plugins/mousejacker/mousejacker_ducky.c b/applications/plugins/mousejacker/mousejacker_ducky.c
index b3b04d836..04b0bfaca 100644
--- a/applications/plugins/mousejacker/mousejacker_ducky.c
+++ b/applications/plugins/mousejacker/mousejacker_ducky.c
@@ -3,6 +3,7 @@
static const char ducky_cmd_comment[] = {"REM"};
static const char ducky_cmd_delay[] = {"DELAY "};
static const char ducky_cmd_string[] = {"STRING "};
+static const char ducky_cmd_altstring[] = {"ALTSTRING "};
static const char ducky_cmd_repeat[] = {"REPEAT "};
static uint8_t LOGITECH_HID_TEMPLATE[] =
@@ -11,6 +12,10 @@ static uint8_t LOGITECH_HELLO[] = {0x00, 0x4F, 0x00, 0x04, 0xB0, 0x10, 0x00, 0x0
static uint8_t LOGITECH_KEEPALIVE[] = {0x00, 0x40, 0x00, 0x55, 0x6B};
uint8_t prev_hid = 0;
+static bool holding_ctrl = false;
+static bool holding_shift = false;
+static bool holding_alt = false;
+static bool holding_gui = false;
#define RT_THRESHOLD 50
#define LOGITECH_MIN_CHANNEL 2
@@ -65,7 +70,10 @@ MJDuckyKey mj_ducky_keys[] = {{" ", 44, 0}, {"!", 30, 2}, {"\""
{"LEFTARROW", 80, 0}, {"RIGHTARROW", 79, 0}, {"PAGEDOWN", 78, 0},
{"PAUSE", 72, 0}, {"SPACE", 44, 0}, {"UPARROW", 82, 0},
{"F11", 68, 0}, {"F7", 64, 0}, {"UP", 82, 0},
- {"LEFT", 80, 0}};
+ {"LEFT", 80, 0}, {"NUM 1", 89, 0}, {"NUM 2", 90, 0},
+ {"NUM 3", 91, 0}, {"NUM 4", 92, 0}, {"NUM 5", 93, 0},
+ {"NUM 6", 94, 0}, {"NUM 7", 95, 0}, {"NUM 8", 96, 0},
+ {"NUM 9", 97, 0}, {"NUM 0", 98, 0}};
/*
static bool mj_ducky_get_number(const char* param, uint32_t* val) {
@@ -89,7 +97,8 @@ static uint32_t mj_ducky_get_command_len(const char* line) {
static bool mj_get_ducky_key(char* key, size_t keylen, MJDuckyKey* dk) {
//FURI_LOG_D(TAG, "looking up key %s with length %d", key, keylen);
for(uint i = 0; i < sizeof(mj_ducky_keys) / sizeof(MJDuckyKey); i++) {
- if(!strncmp(mj_ducky_keys[i].name, key, keylen)) {
+ if(strlen(mj_ducky_keys[i].name) == keylen &&
+ !strncmp(mj_ducky_keys[i].name, key, keylen)) {
memcpy(dk, &mj_ducky_keys[i], sizeof(MJDuckyKey));
return true;
}
@@ -152,6 +161,30 @@ static void build_hid_packet(uint8_t mod, uint8_t hid, uint8_t* payload) {
checksum(payload, LOGITECH_HID_TEMPLATE_SIZE);
}
+static void release_key(
+ FuriHalSpiBusHandle* handle,
+ uint8_t* addr,
+ uint8_t addr_size,
+ uint8_t rate,
+ PluginState* plugin_state) {
+ // This function release keys currently pressed, but keep pressing special keys
+ // if holding mod keys variable are set to true
+
+ uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
+ build_hid_packet(
+ 0 | holding_ctrl | holding_shift << 1 | holding_alt << 2 | holding_gui << 3,
+ 0,
+ hid_payload);
+ inject_packet(
+ handle,
+ addr,
+ addr_size,
+ rate,
+ hid_payload,
+ LOGITECH_HID_TEMPLATE_SIZE,
+ plugin_state); // empty hid packet
+}
+
static void send_hid_packet(
FuriHalSpiBusHandle* handle,
uint8_t* addr,
@@ -161,24 +194,22 @@ static void send_hid_packet(
uint8_t hid,
PluginState* plugin_state) {
uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
- build_hid_packet(0, 0, hid_payload);
- if(hid == prev_hid)
- inject_packet(
- handle,
- addr,
- addr_size,
- rate,
- hid_payload,
- LOGITECH_HID_TEMPLATE_SIZE,
- plugin_state); // empty hid packet
+ if(hid == prev_hid) release_key(handle, addr, addr_size, rate, plugin_state);
prev_hid = hid;
- build_hid_packet(mod, hid, hid_payload);
+ build_hid_packet(
+ mod | holding_ctrl | holding_shift << 1 | holding_alt << 2 | holding_gui << 3,
+ hid,
+ hid_payload);
inject_packet(
handle, addr, addr_size, rate, hid_payload, LOGITECH_HID_TEMPLATE_SIZE, plugin_state);
furi_delay_ms(12);
}
+static bool ducky_end_line(const char chr) {
+ return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'));
+}
+
// returns false if there was an error processing script line
static bool mj_process_ducky_line(
FuriHalSpiBusHandle* handle,
@@ -251,6 +282,32 @@ static bool mj_process_ducky_line(
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
}
+ return true;
+ } else if(strncmp(line_tmp, ducky_cmd_altstring, strlen(ducky_cmd_altstring)) == 0) {
+ // ALTSTRING
+ line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
+ for(size_t i = 0; i < strlen(line_tmp); i++) {
+ if((line_tmp[i] < ' ') || (line_tmp[i] > '~')) {
+ continue; // Skip non-printable chars
+ }
+
+ char alt_code[4];
+ // Getting altcode of the char
+ snprintf(alt_code, 4, "%u", line_tmp[i]);
+
+ uint8_t j = 0;
+ while(!ducky_end_line(alt_code[j])) {
+ char pad_num[5] = {'N', 'U', 'M', ' ', alt_code[j]};
+ if(!mj_get_ducky_key(pad_num, 5, &dk)) return false;
+ holding_alt = true;
+ FURI_LOG_D(TAG, "Sending %s", pad_num);
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ j++;
+ }
+ holding_alt = false;
+ release_key(handle, addr, addr_size, rate, plugin_state);
+ }
+
return true;
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
// REPEAT
@@ -269,7 +326,9 @@ static bool mj_process_ducky_line(
} else if(strncmp(line_tmp, "ALT", strlen("ALT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4, dk.hid, plugin_state);
+ holding_alt = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_alt = false;
return true;
} else if(
strncmp(line_tmp, "GUI", strlen("GUI")) == 0 ||
@@ -277,33 +336,47 @@ static bool mj_process_ducky_line(
strncmp(line_tmp, "COMMAND", strlen("COMMAND")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 8, dk.hid, plugin_state);
+ holding_gui = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_gui = false;
return true;
} else if(
strncmp(line_tmp, "CTRL-ALT", strlen("CTRL-ALT")) == 0 ||
strncmp(line_tmp, "CONTROL-ALT", strlen("CONTROL-ALT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 1, dk.hid, plugin_state);
+ holding_ctrl = true;
+ holding_alt = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_ctrl = false;
+ holding_alt = false;
return true;
} else if(
strncmp(line_tmp, "CTRL-SHIFT", strlen("CTRL-SHIFT")) == 0 ||
strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1 | 2, dk.hid, plugin_state);
+ holding_ctrl = true;
+ holding_shift = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_ctrl = false;
+ holding_shift = false;
return true;
} else if(
strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 ||
strncmp(line_tmp, "CONTROL", strlen("CONTROL")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1, dk.hid, plugin_state);
+ holding_ctrl = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_ctrl = false;
return true;
} else if(strncmp(line_tmp, "SHIFT", strlen("SHIFT")) == 0) {
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
- send_hid_packet(handle, addr, addr_size, rate, dk.mod | 2, dk.hid, plugin_state);
+ holding_shift = true;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ holding_shift = false;
return true;
} else if(
strncmp(line_tmp, "ESC", strlen("ESC")) == 0 ||
@@ -344,6 +417,10 @@ static bool mj_process_ducky_line(
if(!mj_get_ducky_key("SPACE", 5, &dk)) return false;
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
return true;
+ } else if(strncmp(line_tmp, "TAB", strlen("TAB")) == 0) {
+ if(!mj_get_ducky_key("TAB", 3, &dk)) return false;
+ send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid, plugin_state);
+ return true;
}
return false;
diff --git a/applications/plugins/namechanger/application.fam b/applications/plugins/namechanger/application.fam
deleted file mode 100644
index 704b643c5..000000000
--- a/applications/plugins/namechanger/application.fam
+++ /dev/null
@@ -1,13 +0,0 @@
-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",
-)
diff --git a/applications/plugins/namechanger/icons/DolphinNice_96x59.png b/applications/plugins/namechanger/icons/DolphinNice_96x59.png
deleted file mode 100644
index a299d3630..000000000
Binary files a/applications/plugins/namechanger/icons/DolphinNice_96x59.png and /dev/null differ
diff --git a/applications/plugins/namechanger/icons/MarioBlock.png b/applications/plugins/namechanger/icons/MarioBlock.png
deleted file mode 100644
index 86f159966..000000000
Binary files a/applications/plugins/namechanger/icons/MarioBlock.png and /dev/null differ
diff --git a/applications/plugins/namechanger/icons/namechanger_10px.png b/applications/plugins/namechanger/icons/namechanger_10px.png
deleted file mode 100644
index 60facf25e..000000000
Binary files a/applications/plugins/namechanger/icons/namechanger_10px.png and /dev/null differ
diff --git a/applications/plugins/namechanger/namechanger.c b/applications/plugins/namechanger/namechanger.c
deleted file mode 100644
index 17abcea72..000000000
--- a/applications/plugins/namechanger/namechanger.c
+++ /dev/null
@@ -1,162 +0,0 @@
-#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);
-}
-
-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_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);
-}
-
-int32_t namechanger_app() {
- NameChanger* namechanger = namechanger_alloc();
-
- 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;
-}
-
-bool namechanger_name_write(NameChanger* namechanger, char* name) {
- FuriString* file_path = furi_string_alloc();
- furi_string_set(file_path, "/ext/dolphin/name.txt");
-
- bool result = false;
-
- //If name is not "eraseerase" (set by Revert) then write name to file
- //otherwise, remove name.txt
-
- if(strcmp(name, "eraseerase") != 0) {
- //save
- FlipperFormat* file = flipper_format_file_alloc(namechanger->storage);
-
- do {
- // 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(!flipper_format_write_string_cstr(file, "Name", name)) {
- break;
- }
-
- result = true;
- } while(false);
-
- flipper_format_free(file);
-
- if(!result) {
- FURI_LOG_E(TAG, "Cannot save name file.");
- }
- } else {
- result = storage_simply_remove(namechanger->storage, furi_string_get_cstr(file_path));
- }
-
- return result;
-}
\ No newline at end of file
diff --git a/applications/plugins/namechanger/namechanger.h b/applications/plugins/namechanger/namechanger.h
deleted file mode 100644
index e3355db1d..000000000
--- a/applications/plugins/namechanger/namechanger.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#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];
-
- 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_write(NameChanger* namechanger, char* name);
-void namechanger_text_store_set(NameChanger* namechanger, const char* text, ...);
diff --git a/applications/plugins/namechanger/namechanger_10px.png b/applications/plugins/namechanger/namechanger_10px.png
deleted file mode 100644
index 60facf25e..000000000
Binary files a/applications/plugins/namechanger/namechanger_10px.png and /dev/null differ
diff --git a/applications/plugins/namechanger/namechanger_custom_event.h b/applications/plugins/namechanger/namechanger_custom_event.h
deleted file mode 100644
index 3485c870b..000000000
--- a/applications/plugins/namechanger/namechanger_custom_event.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-enum NameChangerCustomEvent {
- NameChangerCustomEventBack,
- NameChangerCustomEventTextEditResult,
-};
diff --git a/applications/plugins/namechanger/scenes/namechanger_scene.c b/applications/plugins/namechanger/scenes/namechanger_scene.c
deleted file mode 100644
index 82f96e466..000000000
--- a/applications/plugins/namechanger/scenes/namechanger_scene.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#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
deleted file mode 100644
index 42071ab98..000000000
--- a/applications/plugins/namechanger/scenes/namechanger_scene.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#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
deleted file mode 100644
index 32977d51a..000000000
--- a/applications/plugins/namechanger/scenes/namechanger_scene_change.c
+++ /dev/null
@@ -1,56 +0,0 @@
-#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;
-
- namechanger_text_store_set(namechanger, "%s", furi_hal_version_get_name_ptr());
-
- 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);
-}
-
-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_name_write(namechanger, namechanger->text_store)) {
- scene_manager_next_scene(
- namechanger->scene_manager, NameChangerSceneChangeSuccess);
- } else {
- scene_manager_search_and_switch_to_previous_scene(
- namechanger->scene_manager, NameChangerSceneStart);
- }
- } else {
- scene_manager_search_and_switch_to_previous_scene(
- namechanger->scene_manager, NameChangerSceneStart);
- }
- }
- 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
deleted file mode 100644
index 7574c3a0e..000000000
--- a/applications/plugins/namechanger/scenes/namechanger_scene_change_success.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#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_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
deleted file mode 100644
index 41bbaef6b..000000000
--- a/applications/plugins/namechanger/scenes/namechanger_scene_config.h
+++ /dev/null
@@ -1,5 +0,0 @@
-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)
\ No newline at end of file
diff --git a/applications/plugins/namechanger/scenes/namechanger_scene_revert.c b/applications/plugins/namechanger/scenes/namechanger_scene_revert.c
deleted file mode 100644
index 156817651..000000000
--- a/applications/plugins/namechanger/scenes/namechanger_scene_revert.c
+++ /dev/null
@@ -1,53 +0,0 @@
-#include "../namechanger.h"
-
-static void
- namechanger_scene_revert_widget_callback(GuiButtonType result, InputType type, void* context) {
- NameChanger* namechanger = context;
- if(type == InputTypeShort) {
- view_dispatcher_send_custom_event(namechanger->view_dispatcher, result);
- }
-}
-
-void namechanger_scene_revert_on_enter(void* context) {
- NameChanger* namechanger = context;
- Widget* widget = namechanger->widget;
- widget_add_text_box_element(
- widget, 0, 0, 128, 25, AlignCenter, AlignCenter, "\e#Revert Name?\e#", false);
- widget_add_button_element(
- widget, GuiButtonTypeLeft, "Cancel", namechanger_scene_revert_widget_callback, namechanger);
- widget_add_button_element(
- widget,
- GuiButtonTypeRight,
- "Revert",
- namechanger_scene_revert_widget_callback,
- namechanger);
- 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;
- if(event.type == SceneManagerEventTypeBack) {
- consumed = true;
- } else if(event.type == SceneManagerEventTypeCustom) {
- consumed = true;
- if(event.event == GuiButtonTypeRight) {
- if(namechanger_name_write(namechanger, "eraseerase")) {
- scene_manager_next_scene(
- namechanger->scene_manager, NameChangerSceneRevertSuccess);
- } else {
- scene_manager_search_and_switch_to_previous_scene(
- namechanger->scene_manager, NameChangerSceneStart);
- }
- } else if(event.event == GuiButtonTypeLeft) {
- scene_manager_search_and_switch_to_previous_scene(
- namechanger->scene_manager, NameChangerSceneStart);
- }
- }
- return consumed;
-}
-
-void namechanger_scene_revert_on_exit(void* context) {
- NameChanger* namechanger = context;
- 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
deleted file mode 100644
index 354a6eadf..000000000
--- a/applications/plugins/namechanger/scenes/namechanger_scene_revert_success.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#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_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
deleted file mode 100644
index 3fe93f5a2..000000000
--- a/applications/plugins/namechanger/scenes/namechanger_scene_start.c
+++ /dev/null
@@ -1,58 +0,0 @@
-#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/nrfsniff/nrfsniff.c b/applications/plugins/nrfsniff/nrfsniff.c
index ce2836152..2dbf8f851 100644
--- a/applications/plugins/nrfsniff/nrfsniff.c
+++ b/applications/plugins/nrfsniff/nrfsniff.c
@@ -291,7 +291,7 @@ static void wrap_up(Storage* storage, NotificationApp* notification) {
hexlify(addr, 5, top_address);
found_count++;
save_addr_to_file(storage, addr, 5, notification);
- DOLPHIN_DEED(getRandomDeed());
+ // DOLPHIN_DEED(getRandomDeed());
if(confirmed_idx < MAX_CONFIRMED) memcpy(confirmed[confirmed_idx++], addr, 5);
break;
}
diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam
index 5d331e708..48cbba316 100644
--- a/applications/plugins/picopass/application.fam
+++ b/applications/plugins/picopass/application.fam
@@ -1,6 +1,6 @@
App(
appid="Picopass",
- name="PicoPass Reader",
+ name="PicoPass",
apptype=FlipperAppType.EXTERNAL,
targets=["f7"],
entry_point="picopass_app",
@@ -9,7 +9,7 @@ App(
"gui",
],
stack_size=4 * 1024,
- order=175,
+ order=30,
fap_icon="125_10px.png",
fap_category="Tools",
fap_libs=["mbedtls"],
diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.c b/applications/plugins/picopass/helpers/iclass_elite_dict.c
index 455eb23c1..e8c13dd1d 100644
--- a/applications/plugins/picopass/helpers/iclass_elite_dict.c
+++ b/applications/plugins/picopass/helpers/iclass_elite_dict.c
@@ -3,8 +3,8 @@
#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 ICLASS_ELITE_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_elite_dict.txt")
+#define ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt")
#define TAG "IclassEliteDict"
@@ -21,10 +21,10 @@ bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) {
bool dict_present = false;
if(dict_type == IclassEliteDictTypeFlipper) {
- dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) ==
- FSE_OK;
+ dict_present =
+ (storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_NAME, NULL) == FSE_OK);
} else if(dict_type == IclassEliteDictTypeUser) {
- dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK;
+ dict_present = (storage_common_stat(storage, ICLASS_ELITE_DICT_USER_NAME, NULL) == FSE_OK);
}
furi_record_close(RECORD_STORAGE);
@@ -36,27 +36,26 @@ 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)) {
+ dict->stream, ICLASS_ELITE_DICT_FLIPPER_NAME, 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)) {
+ dict->stream, ICLASS_ELITE_DICT_USER_NAME, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
buffered_file_stream_close(dict->stream);
break;
}
}
// Read total amount of keys
- while(true) {
+ while(true) { //-V547
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;
@@ -69,12 +68,13 @@ IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) {
FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys);
} while(false);
- if(!dict_loaded) {
+ if(!dict_loaded) { //-V547
buffered_file_stream_close(dict->stream);
free(dict);
dict = NULL;
}
+ furi_record_close(RECORD_STORAGE);
furi_string_free(next_line);
return dict;
diff --git a/applications/plugins/picopass/picopass.c b/applications/plugins/picopass/picopass.c
index 805f6760e..c1428b2fb 100644
--- a/applications/plugins/picopass/picopass.c
+++ b/applications/plugins/picopass/picopass.c
@@ -137,9 +137,9 @@ void picopass_text_store_clear(Picopass* picopass) {
memset(picopass->text_store, 0, sizeof(picopass->text_store));
}
-static const NotificationSequence picopass_sequence_blink_start_blue = {
+static const NotificationSequence picopass_sequence_blink_start_cyan = {
&message_blink_start_10,
- &message_blink_set_color_blue,
+ &message_blink_set_color_cyan,
&message_do_not_reset,
NULL,
};
@@ -150,7 +150,7 @@ static const NotificationSequence picopass_sequence_blink_stop = {
};
void picopass_blink_start(Picopass* picopass) {
- notification_message(picopass->notifications, &picopass_sequence_blink_start_blue);
+ notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan);
}
void picopass_blink_stop(Picopass* picopass) {
@@ -171,8 +171,26 @@ void picopass_show_loading_popup(void* context, bool show) {
}
}
+static void picopass_migrate_from_old_folder() {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ storage_common_migrate(storage, "/ext/picopass", STORAGE_APP_DATA_PATH_PREFIX);
+ furi_record_close(RECORD_STORAGE);
+}
+
+bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size) {
+ bool result = size > 0;
+ while(size > 0) {
+ result &= (*data == pattern);
+ data++;
+ size--;
+ }
+ return result;
+}
+
int32_t picopass_app(void* p) {
UNUSED(p);
+ picopass_migrate_from_old_folder();
+
Picopass* picopass = picopass_alloc();
scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart);
diff --git a/applications/plugins/picopass/picopass_device.c b/applications/plugins/picopass/picopass_device.c
index de01834bc..b5ba8ba78 100644
--- a/applications/plugins/picopass/picopass_device.c
+++ b/applications/plugins/picopass/picopass_device.c
@@ -48,13 +48,9 @@ static bool picopass_device_save_file(
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 picopass 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 picopass directory if necessary
- if(!storage_simply_mkdir(dev->storage, PICOPASS_APP_FOLDER)) break;
// First remove picopass device file if it was saved
furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
}
@@ -126,10 +122,11 @@ static bool picopass_device_save_file(
bool picopass_device_save(PicopassDevice* dev, const char* dev_name) {
if(dev->format == PicopassDeviceSaveFormatHF) {
return picopass_device_save_file(
- dev, dev_name, PICOPASS_APP_FOLDER, PICOPASS_APP_EXTENSION, true);
+ dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, PICOPASS_APP_EXTENSION, true);
} else if(dev->format == PicopassDeviceSaveFormatLF) {
return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true);
}
+
return false;
}
@@ -170,6 +167,8 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo
}
size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0];
+ // Fix for unpersonalized cards that have app_limit set to 0xFF
+ if(app_limit > PICOPASS_MAX_APP_LIMIT) app_limit = PICOPASS_MAX_APP_LIMIT;
for(size_t i = 6; i < app_limit; i++) {
furi_string_printf(temp_str, "Block %d", i);
if(!flipper_format_read_hex(
@@ -225,13 +224,12 @@ void picopass_device_free(PicopassDevice* picopass_dev) {
bool picopass_file_select(PicopassDevice* dev) {
furi_assert(dev);
- // Input events and views are managed by file_browser
FuriString* picopass_app_folder;
- picopass_app_folder = furi_string_alloc_set(PICOPASS_APP_FOLDER);
+ picopass_app_folder = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, PICOPASS_APP_EXTENSION, &I_Nfc_10px);
- browser_options.base_path = PICOPASS_APP_FOLDER;
+ browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
bool res = dialog_file_browser_show(
dev->dialogs, dev->load_path, picopass_app_folder, &browser_options);
@@ -274,7 +272,7 @@ bool picopass_device_delete(PicopassDevice* dev, bool use_load_path) {
furi_string_set(file_path, dev->load_path);
} else {
furi_string_printf(
- file_path, "%s/%s%s", PICOPASS_APP_FOLDER, dev->dev_name, PICOPASS_APP_EXTENSION);
+ file_path, APP_DATA_PATH("%s%s"), dev->dev_name, PICOPASS_APP_EXTENSION);
}
if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break;
deleted = true;
@@ -368,7 +366,7 @@ ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* r
record->CardNumber = (bot >> 1) & 0xFFFF;
record->FacilityCode = (bot >> 17) & 0xFF;
- FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber);
+ FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber);
record->valid = true;
} else {
record->CardNumber = 0;
diff --git a/applications/plugins/picopass/picopass_device.h b/applications/plugins/picopass/picopass_device.h
index 150b095a7..d7d0977df 100644
--- a/applications/plugins/picopass/picopass_device.h
+++ b/applications/plugins/picopass/picopass_device.h
@@ -22,8 +22,8 @@
#define PICOPASS_KD_BLOCK_INDEX 3
#define PICOPASS_KC_BLOCK_INDEX 4
#define PICOPASS_AIA_BLOCK_INDEX 5
+#define PICOPASS_PACS_CFG_BLOCK_INDEX 6
-#define PICOPASS_APP_FOLDER ANY_PATH("picopass")
#define PICOPASS_APP_EXTENSION ".picopass"
#define PICOPASS_APP_SHADOW_EXTENSION ".pas"
@@ -80,7 +80,6 @@ typedef struct {
PicopassDeviceSaveFormat format;
PicopassLoadingCallback loading_cb;
void* loading_cb_ctx;
-
} PicopassDevice;
PicopassDevice* picopass_device_alloc();
diff --git a/applications/plugins/picopass/picopass_i.h b/applications/plugins/picopass/picopass_i.h
index 8f954c83c..79c2a1af8 100644
--- a/applications/plugins/picopass/picopass_i.h
+++ b/applications/plugins/picopass/picopass_i.h
@@ -81,3 +81,15 @@ void picopass_blink_start(Picopass* picopass);
void picopass_blink_stop(Picopass* picopass);
void picopass_show_loading_popup(void* context, bool show);
+
+/** Check if memory is set to pattern
+ *
+ * @warning zero size will return false
+ *
+ * @param[in] data Pointer to the byte array
+ * @param[in] pattern The pattern
+ * @param[in] size The byte array size
+ *
+ * @return True if memory is set to pattern, false otherwise
+ */
+bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size);
diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c
index e886deb33..11ebcf54d 100644
--- a/applications/plugins/picopass/picopass_worker.c
+++ b/applications/plugins/picopass/picopass_worker.c
@@ -5,7 +5,11 @@
#define TAG "PicopassWorker"
const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
-const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00};
+const uint8_t picopass_factory_credit_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00};
+const uint8_t picopass_factory_debit_key[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87};
+const uint8_t picopass_xice_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88};
+const uint8_t picopass_xicl_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88};
+const uint8_t picopass_xics_key[] = {0x66, 0x66, 0x20, 0x20, 0x66, 0x66, 0x88, 0x88};
static void picopass_worker_enable_field() {
furi_hal_nfc_ll_txrx_on();
@@ -191,7 +195,29 @@ static ReturnCode picopass_auth_standard(uint8_t* csn, uint8_t* div_key) {
}
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
- loclass_diversifyKey(csn, picopass_iclass_key, div_key);
+ loclass_iclass_calc_div_key(csn, (uint8_t*)picopass_iclass_key, div_key, false);
+ loclass_opt_doReaderMAC(ccnr, div_key, mac);
+
+ return rfalPicoPassPollerCheck(mac, &chkRes);
+}
+
+static ReturnCode picopass_auth_factory(uint8_t* csn, uint8_t* div_key) {
+ rfalPicoPassReadCheckRes rcRes;
+ rfalPicoPassCheckRes chkRes;
+
+ ReturnCode err;
+
+ uint8_t mac[4] = {0};
+ uint8_t ccnr[12] = {0};
+
+ 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(csn, (uint8_t*)picopass_factory_debit_key, div_key, false);
loclass_opt_doReaderMAC(ccnr, div_key, mac);
return rfalPicoPassPollerCheck(mac, &chkRes);
@@ -201,7 +227,8 @@ static ReturnCode picopass_auth_dict(
uint8_t* csn,
PicopassPacs* pacs,
uint8_t* div_key,
- IclassEliteDictType dict_type) {
+ IclassEliteDictType dict_type,
+ bool elite) {
rfalPicoPassReadCheckRes rcRes;
rfalPicoPassCheckRes chkRes;
@@ -246,7 +273,7 @@ static ReturnCode picopass_auth_dict(
}
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
- loclass_iclass_calc_div_key(csn, key, div_key, true);
+ loclass_iclass_calc_div_key(csn, key, div_key, elite);
loclass_opt_doReaderMAC(ccnr, div_key, mac);
err = rfalPicoPassPollerCheck(mac, &chkRes);
@@ -264,29 +291,51 @@ static ReturnCode picopass_auth_dict(
ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
ReturnCode err;
- FURI_LOG_E(TAG, "Trying standard legacy key");
+ FURI_LOG_I(TAG, "Trying standard legacy key");
err = picopass_auth_standard(
AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data);
if(err == ERR_NONE) {
+ memcpy(pacs->key, picopass_iclass_key, PICOPASS_BLOCK_LEN);
return ERR_NONE;
}
- FURI_LOG_E(TAG, "Starting user dictionary attack");
+ FURI_LOG_I(TAG, "Trying factory default key");
+ err = picopass_auth_factory(
+ AA1[PICOPASS_CSN_BLOCK_INDEX].data, AA1[PICOPASS_KD_BLOCK_INDEX].data);
+ if(err == ERR_NONE) {
+ memcpy(pacs->key, picopass_factory_debit_key, PICOPASS_BLOCK_LEN);
+ return ERR_NONE;
+ }
+
+ FURI_LOG_I(TAG, "Starting user dictionary attack [Elite KDF]");
err = picopass_auth_dict(
AA1[PICOPASS_CSN_BLOCK_INDEX].data,
pacs,
AA1[PICOPASS_KD_BLOCK_INDEX].data,
- IclassEliteDictTypeUser);
+ IclassEliteDictTypeUser,
+ true);
if(err == ERR_NONE) {
return ERR_NONE;
}
- FURI_LOG_E(TAG, "Starting in-built dictionary attack");
+ FURI_LOG_I(TAG, "Starting system dictionary attack [Elite KDF]");
err = picopass_auth_dict(
AA1[PICOPASS_CSN_BLOCK_INDEX].data,
pacs,
AA1[PICOPASS_KD_BLOCK_INDEX].data,
- IclassEliteDictTypeFlipper);
+ IclassEliteDictTypeFlipper,
+ true);
+ if(err == ERR_NONE) {
+ return ERR_NONE;
+ }
+
+ FURI_LOG_I(TAG, "Starting system dictionary attack [Standard KDF]");
+ err = picopass_auth_dict(
+ AA1[PICOPASS_CSN_BLOCK_INDEX].data,
+ pacs,
+ AA1[PICOPASS_KD_BLOCK_INDEX].data,
+ IclassEliteDictTypeFlipper,
+ false);
if(err == ERR_NONE) {
return ERR_NONE;
}
@@ -364,7 +413,7 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) {
}
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
- loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key);
+ loclass_iclass_calc_div_key(selRes.CSN, (uint8_t*)picopass_iclass_key, div_key, false);
loclass_opt_doReaderMAC(ccnr, div_key, mac);
err = rfalPicoPassPollerCheck(mac, &chkRes);
@@ -406,6 +455,86 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) {
return ERR_NONE;
}
+ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* newBlock) {
+ rfalPicoPassIdentifyRes idRes;
+ rfalPicoPassSelectRes selRes;
+ rfalPicoPassReadCheckRes rcRes;
+ rfalPicoPassCheckRes chkRes;
+
+ ReturnCode err;
+
+ uint8_t mac[4] = {0};
+ uint8_t ccnr[12] = {0};
+
+ err = rfalPicoPassPollerIdentify(&idRes);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
+ return err;
+ }
+
+ err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
+ return err;
+ }
+
+ 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
+
+ if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN) != 0) {
+ FURI_LOG_E(TAG, "Wrong CSN for write");
+ return ERR_REQUEST;
+ }
+
+ loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_KD_BLOCK_INDEX].data, mac);
+ err = rfalPicoPassPollerCheck(mac, &chkRes);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
+ return err;
+ }
+
+ FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", blockNo);
+ uint8_t data[9] = {
+ blockNo,
+ newBlock[0],
+ newBlock[1],
+ newBlock[2],
+ newBlock[3],
+ newBlock[4],
+ newBlock[5],
+ newBlock[6],
+ newBlock[7]};
+ loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_KD_BLOCK_INDEX].data, mac);
+ FURI_LOG_D(
+ TAG,
+ "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
+ blockNo,
+ data[1],
+ data[2],
+ data[3],
+ data[4],
+ data[5],
+ data[6],
+ data[7],
+ data[8],
+ mac[0],
+ mac[1],
+ mac[2],
+ mac[3]);
+
+ err = rfalPicoPassPollerWriteBlock(data[0], data + 1, mac);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err);
+ return err;
+ }
+
+ return ERR_NONE;
+}
+
int32_t picopass_worker_task(void* context) {
PicopassWorker* picopass_worker = context;
@@ -414,6 +543,8 @@ int32_t picopass_worker_task(void* context) {
picopass_worker_detect(picopass_worker);
} else if(picopass_worker->state == PicopassWorkerStateWrite) {
picopass_worker_write(picopass_worker);
+ } else if(picopass_worker->state == PicopassWorkerStateWriteKey) {
+ picopass_worker_write_key(picopass_worker);
}
picopass_worker_disable_field(ERR_NONE);
@@ -448,7 +579,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
}
// Thank you proxmark!
- pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0);
+ pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8);
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");
@@ -520,3 +651,46 @@ void picopass_worker_write(PicopassWorker* picopass_worker) {
furi_delay_ms(100);
}
}
+
+void picopass_worker_write_key(PicopassWorker* picopass_worker) {
+ PicopassDeviceData* dev_data = picopass_worker->dev_data;
+ PicopassBlock* AA1 = dev_data->AA1;
+ PicopassPacs* pacs = &dev_data->pacs;
+ ReturnCode err;
+ PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
+
+ uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
+ uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data;
+ uint8_t fuses = configBlock[7];
+ uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data;
+
+ uint8_t newKey[PICOPASS_BLOCK_LEN] = {0};
+ loclass_iclass_calc_div_key(csn, pacs->key, newKey, false);
+
+ if((fuses & 0x80) == 0x80) {
+ FURI_LOG_D(TAG, "Plain write for personalized mode key change");
+ } else {
+ FURI_LOG_D(TAG, "XOR write for application mode key change");
+ // XOR when in application mode
+ for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
+ newKey[i] ^= oldKey[i];
+ }
+ }
+
+ while(picopass_worker->state == PicopassWorkerStateWriteKey) {
+ if(picopass_detect_card(1000) == ERR_NONE) {
+ err = picopass_write_block(AA1, PICOPASS_KD_BLOCK_INDEX, newKey);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "picopass_write_block error %d", err);
+ nextState = PicopassWorkerEventFail;
+ }
+
+ // Notify caller and exit
+ if(picopass_worker->callback) {
+ picopass_worker->callback(nextState, picopass_worker->context);
+ }
+ break;
+ }
+ furi_delay_ms(100);
+ }
+}
diff --git a/applications/plugins/picopass/picopass_worker.h b/applications/plugins/picopass/picopass_worker.h
index 29a890a18..02b088b24 100644
--- a/applications/plugins/picopass/picopass_worker.h
+++ b/applications/plugins/picopass/picopass_worker.h
@@ -12,6 +12,7 @@ typedef enum {
// Main worker states
PicopassWorkerStateDetect,
PicopassWorkerStateWrite,
+ PicopassWorkerStateWriteKey,
// Transition
PicopassWorkerStateStop,
} PicopassWorkerState;
diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/plugins/picopass/picopass_worker_i.h
index ded40e6c6..f41cfce45 100644
--- a/applications/plugins/picopass/picopass_worker_i.h
+++ b/applications/plugins/picopass/picopass_worker_i.h
@@ -31,3 +31,4 @@ int32_t picopass_worker_task(void* context);
void picopass_worker_detect(PicopassWorker* picopass_worker);
void picopass_worker_write(PicopassWorker* picopass_worker);
+void picopass_worker_write_key(PicopassWorker* picopass_worker);
diff --git a/applications/plugins/picopass/scenes/picopass_scene_card_menu.c b/applications/plugins/picopass/scenes/picopass_scene_card_menu.c
index a424b919a..fe63f7c86 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_card_menu.c
+++ b/applications/plugins/picopass/scenes/picopass_scene_card_menu.c
@@ -3,6 +3,7 @@
enum SubmenuIndex {
SubmenuIndexSave,
SubmenuIndexSaveAsLF,
+ SubmenuIndexChangeKey,
};
void picopass_scene_card_menu_submenu_callback(void* context, uint32_t index) {
@@ -25,6 +26,13 @@ void picopass_scene_card_menu_on_enter(void* context) {
picopass_scene_card_menu_submenu_callback,
picopass);
}
+ submenu_add_item(
+ submenu,
+ "Change Key",
+ SubmenuIndexChangeKey,
+ picopass_scene_card_menu_submenu_callback,
+ picopass);
+
submenu_set_selected_item(
picopass->submenu,
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneCardMenu));
@@ -49,6 +57,11 @@ bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
picopass->dev->format = PicopassDeviceSaveFormatLF;
scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName);
consumed = true;
+ } else if(event.event == SubmenuIndexChangeKey) {
+ scene_manager_set_scene_state(
+ picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexChangeKey);
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyMenu);
+ consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
diff --git a/applications/plugins/picopass/scenes/picopass_scene_config.h b/applications/plugins/picopass/scenes/picopass_scene_config.h
index 27d6bbcd7..f5a90d46e 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_config.h
+++ b/applications/plugins/picopass/scenes/picopass_scene_config.h
@@ -11,3 +11,6 @@ ADD_SCENE(picopass, delete, Delete)
ADD_SCENE(picopass, delete_success, DeleteSuccess)
ADD_SCENE(picopass, write_card, WriteCard)
ADD_SCENE(picopass, write_card_success, WriteCardSuccess)
+ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess)
+ADD_SCENE(picopass, write_key, WriteKey)
+ADD_SCENE(picopass, key_menu, KeyMenu)
diff --git a/applications/plugins/picopass/scenes/picopass_scene_key_menu.c b/applications/plugins/picopass/scenes/picopass_scene_key_menu.c
new file mode 100644
index 000000000..b1db37f81
--- /dev/null
+++ b/applications/plugins/picopass/scenes/picopass_scene_key_menu.c
@@ -0,0 +1,100 @@
+#include "../picopass_i.h"
+
+enum SubmenuIndex {
+ SubmenuIndexWriteStandard,
+ SubmenuIndexWriteiCE,
+ SubmenuIndexWriteiCL,
+ SubmenuIndexWriteiCS,
+ SubmenuIndexWriteCustom, //TODO: user input of key
+};
+
+extern const uint8_t picopass_xice_key[];
+extern const uint8_t picopass_xicl_key[];
+extern const uint8_t picopass_xics_key[];
+extern const uint8_t picopass_iclass_key[];
+
+void picopass_scene_key_menu_submenu_callback(void* context, uint32_t index) {
+ Picopass* picopass = context;
+
+ view_dispatcher_send_custom_event(picopass->view_dispatcher, index);
+}
+
+void picopass_scene_key_menu_on_enter(void* context) {
+ Picopass* picopass = context;
+ Submenu* submenu = picopass->submenu;
+
+ submenu_add_item(
+ submenu,
+ "Write Standard",
+ SubmenuIndexWriteStandard,
+ picopass_scene_key_menu_submenu_callback,
+ picopass);
+ submenu_add_item(
+ submenu,
+ "Write iCE",
+ SubmenuIndexWriteiCE,
+ picopass_scene_key_menu_submenu_callback,
+ picopass);
+ submenu_add_item(
+ submenu,
+ "Write iCL",
+ SubmenuIndexWriteiCL,
+ picopass_scene_key_menu_submenu_callback,
+ picopass);
+ submenu_add_item(
+ submenu,
+ "Write iCS",
+ SubmenuIndexWriteiCS,
+ picopass_scene_key_menu_submenu_callback,
+ picopass);
+
+ submenu_set_selected_item(
+ picopass->submenu,
+ scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneKeyMenu));
+
+ view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu);
+}
+
+bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) {
+ Picopass* picopass = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == SubmenuIndexWriteStandard) {
+ scene_manager_set_scene_state(
+ picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteStandard);
+ memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN);
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+ consumed = true;
+ } else if(event.event == SubmenuIndexWriteiCE) {
+ scene_manager_set_scene_state(
+ picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE);
+ memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, PICOPASS_BLOCK_LEN);
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+ consumed = true;
+ } else if(event.event == SubmenuIndexWriteiCL) {
+ scene_manager_set_scene_state(
+ picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE);
+ memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, PICOPASS_BLOCK_LEN);
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+ consumed = true;
+ } else if(event.event == SubmenuIndexWriteiCS) {
+ scene_manager_set_scene_state(
+ picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE);
+ memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, PICOPASS_BLOCK_LEN);
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+ consumed = true;
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ picopass->scene_manager, PicopassSceneStart);
+ }
+
+ return consumed;
+}
+
+void picopass_scene_key_menu_on_exit(void* context) {
+ Picopass* picopass = context;
+
+ submenu_reset(picopass->submenu);
+}
diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card.c b/applications/plugins/picopass/scenes/picopass_scene_read_card.c
index 8188207a2..c62cba8ea 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_read_card.c
+++ b/applications/plugins/picopass/scenes/picopass_scene_read_card.c
@@ -1,6 +1,8 @@
#include "../picopass_i.h"
#include
+extern const uint8_t picopass_factory_debit_key[];
+
void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) {
UNUSED(event);
Picopass* picopass = context;
@@ -34,7 +36,14 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassCustomEventWorkerExit) {
- scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
+ if(memcmp(
+ picopass->dev->dev_data.pacs.key,
+ picopass_factory_debit_key,
+ PICOPASS_BLOCK_LEN) == 0) {
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess);
+ } else {
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
+ }
consumed = true;
}
}
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 bb170ac45..f078d460a 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c
+++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c
@@ -15,6 +15,7 @@ void picopass_scene_read_card_success_widget_callback(
void picopass_scene_read_card_success_on_enter(void* context) {
Picopass* picopass = context;
+
FuriString* csn_str = furi_string_alloc_set("CSN:");
FuriString* credential_str = furi_string_alloc();
FuriString* wiegand_str = furi_string_alloc();
@@ -30,27 +31,31 @@ void picopass_scene_read_card_success_on_enter(void* context) {
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
Widget* widget = picopass->widget;
- uint8_t csn[PICOPASS_BLOCK_LEN];
- memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN);
+ uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
+ memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
- furi_string_cat_printf(csn_str, " %02X", csn[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) {
+ bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN);
+ bool empty =
+ picopass_is_memset(AA1[PICOPASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN);
+
+ if(no_key) {
furi_string_cat_printf(wiegand_str, "Read Failed");
if(pacs->se_enabled) {
furi_string_cat_printf(credential_str, "SE enabled");
}
+ } else if(empty) {
+ furi_string_cat_printf(wiegand_str, "Empty");
+ } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
+ // Neither of these are valid. Indicates the block was all 0x00 or all 0xff
+ furi_string_cat_printf(wiegand_str, "Invalid PACS");
- widget_add_button_element(
- widget,
- GuiButtonTypeLeft,
- "Retry",
- picopass_scene_read_card_success_widget_callback,
- picopass);
-
+ if(pacs->se_enabled) {
+ furi_string_cat_printf(credential_str, "SE enabled");
+ }
} else {
size_t bytesLength = 1 + pacs->record.bitLength / 8;
furi_string_set(credential_str, "");
@@ -69,12 +74,18 @@ void picopass_scene_read_card_success_on_enter(void* context) {
furi_string_cat_printf(sio_str, "+SIO");
}
- widget_add_button_element(
- widget,
- GuiButtonTypeLeft,
- "Retry",
- picopass_scene_read_card_success_widget_callback,
- picopass);
+ if(pacs->key) {
+ if(pacs->sio) {
+ furi_string_cat_printf(sio_str, " ");
+ }
+ furi_string_cat_printf(sio_str, "Key: ");
+
+ uint8_t key[PICOPASS_BLOCK_LEN];
+ memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN);
+ for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
+ furi_string_cat_printf(sio_str, "%02X", key[i]);
+ }
+ }
widget_add_button_element(
widget,
@@ -84,6 +95,13 @@ void picopass_scene_read_card_success_on_enter(void* context) {
picopass);
}
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeLeft,
+ "Retry",
+ picopass_scene_read_card_success_widget_callback,
+ picopass);
+
widget_add_string_element(
widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str));
widget_add_string_element(
diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c
new file mode 100644
index 000000000..b98951dcf
--- /dev/null
+++ b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c
@@ -0,0 +1,81 @@
+#include "../picopass_i.h"
+#include
+
+extern const uint8_t picopass_iclass_key[];
+
+void picopass_scene_read_factory_success_widget_callback(
+ GuiButtonType result,
+ InputType type,
+ void* context) {
+ furi_assert(context);
+ Picopass* picopass = context;
+
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(picopass->view_dispatcher, result);
+ }
+}
+
+void picopass_scene_read_factory_success_on_enter(void* context) {
+ Picopass* picopass = context;
+ FuriString* title = furi_string_alloc_set("Factory Default");
+ FuriString* subtitle = furi_string_alloc_set("");
+
+ DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
+
+ // Send notification
+ notification_message(picopass->notifications, &sequence_success);
+
+ // Setup view
+ Widget* widget = picopass->widget;
+ //PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
+ PicopassBlock* AA1 = picopass->dev->dev_data.AA1;
+
+ uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data;
+ uint8_t fuses = configBlock[7];
+
+ if((fuses & 0x80) == 0x80) {
+ furi_string_cat_printf(subtitle, "Personalization mode");
+ } else {
+ furi_string_cat_printf(subtitle, "Application mode");
+ }
+
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeCenter,
+ "Write Standard iClass Key",
+ picopass_scene_read_factory_success_widget_callback,
+ picopass);
+
+ widget_add_string_element(
+ widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(title));
+ widget_add_string_element(
+ widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(subtitle));
+
+ furi_string_free(title);
+ furi_string_free(subtitle);
+
+ view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
+}
+
+bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEvent event) {
+ Picopass* picopass = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == GuiButtonTypeLeft) {
+ consumed = scene_manager_previous_scene(picopass->scene_manager);
+ } else if(event.event == GuiButtonTypeCenter) {
+ memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN);
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+ consumed = true;
+ }
+ }
+ return consumed;
+}
+
+void picopass_scene_read_factory_success_on_exit(void* context) {
+ Picopass* picopass = context;
+
+ // Clear view
+ widget_reset(picopass->widget);
+}
diff --git a/applications/plugins/picopass/scenes/picopass_scene_save_name.c b/applications/plugins/picopass/scenes/picopass_scene_save_name.c
index 8239002d9..baf882b80 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_save_name.c
+++ b/applications/plugins/picopass/scenes/picopass_scene_save_name.c
@@ -21,7 +21,7 @@ void picopass_scene_save_name_on_enter(void* context) {
} else {
picopass_text_store_set(picopass, picopass->dev->dev_name);
}
- text_input_set_header_text(text_input, "Name The Card");
+ text_input_set_header_text(text_input, "Name the card");
text_input_set_result_callback(
text_input,
picopass_scene_save_name_text_input_callback,
@@ -31,12 +31,10 @@ void picopass_scene_save_name_on_enter(void* context) {
dev_name_empty);
FuriString* folder_path;
- folder_path = furi_string_alloc();
+ folder_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
if(furi_string_end_with(picopass->dev->load_path, PICOPASS_APP_EXTENSION)) {
path_extract_dirname(furi_string_get_cstr(picopass->dev->load_path), folder_path);
- } else {
- furi_string_set(folder_path, PICOPASS_APP_FOLDER);
}
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
diff --git a/applications/plugins/picopass/scenes/picopass_scene_start.c b/applications/plugins/picopass/scenes/picopass_scene_start.c
index 6d1aeedcd..d33a1d264 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_start.c
+++ b/applications/plugins/picopass/scenes/picopass_scene_start.c
@@ -32,13 +32,18 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) {
+ scene_manager_set_scene_state(
+ picopass->scene_manager, PicopassSceneStart, SubmenuIndexRead);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard);
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
+ // Explicitly save state so that the correct item is
+ // reselected if the user cancels loading a file.
+ scene_manager_set_scene_state(
+ picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect);
consumed = true;
}
- scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event);
}
return consumed;
diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c
index 108e7d1ce..4bbca816a 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c
+++ b/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c
@@ -16,6 +16,7 @@ void picopass_scene_write_card_success_widget_callback(
void picopass_scene_write_card_success_on_enter(void* context) {
Picopass* picopass = context;
Widget* widget = picopass->widget;
+ FuriString* str = furi_string_alloc_set("Write Success!");
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
@@ -29,6 +30,18 @@ void picopass_scene_write_card_success_on_enter(void* context) {
picopass_scene_write_card_success_widget_callback,
picopass);
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeRight,
+ "Menu",
+ picopass_scene_write_card_success_widget_callback,
+ picopass);
+
+ widget_add_string_element(
+ widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(str));
+
+ furi_string_free(str);
+
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
}
diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_key.c b/applications/plugins/picopass/scenes/picopass_scene_write_key.c
new file mode 100644
index 000000000..0f417e1c3
--- /dev/null
+++ b/applications/plugins/picopass/scenes/picopass_scene_write_key.c
@@ -0,0 +1,53 @@
+#include "../picopass_i.h"
+#include
+
+void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context) {
+ UNUSED(event);
+ Picopass* picopass = context;
+ view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit);
+}
+
+void picopass_scene_write_key_on_enter(void* context) {
+ Picopass* picopass = context;
+ DOLPHIN_DEED(DolphinDeedNfcSave);
+
+ // Setup view
+ Popup* popup = picopass->popup;
+ popup_set_header(popup, "Writing\niClass\nkey", 68, 30, AlignLeft, AlignTop);
+ popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
+
+ // Start worker
+ view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
+ picopass_worker_start(
+ picopass->worker,
+ PicopassWorkerStateWriteKey,
+ &picopass->dev->dev_data,
+ picopass_write_key_worker_callback,
+ picopass);
+
+ picopass_blink_start(picopass);
+}
+
+bool picopass_scene_write_key_on_event(void* context, SceneManagerEvent event) {
+ Picopass* picopass = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == PicopassCustomEventWorkerExit) {
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess);
+ consumed = true;
+ }
+ }
+ return consumed;
+}
+
+void picopass_scene_write_key_on_exit(void* context) {
+ Picopass* picopass = context;
+
+ // Stop worker
+ picopass_worker_stop(picopass->worker);
+ // Clear view
+ popup_reset(picopass->popup);
+
+ picopass_blink_stop(picopass);
+}
diff --git a/applications/plugins/pocsag_pager/application.fam b/applications/plugins/pocsag_pager/application.fam
index 243acaab5..3b1fa313e 100644
--- a/applications/plugins/pocsag_pager/application.fam
+++ b/applications/plugins/pocsag_pager/application.fam
@@ -8,6 +8,6 @@ App(
stack_size=4 * 1024,
order=50,
fap_icon="pocsag_pager_10px.png",
- fap_category="Tools",
+ fap_category="Sub-GHz",
fap_icon_assets="images",
)
diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app.c b/applications/plugins/pocsag_pager/pocsag_pager_app.c
index 3ac242304..123b3ee9d 100644
--- a/applications/plugins/pocsag_pager/pocsag_pager_app.c
+++ b/applications/plugins/pocsag_pager/pocsag_pager_app.c
@@ -122,6 +122,9 @@ POCSAGPagerApp* pocsag_pager_app_alloc() {
app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode);
subghz_worker_set_context(app->txrx->worker, app->txrx->receiver);
+ // Enable power for External CC1101 if it is connected
+ furi_hal_subghz_enable_ext_power();
+
furi_hal_power_suppress_charge_enter();
scene_manager_next_scene(app->scene_manager, POCSAGPagerSceneStart);
@@ -135,6 +138,9 @@ void pocsag_pager_app_free(POCSAGPagerApp* app) {
//CC1101 off
pcsg_sleep(app);
+ // Disable power for External CC1101 if it was enabled and module is connected
+ furi_hal_subghz_disable_ext_power();
+
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewSubmenu);
submenu_free(app->submenu);
diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app_i.c b/applications/plugins/pocsag_pager/pocsag_pager_app_i.c
index ba6e87c28..ff73ab50e 100644
--- a/applications/plugins/pocsag_pager/pocsag_pager_app_i.c
+++ b/applications/plugins/pocsag_pager/pocsag_pager_app_i.c
@@ -40,7 +40,7 @@ void pcsg_begin(POCSAGPagerApp* app, uint8_t* 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);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
app->txrx->txrx_state = PCSGTxRxStateIDLE;
}
@@ -54,7 +54,7 @@ uint32_t pcsg_rx(POCSAGPagerApp* app, uint32_t frequency) {
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_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_flush_rx();
furi_hal_subghz_rx();
diff --git a/applications/plugins/pocsag_pager/protocols/pcsg_generic.c b/applications/plugins/pocsag_pager/protocols/pcsg_generic.c
index 890ed43d7..5425fd512 100644
--- a/applications/plugins/pocsag_pager/protocols/pcsg_generic.c
+++ b/applications/plugins/pocsag_pager/protocols/pcsg_generic.c
@@ -21,12 +21,12 @@ void pcsg_block_generic_get_preset_name(const char* preset_name, FuriString* pre
furi_string_set(preset_str, preset_name_temp);
}
-bool pcsg_block_generic_serialize(
+SubGhzProtocolStatus pcsg_block_generic_serialize(
PCSGBlockGeneric* instance,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(instance);
- bool res = false;
+ SubGhzProtocolStatus res = SubGhzProtocolStatusError;
FuriString* temp_str;
temp_str = furi_string_alloc();
do {
@@ -75,15 +75,16 @@ bool pcsg_block_generic_serialize(
break;
}
- res = true;
+ res = SubGhzProtocolStatusOk;
} while(false);
furi_string_free(temp_str);
return res;
}
-bool pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* flipper_format) {
+SubGhzProtocolStatus
+ pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* flipper_format) {
furi_assert(instance);
- bool res = false;
+ SubGhzProtocolStatus res = SubGhzProtocolStatusError;
FuriString* temp_data = furi_string_alloc();
FuriString* temp_data2 = furi_string_alloc();
@@ -113,7 +114,7 @@ bool pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* f
instance->result_msg = furi_string_alloc_set(temp_data);
}
- res = true;
+ res = SubGhzProtocolStatusOk;
} while(0);
furi_string_free(temp_data);
diff --git a/applications/plugins/pocsag_pager/protocols/pcsg_generic.h b/applications/plugins/pocsag_pager/protocols/pcsg_generic.h
index ff925b6c9..126b54ffe 100644
--- a/applications/plugins/pocsag_pager/protocols/pcsg_generic.h
+++ b/applications/plugins/pocsag_pager/protocols/pcsg_generic.h
@@ -35,7 +35,7 @@ void pcsg_block_generic_get_preset_name(const char* preset_name, FuriString* pre
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return true On success
*/
-bool pcsg_block_generic_serialize(
+SubGhzProtocolStatus pcsg_block_generic_serialize(
PCSGBlockGeneric* instance,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
@@ -46,7 +46,8 @@ bool pcsg_block_generic_serialize(
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
-bool pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* flipper_format);
+SubGhzProtocolStatus
+ pcsg_block_generic_deserialize(PCSGBlockGeneric* instance, FlipperFormat* flipper_format);
float pcsg_block_generic_fahrenheit_to_celsius(float fahrenheit);
diff --git a/applications/plugins/pocsag_pager/protocols/pocsag.c b/applications/plugins/pocsag_pager/protocols/pocsag.c
index 69d09d554..ca210c2a4 100644
--- a/applications/plugins/pocsag_pager/protocols/pocsag.c
+++ b/applications/plugins/pocsag_pager/protocols/pocsag.c
@@ -288,7 +288,7 @@ uint8_t subghz_protocol_decoder_pocsag_get_hash_data(void* context) {
return hash;
}
-bool subghz_protocol_decoder_pocsag_serialize(
+SubGhzProtocolStatus subghz_protocol_decoder_pocsag_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
@@ -296,31 +296,35 @@ bool subghz_protocol_decoder_pocsag_serialize(
SubGhzProtocolDecoderPocsag* instance = context;
uint32_t msg_len;
- if(!pcsg_block_generic_serialize(&instance->generic, flipper_format, preset)) return false;
+ if(SubGhzProtocolStatusOk !=
+ pcsg_block_generic_serialize(&instance->generic, flipper_format, preset))
+ return SubGhzProtocolStatusError;
msg_len = furi_string_size(instance->done_msg);
if(!flipper_format_write_uint32(flipper_format, "MsgLen", &msg_len, 1)) {
FURI_LOG_E(TAG, "Error adding MsgLen");
- return false;
+ return SubGhzProtocolStatusError;
}
uint8_t* s = (uint8_t*)furi_string_get_cstr(instance->done_msg);
if(!flipper_format_write_hex(flipper_format, "Msg", s, msg_len)) {
FURI_LOG_E(TAG, "Error adding Msg");
- return false;
+ return SubGhzProtocolStatusError;
}
- return true;
+ return SubGhzProtocolStatusOk;
}
-bool subghz_protocol_decoder_pocsag_deserialize(void* context, FlipperFormat* flipper_format) {
+SubGhzProtocolStatus
+ subghz_protocol_decoder_pocsag_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderPocsag* instance = context;
- bool ret = false;
+ SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
uint32_t msg_len;
uint8_t* buf;
do {
- if(!pcsg_block_generic_deserialize(&instance->generic, flipper_format)) {
+ if(SubGhzProtocolStatusOk !=
+ pcsg_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
@@ -338,7 +342,7 @@ bool subghz_protocol_decoder_pocsag_deserialize(void* context, FlipperFormat* fl
furi_string_set_strn(instance->done_msg, (const char*)buf, msg_len);
free(buf);
- ret = true;
+ ret = SubGhzProtocolStatusOk;
} while(false);
return ret;
}
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c b/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c
index 3f41b9b5d..658b70fea 100644
--- a/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c
+++ b/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c
@@ -195,6 +195,10 @@ bool pocsag_pager_scene_receiver_on_event(void* context, SceneManagerEvent event
pcsg_hopper_update(app);
pocsag_pager_scene_receiver_update_statusbar(app);
}
+ // Get current RSSI
+ float rssi = furi_hal_subghz_get_rssi();
+ pcsg_receiver_rssi(app->pcsg_receiver, rssi);
+
if(app->txrx->txrx_state == PCSGTxRxStateRx) {
notification_message(app->notifications, &sequence_blink_cyan_10);
}
diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c
index 972c8dafb..3ab5898a0 100644
--- a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c
+++ b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c
@@ -12,6 +12,8 @@
#define MENU_ITEMS 4u
#define UNLOCK_CNT 3
+#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f
+
typedef struct {
FuriString* item_str;
uint8_t type;
@@ -58,8 +60,24 @@ typedef struct {
uint16_t list_offset;
uint16_t history_item;
PCSGReceiverBarShow bar_show;
+ uint8_t u_rssi;
} PCSGReceiverModel;
+void pcsg_receiver_rssi(PCSGReceiver* instance, float rssi) {
+ furi_assert(instance);
+ with_view_model(
+ instance->view,
+ PCSGReceiverModel * model,
+ {
+ if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) {
+ model->u_rssi = 0;
+ } else {
+ model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN);
+ }
+ },
+ true);
+}
+
void pcsg_view_receiver_set_lock(PCSGReceiver* pcsg_receiver, PCSGLock lock) {
furi_assert(pcsg_receiver);
pcsg_receiver->lock_count = 0;
@@ -167,13 +185,23 @@ static void pcsg_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scr
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
}
+static void pcsg_view_rssi_draw(Canvas* canvas, PCSGReceiverModel* model) {
+ for(uint8_t i = 1; i < model->u_rssi; i++) {
+ if(i % 5) {
+ canvas_draw_dot(canvas, 46 + i, 50);
+ canvas_draw_dot(canvas, 47 + i, 51);
+ canvas_draw_dot(canvas, 46 + i, 52);
+ }
+ }
+}
+
void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* 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);
+ //canvas_draw_line(canvas, 46, 51, 125, 51);
bool scrollbar = model->history_item > 4;
FuriString* str_buff;
@@ -207,10 +235,13 @@ void pcsg_view_receiver_draw(Canvas* canvas, PCSGReceiverModel* model) {
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_draw_line(canvas, 46, 51, 125, 51);
canvas_set_font(canvas, FontSecondary);
}
+ // Draw RSSI
+ pcsg_view_rssi_draw(canvas, model);
+
switch(model->bar_show) {
case PCSGReceiverBarShowLock:
canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h
index 5ea2d4859..a13f5cdc7 100644
--- a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h
+++ b/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h
@@ -8,6 +8,8 @@ typedef struct PCSGReceiver PCSGReceiver;
typedef void (*PCSGReceiverCallback)(PCSGCustomEvent event, void* context);
+void pcsg_receiver_rssi(PCSGReceiver* instance, float rssi);
+
void pcsg_view_receiver_set_lock(PCSGReceiver* pcsg_receiver, PCSGLock keyboard);
void pcsg_view_receiver_set_callback(
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.c b/applications/plugins/pomodoro/flipp_pomodoro_app.c
index ed5b8282b..2ddf8cf68 100644
--- a/applications/plugins/pomodoro/flipp_pomodoro_app.c
+++ b/applications/plugins/pomodoro/flipp_pomodoro_app.c
@@ -29,11 +29,6 @@ static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event)
app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
return CustomEventConsumed;
case FlippPomodoroAppCustomEventStageComplete:
- if(flipp_pomodoro__get_stage(app->state) == FlippPomodoroStageFocus) {
- // REGISTER a deed on work stage complete to get an acheivement
- DOLPHIN_DEED(DolphinDeedPluginGameWin);
- };
-
flipp_pomodoro__toggle_stage(app->state);
notification_message(
app->notification_app,
@@ -98,4 +93,4 @@ int32_t flipp_pomodoro_app(void* p) {
flipp_pomodoro_app_free(app);
return 0;
-};
\ No newline at end of file
+};
diff --git a/applications/plugins/protoview/README.md b/applications/plugins/protoview/README.md
index 71083cdef..7523913ca 100644
--- a/applications/plugins/protoview/README.md
+++ b/applications/plugins/protoview/README.md
@@ -285,3 +285,5 @@ A big thank you to the RTL433 author, [Benjamin Larsson](https://github.com/merb
* As a sourve of documentation for protocols.
* As an awesome way to visualize and understand protocols, via [these great web tools](https://triq.org/).
* To have tons of fun with RTLSDR in general, now and in the past.
+
+The application icon was designed by Stefano Liuzzo.
diff --git a/applications/plugins/protoview/app.c b/applications/plugins/protoview/app.c
index 4765da9e3..a4dab9f40 100644
--- a/applications/plugins/protoview/app.c
+++ b/applications/plugins/protoview/app.c
@@ -3,32 +3,6 @@
#include "app.h"
-/* If this define is enabled, ProtoView is going to mess with the
- * otherwise opaque SubGhzWorker structure in order to disable
- * its filter for samples shorter than a given amount (30us at the
- * time I'm writing this comment).
- *
- * This structure must be taken in sync with the one of the firmware. */
-#define PROTOVIEW_DISABLE_SUBGHZ_FILTER 0
-
-#ifdef PROTOVIEW_DISABLE_SUBGHZ_FILTER
-struct SubGhzWorker {
- FuriThread* thread;
- FuriStreamBuffer* stream;
-
- volatile bool running;
- volatile bool overrun;
-
- LevelDuration filter_level_duration;
- bool filter_running;
- uint16_t filter_duration;
-
- SubGhzWorkerOverrunCallback overrun_callback;
- SubGhzWorkerPairCallback pair_callback;
- void* context;
-};
-#endif
-
RawSamplesBuffer *RawSamples, *DetectedSamples;
extern const SubGhzProtocolRegistry protoview_protocol_registry;
@@ -42,6 +16,7 @@ extern const SubGhzProtocolRegistry protoview_protocol_registry;
* and setting color to black. */
static void render_callback(Canvas* const canvas, void* ctx) {
ProtoViewApp* app = ctx;
+ furi_mutex_acquire(app->view_updating_mutex, FuriWaitForever);
/* Clear screen. */
canvas_set_color(canvas, ColorWhite);
@@ -74,6 +49,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
/* Draw the alert box if set. */
ui_draw_alert_if_needed(canvas, app);
+ furi_mutex_release(app->view_updating_mutex);
}
/* Here all we do is putting the events into the queue that will be handled
@@ -91,6 +67,8 @@ static void input_callback(InputEvent* input_event, void* ctx) {
* special views ViewGoNext and ViewGoPrev in order to move to
* the logical next/prev view. */
static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) {
+ furi_mutex_acquire(app->view_updating_mutex, FuriWaitForever);
+
/* Switch to the specified view. */
ProtoViewCurrentView old = app->current_view;
if(switchto == ViewGoNext) {
@@ -106,22 +84,10 @@ static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) {
}
ProtoViewCurrentView new = app->current_view;
- /* Set the current subview of the view we just left to zero. This is
- * the main subview of the old view. When re re-enter the view we are
- * lefting, we want to see the main thing again. */
- app->current_subview[old] = 0;
-
- /* Reset the view private data each time, before calling the enter/exit
- * callbacks that may want to setup some state. */
- memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN);
-
- /* Call the enter/exit view callbacks if needed. */
+ /* Call the exit view callbacks. */
if(old == ViewDirectSampling) view_exit_direct_sampling(app);
- if(new == ViewDirectSampling) view_enter_direct_sampling(app);
if(old == ViewBuildMessage) view_exit_build_message(app);
- if(new == ViewBuildMessage) view_enter_build_message(app);
if(old == ViewInfo) view_exit_info(app);
-
/* The frequency/modulation settings are actually a single view:
* as long as the user stays between the two modes of this view we
* don't need to call the exit-view callback. */
@@ -129,7 +95,24 @@ static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) {
(old == ViewModulationSettings && new != ViewFrequencySettings))
view_exit_settings(app);
+ /* Reset the view private data each time, before calling the enter
+ * callbacks that may want to setup some state. */
+ memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN);
+
+ /* Call the enter view callbacks after all the exit callback
+ * of the old view was already executed. */
+ if(new == ViewDirectSampling) view_enter_direct_sampling(app);
+ if(new == ViewBuildMessage) view_enter_build_message(app);
+
+ /* Set the current subview of the view we just left to zero. This is
+ * the main subview of the old view. When we re-enter the view we are
+ * lefting, we want to see the main thing again. */
+ app->current_subview[old] = 0;
+
+ /* If there is an alert on screen, dismiss it: if the user is
+ * switching view she already read it. */
ui_dismiss_alert(app);
+ furi_mutex_release(app->view_updating_mutex);
}
/* Allocate the application state and initialize a number of stuff.
@@ -143,7 +126,7 @@ ProtoViewApp* protoview_app_alloc() {
//init setting
app->setting = subghz_setting_alloc();
- subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt"));
+ subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user"));
// GUI
app->gui = furi_record_open(RECORD_GUI);
@@ -158,6 +141,7 @@ ProtoViewApp* protoview_app_alloc() {
app->show_text_input = false;
app->alert_dismiss_time = 0;
app->current_view = ViewRawPulses;
+ app->view_updating_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
for(int j = 0; j < ViewLast; j++) app->current_subview[j] = 0;
app->direct_sampling_enabled = false;
app->view_privdata = malloc(PROTOVIEW_VIEW_PRIVDATA_LEN);
@@ -174,29 +158,18 @@ ProtoViewApp* protoview_app_alloc() {
// Init Worker & Protocol
app->txrx = malloc(sizeof(ProtoViewTxRx));
- /* Setup rx worker and environment. */
+ /* Setup rx state. */
app->txrx->freq_mod_changed = false;
app->txrx->debug_timer_sampling = false;
app->txrx->last_g0_change_time = DWT->CYCCNT;
app->txrx->last_g0_value = false;
- app->txrx->worker = subghz_worker_alloc();
-#ifdef PROTOVIEW_DISABLE_SUBGHZ_FILTER
- app->txrx->worker->filter_running = 0;
-#endif
- app->txrx->environment = subghz_environment_alloc();
- subghz_environment_set_protocol_registry(
- app->txrx->environment, (void*)&protoview_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);
app->frequency = subghz_setting_get_default_frequency(app->setting);
app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */
+ // Enable power for External CC1101 if it is connected
+ furi_hal_subghz_enable_ext_power();
+
furi_hal_power_suppress_charge_enter();
app->running = 1;
@@ -212,6 +185,9 @@ void protoview_app_free(ProtoViewApp* app) {
// Put CC1101 on sleep, this also restores charging.
radio_sleep(app);
+ // Disable power for External CC1101 if it was enabled and module is connected
+ furi_hal_subghz_disable_ext_power();
+
// View related.
view_port_enabled_set(app->view_port, false);
gui_remove_view_port(app->gui, app->view_port);
@@ -219,17 +195,13 @@ void protoview_app_free(ProtoViewApp* app) {
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
furi_message_queue_free(app->event_queue);
+ furi_mutex_free(app->view_updating_mutex);
app->gui = NULL;
// Frequency setting.
subghz_setting_free(app->setting);
// Worker stuff.
- if(!app->txrx->debug_timer_sampling) {
- subghz_receiver_free(app->txrx->receiver);
- subghz_environment_free(app->txrx->environment);
- subghz_worker_free(app->txrx->worker);
- }
free(app->txrx);
// Raw samples buffers.
@@ -259,7 +231,7 @@ static void timer_callback(void* ctx) {
}
if(delta < RawSamples->total / 2) return;
app->signal_last_scan_idx = RawSamples->idx;
- scan_for_signal(app, RawSamples);
+ scan_for_signal(app, RawSamples, ProtoViewModulations[app->modulation].duration_filter);
}
/* This is the navigation callback we use in the view dispatcher used
diff --git a/applications/plugins/protoview/app.h b/applications/plugins/protoview/app.h
index 85007e345..624c118e2 100644
--- a/applications/plugins/protoview/app.h
+++ b/applications/plugins/protoview/app.h
@@ -17,9 +17,6 @@
#include
#include
#include
-#include
-#include
-#include
#include
#include "raw_samples.h"
@@ -28,7 +25,7 @@
#define BITMAP_SEEK_NOT_FOUND UINT32_MAX // Returned by function as sentinel
#define PROTOVIEW_VIEW_PRIVDATA_LEN 64 // View specific private data len
-#define DEBUG_MSG 1
+#define DEBUG_MSG 0
/* Forward declarations. */
@@ -69,8 +66,11 @@ typedef struct {
const char* name; // Name to show to the user.
const char* id; // Identifier in the Flipper API/file.
FuriHalSubGhzPreset preset; // The preset ID.
- uint8_t* custom; // If not null, a set of registers for
- // the CC1101, specifying a custom preset.
+ uint8_t* custom; /* If not null, a set of registers for
+ the CC1101, specifying a custom preset.*/
+ uint32_t duration_filter; /* Ignore pulses and gaps that are less
+ than the specified microseconds. This
+ depends on the data rate. */
} ProtoViewModulation;
extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */
@@ -82,9 +82,6 @@ struct ProtoViewTxRx {
bool freq_mod_changed; /* The user changed frequency and/or modulation
from the interface. There is to restart the
radio with the right parameters. */
- SubGhzWorker* worker; /* Our background worker. */
- SubGhzEnvironment* environment;
- SubGhzReceiver* receiver;
TxRxState txrx_state; /* Receiving, idle or sleeping? */
/* Timer sampling mode state. */
@@ -108,6 +105,10 @@ struct ProtoViewApp {
ViewPort* view_port; /* We just use a raw viewport and we render
everything into the low level canvas. */
ProtoViewCurrentView current_view; /* Active left-right view ID. */
+ FuriMutex* view_updating_mutex; /* The Flipper GUI calls the screen redraw
+ callback in a different thread. We
+ use this mutex to protect the redraw
+ from changes in app->view_privdata. */
int current_subview[ViewLast]; /* Active up-down subview ID. */
FuriMessageQueue* event_queue; /* Keypress events go here. */
@@ -238,7 +239,7 @@ typedef struct ProtoViewDecoder {
extern RawSamplesBuffer *RawSamples, *DetectedSamples;
-/* app_radio.c */
+/* app_subghz.c */
void radio_begin(ProtoViewApp* app);
uint32_t radio_rx(ProtoViewApp* app);
void radio_idle(ProtoViewApp* app);
@@ -247,11 +248,12 @@ void radio_sleep(ProtoViewApp* app);
void raw_sampling_worker_start(ProtoViewApp* app);
void raw_sampling_worker_stop(ProtoViewApp* app);
void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx);
+void protoview_rx_callback(bool level, uint32_t duration, void* context);
/* signal.c */
uint32_t duration_delta(uint32_t a, uint32_t b);
void reset_current_signal(ProtoViewApp* app);
-void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source);
+void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source, uint32_t min_duration);
bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos);
void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val);
void bitmap_copy(
@@ -271,6 +273,15 @@ uint32_t bitmap_seek_bits(
uint32_t startpos,
uint32_t maxbits,
const char* bits);
+bool bitmap_match_bitmap(
+ uint8_t* b1,
+ uint32_t b1len,
+ uint32_t b1off,
+ uint8_t* b2,
+ uint32_t b2len,
+ uint32_t b2off,
+ uint32_t cmplen);
+void bitmap_to_string(char* dst, uint8_t* b, uint32_t blen, uint32_t off, uint32_t len);
uint32_t convert_from_line_code(
uint8_t* buf,
uint64_t buflen,
@@ -339,7 +350,7 @@ void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint
void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits);
void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits);
void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits);
-void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s);
+void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s, size_t len);
void fieldset_add_bytes(
ProtoViewFieldSet* fs,
const char* name,
@@ -359,3 +370,5 @@ void field_set_from_field(ProtoViewField* dst, ProtoViewField* src);
/* crc.c */
uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly);
+uint8_t sum_bytes(const uint8_t* data, size_t len, uint8_t init);
+uint8_t xor_bytes(const uint8_t* data, size_t len, uint8_t init);
diff --git a/applications/plugins/protoview/app_subghz.c b/applications/plugins/protoview/app_subghz.c
index 73e0e16ae..69197df5c 100644
--- a/applications/plugins/protoview/app_subghz.c
+++ b/applications/plugins/protoview/app_subghz.c
@@ -9,31 +9,32 @@
#include
#include
-void raw_sampling_worker_start(ProtoViewApp* app);
-void raw_sampling_worker_stop(ProtoViewApp* app);
+void raw_sampling_timer_start(ProtoViewApp* app);
+void raw_sampling_timer_stop(ProtoViewApp* app);
ProtoViewModulation ProtoViewModulations[] = {
- {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL},
- {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL},
+ {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL, 30},
+ {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL, 30},
{"2FSK 2.38Khz",
"FuriHalSubGhzPreset2FSKDev238Async",
FuriHalSubGhzPreset2FSKDev238Async,
- NULL},
+ NULL,
+ 30},
{"2FSK 47.6Khz",
"FuriHalSubGhzPreset2FSKDev476Async",
FuriHalSubGhzPreset2FSKDev476Async,
- NULL},
- {"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs},
- {"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs},
- {"TPMS 3 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_fsk_async_regs},
- {"TPMS 4 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms4_fsk_async_regs},
- {NULL, NULL, 0, NULL} /* End of list sentinel. */
+ NULL,
+ 30},
+ {"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs, 30},
+ {"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs, 30},
+ {"TPMS 3 (GFSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_gfsk_async_regs, 30},
+ {"OOK 40kBaud", NULL, 0, (uint8_t*)protoview_subghz_40k_ook_async_regs, 15},
+ {"FSK 40kBaud", NULL, 0, (uint8_t*)protoview_subghz_40k_fsk_async_regs, 15},
+ {NULL, NULL, 0, NULL, 0} /* End of list sentinel. */
};
/* Called after the application initialization in order to setup the
- * subghz system and put it into idle state. If the user wants to start
- * receiving we will call radio_rx() to start a receiving worker and
- * associated thread. */
+ * subghz system and put it into idle state. */
void radio_begin(ProtoViewApp* app) {
furi_assert(app);
furi_hal_subghz_reset();
@@ -51,13 +52,23 @@ void radio_begin(ProtoViewApp* app) {
} else {
furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom);
}
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
app->txrx->txrx_state = TxRxStateIDLE;
}
/* ================================= Reception ============================== */
-/* Setup subghz to start receiving using a background worker. */
+/* We avoid the subghz provided abstractions and put the data in our
+ * simple abstraction: the RawSamples circular buffer. */
+void protoview_rx_callback(bool level, uint32_t duration, void* context) {
+ UNUSED(context);
+ /* Add data to the circular buffer. */
+ raw_samples_add(RawSamples, level, duration);
+ // FURI_LOG_E(TAG, "FEED: %d %d", (int)level, (int)duration);
+ return;
+}
+
+/* Setup the CC1101 to start receiving using a background worker. */
uint32_t radio_rx(ProtoViewApp* app) {
furi_assert(app);
if(!furi_hal_subghz_is_frequency_valid(app->frequency)) {
@@ -69,12 +80,11 @@ uint32_t radio_rx(ProtoViewApp* app) {
furi_hal_subghz_idle(); /* Put it into idle state in case it is sleeping. */
uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency);
FURI_LOG_E(TAG, "Switched to frequency: %lu", value);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_flush_rx();
furi_hal_subghz_rx();
if(!app->txrx->debug_timer_sampling) {
- furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker);
- subghz_worker_start(app->txrx->worker);
+ furi_hal_subghz_start_async_rx(protoview_rx_callback, NULL);
} else {
raw_sampling_worker_start(app);
}
@@ -82,16 +92,13 @@ uint32_t radio_rx(ProtoViewApp* app) {
return value;
}
-/* Stop subghz worker (if active), put radio on idle state. */
+/* Stop receiving (if active) and put the radio on idle state. */
void radio_rx_end(ProtoViewApp* app) {
furi_assert(app);
if(app->txrx->txrx_state == TxRxStateRx) {
if(!app->txrx->debug_timer_sampling) {
- if(subghz_worker_is_running(app->txrx->worker)) {
- subghz_worker_stop(app->txrx->worker);
- furi_hal_subghz_stop_async_rx();
- }
+ furi_hal_subghz_stop_async_rx();
} else {
raw_sampling_worker_stop(app);
}
@@ -104,8 +111,8 @@ void radio_rx_end(ProtoViewApp* app) {
void radio_sleep(ProtoViewApp* app) {
furi_assert(app);
if(app->txrx->txrx_state == TxRxStateRx) {
- /* We can't go from having an active RX worker to sleeping.
- * Stop the RX subsystems first. */
+ /* Stop the asynchronous receiving system before putting the
+ * chip into sleep. */
radio_rx_end(app);
}
furi_hal_subghz_sleep();
@@ -127,8 +134,9 @@ void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder
furi_hal_subghz_idle();
uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency);
FURI_LOG_E(TAG, "Switched to frequency: %lu", value);
- furi_hal_gpio_write(&gpio_cc1101_g0, false);
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+ furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false);
+ furi_hal_gpio_init(
+ furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
furi_hal_subghz_start_async_tx(data_feeder, ctx);
while(!furi_hal_subghz_is_async_tx_complete()) furi_delay_ms(10);
@@ -149,7 +157,7 @@ void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder
void protoview_timer_isr(void* ctx) {
ProtoViewApp* app = ctx;
- bool level = furi_hal_gpio_read(&gpio_cc1101_g0);
+ bool level = furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin);
if(app->txrx->last_g0_value != level) {
uint32_t now = DWT->CYCCNT;
uint32_t dur = now - app->txrx->last_g0_change_time;
diff --git a/applications/plugins/protoview/appicon.png b/applications/plugins/protoview/appicon.png
index 7ce5c4eff..fa7122515 100644
Binary files a/applications/plugins/protoview/appicon.png and b/applications/plugins/protoview/appicon.png differ
diff --git a/applications/plugins/protoview/application.fam b/applications/plugins/protoview/application.fam
index d3614524c..22ad2d628 100644
--- a/applications/plugins/protoview/application.fam
+++ b/applications/plugins/protoview/application.fam
@@ -8,5 +8,5 @@ App(
stack_size=8 * 1024,
order=50,
fap_icon="appicon.png",
- fap_category="Tools",
+ fap_category="Sub-GHz",
)
diff --git a/applications/plugins/protoview/crc.c b/applications/plugins/protoview/crc.c
index 94d482972..e99786952 100644
--- a/applications/plugins/protoview/crc.c
+++ b/applications/plugins/protoview/crc.c
@@ -1,3 +1,6 @@
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license. */
+
#include
#include
@@ -17,3 +20,17 @@ uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly) {
}
return crc;
}
+
+/* Sum all the specified bytes modulo 256.
+ * Initialize the sum with 'init' (usually 0). */
+uint8_t sum_bytes(const uint8_t* data, size_t len, uint8_t init) {
+ for(size_t i = 0; i < len; i++) init += data[i];
+ return init;
+}
+
+/* Perform the bitwise xor of all the specified bytes.
+ * Initialize xor value with 'init' (usually 0). */
+uint8_t xor_bytes(const uint8_t* data, size_t len, uint8_t init) {
+ for(size_t i = 0; i < len; i++) init ^= data[i];
+ return init;
+}
diff --git a/applications/plugins/protoview/custom_presets.h b/applications/plugins/protoview/custom_presets.h
index 00aa49945..ef1c56a94 100644
--- a/applications/plugins/protoview/custom_presets.h
+++ b/applications/plugins/protoview/custom_presets.h
@@ -1,5 +1,4 @@
#include
-
/* ========================== DATA RATE SETTINGS ===============================
*
* This is how to configure registers MDMCFG3 and MDMCFG4.
@@ -131,8 +130,8 @@ static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = {
{CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz
{CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz
{CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync
- {CC1101_MDMCFG3, /*0x93*/ 0x32}, // Data rate is 10kBaud
- {CC1101_MDMCFG4, /*0x18*/ 0x17}, // Rx BW filter is 650.000kHz
+ {CC1101_MDMCFG3, 0x93}, // Data rate is 10kBaud
+ {CC1101_MDMCFG4, 0x18}, // Rx BW filter is 650.000kHz
/* Main Radio Control State Machine */
{CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)
@@ -164,8 +163,57 @@ static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = {
{0, 0},
{0, 0}};
+/* GFSK 19k dev, 325 Khz filter, 20kBaud. Different AGI settings.
+ * Works well with Toyota. */
+static uint8_t protoview_subghz_tpms3_gfsk_async_regs[][2] = {
+ /* GPIO GD0 */
+ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input
+
+ /* Frequency Synthesizer Control */
+ {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz
+
+ /* Packet engine */
+ {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening
+ {CC1101_PKTCTRL1, 0x04},
+
+ // // Modem Configuration
+ {CC1101_MDMCFG0, 0x00},
+ {CC1101_MDMCFG1, 0x02}, // 2 is the channel spacing exponet: not used
+ {CC1101_MDMCFG2, 0x10}, // GFSK without any other check
+ {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud
+ {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz
+ {CC1101_DEVIATN, 0x34}, // Deviation 19.04 Khz.
+
+ /* Main Radio Control State Machine */
+ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)
+
+ /* Frequency Offset Compensation Configuration */
+ {CC1101_FOCCFG,
+ 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
+
+ /* Automatic Gain Control */
+ {CC1101_AGCCTRL0, 0x80},
+ {CC1101_AGCCTRL1, 0x58},
+ {CC1101_AGCCTRL2, 0x87},
+
+ /* Wake on radio and timeouts control */
+ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours
+
+ /* Frontend configuration */
+ {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer
+ {CC1101_FREND1, 0x56},
+
+ /* End */
+ {0, 0},
+
+ /* CC1101 2FSK PATABLE. */
+ {0xC0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0}};
+
/* 40 KBaud, 2FSK, 28 kHz deviation, 270 Khz bandwidth filter. */
-static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = {
+static uint8_t protoview_subghz_40k_fsk_async_regs[][2] = {
/* GPIO GD0 */
{CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input
@@ -215,50 +263,55 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = {
{0, 0},
{0, 0}};
-/* FSK 19k dev, 325 Khz filter, 20kBaud. Works well with Toyota. */
-static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = {
+/* This is like the default Flipper OOK 640Khz bandwidth preset, but
+ * the bandwidth is changed to 40kBaud, in order to receive signals
+ * with a pulse width ~25us/30us. */
+static const uint8_t protoview_subghz_40k_ook_async_regs[][2] = {
/* GPIO GD0 */
{CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input
+ /* FIFO and internals */
+ {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION
+
+ /* Packet engine */
+ {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening
+
/* Frequency Synthesizer Control */
{CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz
- /* Packet engine */
- {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening
- {CC1101_PKTCTRL1, 0x04},
-
- // // Modem Configuration
- {CC1101_MDMCFG0, 0x00},
- {CC1101_MDMCFG1, 0x02}, // 2 is the channel spacing exponet: not used
- {CC1101_MDMCFG2, 0x10}, // GFSK without any other check
- {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud
- {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz
- {CC1101_DEVIATN, 0x34}, // Deviation 19.04 Khz.
+ // Modem Configuration
+ {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz
+ {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz
+ {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync
+ {CC1101_MDMCFG3, 0x93}, // Data rate is 40kBaud
+ {CC1101_MDMCFG4, 0x1A}, // Rx BW filter is 650.000kHz
/* Main Radio Control State Machine */
{CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)
/* Frequency Offset Compensation Configuration */
{CC1101_FOCCFG,
- 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
+ 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
/* Automatic Gain Control */
- {CC1101_AGCCTRL0, 0x80},
- {CC1101_AGCCTRL1, 0x58},
- {CC1101_AGCCTRL2, 0x87},
+ {CC1101_AGCCTRL0,
+ 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary
+ {CC1101_AGCCTRL1,
+ 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET
+ {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB
/* Wake on radio and timeouts control */
{CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours
/* Frontend configuration */
- {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer
- {CC1101_FREND1, 0x56},
+ {CC1101_FREND0, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1]
+ {CC1101_FREND1, 0xB6}, //
/* End */
{0, 0},
- /* CC1101 2FSK PATABLE. */
- {0xC0, 0},
+ /* CC1101 OOK PATABLE. */
+ {0, 0xC0},
{0, 0},
{0, 0},
{0, 0}};
diff --git a/applications/plugins/protoview/data_feed.c b/applications/plugins/protoview/data_feed.c
deleted file mode 100644
index 81d1a8020..000000000
--- a/applications/plugins/protoview/data_feed.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
- * See the LICENSE file for information about the license. */
-
-#include
-#include
-#include
-#include
-#include
-#include "raw_samples.h"
-
-#define TAG "PROTOVIEW-protocol"
-
-const SubGhzProtocol subghz_protocol_protoview;
-
-/* The feed() method puts data in the RawSamples global (protected by
- * a mutex). */
-extern RawSamplesBuffer* RawSamples;
-
-/* This is totally dummy: we just define the decoder base for the async
- * system to work but we don't really use it if not to collect raw
- * data via the feed() method. */
-typedef struct SubGhzProtocolDecoderprotoview {
- SubGhzProtocolDecoderBase base;
-} SubGhzProtocolDecoderprotoview;
-
-void* subghz_protocol_decoder_protoview_alloc(SubGhzEnvironment* environment) {
- UNUSED(environment);
-
- SubGhzProtocolDecoderprotoview* instance = malloc(sizeof(SubGhzProtocolDecoderprotoview));
- instance->base.protocol = &subghz_protocol_protoview;
- return instance;
-}
-
-void subghz_protocol_decoder_protoview_free(void* context) {
- furi_assert(context);
- SubGhzProtocolDecoderprotoview* instance = context;
- free(instance);
-}
-
-void subghz_protocol_decoder_protoview_reset(void* context) {
- furi_assert(context);
-}
-
-/* That's the only thig we really use of the protocol decoder
- * implementation. We avoid the subghz provided abstractions and put
- * the data in our simple abstraction: the RawSamples circular buffer. */
-void subghz_protocol_decoder_protoview_feed(void* context, bool level, uint32_t duration) {
- furi_assert(context);
- UNUSED(context);
-
- /* Add data to the circular buffer. */
- raw_samples_add(RawSamples, level, duration);
- // FURI_LOG_E(TAG, "FEED: %d %d", (int)level, (int)duration);
- return;
-}
-
-/* The only scope of this method is to avoid duplicated messages in the
- * Subghz history, which we don't use. */
-uint8_t subghz_protocol_decoder_protoview_get_hash_data(void* context) {
- furi_assert(context);
- return 123;
-}
-
-/* Not used. */
-bool subghz_protocol_decoder_protoview_serialize(
- void* context,
- FlipperFormat* flipper_format,
- SubGhzRadioPreset* preset) {
- UNUSED(context);
- UNUSED(flipper_format);
- UNUSED(preset);
- return false;
-}
-
-/* Not used. */
-bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) {
- UNUSED(context);
- UNUSED(flipper_format);
- return false;
-}
-
-void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) {
- furi_assert(context);
- furi_string_cat_printf(output, "Protoview");
-}
-
-const SubGhzProtocolDecoder subghz_protocol_protoview_decoder = {
- .alloc = subghz_protocol_decoder_protoview_alloc,
- .free = subghz_protocol_decoder_protoview_free,
- .reset = subghz_protocol_decoder_protoview_reset,
- .feed = subghz_protocol_decoder_protoview_feed,
- .get_hash_data = subghz_protocol_decoder_protoview_get_hash_data,
- .serialize = subghz_protocol_decoder_protoview_serialize,
- .deserialize = subghz_protocol_decoder_protoview_deserialize,
- .get_string = subhz_protocol_decoder_protoview_get_string,
-};
-
-/* Well, we don't really target a specific protocol. So let's put flags
- * that make sense. */
-const SubGhzProtocol subghz_protocol_protoview = {
- .name = "Protoview",
- .type = SubGhzProtocolTypeStatic,
- .flag = SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
- .decoder = &subghz_protocol_protoview_decoder,
-};
-
-/* Our table has just the single dummy protocol we defined for the
- * sake of data collection. */
-const SubGhzProtocol* protoview_protocol_registry_items[] = {
- &subghz_protocol_protoview,
-};
-
-const SubGhzProtocolRegistry protoview_protocol_registry = {
- .items = protoview_protocol_registry_items,
- .size = COUNT_OF(protoview_protocol_registry_items)};
diff --git a/applications/plugins/protoview/fields.c b/applications/plugins/protoview/fields.c
index 47d573f4f..18dee6502 100644
--- a/applications/plugins/protoview/fields.c
+++ b/applications/plugins/protoview/fields.c
@@ -314,11 +314,16 @@ void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, ui
fieldset_add_field(fs, f);
}
-/* Allocate and append a string field. */
-void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s) {
+/* Allocate and append a string field. The string 's' does not need to point
+ * to a null terminated string, but must have at least 'len' valid bytes
+ * starting from the pointer. The field object will be correctly null
+ * terminated. */
+void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s, size_t len) {
ProtoViewField* f = field_new(FieldTypeStr, name);
- f->str = strdup(s);
- f->len = strlen(s);
+ f->len = len;
+ f->str = malloc(len + 1);
+ memcpy(f->str, s, len);
+ f->str[len] = 0;
fieldset_add_field(fs, f);
}
diff --git a/applications/plugins/protoview/protocols/b4b1.c b/applications/plugins/protoview/protocols/b4b1.c
index 52c59d24b..6d0b8237a 100644
--- a/applications/plugins/protoview/protocols/b4b1.c
+++ b/applications/plugins/protoview/protocols/b4b1.c
@@ -1,4 +1,7 @@
-/* PT/SC remotes. Usually 443.92 Mhz OOK.
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * PT/SC remotes. Usually 443.92 Mhz OOK.
*
* This line code is used in many remotes such as Princeton chips
* named PT2262, Silian Microelectronics SC5262 and others.
@@ -11,10 +14,15 @@
static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
if(numbits < 30) return false;
- const char* sync_patterns[3] = {
- "10000000000000000000000000000001", /* 30 zero bits. */
- "100000000000000000000000000000001", /* 31 zero bits. */
- "1000000000000000000000000000000001", /* 32 zero bits. */
+
+ /* Test different pulse + gap + first byte possibilities. */
+ const char* sync_patterns[6] = {
+ "100000000000000000000000000000011101", /* 30 times gap + one. */
+ "100000000000000000000000000000010001", /* 30 times gap + zero. */
+ "1000000000000000000000000000000011101", /* 31 times gap + one. */
+ "1000000000000000000000000000000010001", /* 31 times gap + zero. */
+ "10000000000000000000000000000000011101", /* 32 times gap + one. */
+ "10000000000000000000000000000000010001", /* 32 times gap + zero. */
};
uint32_t off;
@@ -24,11 +32,11 @@ static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoView
if(off != BITMAP_SEEK_NOT_FOUND) break;
}
if(off == BITMAP_SEEK_NOT_FOUND) return false;
- if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu", off);
+ if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble id:%d at: %lu", j, off);
info->start_off = off;
- // Seek data setction. Why -1? Last bit is data.
- off += strlen(sync_patterns[j]) - 1;
+ // Seek data setction. Why -5? Last 5 half-bit-times are data.
+ off += strlen(sync_patterns[j]) - 5;
uint8_t d[3]; /* 24 bits of data. */
uint32_t decoded = convert_from_line_code(d, sizeof(d), bits, numbytes, off, "1000", "1110");
diff --git a/applications/plugins/protoview/protocols/keeloq.c b/applications/plugins/protoview/protocols/keeloq.c
index 298c690d4..c09cc35fc 100644
--- a/applications/plugins/protoview/protocols/keeloq.c
+++ b/applications/plugins/protoview/protocols/keeloq.c
@@ -1,4 +1,7 @@
-/* Microchip HCS200/HCS300/HSC301 KeeLoq, rolling code remotes.
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * Microchip HCS200/HCS300/HSC301 KeeLoq, rolling code remotes.
*
* Usually 443.92 Mhz OOK, ~200us or ~400us pulse len, depending
* on the configuration.
diff --git a/applications/plugins/protoview/protocols/oregon2.c b/applications/plugins/protoview/protocols/oregon2.c
index f67e85a2d..f7b123deb 100644
--- a/applications/plugins/protoview/protocols/oregon2.c
+++ b/applications/plugins/protoview/protocols/oregon2.c
@@ -1,4 +1,7 @@
-/* Oregon remote termometers. Usually 443.92 Mhz OOK.
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * Oregon remote termometers. Usually 443.92 Mhz OOK.
*
* The protocol is described here:
* https://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf
diff --git a/applications/plugins/protoview/protocols/pvchat.c b/applications/plugins/protoview/protocols/pvchat.c
new file mode 100644
index 000000000..779248c5b
--- /dev/null
+++ b/applications/plugins/protoview/protocols/pvchat.c
@@ -0,0 +1,205 @@
+#include "../app.h"
+
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * ----------------------------------------------------------
+ * ProtoView chat protocol. This is just a fun test protocol
+ * that can be used between two Flippers in order to send
+ * and receive text messages.
+ * ----------------------------------------------------------
+ *
+ * Protocol description
+ * ====================
+ *
+ * The protocol works with different data rates. However here is defined
+ * to use a short pulse/gap duration of 300us and a long pulse/gap
+ * duration of 600us. Even with the Flipper hardware, the protocol works
+ * with 100/200us, but becomes less reliable and standard presets can't
+ * be used because of the higher data rate.
+ *
+ * In the following description we have that:
+ *
+ * "1" represents a pulse of one-third bit time (300us)
+ * "0" represents a gap of one-third bit time (300us)
+ *
+ * The message starts with a preamble + a sync pattern:
+ *
+ * preamble = 1010101010101010 x 3
+ * sync = 1100110011001010
+ *
+ * The a variable amount of bytes follow, where each bit
+ * is encoded in the following way:
+ *
+ * zero 100 (300 us pulse, 600 us gap)
+ * one 110 (600 us pulse, 300 us gap)
+ *
+ * Bytes are sent MSB first, so receiving, in sequence, bits
+ * 11100001, means byte E1.
+ *
+ * This is the data format:
+ *
+ * +--+------+-------+--+--+--+
+ * |SL|Sender|Message|FF|AA|CS|
+ * +--+------+-------+--+--+--+
+ * | | |
+ * | | \_ N bytes of message terminated by FF AA + 1 byte of checksum
+ * | |
+ * | \_ SL bytes of sender name
+ * \
+ * \_ 1 byte of sender len, 8 bit unsigned integer.
+ *
+ *
+ * Checksum = sum of bytes modulo 256, with checksum set
+ * to 0 for the computation.
+ *
+ * Design notes
+ * ============
+ *
+ * The protocol is designed in order to have certain properties:
+ *
+ * 1. Pulses and gaps can only be 100 or 200 microseconds, so the
+ * message can be described, encoded and decoded with only two
+ * fixed durations.
+ *
+ * 2. The preamble + sync is designed to have a well recognizable
+ * pattern that can't be reproduced just for accident inside
+ * the encoded pattern. There is no combinatio of encoded bits
+ * leading to the preamble+sync. Also the sync pattern final
+ * part can't be mistaken for actual bits of data, since it
+ * contains alternating short pulses/gaps at 100us.
+ *
+ * 3. Data encoding wastes some bandwidth in order to be more
+ * robust. Even so, with a 300us clock period, a single bit
+ * bit takes 900us, reaching a data transfer of 138 characters per
+ * second. More than enough for the simple chat we have here.
+ */
+
+static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
+ const char* sync_pattern = "1010101010101010" // Preamble
+ "1100110011001010"; // Sync
+ uint8_t sync_len = 32;
+
+ /* This is a variable length message, however the minimum length
+ * requires a sender len byte (of value zero) and the terminator
+ * FF 00 plus checksum: a total of 4 bytes. */
+ if(numbits - sync_len < 8 * 4) return false;
+
+ uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern);
+ if(off == BITMAP_SEEK_NOT_FOUND) return false;
+ FURI_LOG_E(TAG, "Chat preamble+sync found");
+
+ /* If there is room on the left, let's mark the start of the message
+ * a bit before: we don't try to detect all the preamble, but only
+ * the first part, however it is likely present. */
+ if(off >= 16) {
+ off -= 16;
+ sync_len += 16;
+ }
+
+ info->start_off = off;
+ off += sync_len; /* Skip preamble and sync. */
+
+ uint8_t raw[64] = {(uint8_t)'.'};
+ uint32_t decoded =
+ convert_from_line_code(raw, sizeof(raw), bits, numbytes, off, "100", "110"); /* PWM */
+ FURI_LOG_E(TAG, "Chat decoded bits: %lu", decoded);
+
+ if(decoded < 8 * 4) return false; /* Min message len. */
+
+ // The message needs to have a two bytes terminator before
+ // the checksum.
+ uint32_t j;
+ for(j = 0; j < sizeof(raw) - 1; j++)
+ if(raw[j] == 0xff && raw[j + 1] == 0xaa) break;
+
+ if(j == sizeof(raw) - 1) {
+ FURI_LOG_E(TAG, "Chat: terminator not found");
+ return false; // No terminator found.
+ }
+
+ uint32_t datalen = j + 3; // If the terminator was found at j, then
+ // we need to sum three more bytes to have
+ // the len: FF itself, AA, checksum.
+ info->pulses_count = sync_len + 8 * 3 * datalen;
+
+ // Check if the control sum matches.
+ if(sum_bytes(raw, datalen - 1, 0) != raw[datalen - 1]) {
+ FURI_LOG_E(TAG, "Chat: checksum mismatch");
+ return false;
+ }
+
+ // Check if the length of the sender looks sane
+ uint8_t senderlen = raw[0];
+ if(senderlen >= sizeof(raw)) {
+ FURI_LOG_E(TAG, "Chat: invalid sender length");
+ return false; // Overflow
+ }
+
+ fieldset_add_str(info->fieldset, "sender", (char*)raw + 1, senderlen);
+ fieldset_add_str(
+ info->fieldset, "message", (char*)raw + 1 + senderlen, datalen - senderlen - 4);
+ return true;
+}
+
+/* Give fields and defaults for the signal creator. */
+static void get_fields(ProtoViewFieldSet* fieldset) {
+ fieldset_add_str(fieldset, "sender", "Carol", 5);
+ fieldset_add_str(fieldset, "message", "Anyone hearing?", 15);
+}
+
+/* Create a signal. */
+static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fs) {
+ uint32_t te = 300; /* Short pulse duration in microseconds.
+ Our protocol needs three symbol times to send
+ a bit, so 300 us per bit = 3.33 kBaud. */
+
+ // Preamble: 24 alternating 300us pulse/gap pairs.
+ for(int j = 0; j < 24; j++) {
+ raw_samples_add(samples, true, te);
+ raw_samples_add(samples, false, te);
+ }
+
+ // Sync: 3 alternating 600 us pulse/gap pairs.
+ for(int j = 0; j < 3; j++) {
+ raw_samples_add(samples, true, te * 2);
+ raw_samples_add(samples, false, te * 2);
+ }
+
+ // Sync: plus 2 alternating 300 us pluse/gap pairs.
+ for(int j = 0; j < 2; j++) {
+ raw_samples_add(samples, true, te);
+ raw_samples_add(samples, false, te);
+ }
+
+ // Data: build the array.
+ uint32_t datalen = 1 + fs->fields[0]->len + // Userlen + Username
+ fs->fields[1]->len + 3; // Message + FF + 00 + CRC
+ uint8_t *data = malloc(datalen), *p = data;
+ *p++ = fs->fields[0]->len;
+ memcpy(p, fs->fields[0]->str, fs->fields[0]->len);
+ p += fs->fields[0]->len;
+ memcpy(p, fs->fields[1]->str, fs->fields[1]->len);
+ p += fs->fields[1]->len;
+ *p++ = 0xff;
+ *p++ = 0xaa;
+ *p = sum_bytes(data, datalen - 1, 0);
+
+ // Emit bits
+ for(uint32_t j = 0; j < datalen * 8; j++) {
+ if(bitmap_get(data, datalen, j)) {
+ raw_samples_add(samples, true, te * 2);
+ raw_samples_add(samples, false, te);
+ } else {
+ raw_samples_add(samples, true, te);
+ raw_samples_add(samples, false, te * 2);
+ }
+ }
+ free(data);
+}
+
+ProtoViewDecoder ProtoViewChatDecoder = {
+ .name = "ProtoView chat",
+ .decode = decode,
+ .get_fields = get_fields,
+ .build_message = build_message};
diff --git a/applications/plugins/protoview/protocols/tpms/citroen.c b/applications/plugins/protoview/protocols/tpms/citroen.c
index ecd8fb983..a86e5f7d6 100644
--- a/applications/plugins/protoview/protocols/tpms/citroen.c
+++ b/applications/plugins/protoview/protocols/tpms/citroen.c
@@ -1,4 +1,7 @@
-/* Citroen TPMS. Usually 443.92 Mhz FSK.
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * Citroen TPMS. Usually 443.92 Mhz FSK.
*
* Preamble of ~14 high/low 52 us pulses
* Sync of high 100us pulse then 50us low
diff --git a/applications/plugins/protoview/protocols/tpms/ford.c b/applications/plugins/protoview/protocols/tpms/ford.c
index 3816e72f9..066332590 100644
--- a/applications/plugins/protoview/protocols/tpms/ford.c
+++ b/applications/plugins/protoview/protocols/tpms/ford.c
@@ -1,4 +1,7 @@
-/* Ford tires TPMS. Usually 443.92 Mhz FSK (in Europe).
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * Ford tires TPMS. Usually 443.92 Mhz FSK (in Europe).
*
* 52 us short pules
* Preamble: 0101010101010101010101010101
diff --git a/applications/plugins/protoview/protocols/tpms/renault.c b/applications/plugins/protoview/protocols/tpms/renault.c
index 3d8fc43d5..0755a515f 100644
--- a/applications/plugins/protoview/protocols/tpms/renault.c
+++ b/applications/plugins/protoview/protocols/tpms/renault.c
@@ -1,4 +1,7 @@
-/* Renault tires TPMS. Usually 443.92 Mhz FSK.
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * Renault tires TPMS. Usually 443.92 Mhz FSK.
*
* Preamble + sync + Manchester bits. ~48us short pulse.
* 9 Bytes in total not counting the preamble. */
diff --git a/applications/plugins/protoview/protocols/tpms/schrader.c b/applications/plugins/protoview/protocols/tpms/schrader.c
index 7dc85a2cb..2b53d313c 100644
--- a/applications/plugins/protoview/protocols/tpms/schrader.c
+++ b/applications/plugins/protoview/protocols/tpms/schrader.c
@@ -1,4 +1,7 @@
-/* Schrader TPMS. Usually 443.92 Mhz OOK, 120us pulse len.
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * Schrader TPMS. Usually 443.92 Mhz OOK, 120us pulse len.
*
* 500us high pulse + Preamble + Manchester coded bits where:
* 1 = 10
diff --git a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c
index 45accf1a1..b186f57b1 100644
--- a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c
+++ b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c
@@ -1,4 +1,7 @@
-/* Schrader variant EG53MA4 TPMS.
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * Schrader variant EG53MA4 TPMS.
* Usually 443.92 Mhz OOK, 100us pulse len.
*
* Preamble: alternating pulse/gap, 100us.
diff --git a/applications/plugins/protoview/protocols/tpms/toyota.c b/applications/plugins/protoview/protocols/tpms/toyota.c
index b80af7647..3a02447b7 100644
--- a/applications/plugins/protoview/protocols/tpms/toyota.c
+++ b/applications/plugins/protoview/protocols/tpms/toyota.c
@@ -1,4 +1,7 @@
-/* Toyota tires TPMS. Usually 443.92 Mhz FSK (In Europe).
+/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * Toyota tires TPMS. Usually 443.92 Mhz FSK (In Europe).
*
* Preamble + sync + 64 bits of data. ~48us short pulse length.
*
diff --git a/applications/plugins/protoview/protocols/unknown.c b/applications/plugins/protoview/protocols/unknown.c
new file mode 100644
index 000000000..9e2f4a893
--- /dev/null
+++ b/applications/plugins/protoview/protocols/unknown.c
@@ -0,0 +1,326 @@
+#include "../app.h"
+
+/* Copyright (C) 2023 Salvatore Sanfilippo -- All Rights Reserved
+ * See the LICENSE file for information about the license.
+ *
+ * ----------------------------------------------------------------------------
+ * The "unknown" decoder fires as the last one, once we are sure no other
+ * decoder was able to identify the signal. The goal is to detect the
+ * preamble and line code used in the received signal, then turn the
+ * decoded bits into bytes.
+ *
+ * The techniques used for the detection are described in the comments
+ * below.
+ * ----------------------------------------------------------------------------
+ */
+
+/* Scan the signal bitmap looking for a PWM modulation. In this case
+ * for PWM we are referring to two exact patterns of high and low
+ * signal (each bit in the bitmap is worth the smallest gap/pulse duration
+ * we detected) that repeat each other in a given segment of the message.
+ *
+ * This modulation is quite common, for instance sometimes zero and
+ * one are rappresented by a 700us pulse followed by 350 gap,
+ * and 350us pulse followed by a 700us gap. So the signal bitmap received
+ * by the decoder would contain 110 and 100 symbols.
+ *
+ * The way this function work is commented inline.
+ *
+ * The function returns the number of consecutive symbols found, having
+ * a symbol length of 'symlen' (3 in the above example), and stores
+ * in *s1i the offset of the first symbol found, and in *s2i the offset
+ * of the second symbol. The function can't tell which is one and which
+ * zero. */
+static uint32_t find_pwm(
+ uint8_t* bits,
+ uint32_t numbytes,
+ uint32_t numbits,
+ uint32_t symlen,
+ uint32_t* s1i,
+ uint32_t* s2i) {
+ uint32_t best_count = 0; /* Max number of symbols found in this try. */
+ uint32_t best_idx1 = 0; /* First symbol offset of longest sequence found.
+ * This is also the start sequence offset. */
+ uint32_t best_idx2 = 0; /* Second symbol offset. */
+
+ /* Try all the possible symbol offsets that are less of our
+ * symbol len. This is likely not really useful but we take
+ * a conservative approach. Because if have have, for instance,
+ * repeating symbols "100" and "110", they will form a sequence
+ * that is choerent at different offsets, but out-of-sync.
+ *
+ * Anyway at the end of the function we try to fix the sync. */
+ for(uint32_t off = 0; off < symlen; off++) {
+ uint32_t c = 0; // Number of contiguous symbols found.
+ uint32_t c1 = 0, c2 = 0; // Occurrences of first/second symbol.
+ *s1i = off; // Assume we start at one symbol boundaty.
+ *s2i = UINT32_MAX; // Second symbol first index still unknown.
+ uint32_t next = off;
+
+ /* We scan the whole bitmap in one pass, resetting the state
+ * each time we find a pattern that is not one of the two
+ * symbols we found so far. */
+ while(next < numbits - symlen) {
+ bool match1 = bitmap_match_bitmap(bits, numbytes, next, bits, numbytes, *s1i, symlen);
+ if(!match1 && *s2i == UINT32_MAX) {
+ /* It's not the first sybol. We don't know how the
+ * second look like. Assume we found an occurrence of
+ * the second symbol. */
+ *s2i = next;
+ }
+
+ bool match2 = bitmap_match_bitmap(bits, numbytes, next, bits, numbytes, *s2i, symlen);
+
+ /* One or the other should match. */
+ if(match1 || match2) {
+ c++;
+ if(match1) c1++;
+ if(match2) c2++;
+ if(c > best_count && c1 >= best_count / 5 && // Require enough presence of both
+ c2 >= best_count / 5) // zero and one.
+ {
+ best_count = c;
+ best_idx1 = *s1i;
+ best_idx2 = *s2i;
+ }
+ next += symlen;
+ } else {
+ /* No match. Continue resetting the signal info. */
+ c = 0; // Start again to count contiguous symbols.
+ c1 = 0;
+ c2 = 0;
+ *s1i = next; // First symbol always at start.
+ *s2i = UINT32_MAX; // Second symbol unknown.
+ }
+ }
+ }
+
+ /* We don't know if we are really synchronized with the bits at this point.
+ * For example if zero bit is 100 and one bit is 110 in a specific
+ * line code, our detector could randomly believe it's 001 and 101.
+ * However PWD line codes normally start with a pulse in both symbols.
+ * If that is the case, let's align. */
+ uint32_t shift;
+ for(shift = 0; shift < symlen; shift++) {
+ if(bitmap_get(bits, numbytes, best_idx1 + shift) &&
+ bitmap_get(bits, numbytes, best_idx2 + shift))
+ break;
+ }
+ if(shift != symlen) {
+ best_idx1 += shift;
+ best_idx2 += shift;
+ }
+
+ *s1i = best_idx1;
+ *s2i = best_idx2;
+ return best_count;
+}
+
+/* Find the longest sequence that looks like Manchester coding.
+ *
+ * Manchester coding requires each pairs of bits to be either
+ * 01 or 10. We'll have to try odd and even offsets to be
+ * sure to find it.
+ *
+ * Note that this will also detect differential Manchester, but
+ * will report it as Manchester. I can't think of any way to
+ * distinguish between the two line codes, because shifting them
+ * one symbol will make one to look like the other.
+ *
+ * Only option could be to decode the message with both line
+ * codes and use statistical properties (common byte values)
+ * to determine what's more likely, but this looks very fragile.
+ *
+ * Fortunately differential Manchester is more rarely used,
+ * so we can assume Manchester most of the times. Yet we are left
+ * with the indetermination about zero being pulse-gap or gap-pulse
+ * or the other way around.
+ *
+ * If the 'only_raising' parameter is true, the function detects
+ * only sequences going from gap to pulse: this is useful in order
+ * to locate preambles of alternating gaps and pulses. */
+static uint32_t find_alternating_bits(
+ uint8_t* bits,
+ uint32_t numbytes,
+ uint32_t numbits,
+ uint32_t* start,
+ bool only_raising) {
+ uint32_t best_count = 0; // Max number of symbols found
+ uint32_t best_off = 0; // Max symbols start offset.
+ for(int odd = 0; odd < 2; odd++) {
+ uint32_t count = 0; // Symbols found so far
+ uint32_t start_off = odd;
+ uint32_t j = odd;
+ while(j < numbits - 1) {
+ bool bit1 = bitmap_get(bits, numbytes, j);
+ bool bit2 = bitmap_get(bits, numbytes, j + 1);
+ if((!only_raising && bit1 != bit2) || (only_raising && !bit1 && bit2)) {
+ count++;
+ if(count > best_count) {
+ best_count = count;
+ best_off = start_off;
+ }
+ } else {
+ /* End of sequence. Continue with the next
+ * part of the signal. */
+ count = 0;
+ start_off = j + 2;
+ }
+ j += 2;
+ }
+ }
+ *start = best_off;
+ return best_count;
+}
+
+/* Wrapper to find Manchester code. */
+static uint32_t
+ find_manchester(uint8_t* bits, uint32_t numbytes, uint32_t numbits, uint32_t* start) {
+ return find_alternating_bits(bits, numbytes, numbits, start, false);
+}
+
+/* Wrapper to find preamble sections. */
+static uint32_t
+ find_preamble(uint8_t* bits, uint32_t numbytes, uint32_t numbits, uint32_t* start) {
+ return find_alternating_bits(bits, numbytes, numbits, start, true);
+}
+
+typedef enum {
+ LineCodeNone,
+ LineCodeManchester,
+ LineCodePWM3,
+ LineCodePWM4,
+} LineCodeGuess;
+
+static char* get_linecode_name(LineCodeGuess lc) {
+ switch(lc) {
+ case LineCodeNone:
+ return "none";
+ case LineCodeManchester:
+ return "Manchester";
+ case LineCodePWM3:
+ return "PWM3";
+ case LineCodePWM4:
+ return "PWM4";
+ }
+ return "unknown";
+}
+
+static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) {
+ /* No decoder was able to detect this message. Let's try if we can
+ * find some structure. To start, we'll see if it looks like is
+ * manchester coded, or PWM with symbol len of 3 or 4. */
+
+ /* For PWM, start1 and start2 are the offsets at which the two
+ * sequences composing the message appear the first time.
+ * So start1 is also the message start offset. Start2 is not used
+ * for Manchester, that does not have two separated symbols like
+ * PWM. */
+ uint32_t start1 = 0, start2 = 0;
+ uint32_t msgbits; // Number of message bits in the bitmap, so
+ // this will be the number of symbols, not actual
+ // bits after the message is decoded.
+ uint32_t tmp1, tmp2; // Temp vars to store the start.
+ uint32_t minbits = 16; // Less than that gets undetected.
+ uint32_t pwm_len; // Bits per symbol, in the case of PWM.
+ LineCodeGuess linecode = LineCodeNone;
+
+ // Try PWM3
+ uint32_t pwm3_bits = find_pwm(bits, numbytes, numbits, 3, &tmp1, &tmp2);
+ if(pwm3_bits >= minbits) {
+ linecode = LineCodePWM3;
+ start1 = tmp1;
+ start2 = tmp2;
+ pwm_len = 3;
+ msgbits = pwm3_bits * pwm_len;
+ }
+
+ // Try PWM4
+ uint32_t pwm4_bits = find_pwm(bits, numbytes, numbits, 4, &tmp1, &tmp2);
+ if(pwm4_bits >= minbits && pwm4_bits > pwm3_bits) {
+ linecode = LineCodePWM4;
+ start1 = tmp1;
+ start2 = tmp2;
+ pwm_len = 4;
+ msgbits = pwm3_bits * pwm_len;
+ }
+
+ // Try Manchester
+ uint32_t manchester_bits = find_manchester(bits, numbytes, numbits, &tmp1);
+ if(manchester_bits > minbits && manchester_bits > pwm3_bits && manchester_bits > pwm4_bits) {
+ linecode = LineCodeManchester;
+ start1 = tmp1;
+ msgbits = manchester_bits * 2;
+ FURI_LOG_E(TAG, "MANCHESTER START: %lu", tmp1);
+ }
+
+ if(linecode == LineCodeNone) return false;
+
+ /* Often there is a preamble before the signal. We'll try to find
+ * it, and if it is not too far away from our signal, we'll claim
+ * our signal starts at the preamble. */
+ uint32_t preamble_len = find_preamble(bits, numbytes, numbits, &tmp1);
+ uint32_t min_preamble_len = 10;
+ uint32_t max_preamble_distance = 32;
+ uint32_t preamble_start = 0;
+ bool preamble_found = false;
+
+ /* Note that because of the following checks, if the Manchester detector
+ * detected the preamble bits as data, we are ok with that, since it
+ * means that the synchronization is not designed to "break" the bits
+ * flow. In this case we ignore the preamble and return all as data. */
+ if(preamble_len >= min_preamble_len && // Not too short.
+ tmp1 < start1 && // Should be before the data.
+ start1 - tmp1 <= max_preamble_distance) // Not too far.
+ {
+ preamble_start = tmp1;
+ preamble_found = true;
+ }
+
+ info->start_off = preamble_found ? preamble_start : start1;
+ info->pulses_count = (start1 + msgbits) - info->start_off;
+ info->pulses_count += 20; /* Add a few more, so that if the user resends
+ * the message, it is more likely we will
+ * transfer all that is needed, like a message
+ * terminator (that we don't detect). */
+
+ if(preamble_found) FURI_LOG_E(TAG, "PREAMBLE AT: %lu", preamble_start);
+ FURI_LOG_E(TAG, "START: %lu", info->start_off);
+ FURI_LOG_E(TAG, "MSGBITS: %lu", msgbits);
+ FURI_LOG_E(TAG, "DATASTART: %lu", start1);
+ FURI_LOG_E(TAG, "PULSES: %lu", info->pulses_count);
+
+ /* We think there is a message and we know where it starts and the
+ * line code used. We can turn it into bits and bytes. */
+ uint32_t decoded;
+ uint8_t data[32];
+ uint32_t datalen;
+
+ char symbol1[5], symbol2[5];
+ if(linecode == LineCodePWM3 || linecode == LineCodePWM4) {
+ bitmap_to_string(symbol1, bits, numbytes, start1, pwm_len);
+ bitmap_to_string(symbol2, bits, numbytes, start2, pwm_len);
+ } else if(linecode == LineCodeManchester) {
+ memcpy(symbol1, "01", 3);
+ memcpy(symbol2, "10", 3);
+ }
+
+ decoded = convert_from_line_code(data, sizeof(data), bits, numbytes, start1, symbol1, symbol2);
+ datalen = (decoded + 7) / 8;
+
+ char* linecode_name = get_linecode_name(linecode);
+ fieldset_add_str(info->fieldset, "line code", linecode_name, strlen(linecode_name));
+ fieldset_add_uint(info->fieldset, "data bits", decoded, 8);
+ if(preamble_found) fieldset_add_uint(info->fieldset, "preamble len", preamble_len, 8);
+ fieldset_add_str(info->fieldset, "first symbol", symbol1, strlen(symbol1));
+ fieldset_add_str(info->fieldset, "second symbol", symbol2, strlen(symbol2));
+ for(uint32_t j = 0; j < datalen; j++) {
+ char label[16];
+ snprintf(label, sizeof(label), "data[%lu]", j);
+ fieldset_add_bytes(info->fieldset, label, data + j, 2);
+ }
+ return true;
+}
+
+ProtoViewDecoder UnknownDecoder =
+ {.name = "Unknown", .decode = decode, .get_fields = NULL, .build_message = NULL};
diff --git a/applications/plugins/protoview/signal.c b/applications/plugins/protoview/signal.c
index a1c4b2b8f..71fbe823f 100644
--- a/applications/plugins/protoview/signal.c
+++ b/applications/plugins/protoview/signal.c
@@ -5,6 +5,43 @@
bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info);
+/* =============================================================================
+ * Protocols table.
+ *
+ * Supported protocols go here, with the relevant implementation inside
+ * protocols/.c
+ * ===========================================================================*/
+
+extern ProtoViewDecoder Oregon2Decoder;
+extern ProtoViewDecoder B4B1Decoder;
+extern ProtoViewDecoder RenaultTPMSDecoder;
+extern ProtoViewDecoder ToyotaTPMSDecoder;
+extern ProtoViewDecoder SchraderTPMSDecoder;
+extern ProtoViewDecoder SchraderEG53MA4TPMSDecoder;
+extern ProtoViewDecoder CitroenTPMSDecoder;
+extern ProtoViewDecoder FordTPMSDecoder;
+extern ProtoViewDecoder KeeloqDecoder;
+extern ProtoViewDecoder ProtoViewChatDecoder;
+extern ProtoViewDecoder UnknownDecoder;
+
+ProtoViewDecoder* Decoders[] = {
+ &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */
+ &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */
+ &RenaultTPMSDecoder, /* Renault TPMS. */
+ &ToyotaTPMSDecoder, /* Toyota TPMS. */
+ &SchraderTPMSDecoder, /* Schrader TPMS. */
+ &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */
+ &CitroenTPMSDecoder, /* Citroen TPMS. */
+ &FordTPMSDecoder, /* Ford TPMS. */
+ &KeeloqDecoder, /* Keeloq remote. */
+ &ProtoViewChatDecoder, /* Protoview simple text messages. */
+
+ /* Warning: the following decoder must stay at the end of the
+ * list. Otherwise would detect most signals and prevent the actaul
+ * decoders from handling them. */
+ &UnknownDecoder, /* General protocol detector. */
+ NULL};
+
/* =============================================================================
* Raw signal detection
* ===========================================================================*/
@@ -39,23 +76,28 @@ void reset_current_signal(ProtoViewApp* app) {
* For instance Oregon2 sensors, in the case of protocol 2.1 will send
* pulses of ~400us (RF on) VS ~580us (RF off). */
#define SEARCH_CLASSES 3
-uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) {
+uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx, uint32_t min_duration) {
struct {
uint32_t dur[2]; /* dur[0] = low, dur[1] = high */
uint32_t count[2]; /* Associated observed frequency. */
} classes[SEARCH_CLASSES];
memset(classes, 0, sizeof(classes));
- uint32_t minlen = 30, maxlen = 4000; /* Depends on data rate, here we
- allow for high and low. */
+
+ // Set a min/max duration limit for samples to be considered part of a
+ // coherent signal. The maximum length is fixed while the minimum
+ // is passed as argument, as depends on the data rate and in general
+ // on the signal to analyze.
+ uint32_t max_duration = 4000;
+
uint32_t len = 0; /* Observed len of coherent samples. */
s->short_pulse_dur = 0;
- for(uint32_t j = idx; j < idx + 500; j++) {
+ for(uint32_t j = idx; j < idx + s->total; j++) {
bool level;
uint32_t dur;
raw_samples_get(s, j, &level, &dur);
- if(dur < minlen || dur > maxlen) break; /* return. */
+ if(dur < min_duration || dur > max_duration) break; /* return. */
/* Let's see if it matches a class we already have or if we
* can populate a new (yet empty) class. */
@@ -142,7 +184,7 @@ void notify_signal_detected(ProtoViewApp* app, bool decoded) {
* in order to find a coherent signal. If a signal that does not appear to
* be just noise is found, it is set in DetectedSamples global signal
* buffer, that is what is rendered on the screen. */
-void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) {
+void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source, uint32_t min_duration) {
/* We need to work on a copy: the source buffer may be populated
* by the background thread receiving data. */
RawSamplesBuffer* copy = raw_samples_alloc();
@@ -157,7 +199,7 @@ void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) {
uint32_t i = 0;
while(i < copy->total - 1) {
- uint32_t thislen = search_coherent_signal(copy, i);
+ uint32_t thislen = search_coherent_signal(copy, i, min_duration);
/* For messages that are long enough, attempt decoding. */
if(thislen > minlen) {
@@ -179,8 +221,11 @@ void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) {
/* Accept this signal as the new signal if either it's longer
* than the previous undecoded one, or the previous one was
* unknown and this is decoded. */
- if((thislen > app->signal_bestlen && app->signal_decoded == false) ||
- (app->signal_decoded == false && decoded)) {
+ bool oldsignal_not_decoded = app->signal_decoded == false ||
+ app->msg_info->decoder == &UnknownDecoder;
+
+ if(oldsignal_not_decoded &&
+ (thislen > app->signal_bestlen || (decoded && info->decoder != &UnknownDecoder))) {
free_msg_info(app->msg_info);
app->msg_info = info;
app->signal_bestlen = thislen;
@@ -194,7 +239,7 @@ void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) {
DetectedSamples->short_pulse_dur);
adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur);
- notify_signal_detected(app, decoded);
+ if(app->msg_info->decoder != &UnknownDecoder) notify_signal_detected(app, decoded);
} else {
/* If the structure was not filled, discard it. Otherwise
* now the owner is app->msg_info. */
@@ -387,6 +432,33 @@ uint32_t bitmap_seek_bits(
return BITMAP_SEEK_NOT_FOUND;
}
+/* Compare bitmaps b1 and b2 (possibly overlapping or the same bitmap),
+ * at the specified offsets, for cmplen bits. Returns true if the
+ * exact same bits are found, otherwise false. */
+bool bitmap_match_bitmap(
+ uint8_t* b1,
+ uint32_t b1len,
+ uint32_t b1off,
+ uint8_t* b2,
+ uint32_t b2len,
+ uint32_t b2off,
+ uint32_t cmplen) {
+ for(uint32_t j = 0; j < cmplen; j++) {
+ bool bit1 = bitmap_get(b1, b1len, b1off + j);
+ bool bit2 = bitmap_get(b2, b2len, b2off + j);
+ if(bit1 != bit2) return false;
+ }
+ return true;
+}
+
+/* Convert 'len' bitmap bits of the bitmap 'bitmap' into a null terminated
+ * string, stored at 'dst', that must have space at least for len+1 bytes.
+ * The bits are extracted from the specified offset. */
+void bitmap_to_string(char* dst, uint8_t* b, uint32_t blen, uint32_t off, uint32_t len) {
+ for(uint32_t j = 0; j < len; j++) dst[j] = bitmap_get(b, blen, off + j) ? '1' : '0';
+ dst[len] = 0;
+}
+
/* Set the pattern 'pat' into the bitmap 'b' of max length 'blen' bytes,
* starting from the specified offset.
*
@@ -503,7 +575,7 @@ uint32_t convert_from_line_code(
}
/* Convert the differential Manchester code to bits. This is similar to
- * convert_from_line_code() but specific for Manchester. The user must
+ * convert_from_line_code() but specific for diff-Manchester. The user must
* supply the value of the previous symbol before this stream, since
* in differential codings the next bits depend on the previous one.
*
@@ -528,31 +600,6 @@ uint32_t convert_from_diff_manchester(
return decoded;
}
-/* Supported protocols go here, with the relevant implementation inside
- * protocols/.c */
-
-extern ProtoViewDecoder Oregon2Decoder;
-extern ProtoViewDecoder B4B1Decoder;
-extern ProtoViewDecoder RenaultTPMSDecoder;
-extern ProtoViewDecoder ToyotaTPMSDecoder;
-extern ProtoViewDecoder SchraderTPMSDecoder;
-extern ProtoViewDecoder SchraderEG53MA4TPMSDecoder;
-extern ProtoViewDecoder CitroenTPMSDecoder;
-extern ProtoViewDecoder FordTPMSDecoder;
-extern ProtoViewDecoder KeeloqDecoder;
-
-ProtoViewDecoder* Decoders[] = {
- &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */
- &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */
- &RenaultTPMSDecoder, /* Renault TPMS. */
- &ToyotaTPMSDecoder, /* Toyota TPMS. */
- &SchraderTPMSDecoder, /* Schrader TPMS. */
- &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */
- &CitroenTPMSDecoder, /* Citroen TPMS. */
- &FordTPMSDecoder, /* Ford TPMS. */
- &KeeloqDecoder, /* Keeloq remote. */
- NULL};
-
/* Free the message info and allocated data. */
void free_msg_info(ProtoViewMsgInfo* i) {
if(i == NULL) return;
diff --git a/applications/plugins/protoview/view_build.c b/applications/plugins/protoview/view_build.c
index 955855902..57e6e4fbc 100644
--- a/applications/plugins/protoview/view_build.c
+++ b/applications/plugins/protoview/view_build.c
@@ -205,7 +205,7 @@ static void process_input_set_fields(ProtoViewApp* app, InputEvent input) {
privdata->decoder->build_message(rs, privdata->fieldset);
app->signal_decoded = false; // So that the new signal will be
// accepted as the current signal.
- scan_for_signal(app, rs);
+ scan_for_signal(app, rs, 5);
raw_samples_free(rs);
ui_show_alert(app, "Done: press back key", 3000);
}
diff --git a/applications/plugins/protoview/view_direct_sampling.c b/applications/plugins/protoview/view_direct_sampling.c
index 1ab90f096..0268e5297 100644
--- a/applications/plugins/protoview/view_direct_sampling.c
+++ b/applications/plugins/protoview/view_direct_sampling.c
@@ -2,63 +2,161 @@
* See the LICENSE file for information about the license. */
#include "app.h"
-
#include
+static void direct_sampling_timer_start(ProtoViewApp* app);
+static void direct_sampling_timer_stop(ProtoViewApp* app);
+
+#define CAPTURED_BITMAP_BITS (128 * 64)
+#define CAPTURED_BITMAP_BYTES (CAPTURED_BITMAP_BITS / 8)
+#define DEFAULT_USEC_PER_PIXEL 50
+#define USEC_PER_PIXEL_SMALL_CHANGE 5
+#define USEC_PER_PIXEL_LARGE_CHANGE 25
+#define USEC_PER_PIXEL_MIN 5
+#define USEC_PER_PIXEL_MAX 300
+typedef struct {
+ uint8_t* captured; // Bitmap with the last captured screen.
+ uint32_t captured_idx; // Current index to write into the bitmap
+ uint32_t usec_per_pixel; // Number of useconds a pixel should represent
+ bool show_usage_info;
+} DirectSamplingViewPrivData;
+
/* Read directly from the G0 CC1101 pin, and draw a black or white
* dot depending on the level. */
void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app) {
- if(!app->direct_sampling_enabled) {
+ DirectSamplingViewPrivData* privdata = app->view_privdata;
+
+ if(!app->direct_sampling_enabled && privdata->show_usage_info) {
canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 2, 9, "Direct sampling is a special");
- canvas_draw_str(canvas, 2, 18, "mode that displays the signal");
- canvas_draw_str(canvas, 2, 27, "captured in real time. Like in");
- canvas_draw_str(canvas, 2, 36, "a old CRT TV. It's very slow.");
- canvas_draw_str(canvas, 2, 45, "Can crash your Flipper.");
+ canvas_draw_str(canvas, 2, 9, "Direct sampling displays the");
+ canvas_draw_str(canvas, 2, 18, "the captured signal in real");
+ canvas_draw_str(canvas, 2, 27, "time, like in a CRT TV set.");
+ canvas_draw_str(canvas, 2, 36, "Use UP/DOWN to change the");
+ canvas_draw_str(canvas, 2, 45, "resolution (usec/pixel).");
canvas_set_font(canvas, FontPrimary);
- canvas_draw_str(canvas, 14, 60, "To enable press OK");
+ canvas_draw_str(canvas, 5, 60, "To start/stop, press OK");
return;
}
+ privdata->show_usage_info = false;
+ /* Draw on screen. */
+ int idx = 0;
for(int y = 0; y < 64; y++) {
for(int x = 0; x < 128; x++) {
- bool level = furi_hal_gpio_read(&gpio_cc1101_g0);
+ bool level = bitmap_get(privdata->captured, CAPTURED_BITMAP_BYTES, idx++);
if(level) canvas_draw_dot(canvas, x, y);
- /* Busy loop: this is a terrible approach as it blocks
- * everything else, but for now it's the best we can do
- * to obtain direct data with some spacing. */
- uint32_t x = 250;
- while(x--)
- ;
}
}
+
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%lu usec/px", privdata->usec_per_pixel);
canvas_set_font(canvas, FontSecondary);
- canvas_draw_str_with_border(canvas, 36, 60, "Direct sampling", ColorWhite, ColorBlack);
+ canvas_draw_str_with_border(canvas, 1, 60, buf, ColorWhite, ColorBlack);
}
/* Handle input */
void process_input_direct_sampling(ProtoViewApp* app, InputEvent input) {
+ DirectSamplingViewPrivData* privdata = app->view_privdata;
+
if(input.type == InputTypePress && input.key == InputKeyOk) {
app->direct_sampling_enabled = !app->direct_sampling_enabled;
}
+
+ if((input.key == InputKeyUp || input.key == InputKeyDown) &&
+ (input.type == InputTypePress || input.type == InputTypeRepeat)) {
+ uint32_t change = input.type == InputTypePress ? USEC_PER_PIXEL_SMALL_CHANGE :
+ USEC_PER_PIXEL_LARGE_CHANGE;
+ if(input.key == InputKeyUp) change = -change;
+ privdata->usec_per_pixel += change;
+ if(privdata->usec_per_pixel < USEC_PER_PIXEL_MIN)
+ privdata->usec_per_pixel = USEC_PER_PIXEL_MIN;
+ else if(privdata->usec_per_pixel > USEC_PER_PIXEL_MAX)
+ privdata->usec_per_pixel = USEC_PER_PIXEL_MAX;
+ /* Update the timer frequency. */
+ direct_sampling_timer_stop(app);
+ direct_sampling_timer_start(app);
+ }
}
/* Enter view. Stop the subghz thread to prevent access as we read
* the CC1101 data directly. */
void view_enter_direct_sampling(ProtoViewApp* app) {
+ /* Set view defaults. */
+ DirectSamplingViewPrivData* privdata = app->view_privdata;
+ privdata->usec_per_pixel = DEFAULT_USEC_PER_PIXEL;
+ privdata->captured = malloc(CAPTURED_BITMAP_BYTES);
+ privdata->show_usage_info = true;
+
if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) {
- subghz_worker_stop(app->txrx->worker);
+ furi_hal_subghz_stop_async_rx();
+
+ /* To read data asynchronously directly from the view, we need
+ * to put the CC1101 back into reception mode (the previous call
+ * to stop the async RX will put it into idle) and configure the
+ * G0 pin for reading. */
+ furi_hal_subghz_rx();
+ furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow);
} else {
raw_sampling_worker_stop(app);
}
+
+ // Start the timer to capture raw data
+ direct_sampling_timer_start(app);
}
/* Exit view. Restore the subghz thread. */
void view_exit_direct_sampling(ProtoViewApp* app) {
+ DirectSamplingViewPrivData* privdata = app->view_privdata;
+ if(privdata->captured) free(privdata->captured);
+ app->direct_sampling_enabled = false;
+
+ direct_sampling_timer_stop(app);
+
+ /* Restart normal data feeding. */
if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) {
- subghz_worker_start(app->txrx->worker);
+ furi_hal_subghz_start_async_rx(protoview_rx_callback, NULL);
} else {
raw_sampling_worker_start(app);
}
- app->direct_sampling_enabled = false;
+}
+
+/* =========================== Timer implementation ========================= */
+
+static void ds_timer_isr(void* ctx) {
+ ProtoViewApp* app = ctx;
+ DirectSamplingViewPrivData* privdata = app->view_privdata;
+
+ if(app->direct_sampling_enabled) {
+ bool level = furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin);
+ bitmap_set(privdata->captured, CAPTURED_BITMAP_BYTES, privdata->captured_idx, level);
+ privdata->captured_idx = (privdata->captured_idx + 1) % CAPTURED_BITMAP_BITS;
+ }
+ LL_TIM_ClearFlag_UPDATE(TIM2);
+}
+
+static void direct_sampling_timer_start(ProtoViewApp* app) {
+ DirectSamplingViewPrivData* privdata = app->view_privdata;
+
+ LL_TIM_InitTypeDef tim_init = {
+ .Prescaler = 63, /* CPU frequency is ~64Mhz. */
+ .CounterMode = LL_TIM_COUNTERMODE_UP,
+ .Autoreload = privdata->usec_per_pixel};
+
+ 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, ds_timer_isr, app);
+ LL_TIM_EnableIT_UPDATE(TIM2);
+ LL_TIM_EnableCounter(TIM2);
+}
+
+static void direct_sampling_timer_stop(ProtoViewApp* app) {
+ UNUSED(app);
+ FURI_CRITICAL_ENTER();
+ LL_TIM_DisableCounter(TIM2);
+ LL_TIM_DisableIT_UPDATE(TIM2);
+ furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL);
+ LL_TIM_DeInit(TIM2);
+ FURI_CRITICAL_EXIT();
}
diff --git a/applications/plugins/protoview/view_settings.c b/applications/plugins/protoview/view_settings.c
index 94d80cfb5..09abf5a2a 100644
--- a/applications/plugins/protoview/view_settings.c
+++ b/applications/plugins/protoview/view_settings.c
@@ -88,7 +88,7 @@ void process_input_settings(ProtoViewApp* app, InputEvent input) {
if(input.key == InputKeyUp) {
modid = modid == 0 ? count - 1 : modid - 1;
} else if(input.key == InputKeyDown) {
- modid = (modid + 1) % (count ? count : 1);
+ modid = (modid + 1) % count;
} else {
return;
}
diff --git a/applications/plugins/snake_game/snake_game.c b/applications/plugins/snake_game/snake_game.c
index 2815e2f37..d2eb22f13 100644
--- a/applications/plugins/snake_game/snake_game.c
+++ b/applications/plugins/snake_game/snake_game.c
@@ -2,7 +2,6 @@
#include
#include
#include
-#include
#include
#include
@@ -346,8 +345,6 @@ int32_t snake_game_app(void* p) {
notification_message_block(notification, &sequence_display_backlight_enforce_on);
- DOLPHIN_DEED(DolphinDeedPluginGameStart);
-
SnakeEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
diff --git a/applications/plugins/solitaire/solitaire.c b/applications/plugins/solitaire/solitaire.c
index 8893b3986..78205ad69 100644
--- a/applications/plugins/solitaire/solitaire.c
+++ b/applications/plugins/solitaire/solitaire.c
@@ -1,5 +1,4 @@
#include
-#include
#include
#include
#include "defines.h"
@@ -255,7 +254,9 @@ bool place_on_top(Card* where, Card what) {
int8_t b_letter = (int8_t)what.character;
if(a_letter == 12) a_letter = -1;
if(b_letter == 12) b_letter = -1;
+
if(where->disabled && b_letter != -1) return false;
+
if((a_letter + 1) == b_letter) {
where->disabled = what.disabled;
where->pip = what.pip;
@@ -488,6 +489,7 @@ int32_t solitaire_app(void* p) {
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);
@@ -566,4 +568,4 @@ free_and_exit:
free(game_state);
furi_message_queue_free(event_queue);
return return_code;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/spectrum_analyzer/application.fam b/applications/plugins/spectrum_analyzer/application.fam
index 04bb946ee..344c2244f 100644
--- a/applications/plugins/spectrum_analyzer/application.fam
+++ b/applications/plugins/spectrum_analyzer/application.fam
@@ -8,5 +8,5 @@ App(
stack_size=2 * 1024,
order=12,
fap_icon="spectrum_10px.png",
- fap_category="Tools",
+ fap_category="Sub-GHz",
)
diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c b/applications/plugins/spectrum_analyzer/spectrum_analyzer.c
index 8d68c5a1a..99c12adf7 100644
--- a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c
+++ b/applications/plugins/spectrum_analyzer/spectrum_analyzer.c
@@ -392,6 +392,9 @@ void spectrum_analyzer_free(SpectrumAnalyzer* instance) {
furi_hal_subghz_idle();
furi_hal_subghz_sleep();
+
+ // Disable power for External CC1101 if it was enabled and module is connected
+ furi_hal_subghz_disable_ext_power();
}
int32_t spectrum_analyzer_app(void* p) {
@@ -400,6 +403,9 @@ int32_t spectrum_analyzer_app(void* p) {
SpectrumAnalyzer* spectrum_analyzer = spectrum_analyzer_alloc();
InputEvent input;
+ // Enable power for External CC1101 if it is connected
+ furi_hal_subghz_enable_ext_power();
+
furi_hal_power_suppress_charge_enter();
FURI_LOG_D("Spectrum", "Main Loop - Starting worker");
diff --git a/applications/plugins/spi_mem_manager/application.fam b/applications/plugins/spi_mem_manager/application.fam
index 09d801876..21e70980a 100644
--- a/applications/plugins/spi_mem_manager/application.fam
+++ b/applications/plugins/spi_mem_manager/application.fam
@@ -1,13 +1,13 @@
App(
appid="spi_mem_manager",
- name="SPI Mem Manager",
+ name="[SPI] Mem Manager",
apptype=FlipperAppType.EXTERNAL,
entry_point="spi_mem_app",
requires=["gui"],
stack_size=1 * 2048,
order=30,
fap_icon="images/Dip8_10px.png",
- fap_category="Tools",
+ fap_category="GPIO",
fap_icon_assets="images",
fap_private_libs=[
Lib(
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c
index b664df687..38d064a4d 100644
--- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c
+++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c
@@ -60,7 +60,7 @@ bool spi_mem_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect);
success = true;
} else if(event.event == SPIMemSceneStartSubmenuIndexSaved) {
- furi_string_set(app->file_path, SPI_MEM_FILE_FOLDER);
+ furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX);
scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectFile);
success = true;
} else if(event.event == SPIMemSceneStartSubmenuIndexErase) {
diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.c b/applications/plugins/spi_mem_manager/spi_mem_app.c
index 63531b74c..96c3632d0 100644
--- a/applications/plugins/spi_mem_manager/spi_mem_app.c
+++ b/applications/plugins/spi_mem_manager/spi_mem_app.c
@@ -16,9 +16,9 @@ static bool spi_mem_back_event_callback(void* context) {
}
SPIMemApp* spi_mem_alloc(void) {
- SPIMemApp* instance = malloc(sizeof(SPIMemApp));
+ SPIMemApp* instance = malloc(sizeof(SPIMemApp)); //-V799
- instance->file_path = furi_string_alloc();
+ instance->file_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX);
instance->gui = furi_record_open(RECORD_GUI);
instance->notifications = furi_record_open(RECORD_NOTIFICATION);
instance->view_dispatcher = view_dispatcher_alloc();
@@ -37,7 +37,8 @@ SPIMemApp* spi_mem_alloc(void) {
instance->text_input = text_input_alloc();
instance->mode = SPIMemModeUnknown;
- furi_string_set(instance->file_path, SPI_MEM_FILE_FOLDER);
+ // Migrate data from old sd-card folder
+ storage_common_migrate(instance->storage, EXT_PATH("spimem"), STORAGE_APP_DATA_PATH_PREFIX);
view_dispatcher_enable_queue(instance->view_dispatcher);
view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);
@@ -70,7 +71,7 @@ SPIMemApp* spi_mem_alloc(void) {
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external);
scene_manager_next_scene(instance->scene_manager, SPIMemSceneStart);
return instance;
-}
+} //-V773
void spi_mem_free(SPIMemApp* instance) {
view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewSubmenu);
@@ -105,7 +106,6 @@ void spi_mem_free(SPIMemApp* instance) {
int32_t spi_mem_app(void* p) {
UNUSED(p);
SPIMemApp* instance = spi_mem_alloc();
- spi_mem_file_create_folder(instance);
view_dispatcher_run(instance->view_dispatcher);
spi_mem_free(instance);
return 0;
diff --git a/applications/plugins/spi_mem_manager/spi_mem_app_i.h b/applications/plugins/spi_mem_manager/spi_mem_app_i.h
index 4ce056175..285ca66d2 100644
--- a/applications/plugins/spi_mem_manager/spi_mem_app_i.h
+++ b/applications/plugins/spi_mem_manager/spi_mem_app_i.h
@@ -24,7 +24,6 @@
#define TAG "SPIMem"
#define SPI_MEM_FILE_EXTENSION ".bin"
-#define SPI_MEM_FILE_FOLDER EXT_PATH("spimem")
#define SPI_MEM_FILE_NAME_SIZE 100
#define SPI_MEM_TEXT_BUFFER_SIZE 128
diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.c b/applications/plugins/spi_mem_manager/spi_mem_files.c
index a7374da19..9b787bd7f 100644
--- a/applications/plugins/spi_mem_manager/spi_mem_files.c
+++ b/applications/plugins/spi_mem_manager/spi_mem_files.c
@@ -1,11 +1,5 @@
#include "spi_mem_app_i.h"
-void spi_mem_file_create_folder(SPIMemApp* app) {
- if(!storage_simply_mkdir(app->storage, SPI_MEM_FILE_FOLDER)) {
- dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
- }
-}
-
bool spi_mem_file_delete(SPIMemApp* app) {
return (storage_simply_remove(app->storage, furi_string_get_cstr(app->file_path)));
}
@@ -13,7 +7,7 @@ bool spi_mem_file_delete(SPIMemApp* app) {
bool spi_mem_file_select(SPIMemApp* app) {
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, SPI_MEM_FILE_EXTENSION, &I_Dip8_10px);
- browser_options.base_path = SPI_MEM_FILE_FOLDER;
+ browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
bool success =
dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options);
return success;
diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.h b/applications/plugins/spi_mem_manager/spi_mem_files.h
index 0e735d951..6a529d327 100644
--- a/applications/plugins/spi_mem_manager/spi_mem_files.h
+++ b/applications/plugins/spi_mem_manager/spi_mem_files.h
@@ -1,7 +1,6 @@
#pragma once
#include "spi_mem_app.h"
-void spi_mem_file_create_folder(SPIMemApp* app);
bool spi_mem_file_select(SPIMemApp* app);
bool spi_mem_file_create(SPIMemApp* app, const char* file_name);
bool spi_mem_file_delete(SPIMemApp* app);
diff --git a/applications/plugins/subbrute b/applications/plugins/subbrute
new file mode 160000
index 000000000..ed94bc878
--- /dev/null
+++ b/applications/plugins/subbrute
@@ -0,0 +1 @@
+Subproject commit ed94bc87868b80bf41dad754f3747a9f40551995
diff --git a/applications/plugins/subbrute/README.md b/applications/plugins/subbrute/README.md
deleted file mode 100644
index 98c1503d0..000000000
--- a/applications/plugins/subbrute/README.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# SubGHz Bruteforcer Plugin for Flipper Zero
-
-SubGhz Bruteforcer from [Unleashed Firmware](https://github.com/DarkFlippers/unleashed-firmware)
-
-### Disclaimer
-
-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.
-
-### Supported Protocols:
-
-#### CAME
-
-- CAME 12bit 303MHz
-- CAME 12bit 307MHz
-- CAME 12bit 315MHz
-- CAME 12bit 433MHz
-- CAME 12bit 868MHz
-
-#### NICE
-
-- NICE 12bit 433MHz
-- NICE 12bit 868MHz
-
-#### Ansonic
-
-- Ansonic 12bit 433.075MHz
-- Ansonic 12bit 433.920MHz
-- Ansonic 12bit 434.075MHz
-
-#### Holtek
-
-- Holtek HT12X 12bit 433.920MHz
-
-#### Chamberlain
-
-- 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
-
-- Linear 10bit 300MHz
-- Linear 10bit 310MHz
-
-#### UNILARM
-
-- UNILARM 25bit 330MHz
-- UNILARM 25bit 433MHz
-
-#### SMC5326
-
-- SMC5326 25bit 330MHz
-- SMC5326 25bit 433MHz
-
-#### PT2260
-
-- PT2260 24bit 315MHz
-- PT2260 24bit 330MHz
-- PT2260 24bit 390MHz
-- PT2260 24bit 433MHz
-
-#### Additional
-
-- BF Existing dump works for most other static protocols supported by Flipper Zero
diff --git a/applications/plugins/subbrute/application.fam b/applications/plugins/subbrute/application.fam
deleted file mode 100644
index 20a5f5367..000000000
--- a/applications/plugins/subbrute/application.fam
+++ /dev/null
@@ -1,13 +0,0 @@
-App(
- appid="SubGHz_Bruteforcer",
- name="Sub-GHz Bruteforcer",
- apptype=FlipperAppType.EXTERNAL,
- entry_point="subbrute_app",
- cdefines=["APP_SUB_BRUTE"],
- requires=["gui", "dialogs"],
- stack_size=2 * 1024,
- order=11,
- fap_icon="images/subbrute_10px.png",
- fap_category="Tools",
- fap_icon_assets="images",
-)
diff --git a/applications/plugins/subbrute/helpers/gui_top_buttons.c b/applications/plugins/subbrute/helpers/gui_top_buttons.c
deleted file mode 100644
index 0415c5ae7..000000000
--- a/applications/plugins/subbrute/helpers/gui_top_buttons.c
+++ /dev/null
@@ -1,59 +0,0 @@
-#include "gui_top_buttons.h"
-
-void elements_button_top_left(Canvas* canvas, const char* str) {
- const Icon* icon = &I_ButtonUp_7x4;
-
- const uint8_t button_height = 12;
- const uint8_t vertical_offset = 3;
- const uint8_t horizontal_offset = 3;
- const uint8_t string_width = canvas_string_width(canvas, str);
- const uint8_t icon_h_offset = 3;
- const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
- const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset;
- const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
-
- const uint8_t x = 0;
- const uint8_t y = 0 + button_height;
-
- 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);
- canvas_draw_str(
- canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
- canvas_invert_color(canvas);
-}
-
-void elements_button_top_right(Canvas* canvas, const char* str) {
- const Icon* icon = &I_ButtonDown_7x4;
-
- const uint8_t button_height = 12;
- const uint8_t vertical_offset = 3;
- const uint8_t horizontal_offset = 3;
- const uint8_t string_width = canvas_string_width(canvas, str);
- const uint8_t icon_h_offset = 3;
- const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
- const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1;
- const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
-
- const uint8_t x = canvas_width(canvas);
- const uint8_t y = 0 + button_height;
-
- 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);
- canvas_draw_icon(
- canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon);
- canvas_invert_color(canvas);
-}
\ No newline at end of file
diff --git a/applications/plugins/subbrute/helpers/gui_top_buttons.h b/applications/plugins/subbrute/helpers/gui_top_buttons.h
deleted file mode 100644
index b5ca507b7..000000000
--- a/applications/plugins/subbrute/helpers/gui_top_buttons.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-
-/**
- * Thanks to the author of metronome
- * @param canvas
- * @param str
- */
-void elements_button_top_left(Canvas* canvas, const char* str);
-
-/**
- * Thanks to the author of metronome
- * @param canvas
- * @param str
- */
-void elements_button_top_right(Canvas* canvas, const char* str);
\ No newline at end of file
diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.c b/applications/plugins/subbrute/helpers/subbrute_worker.c
deleted file mode 100644
index 118e63c65..000000000
--- a/applications/plugins/subbrute/helpers/subbrute_worker.c
+++ /dev/null
@@ -1,437 +0,0 @@
-#include "subbrute_worker_private.h"
-#include
-#include
-#include
-#include
-#include
-
-#define TAG "SubBruteWorker"
-#define SUBBRUTE_TX_TIMEOUT 5
-#define SUBBRUTE_MANUAL_TRANSMIT_INTERVAL 400
-
-SubBruteWorker* subbrute_worker_alloc() {
- SubBruteWorker* instance = malloc(sizeof(SubBruteWorker));
-
- instance->state = SubBruteWorkerStateIDLE;
- instance->step = 0;
- instance->worker_running = false;
- instance->initiated = false;
- instance->last_time_tx_data = 0;
- instance->load_index = 0;
-
- instance->thread = furi_thread_alloc();
- furi_thread_set_name(instance->thread, "SubBruteAttackWorker");
- furi_thread_set_stack_size(instance->thread, 2048);
- furi_thread_set_context(instance->thread, instance);
- furi_thread_set_callback(instance->thread, subbrute_worker_thread);
-
- instance->context = NULL;
- instance->callback = NULL;
-
- 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;
-
- return instance;
-}
-
-void subbrute_worker_free(SubBruteWorker* instance) {
- furi_assert(instance);
-
- // I don't know how to free this
- instance->decoder_result = NULL;
-
- if(instance->transmitter != NULL) {
- subghz_transmitter_free(instance->transmitter);
- instance->transmitter = NULL;
- }
-
- subghz_environment_free(instance->environment);
- instance->environment = NULL;
-
- furi_thread_free(instance->thread);
-
- free(instance);
-}
-
-uint64_t subbrute_worker_get_step(SubBruteWorker* instance) {
- return instance->step;
-}
-
-bool subbrute_worker_set_step(SubBruteWorker* instance, uint64_t step) {
- furi_assert(instance);
- if(!subbrute_worker_can_manual_transmit(instance)) {
- FURI_LOG_W(TAG, "Cannot set step during running mode");
- return false;
- }
-
- instance->step = step;
-
- return true;
-}
-
-bool subbrute_worker_init_default_attack(
- SubBruteWorker* instance,
- SubBruteAttacks attack_type,
- uint64_t step,
- const SubBruteProtocol* protocol,
- uint8_t extra_repeats) {
- furi_assert(instance);
-
- if(instance->worker_running) {
- FURI_LOG_W(TAG, "Init Worker when it's running");
- subbrute_worker_stop(instance);
- }
-
- instance->attack = attack_type;
- instance->frequency = protocol->frequency;
- instance->preset = protocol->preset;
- instance->file = protocol->file;
- instance->step = step;
- instance->bits = protocol->bits;
- instance->te = protocol->te;
- instance->repeat = protocol->repeat + extra_repeats;
- instance->load_index = 0;
- instance->file_key = 0;
- instance->two_bytes = false;
-
- instance->max_value =
- subbrute_protocol_calc_max_value(instance->attack, instance->bits, instance->two_bytes);
-
- instance->initiated = true;
- instance->state = SubBruteWorkerStateReady;
- subbrute_worker_send_callback(instance);
-#ifdef FURI_DEBUG
- FURI_LOG_I(
- TAG,
- "subbrute_worker_init_default_attack: %s, bits: %d, preset: %s, file: %s, te: %ld, repeat: %d, max_value: %lld",
- subbrute_protocol_name(instance->attack),
- instance->bits,
- subbrute_protocol_preset(instance->preset),
- subbrute_protocol_file(instance->file),
- instance->te,
- instance->repeat,
- instance->max_value);
-#endif
-
- return true;
-}
-
-bool subbrute_worker_init_file_attack(
- SubBruteWorker* instance,
- uint64_t step,
- uint8_t load_index,
- uint64_t file_key,
- SubBruteProtocol* protocol,
- uint8_t extra_repeats,
- bool two_bytes) {
- furi_assert(instance);
-
- if(instance->worker_running) {
- FURI_LOG_W(TAG, "Init Worker when it's running");
- subbrute_worker_stop(instance);
- }
-
- instance->attack = SubBruteAttackLoadFile;
- instance->frequency = protocol->frequency;
- instance->preset = protocol->preset;
- instance->file = protocol->file;
- instance->step = step;
- instance->bits = protocol->bits;
- instance->te = protocol->te;
- instance->load_index = load_index;
- instance->repeat = protocol->repeat + extra_repeats;
- instance->file_key = file_key;
- instance->two_bytes = two_bytes;
-
- instance->max_value =
- subbrute_protocol_calc_max_value(instance->attack, instance->bits, instance->two_bytes);
-
- instance->initiated = true;
- instance->state = SubBruteWorkerStateReady;
- subbrute_worker_send_callback(instance);
-#ifdef FURI_DEBUG
- FURI_LOG_I(
- TAG,
- "subbrute_worker_init_file_attack: %s, bits: %d, preset: %s, file: %s, te: %ld, repeat: %d, max_value: %lld, key: %llX",
- subbrute_protocol_name(instance->attack),
- instance->bits,
- subbrute_protocol_preset(instance->preset),
- subbrute_protocol_file(instance->file),
- instance->te,
- instance->repeat,
- instance->max_value,
- instance->file_key);
-#endif
-
- return true;
-}
-
-bool subbrute_worker_start(SubBruteWorker* instance) {
- furi_assert(instance);
-
- if(!instance->initiated) {
- FURI_LOG_W(TAG, "Worker not init!");
- return false;
- }
-
- if(instance->worker_running) {
- FURI_LOG_W(TAG, "Worker is already running!");
- return false;
- }
- if(instance->state != SubBruteWorkerStateReady &&
- instance->state != SubBruteWorkerStateFinished) {
- FURI_LOG_W(TAG, "Worker cannot start, invalid device state: %d", instance->state);
- return false;
- }
-
- instance->worker_running = true;
- furi_thread_start(instance->thread);
-
- return true;
-}
-
-void subbrute_worker_stop(SubBruteWorker* instance) {
- furi_assert(instance);
-
- if(!instance->worker_running) {
- return;
- }
-
- instance->worker_running = false;
- furi_thread_join(instance->thread);
-
- furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
- furi_hal_subghz_sleep();
-}
-
-bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t step) {
- furi_assert(instance);
-
- if(!instance->initiated) {
- FURI_LOG_W(TAG, "Worker not init!");
- return false;
- }
- if(instance->worker_running) {
- FURI_LOG_W(TAG, "Worker in running state!");
- return false;
- }
- if(instance->state != SubBruteWorkerStateReady &&
- instance->state != SubBruteWorkerStateFinished) {
- FURI_LOG_W(TAG, "Invalid state for running worker! State: %d", instance->state);
- return false;
- }
-
- uint32_t ticks = furi_get_tick();
- if((ticks - instance->last_time_tx_data) < SUBBRUTE_MANUAL_TRANSMIT_INTERVAL) {
-#if FURI_DEBUG
- FURI_LOG_D(TAG, "Need to wait, current: %ld", ticks - instance->last_time_tx_data);
-#endif
- return false;
- }
-
- instance->last_time_tx_data = ticks;
- instance->step = step;
-
- bool result;
- instance->protocol_name = subbrute_protocol_file(instance->file);
- FlipperFormat* flipper_format = flipper_format_string_alloc();
- Stream* stream = flipper_format_get_raw_stream(flipper_format);
-
- stream_clean(stream);
-
- if(instance->attack == SubBruteAttackLoadFile) {
- subbrute_protocol_file_payload(
- stream,
- step,
- instance->bits,
- instance->te,
- instance->repeat,
- instance->load_index,
- instance->file_key,
- instance->two_bytes);
- } else {
- subbrute_protocol_default_payload(
- stream, instance->file, step, instance->bits, instance->te, instance->repeat);
- }
-
- // size_t written = stream_write_string(stream, payload);
- // if(written <= 0) {
- // FURI_LOG_W(TAG, "Error creating packet! EXIT");
- // result = false;
- // } else {
- subbrute_worker_subghz_transmit(instance, flipper_format);
-
- result = true;
-#if FURI_DEBUG
- FURI_LOG_D(TAG, "Manual transmit done");
-#endif
- // }
-
- flipper_format_free(flipper_format);
- // furi_string_free(payload);
-
- return result;
-}
-
-bool subbrute_worker_is_running(SubBruteWorker* instance) {
- return instance->worker_running;
-}
-
-bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance) {
- furi_assert(instance);
-
- if(!instance->initiated) {
- FURI_LOG_W(TAG, "Worker not init!");
- return false;
- }
-
- return !instance->worker_running && instance->state != SubBruteWorkerStateIDLE &&
- instance->state != SubBruteWorkerStateTx &&
- ((furi_get_tick() - instance->last_time_tx_data) > SUBBRUTE_MANUAL_TRANSMIT_INTERVAL);
-}
-
-void subbrute_worker_set_callback(
- SubBruteWorker* instance,
- SubBruteWorkerCallback callback,
- void* context) {
- furi_assert(instance);
-
- instance->callback = callback;
- instance->context = context;
-}
-
-void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* flipper_format) {
- while(instance->transmit_mode) {
- furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
- }
- instance->transmit_mode = true;
- if(instance->transmitter != NULL) {
- subghz_transmitter_free(instance->transmitter);
- instance->transmitter = NULL;
- }
- instance->transmitter =
- subghz_transmitter_alloc_init(instance->environment, instance->protocol_name);
- subghz_transmitter_deserialize(instance->transmitter, flipper_format);
- furi_hal_subghz_reset();
- furi_hal_subghz_load_preset(instance->preset);
- furi_hal_subghz_set_frequency_and_path(instance->frequency);
- furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter);
-
- while(!furi_hal_subghz_is_async_tx_complete()) {
- furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
- }
- furi_hal_subghz_stop_async_tx();
-
- furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
- furi_hal_subghz_sleep();
- subghz_transmitter_free(instance->transmitter);
- instance->transmitter = NULL;
-
- instance->transmit_mode = false;
-}
-
-void subbrute_worker_send_callback(SubBruteWorker* instance) {
- if(instance->callback != NULL) {
- instance->callback(instance->context, instance->state);
- }
-}
-
-/**
- * Entrypoint for worker
- *
- * @param context SubBruteWorker*
- * @return 0 if ok
- */
-int32_t subbrute_worker_thread(void* context) {
- furi_assert(context);
- SubBruteWorker* instance = (SubBruteWorker*)context;
-
- if(!instance->worker_running) {
- FURI_LOG_W(TAG, "Worker is not set to running state!");
- return -1;
- }
- if(instance->state != SubBruteWorkerStateReady &&
- instance->state != SubBruteWorkerStateFinished) {
- FURI_LOG_W(TAG, "Invalid state for running worker! State: %d", instance->state);
- return -2;
- }
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Worker start");
-#endif
-
- SubBruteWorkerState local_state = instance->state = SubBruteWorkerStateTx;
- subbrute_worker_send_callback(instance);
-
- instance->protocol_name = subbrute_protocol_file(instance->file);
-
- FlipperFormat* flipper_format = flipper_format_string_alloc();
- Stream* stream = flipper_format_get_raw_stream(flipper_format);
-
- while(instance->worker_running) {
- stream_clean(stream);
- if(instance->attack == SubBruteAttackLoadFile) {
- subbrute_protocol_file_payload(
- stream,
- instance->step,
- instance->bits,
- instance->te,
- instance->repeat,
- instance->load_index,
- instance->file_key,
- instance->two_bytes);
- } else {
- subbrute_protocol_default_payload(
- stream,
- instance->file,
- instance->step,
- instance->bits,
- instance->te,
- instance->repeat);
- }
-#ifdef FURI_DEBUG
- //FURI_LOG_I(TAG, "Payload: %s", furi_string_get_cstr(payload));
- //furi_delay_ms(SUBBRUTE_MANUAL_TRANSMIT_INTERVAL / 4);
-#endif
-
- // size_t written = stream_write_stream_write_string(stream, payload);
- // if(written <= 0) {
- // FURI_LOG_W(TAG, "Error creating packet! BREAK");
- // instance->worker_running = false;
- // local_state = SubBruteWorkerStateIDLE;
- // furi_string_free(payload);
- // break;
- // }
-
- subbrute_worker_subghz_transmit(instance, flipper_format);
-
- if(instance->step + 1 > instance->max_value) {
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Worker finished to end");
-#endif
- local_state = SubBruteWorkerStateFinished;
- // furi_string_free(payload);
- break;
- }
- instance->step++;
-
- // furi_string_free(payload);
- furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
- }
-
- flipper_format_free(flipper_format);
-
- instance->worker_running = false; // Because we have error states
- instance->state = local_state == SubBruteWorkerStateTx ? SubBruteWorkerStateReady :
- local_state;
- subbrute_worker_send_callback(instance);
-
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "Worker stop");
-#endif
- return 0;
-}
diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.h b/applications/plugins/subbrute/helpers/subbrute_worker.h
deleted file mode 100644
index 4046f997c..000000000
--- a/applications/plugins/subbrute/helpers/subbrute_worker.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#include "../subbrute_protocols.h"
-
-typedef enum {
- SubBruteWorkerStateIDLE,
- SubBruteWorkerStateReady,
- SubBruteWorkerStateTx,
- SubBruteWorkerStateFinished
-} SubBruteWorkerState;
-
-typedef void (*SubBruteWorkerCallback)(void* context, SubBruteWorkerState state);
-
-typedef struct SubBruteWorker SubBruteWorker;
-
-SubBruteWorker* subbrute_worker_alloc();
-void subbrute_worker_free(SubBruteWorker* instance);
-uint64_t subbrute_worker_get_step(SubBruteWorker* instance);
-bool subbrute_worker_set_step(SubBruteWorker* instance, uint64_t step);
-bool subbrute_worker_is_running(SubBruteWorker* instance);
-bool subbrute_worker_init_default_attack(
- SubBruteWorker* instance,
- SubBruteAttacks attack_type,
- uint64_t step,
- const SubBruteProtocol* protocol,
- uint8_t extra_repeats);
-bool subbrute_worker_init_file_attack(
- SubBruteWorker* instance,
- uint64_t step,
- uint8_t load_index,
- uint64_t file_key,
- SubBruteProtocol* protocol,
- uint8_t extra_repeats,
- bool two_bytes);
-bool subbrute_worker_start(SubBruteWorker* instance);
-void subbrute_worker_stop(SubBruteWorker* instance);
-bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t step);
-bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance);
-void subbrute_worker_set_callback(
- SubBruteWorker* instance,
- SubBruteWorkerCallback callback,
- void* context);
\ No newline at end of file
diff --git a/applications/plugins/subbrute/helpers/subbrute_worker_private.h b/applications/plugins/subbrute/helpers/subbrute_worker_private.h
deleted file mode 100644
index e38e77dc4..000000000
--- a/applications/plugins/subbrute/helpers/subbrute_worker_private.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include "subbrute_worker.h"
-#include
-#include
-#include
-#include
-
-struct SubBruteWorker {
- SubBruteWorkerState state;
- volatile bool worker_running;
- volatile bool initiated;
- volatile bool transmit_mode;
-
- // Current step
- uint64_t step;
-
- // SubGhz
- FuriThread* thread;
- SubGhzProtocolDecoderBase* decoder_result;
- SubGhzEnvironment* environment;
- SubGhzTransmitter* transmitter;
- const char* protocol_name;
-
- // Initiated values
- SubBruteAttacks attack; // Attack state
- uint32_t frequency;
- FuriHalSubGhzPreset preset;
- SubBruteFileProtocol file;
- uint8_t bits;
- uint32_t te;
- uint8_t repeat;
- uint8_t load_index; // Index of group to bruteforce in loaded file
- uint64_t file_key;
- uint64_t max_value; // Max step
- bool two_bytes;
-
- // Manual transmit
- uint32_t last_time_tx_data;
-
- // Callback for changed states
- SubBruteWorkerCallback callback;
- void* context;
-};
-
-int32_t subbrute_worker_thread(void* context);
-void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* flipper_format);
-void subbrute_worker_send_callback(SubBruteWorker* instance);
\ No newline at end of file
diff --git a/applications/plugins/subbrute/images/DolphinNice_96x59.png b/applications/plugins/subbrute/images/DolphinNice_96x59.png
deleted file mode 100644
index a299d3630..000000000
Binary files a/applications/plugins/subbrute/images/DolphinNice_96x59.png and /dev/null differ
diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png
deleted file mode 100644
index 52dc4ad21..000000000
Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_01.png and /dev/null differ
diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png
deleted file mode 100644
index 2dff1c031..000000000
Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_02.png and /dev/null differ
diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png
deleted file mode 100644
index c1e438b01..000000000
Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_03.png and /dev/null differ
diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png
deleted file mode 100644
index 169fb6147..000000000
Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_04.png and /dev/null differ
diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png
deleted file mode 100644
index 79b2bc972..000000000
Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_05.png and /dev/null differ
diff --git a/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png b/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png
deleted file mode 100644
index 8fce0c44d..000000000
Binary files a/applications/plugins/subbrute/images/Sub1ghz_14/frame_06.png and /dev/null differ
diff --git a/applications/plugins/subbrute/images/sub1_10px.png b/applications/plugins/subbrute/images/sub1_10px.png
deleted file mode 100644
index 5a25fdf4e..000000000
Binary files a/applications/plugins/subbrute/images/sub1_10px.png and /dev/null differ
diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_config.h b/applications/plugins/subbrute/scenes/subbrute_scene_config.h
deleted file mode 100644
index 3541df9ac..000000000
--- a/applications/plugins/subbrute/scenes/subbrute_scene_config.h
+++ /dev/null
@@ -1,7 +0,0 @@
-ADD_SCENE(subbrute, load_file, LoadFile)
-ADD_SCENE(subbrute, load_select, LoadSelect)
-ADD_SCENE(subbrute, run_attack, RunAttack)
-ADD_SCENE(subbrute, save_name, SaveName)
-ADD_SCENE(subbrute, save_success, SaveSuccess)
-ADD_SCENE(subbrute, setup_attack, SetupAttack)
-ADD_SCENE(subbrute, start, Start)
\ No newline at end of file
diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c b/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c
deleted file mode 100644
index 8aae1bcad..000000000
--- a/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "../subbrute_i.h"
-#include "subbrute_scene.h"
-
-#define TAG "SubBruteSceneLoadFile"
-
-void subbrute_scene_load_file_on_enter(void* context) {
- furi_assert(context);
- SubBruteState* instance = (SubBruteState*)context;
-
- // Input events and views are managed by file_browser
- FuriString* app_folder;
- FuriString* load_path;
- load_path = furi_string_alloc();
- app_folder = furi_string_alloc_set(SUBBRUTE_PATH);
-
- DialogsFileBrowserOptions browser_options;
- dialog_file_browser_set_basic_options(&browser_options, SUBBRUTE_FILE_EXT, &I_sub1_10px);
-
- SubBruteFileResult load_result = SubBruteFileResultUnknown;
- // TODO: DELETE IT
-#ifdef SUBBRUTE_FAST_TRACK
- bool res = true;
- furi_string_printf(load_path, "%s", "/ext/subghz/princeton.sub");
-#else
- bool res =
- dialog_file_browser_show(instance->dialogs, load_path, app_folder, &browser_options);
-#endif
-#ifdef FURI_DEBUG
- FURI_LOG_D(
- TAG,
- "load_path: %s, app_folder: %s",
- furi_string_get_cstr(load_path),
- furi_string_get_cstr(app_folder));
-#endif
- if(res) {
- load_result =
- subbrute_device_load_from_file(instance->device, furi_string_get_cstr(load_path));
- if(load_result == SubBruteFileResultOk) {
- 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->current_step,
- instance->device->bit_index,
- instance->device->key_from_file,
- instance->device->file_protocol_info,
- extra_repeats,
- instance->device->two_bytes)) {
- furi_crash("Invalid attack set!");
- }
- // Ready to run!
- FURI_LOG_I(TAG, "Ready to run");
- res = true;
- }
- }
-
- if(load_result == SubBruteFileResultOk) {
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadSelect);
- } else {
- FURI_LOG_E(TAG, "Returned error: %d", load_result);
-
- FuriString* dialog_msg;
- dialog_msg = furi_string_alloc();
- furi_string_cat_printf(
- dialog_msg, "Cannot parse\nfile: %s", subbrute_device_error_get_desc(load_result));
- dialog_message_show_storage_error(instance->dialogs, furi_string_get_cstr(dialog_msg));
- furi_string_free(dialog_msg);
- scene_manager_search_and_switch_to_previous_scene(
- instance->scene_manager, SubBruteSceneStart);
- }
- } else {
- scene_manager_search_and_switch_to_previous_scene(
- instance->scene_manager, SubBruteSceneStart);
- }
-
- furi_string_free(app_folder);
- furi_string_free(load_path);
-}
-
-void subbrute_scene_load_file_on_exit(void* context) {
- UNUSED(context);
-}
-
-bool subbrute_scene_load_file_on_event(void* context, SceneManagerEvent event) {
- UNUSED(context);
- UNUSED(event);
- return false;
-}
\ No newline at end of file
diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c b/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c
deleted file mode 100644
index d018e8b4d..000000000
--- a/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "../subbrute_i.h"
-#include "subbrute_scene.h"
-
-#define TAG "SubBruteSceneStart"
-
-void subbrute_scene_load_select_callback(SubBruteCustomEvent event, void* context) {
- furi_assert(context);
-
- SubBruteState* instance = (SubBruteState*)context;
- view_dispatcher_send_custom_event(instance->view_dispatcher, event);
-}
-
-void subbrute_scene_load_select_on_enter(void* context) {
- furi_assert(context);
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "subbrute_scene_load_select_on_enter");
-#endif
- SubBruteState* instance = (SubBruteState*)context;
- SubBruteMainView* view = instance->view_main;
-
- instance->current_view = SubBruteViewMain;
- subbrute_main_view_set_callback(view, subbrute_scene_load_select_callback, instance);
- subbrute_main_view_set_index(
- view, 7, true, instance->device->two_bytes, instance->device->key_from_file);
-
- view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
-}
-
-void subbrute_scene_load_select_on_exit(void* context) {
- UNUSED(context);
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "subbrute_scene_load_select_on_exit");
-#endif
-}
-
-bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) {
- SubBruteState* instance = (SubBruteState*)context;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == SubBruteCustomEventTypeIndexSelected) {
- /*#ifdef FURI_DEBUG && !SUBBRUTE_FAST_TRACK
- view_dispatcher_stop(instance->view_dispatcher);
- consumed = true;
-#else*/
- instance->device->current_step = 0;
- instance->device->bit_index = subbrute_main_view_get_index(instance->view_main);
- instance->device->two_bytes = subbrute_main_view_get_two_bytes(instance->view_main);
- uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main);
- instance->device->max_value = subbrute_protocol_calc_max_value(
- instance->device->attack,
- instance->device->bit_index,
- instance->device->two_bytes);
-
- if(!subbrute_worker_init_file_attack(
- instance->worker,
- instance->device->current_step,
- instance->device->bit_index,
- instance->device->key_from_file,
- instance->device->file_protocol_info,
- extra_repeats,
- instance->device->two_bytes)) {
- furi_crash("Invalid attack set!");
- }
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
- /*#endif*/
- consumed = true;
- } /* else if(event.event == SubBruteCustomEventTypeChangeStepUp) {
- instance->device->two_bytes = true;
- } else if(event.event == SubBruteCustomEventTypeChangeStepDown) {
- instance->device->two_bytes = false;
- }*/
- } else if(event.type == SceneManagerEventTypeBack) {
- if(!scene_manager_search_and_switch_to_previous_scene(
- instance->scene_manager, SubBruteSceneStart)) {
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
- }
- consumed = true;
- }
-
- return consumed;
-}
\ No newline at end of file
diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c b/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c
deleted file mode 100644
index 2f22c25d4..000000000
--- a/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "../subbrute_i.h"
-#include "subbrute_scene.h"
-
-#define TAG "SubBruteSceneRunAttack"
-
-static void subbrute_scene_run_attack_callback(SubBruteCustomEvent event, void* context) {
- furi_assert(context);
-
- SubBruteState* instance = (SubBruteState*)context;
- view_dispatcher_send_custom_event(instance->view_dispatcher, event);
-}
-
-static void
- subbrute_scene_run_attack_device_state_changed(void* context, SubBruteWorkerState state) {
- furi_assert(context);
-
- SubBruteState* instance = (SubBruteState*)context;
-
- if(state == SubBruteWorkerStateIDLE) {
- // Can't be IDLE on this step!
- view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeError);
- } else if(state == SubBruteWorkerStateFinished) {
- view_dispatcher_send_custom_event(
- instance->view_dispatcher, SubBruteCustomEventTypeTransmitFinished);
- }
-}
-void subbrute_scene_run_attack_on_exit(void* context) {
- furi_assert(context);
- SubBruteState* instance = (SubBruteState*)context;
-
- notification_message(instance->notifications, &sequence_blink_stop);
- subbrute_worker_stop(instance->worker);
-}
-
-void subbrute_scene_run_attack_on_enter(void* context) {
- furi_assert(context);
- SubBruteState* instance = (SubBruteState*)context;
- SubBruteAttackView* view = instance->view_attack;
-
- instance->current_view = SubBruteViewAttack;
- subbrute_attack_view_set_callback(view, subbrute_scene_run_attack_callback, instance);
- view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
-
- subbrute_worker_set_callback(
- instance->worker, subbrute_scene_run_attack_device_state_changed, instance);
-
- if(!subbrute_worker_is_running(instance->worker)) {
- subbrute_worker_set_step(instance->worker, instance->device->current_step);
- if(!subbrute_worker_start(instance->worker)) {
- view_dispatcher_send_custom_event(
- instance->view_dispatcher, SubBruteCustomEventTypeError);
- } else {
- notification_message(instance->notifications, &sequence_single_vibro);
- notification_message(instance->notifications, &sequence_blink_start_yellow);
- }
- }
-}
-
-bool subbrute_scene_run_attack_on_event(void* context, SceneManagerEvent event) {
- SubBruteState* instance = (SubBruteState*)context;
- SubBruteAttackView* view = instance->view_attack;
-
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeCustom) {
- uint64_t step = subbrute_worker_get_step(instance->worker);
- instance->device->current_step = step;
- subbrute_attack_view_set_current_step(view, step);
-
- if(event.event == SubBruteCustomEventTypeTransmitFinished) {
- notification_message(instance->notifications, &sequence_display_backlight_on);
- notification_message(instance->notifications, &sequence_double_vibro);
-
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
- } else if(
- event.event == SubBruteCustomEventTypeTransmitNotStarted ||
- event.event == SubBruteCustomEventTypeBackPressed) {
- if(subbrute_worker_is_running(instance->worker)) {
- // Notify
- notification_message(instance->notifications, &sequence_single_vibro);
- }
- // Stop transmit
- scene_manager_search_and_switch_to_previous_scene(
- instance->scene_manager, SubBruteSceneSetupAttack);
- } else if(event.event == SubBruteCustomEventTypeError) {
- notification_message(instance->notifications, &sequence_error);
-
- // Stop transmit
- scene_manager_search_and_switch_to_previous_scene(
- instance->scene_manager, SubBruteSceneSetupAttack);
- } else if(event.event == SubBruteCustomEventTypeUpdateView) {
- //subbrute_attack_view_set_current_step(view, instance->device->current_step);
- }
- consumed = true;
- } else if(event.type == SceneManagerEventTypeTick) {
- uint64_t step = subbrute_worker_get_step(instance->worker);
- instance->device->current_step = step;
- subbrute_attack_view_set_current_step(view, step);
-
- consumed = true;
- }
-
- return consumed;
-}
diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c b/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c
deleted file mode 100644
index bb129e948..000000000
--- a/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c
+++ /dev/null
@@ -1,84 +0,0 @@
-#include "../subbrute_i.h"
-#include "subbrute_scene.h"
-#include
-
-#define TAG "SubBruteSceneSaveFile"
-
-void subbrute_scene_save_name_on_enter(void* context) {
- SubBruteState* instance = (SubBruteState*)context;
-
- // Setup view
- TextInput* text_input = instance->text_input;
- set_random_name(instance->text_store, sizeof(instance->text_store));
-
- text_input_set_header_text(text_input, "Name of file");
- text_input_set_result_callback(
- text_input,
- subbrute_text_input_callback,
- instance,
- instance->text_store,
- SUBBRUTE_MAX_LEN_NAME,
- true);
-
- furi_string_reset(instance->file_path);
- furi_string_set_str(instance->file_path, SUBBRUTE_PATH);
-
- ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
- furi_string_get_cstr(instance->file_path), SUBBRUTE_FILE_EXT, "");
- text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
-
- view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewTextInput);
-}
-
-bool subbrute_scene_save_name_on_event(void* context, SceneManagerEvent event) {
- SubBruteState* instance = (SubBruteState*)context;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeBack) {
- scene_manager_previous_scene(instance->scene_manager);
- return true;
- } else if(
- event.type == SceneManagerEventTypeCustom &&
- event.event == SubBruteCustomEventTypeTextEditDone) {
-#ifdef FURI_DEBUG
- FURI_LOG_D(TAG, "Saving: %s", instance->text_store);
-#endif
- bool success = false;
- if(strcmp(instance->text_store, "")) {
- furi_string_reset(instance->file_path);
- furi_string_cat_printf(
- instance->file_path,
- "%s/%s%s",
- SUBBRUTE_PATH,
- instance->text_store,
- SUBBRUTE_FILE_EXT);
-
- if(subbrute_device_save_file(
- instance->device, furi_string_get_cstr(instance->file_path))) {
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveSuccess);
- success = true;
- consumed = true;
- }
- }
-
- if(!success) {
- dialog_message_show_storage_error(instance->dialogs, "Error during saving!");
- consumed = scene_manager_search_and_switch_to_previous_scene(
- instance->scene_manager, SubBruteSceneSetupAttack);
- }
- }
- return consumed;
-}
-
-void subbrute_scene_save_name_on_exit(void* context) {
- SubBruteState* instance = (SubBruteState*)context;
-
- // Clear view
- void* validator_context = text_input_get_validator_callback_context(instance->text_input);
- text_input_set_validator(instance->text_input, NULL, NULL);
- validator_is_file_free(validator_context);
-
- text_input_reset(instance->text_input);
-
- furi_string_reset(instance->file_path);
-}
diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c b/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c
deleted file mode 100644
index 20b1a0de4..000000000
--- a/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "../subbrute_i.h"
-#include "subbrute_scene.h"
-
-void subbrute_scene_save_success_on_enter(void* context) {
- furi_assert(context);
- SubBruteState* instance = context;
-
- // Setup view
- Popup* popup = instance->popup;
- popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
- popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
- popup_set_timeout(popup, 1500);
- popup_set_context(popup, instance);
- popup_set_callback(popup, subbrute_popup_closed_callback);
- popup_enable_timeout(popup);
- view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewPopup);
-}
-
-bool subbrute_scene_save_success_on_event(void* context, SceneManagerEvent event) {
- furi_assert(context);
-
- SubBruteState* instance = (SubBruteState*)context;
- //SubBruteMainView* view = instance->view_main;
-
- if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == SubBruteCustomEventTypePopupClosed) {
- if(!scene_manager_search_and_switch_to_previous_scene(
- instance->scene_manager, SubBruteSceneSetupAttack)) {
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
- }
- return true;
- }
- }
- return false;
-}
-
-void subbrute_scene_save_success_on_exit(void* context) {
- furi_assert(context);
-
- SubBruteState* instance = (SubBruteState*)context;
-
- // Clear view
- Popup* popup = instance->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);
-}
diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c b/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c
deleted file mode 100644
index c2877c7cb..000000000
--- a/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c
+++ /dev/null
@@ -1,138 +0,0 @@
-#include "../subbrute_i.h"
-#include "subbrute_scene.h"
-
-#define TAG "SubBruteSceneSetupAttack"
-
-static void subbrute_scene_setup_attack_callback(SubBruteCustomEvent event, void* context) {
- furi_assert(context);
-
- SubBruteState* instance = (SubBruteState*)context;
- view_dispatcher_send_custom_event(instance->view_dispatcher, event);
-}
-
-static void
- subbrute_scene_setup_attack_device_state_changed(void* context, SubBruteWorkerState state) {
- furi_assert(context);
-
- SubBruteState* instance = (SubBruteState*)context;
-
- if(state == SubBruteWorkerStateIDLE) {
- // Can't be IDLE on this step!
- view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeError);
- }
-}
-
-void subbrute_scene_setup_attack_on_enter(void* context) {
- furi_assert(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: %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)) {
- subbrute_worker_stop(instance->worker);
- instance->device->current_step = subbrute_worker_get_step(instance->worker);
- }
-
- subbrute_attack_view_init_values(
- view,
- instance->device->attack,
- instance->device->max_value,
- instance->device->current_step,
- false,
- instance->device->extra_repeats);
-
- instance->current_view = SubBruteViewAttack;
- subbrute_attack_view_set_callback(view, subbrute_scene_setup_attack_callback, instance);
- view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
-}
-
-void subbrute_scene_setup_attack_on_exit(void* context) {
- furi_assert(context);
-#ifdef FURI_DEBUG
- FURI_LOG_D(TAG, "subbrute_scene_setup_attack_on_exit");
-#endif
- 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) {
- SubBruteState* instance = (SubBruteState*)context;
- SubBruteAttackView* view = instance->view_attack;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeCustom) {
- if(event.event == SubBruteCustomEventTypeTransmitStarted) {
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack);
- } else if(event.event == SubBruteCustomEventTypeSaveFile) {
- subbrute_attack_view_init_values(
- view,
- instance->device->attack,
- instance->device->max_value,
- instance->device->current_step,
- false,
- instance->device->extra_repeats);
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName);
- } else if(event.event == SubBruteCustomEventTypeBackPressed) {
- subbrute_attack_view_init_values(
- view,
- instance->device->attack,
- instance->device->max_value,
- instance->device->current_step,
- false,
- instance->device->extra_repeats);
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
- } else if(event.event == SubBruteCustomEventTypeError) {
- notification_message(instance->notifications, &sequence_error);
- } else if(event.event == SubBruteCustomEventTypeTransmitCustom) {
- // We can transmit only in not working states
- if(subbrute_worker_can_manual_transmit(instance->worker)) {
- // MANUAL Transmit!
- // Blink
- notification_message(instance->notifications, &sequence_blink_green_100);
- subbrute_worker_transmit_current_key(
- instance->worker, instance->device->current_step);
- // Stop
- notification_message(instance->notifications, &sequence_blink_stop);
- }
- } else if(event.event == SubBruteCustomEventTypeChangeStepUp) {
- // +1
- uint64_t step = subbrute_device_add_step(instance->device, 1);
- subbrute_worker_set_step(instance->worker, step);
- subbrute_attack_view_set_current_step(view, step);
- } else if(event.event == SubBruteCustomEventTypeChangeStepUpMore) {
- // +50
- uint64_t step = subbrute_device_add_step(instance->device, 50);
- subbrute_worker_set_step(instance->worker, step);
- subbrute_attack_view_set_current_step(view, step);
- } else if(event.event == SubBruteCustomEventTypeChangeStepDown) {
- // -1
- uint64_t step = subbrute_device_add_step(instance->device, -1);
- subbrute_worker_set_step(instance->worker, step);
- subbrute_attack_view_set_current_step(view, step);
- } else if(event.event == SubBruteCustomEventTypeChangeStepDownMore) {
- // -50
- uint64_t step = subbrute_device_add_step(instance->device, -50);
- subbrute_worker_set_step(instance->worker, step);
- subbrute_attack_view_set_current_step(view, step);
- }
-
- consumed = true;
- } else if(event.type == SceneManagerEventTypeTick) {
- if(subbrute_worker_is_running(instance->worker)) {
- instance->device->current_step = subbrute_worker_get_step(instance->worker);
- }
- subbrute_attack_view_set_current_step(view, instance->device->current_step);
- consumed = true;
- }
-
- return consumed;
-}
diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_start.c b/applications/plugins/subbrute/scenes/subbrute_scene_start.c
deleted file mode 100644
index 256762d92..000000000
--- a/applications/plugins/subbrute/scenes/subbrute_scene_start.c
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "../subbrute_i.h"
-#include "subbrute_scene.h"
-
-#define TAG "SubBruteSceneStart"
-
-void subbrute_scene_start_callback(SubBruteCustomEvent event, void* context) {
- furi_assert(context);
-
- SubBruteState* instance = (SubBruteState*)context;
-#ifdef FURI_DEBUG
- FURI_LOG_D(TAG, "subbrute_scene_start_callback");
-#endif
- view_dispatcher_send_custom_event(instance->view_dispatcher, event);
-}
-
-void subbrute_scene_start_on_enter(void* context) {
- furi_assert(context);
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "subbrute_scene_start_on_enter");
-#endif
- SubBruteState* instance = (SubBruteState*)context;
- SubBruteMainView* view = instance->view_main;
-
- instance->current_view = SubBruteViewMain;
- subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance);
- subbrute_main_view_set_index(
- view, instance->device->attack, false, instance->device->two_bytes, 0);
-
- view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
-
- // TODO: DELETE IT
-#ifdef SUBBRUTE_FAST_TRACK
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile);
-#endif
-}
-
-void subbrute_scene_start_on_exit(void* context) {
- UNUSED(context);
-#ifdef FURI_DEBUG
- FURI_LOG_I(TAG, "subbrute_scene_start_on_exit");
-#endif
-}
-
-bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) {
- SubBruteState* instance = (SubBruteState*)context;
- bool consumed = false;
-
- if(event.type == SceneManagerEventTypeCustom) {
-#ifdef FURI_DEBUG
- FURI_LOG_D(
- TAG,
- "Event: %ld, SubBruteCustomEventTypeMenuSelected: %s, SubBruteCustomEventTypeLoadFile: %s",
- event.event,
- event.event == SubBruteCustomEventTypeMenuSelected ? "true" : "false",
- event.event == SubBruteCustomEventTypeLoadFile ? "true" : "false");
-#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, extra_repeats) !=
- SubBruteFileResultOk) ||
- (!subbrute_worker_init_default_attack(
- instance->worker,
- attack,
- instance->device->current_step,
- 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;
- }
- } else if(event.type == SceneManagerEventTypeBack) {
- //exit app
- scene_manager_stop(instance->scene_manager);
- view_dispatcher_stop(instance->view_dispatcher);
- consumed = true;
- }
-
- return consumed;
-}
\ No newline at end of file
diff --git a/applications/plugins/subbrute/scenes/subbute_scene.c b/applications/plugins/subbrute/scenes/subbute_scene.c
deleted file mode 100644
index 6d9ba9799..000000000
--- a/applications/plugins/subbrute/scenes/subbute_scene.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "subbrute_scene.h"
-
-// Generate scene on_enter handlers array
-#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
-void (*const subbrute_on_enter_handlers[])(void*) = {
-#include "subbrute_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 subbrute_on_event_handlers[])(void* context, SceneManagerEvent event) = {
-#include "subbrute_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 subbrute_on_exit_handlers[])(void* context) = {
-#include "subbrute_scene_config.h"
-};
-#undef ADD_SCENE
-
-// Initialize scene handlers configuration structure
-const SceneManagerHandlers subbrute_scene_handlers = {
- .on_enter_handlers = subbrute_on_enter_handlers,
- .on_event_handlers = subbrute_on_event_handlers,
- .on_exit_handlers = subbrute_on_exit_handlers,
- .scene_num = SubBruteSceneNum,
-};
diff --git a/applications/plugins/subbrute/subbrute.c b/applications/plugins/subbrute/subbrute.c
deleted file mode 100644
index aaab3aeb1..000000000
--- a/applications/plugins/subbrute/subbrute.c
+++ /dev/null
@@ -1,188 +0,0 @@
-#include "subbrute_i.h"
-#include "subbrute_custom_event.h"
-#include "scenes/subbrute_scene.h"
-
-#define TAG "SubBruteApp"
-
-static bool subbrute_custom_event_callback(void* context, uint32_t event) {
- furi_assert(context);
- SubBruteState* instance = context;
- return scene_manager_handle_custom_event(instance->scene_manager, event);
-}
-
-static bool subbrute_back_event_callback(void* context) {
- furi_assert(context);
- SubBruteState* instance = context;
- return scene_manager_handle_back_event(instance->scene_manager);
-}
-
-static void subbrute_tick_event_callback(void* context) {
- furi_assert(context);
- SubBruteState* instance = context;
- scene_manager_handle_tick_event(instance->scene_manager);
-}
-
-SubBruteState* subbrute_alloc() {
- SubBruteState* instance = malloc(sizeof(SubBruteState));
-
- memset(instance->text_store, 0, sizeof(instance->text_store));
- instance->file_path = furi_string_alloc();
-
- instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance);
- instance->view_dispatcher = view_dispatcher_alloc();
-
- instance->gui = furi_record_open(RECORD_GUI);
-
- view_dispatcher_enable_queue(instance->view_dispatcher);
- view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);
- view_dispatcher_set_custom_event_callback(
- instance->view_dispatcher, subbrute_custom_event_callback);
- view_dispatcher_set_navigation_event_callback(
- instance->view_dispatcher, subbrute_back_event_callback);
- view_dispatcher_set_tick_event_callback(
- instance->view_dispatcher, subbrute_tick_event_callback, 100);
-
- //Dialog
- instance->dialogs = furi_record_open(RECORD_DIALOGS);
-
- // Notifications
- instance->notifications = furi_record_open(RECORD_NOTIFICATION);
-
- // Devices
- instance->device = subbrute_device_alloc();
-
- // SubBruteWorker
- instance->worker = subbrute_worker_alloc();
-
- // TextInput
- instance->text_input = text_input_alloc();
- view_dispatcher_add_view(
- instance->view_dispatcher,
- SubBruteViewTextInput,
- text_input_get_view(instance->text_input));
-
- // Custom Widget
- instance->widget = widget_alloc();
- view_dispatcher_add_view(
- instance->view_dispatcher, SubBruteViewWidget, widget_get_view(instance->widget));
-
- // Popup
- instance->popup = popup_alloc();
- view_dispatcher_add_view(
- instance->view_dispatcher, SubBruteViewPopup, popup_get_view(instance->popup));
-
- // ViewStack
- instance->view_stack = view_stack_alloc();
- view_dispatcher_add_view(
- instance->view_dispatcher, SubBruteViewStack, view_stack_get_view(instance->view_stack));
-
- // SubBruteMainView
- instance->view_main = subbrute_main_view_alloc();
- view_dispatcher_add_view(
- instance->view_dispatcher,
- SubBruteViewMain,
- subbrute_main_view_get_view(instance->view_main));
-
- // SubBruteAttackView
- instance->view_attack = subbrute_attack_view_alloc();
- view_dispatcher_add_view(
- instance->view_dispatcher,
- SubBruteViewAttack,
- subbrute_attack_view_get_view(instance->view_attack));
-
- //instance->flipper_format = flipper_format_string_alloc();
- //instance->environment = subghz_environment_alloc();
-
- return instance;
-}
-
-void subbrute_free(SubBruteState* instance) {
- furi_assert(instance);
-
- // SubBruteWorker
- subbrute_worker_stop(instance->worker);
- subbrute_worker_free(instance->worker);
-
- // SubBruteDevice
- subbrute_device_free(instance->device);
-
- // Notifications
- notification_message(instance->notifications, &sequence_blink_stop);
- furi_record_close(RECORD_NOTIFICATION);
- instance->notifications = NULL;
-
- // View Main
- view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewMain);
- subbrute_main_view_free(instance->view_main);
-
- // View Attack
- view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewAttack);
- subbrute_attack_view_free(instance->view_attack);
-
- // TextInput
- view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewTextInput);
- text_input_free(instance->text_input);
-
- // Custom Widget
- view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewWidget);
- widget_free(instance->widget);
-
- // Popup
- view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewPopup);
- popup_free(instance->popup);
-
- // ViewStack
- view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewStack);
- view_stack_free(instance->view_stack);
-
- //Dialog
- furi_record_close(RECORD_DIALOGS);
- instance->dialogs = NULL;
-
- // Scene manager
- scene_manager_free(instance->scene_manager);
-
- // View Dispatcher
- view_dispatcher_free(instance->view_dispatcher);
-
- // GUI
- furi_record_close(RECORD_GUI);
- instance->gui = NULL;
-
- furi_string_free(instance->file_path);
-
- // The rest
- free(instance);
-}
-
-void subbrute_text_input_callback(void* context) {
- furi_assert(context);
- SubBruteState* instance = context;
- view_dispatcher_send_custom_event(
- instance->view_dispatcher, SubBruteCustomEventTypeTextEditDone);
-}
-
-void subbrute_popup_closed_callback(void* context) {
- furi_assert(context);
- SubBruteState* instance = context;
- view_dispatcher_send_custom_event(
- instance->view_dispatcher, SubBruteCustomEventTypePopupClosed);
-}
-
-// ENTRYPOINT
-int32_t subbrute_app(void* p) {
- UNUSED(p);
-
- SubBruteState* instance = subbrute_alloc();
- view_dispatcher_attach_to_gui(
- instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
- scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
-
- furi_hal_power_suppress_charge_enter();
- notification_message(instance->notifications, &sequence_display_backlight_on);
- view_dispatcher_run(instance->view_dispatcher);
- furi_hal_power_suppress_charge_exit();
- subbrute_free(instance);
-
- return 0;
-}
\ No newline at end of file
diff --git a/applications/plugins/subbrute/subbrute.h b/applications/plugins/subbrute/subbrute.h
deleted file mode 100644
index 5fedb9158..000000000
--- a/applications/plugins/subbrute/subbrute.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-typedef struct SubBruteState SubBruteState;
\ No newline at end of file
diff --git a/applications/plugins/subbrute/subbrute_custom_event.h b/applications/plugins/subbrute/subbrute_custom_event.h
deleted file mode 100644
index 2864f8934..000000000
--- a/applications/plugins/subbrute/subbrute_custom_event.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-typedef enum {
- // Reserve first 100 events for button types and indexes, starting from 0
- SubBruteCustomEventTypeReserved = 100,
-
- SubBruteCustomEventTypeBackPressed,
- SubBruteCustomEventTypeIndexSelected,
- SubBruteCustomEventTypeTransmitStarted,
- SubBruteCustomEventTypeError,
- SubBruteCustomEventTypeTransmitFinished,
- SubBruteCustomEventTypeTransmitNotStarted,
- SubBruteCustomEventTypeTransmitCustom,
- SubBruteCustomEventTypeSaveFile,
- SubBruteCustomEventTypeUpdateView,
- SubBruteCustomEventTypeChangeStepUp,
- SubBruteCustomEventTypeChangeStepDown,
- SubBruteCustomEventTypeChangeStepUpMore,
- SubBruteCustomEventTypeChangeStepDownMore,
-
- SubBruteCustomEventTypeMenuSelected,
- SubBruteCustomEventTypeTextEditDone,
- SubBruteCustomEventTypePopupClosed,
-
- SubBruteCustomEventTypeLoadFile,
-} SubBruteCustomEvent;
\ No newline at end of file
diff --git a/applications/plugins/subbrute/subbrute_device.c b/applications/plugins/subbrute/subbrute_device.c
deleted file mode 100644
index 0971c380e..000000000
--- a/applications/plugins/subbrute/subbrute_device.c
+++ /dev/null
@@ -1,460 +0,0 @@
-#include "subbrute_device.h"
-
-#include
-#include
-#include
-#include
-#include