Merge branch 'dev' into ntag-auto-pwd-capture
3
.github/workflows/amap_analyse.yml
vendored
@@ -62,6 +62,8 @@ jobs:
|
|||||||
|
|
||||||
- name: 'Download build artifacts'
|
- name: 'Download build artifacts'
|
||||||
run: |
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts
|
||||||
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
||||||
chmod 600 ./deploy_key;
|
chmod 600 ./deploy_key;
|
||||||
rsync -avzP \
|
rsync -avzP \
|
||||||
@@ -97,3 +99,4 @@ jobs:
|
|||||||
${{ secrets.AMAP_MARIADB_PORT }} \
|
${{ secrets.AMAP_MARIADB_PORT }} \
|
||||||
${{ secrets.AMAP_MARIADB_DATABASE }} \
|
${{ secrets.AMAP_MARIADB_DATABASE }} \
|
||||||
artifacts/flipper-z-f7-firmware-$SUFFIX.elf.map.all
|
artifacts/flipper-z-f7-firmware-$SUFFIX.elf.map.all
|
||||||
|
|
||||||
|
|||||||
24
.github/workflows/build.yml
vendored
@@ -56,14 +56,14 @@ jobs:
|
|||||||
- name: 'Bundle scripts'
|
- name: 'Bundle scripts'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
run: |
|
run: |
|
||||||
tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts
|
tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug
|
||||||
|
|
||||||
- name: 'Build the firmware'
|
- name: 'Build the firmware'
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
for TARGET in ${TARGETS}; do
|
for TARGET in ${TARGETS}; do
|
||||||
FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
|
FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
|
||||||
updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
|
copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: 'Move upload files'
|
- name: 'Move upload files'
|
||||||
@@ -74,17 +74,6 @@ jobs:
|
|||||||
mv dist/${TARGET}-*/* artifacts/
|
mv dist/${TARGET}-*/* artifacts/
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: 'Bundle self-update package'
|
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
for UPDATEBUNDLE in artifacts/*/; do
|
|
||||||
BUNDLE_NAME="$(echo "$UPDATEBUNDLE" | cut -d'/' -f2)"
|
|
||||||
echo Packaging "${BUNDLE_NAME}"
|
|
||||||
tar czpf "artifacts/flipper-z-${BUNDLE_NAME}.tgz" -C artifacts "${BUNDLE_NAME}"
|
|
||||||
rm -rf "artifacts/${BUNDLE_NAME}"
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: "Check for uncommitted changes"
|
- name: "Check for uncommitted changes"
|
||||||
run: |
|
run: |
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
@@ -97,8 +86,7 @@ jobs:
|
|||||||
- name: 'Bundle core2 firmware'
|
- name: 'Bundle core2 firmware'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
run: |
|
run: |
|
||||||
FBT_TOOLCHAIN_PATH=/opt ./fbt copro_dist
|
cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz"
|
||||||
tar czpf "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" -C assets core2_firmware
|
|
||||||
|
|
||||||
- name: 'Copy .map file'
|
- name: 'Copy .map file'
|
||||||
run: |
|
run: |
|
||||||
@@ -107,6 +95,8 @@ jobs:
|
|||||||
- name: 'Upload artifacts to update server'
|
- name: 'Upload artifacts to update server'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
run: |
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts
|
||||||
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
||||||
chmod 600 ./deploy_key;
|
chmod 600 ./deploy_key;
|
||||||
rsync -avzP --delete --mkpath \
|
rsync -avzP --delete --mkpath \
|
||||||
@@ -174,6 +164,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
for TARGET in ${TARGETS}; do
|
for TARGET in ${TARGETS}; do
|
||||||
FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
|
FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
|
||||||
updater_package DEBUG=0 COMPACT=1
|
updater_package DEBUG=0 COMPACT=1
|
||||||
done
|
done
|
||||||
|
|||||||
1
.github/workflows/check_submodules.yml
vendored
@@ -27,6 +27,7 @@ jobs:
|
|||||||
|
|
||||||
- name: 'Check protobuf branch'
|
- name: 'Check protobuf branch'
|
||||||
run: |
|
run: |
|
||||||
|
git submodule update --init
|
||||||
SUB_PATH="assets/protobuf";
|
SUB_PATH="assets/protobuf";
|
||||||
SUB_BRANCH="dev";
|
SUB_BRANCH="dev";
|
||||||
SUB_COMMITS_MIN=40;
|
SUB_COMMITS_MIN=40;
|
||||||
|
|||||||
2
.github/workflows/lint_c.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
|
|
||||||
- name: 'Check code formatting'
|
- name: 'Check code formatting'
|
||||||
id: syntax_check
|
id: syntax_check
|
||||||
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint
|
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/runner/_work ./fbt lint
|
||||||
|
|
||||||
- name: Report code formatting errors
|
- name: Report code formatting errors
|
||||||
if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request
|
if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request
|
||||||
|
|||||||
2
.github/workflows/lint_python.yml
vendored
@@ -26,4 +26,4 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: 'Check code formatting'
|
- name: 'Check code formatting'
|
||||||
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint_py
|
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/runner/_work ./fbt lint_py
|
||||||
|
|||||||
6
.github/workflows/pvs_studio.yml
vendored
@@ -57,11 +57,11 @@ jobs:
|
|||||||
|
|
||||||
- name: 'Generate compile_comands.json'
|
- name: 'Generate compile_comands.json'
|
||||||
run: |
|
run: |
|
||||||
FBT_TOOLCHAIN_PATH=/opt ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking
|
FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking
|
||||||
|
|
||||||
- name: 'Static code analysis'
|
- name: 'Static code analysis'
|
||||||
run: |
|
run: |
|
||||||
FBT_TOOLCHAIN_PATH=/opt source scripts/toolchain/fbtenv.sh
|
FBT_TOOLCHAIN_PATH=/runner/_work source scripts/toolchain/fbtenv.sh
|
||||||
pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }}
|
pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }}
|
||||||
pvs-studio-analyzer analyze \
|
pvs-studio-analyzer analyze \
|
||||||
@.pvsoptions \
|
@.pvsoptions \
|
||||||
@@ -76,6 +76,8 @@ jobs:
|
|||||||
- name: 'Upload artifacts to update server'
|
- name: 'Upload artifacts to update server'
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
run: |
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts
|
||||||
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
||||||
chmod 600 ./deploy_key;
|
chmod 600 ./deploy_key;
|
||||||
rsync -avrzP --mkpath \
|
rsync -avrzP --mkpath \
|
||||||
|
|||||||
63
SConstruct
@@ -7,7 +7,6 @@
|
|||||||
# construction of certain targets behind command-line options.
|
# construction of certain targets behind command-line options.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
|
|
||||||
DefaultEnvironment(tools=[])
|
DefaultEnvironment(tools=[])
|
||||||
|
|
||||||
@@ -15,17 +14,22 @@ EnsurePythonVersion(3, 8)
|
|||||||
|
|
||||||
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
|
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
|
||||||
|
|
||||||
|
|
||||||
# This environment is created only for loading options & validating file/dir existence
|
# This environment is created only for loading options & validating file/dir existence
|
||||||
fbt_variables = SConscript("site_scons/commandline.scons")
|
fbt_variables = SConscript("site_scons/commandline.scons")
|
||||||
cmd_environment = Environment(tools=[], variables=fbt_variables)
|
cmd_environment = Environment(
|
||||||
Help(fbt_variables.GenerateHelpText(cmd_environment))
|
toolpath=["#/scripts/fbt_tools"],
|
||||||
|
tools=[
|
||||||
|
("fbt_help", {"vars": fbt_variables}),
|
||||||
|
],
|
||||||
|
variables=fbt_variables,
|
||||||
|
)
|
||||||
|
|
||||||
# Building basic environment - tools, utility methods, cross-compilation
|
# Building basic environment - tools, utility methods, cross-compilation
|
||||||
# settings, gcc flags for Cortex-M4, basic builders and more
|
# settings, gcc flags for Cortex-M4, basic builders and more
|
||||||
coreenv = SConscript(
|
coreenv = SConscript(
|
||||||
"site_scons/environ.scons",
|
"site_scons/environ.scons",
|
||||||
exports={"VAR_ENV": cmd_environment},
|
exports={"VAR_ENV": cmd_environment},
|
||||||
|
toolpath=["#/scripts/fbt_tools"],
|
||||||
)
|
)
|
||||||
SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
|
SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
|
||||||
|
|
||||||
@@ -35,41 +39,13 @@ coreenv["ROOT_DIR"] = Dir(".")
|
|||||||
|
|
||||||
# Create a separate "dist" environment and add construction envs to it
|
# Create a separate "dist" environment and add construction envs to it
|
||||||
distenv = coreenv.Clone(
|
distenv = coreenv.Clone(
|
||||||
tools=["fbt_dist", "openocd", "blackmagic", "jflash"],
|
tools=[
|
||||||
OPENOCD_GDB_PIPE=[
|
"fbt_dist",
|
||||||
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
"fbt_debugopts",
|
||||||
|
"openocd",
|
||||||
|
"blackmagic",
|
||||||
|
"jflash",
|
||||||
],
|
],
|
||||||
GDBOPTS_BASE=[
|
|
||||||
"-ex",
|
|
||||||
"target extended-remote ${GDBREMOTE}",
|
|
||||||
"-ex",
|
|
||||||
"set confirm off",
|
|
||||||
"-ex",
|
|
||||||
"set pagination off",
|
|
||||||
],
|
|
||||||
GDBOPTS_BLACKMAGIC=[
|
|
||||||
"-ex",
|
|
||||||
"monitor swdp_scan",
|
|
||||||
"-ex",
|
|
||||||
"monitor debug_bmp enable",
|
|
||||||
"-ex",
|
|
||||||
"attach 1",
|
|
||||||
"-ex",
|
|
||||||
"set mem inaccessible-by-default off",
|
|
||||||
],
|
|
||||||
GDBPYOPTS=[
|
|
||||||
"-ex",
|
|
||||||
"source debug/FreeRTOS/FreeRTOS.py",
|
|
||||||
"-ex",
|
|
||||||
"source debug/flipperapps.py",
|
|
||||||
"-ex",
|
|
||||||
"source debug/PyCortexMDebug/PyCortexMDebug.py",
|
|
||||||
"-ex",
|
|
||||||
"svd_load ${SVD_FILE}",
|
|
||||||
"-ex",
|
|
||||||
"compare-sections",
|
|
||||||
],
|
|
||||||
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
|
|
||||||
ENV=os.environ,
|
ENV=os.environ,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -166,7 +142,7 @@ basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
|
|||||||
distenv.Default(basic_dist)
|
distenv.Default(basic_dist)
|
||||||
|
|
||||||
dist_dir = distenv.GetProjetDirName()
|
dist_dir = distenv.GetProjetDirName()
|
||||||
plugin_dist = [
|
fap_dist = [
|
||||||
distenv.Install(
|
distenv.Install(
|
||||||
f"#/dist/{dist_dir}/apps/debug_elf",
|
f"#/dist/{dist_dir}/apps/debug_elf",
|
||||||
firmware_env["FW_EXTAPPS"]["debug"].values(),
|
firmware_env["FW_EXTAPPS"]["debug"].values(),
|
||||||
@@ -176,9 +152,9 @@ plugin_dist = [
|
|||||||
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
Depends(plugin_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
|
Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
|
||||||
Alias("plugin_dist", plugin_dist)
|
Alias("fap_dist", fap_dist)
|
||||||
# distenv.Default(plugin_dist)
|
# distenv.Default(fap_dist)
|
||||||
|
|
||||||
plugin_resources_dist = list(
|
plugin_resources_dist = list(
|
||||||
distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
|
distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
|
||||||
@@ -189,9 +165,10 @@ distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist)
|
|||||||
|
|
||||||
# Target for bundling core2 package for qFlipper
|
# Target for bundling core2 package for qFlipper
|
||||||
copro_dist = distenv.CoproBuilder(
|
copro_dist = distenv.CoproBuilder(
|
||||||
distenv.Dir("assets/core2_firmware"),
|
"#/build/core2_firmware.tgz",
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
distenv.AlwaysBuild(copro_dist)
|
||||||
distenv.Alias("copro_dist", copro_dist)
|
distenv.Alias("copro_dist", copro_dist)
|
||||||
|
|
||||||
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
|
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
|
|
||||||
|
/* Magic happens here -- this file is generated by fbt.
|
||||||
|
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
|
||||||
#include "example_images_icons.h"
|
#include "example_images_icons.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -231,7 +231,8 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
static int32_t
|
||||||
|
ducky_parse_line(BadUsbScript* bad_usb, FuriString* line, char* error, size_t error_len) {
|
||||||
uint32_t line_len = furi_string_size(line);
|
uint32_t line_len = furi_string_size(line);
|
||||||
const char* line_tmp = furi_string_get_cstr(line);
|
const char* line_tmp = furi_string_get_cstr(line);
|
||||||
bool state = false;
|
bool state = false;
|
||||||
@@ -261,6 +262,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
if((state) && (delay_val > 0)) {
|
if((state) && (delay_val > 0)) {
|
||||||
return (int32_t)delay_val;
|
return (int32_t)delay_val;
|
||||||
}
|
}
|
||||||
|
if(error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return SCRIPT_STATE_ERROR;
|
return SCRIPT_STATE_ERROR;
|
||||||
} else if(
|
} else if(
|
||||||
(strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
|
(strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
|
||||||
@@ -268,17 +272,26 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
// DEFAULT_DELAY
|
// DEFAULT_DELAY
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_get_number(line_tmp, &bad_usb->defdelay);
|
state = ducky_get_number(line_tmp, &bad_usb->defdelay);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
||||||
// STRING
|
// STRING
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_string(line_tmp);
|
state = ducky_string(line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid string %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
|
||||||
// ALTCHAR
|
// ALTCHAR
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
ducky_numlock_on();
|
ducky_numlock_on();
|
||||||
state = ducky_altchar(line_tmp);
|
state = ducky_altchar(line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid altchar %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(
|
} else if(
|
||||||
(strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
|
(strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
|
||||||
@@ -287,11 +300,17 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
ducky_numlock_on();
|
ducky_numlock_on();
|
||||||
state = ducky_altstring(line_tmp);
|
state = ducky_altstring(line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid altstring %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
|
||||||
// REPEAT
|
// REPEAT
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt);
|
state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
|
||||||
// SYSRQ
|
// SYSRQ
|
||||||
@@ -304,7 +323,12 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
} else {
|
} else {
|
||||||
// Special keys + modifiers
|
// Special keys + modifiers
|
||||||
uint16_t key = ducky_get_keycode(line_tmp, false);
|
uint16_t key = ducky_get_keycode(line_tmp, false);
|
||||||
if(key == HID_KEYBOARD_NONE) return SCRIPT_STATE_ERROR;
|
if(key == HID_KEYBOARD_NONE) {
|
||||||
|
if(error != NULL) {
|
||||||
|
snprintf(error, error_len, "No keycode defined for %s", line_tmp);
|
||||||
|
}
|
||||||
|
return SCRIPT_STATE_ERROR;
|
||||||
|
}
|
||||||
if((key & 0xFF00) != 0) {
|
if((key & 0xFF00) != 0) {
|
||||||
// It's a modifier key
|
// It's a modifier key
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
@@ -314,6 +338,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
furi_hal_hid_kb_release(key);
|
furi_hal_hid_kb_release(key);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
if(error != NULL) {
|
||||||
|
strncpy(error, "Unknown error", error_len);
|
||||||
|
}
|
||||||
return SCRIPT_STATE_ERROR;
|
return SCRIPT_STATE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +419,8 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
|||||||
|
|
||||||
if(bad_usb->repeat_cnt > 0) {
|
if(bad_usb->repeat_cnt > 0) {
|
||||||
bad_usb->repeat_cnt--;
|
bad_usb->repeat_cnt--;
|
||||||
delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev);
|
delay_val = ducky_parse_line(
|
||||||
|
bad_usb, bad_usb->line_prev, bad_usb->st.error, sizeof(bad_usb->st.error));
|
||||||
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||||
return 0;
|
return 0;
|
||||||
} else if(delay_val < 0) { // Script error
|
} else if(delay_val < 0) { // Script error
|
||||||
@@ -426,7 +454,9 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
|||||||
bad_usb->st.line_cur++;
|
bad_usb->st.line_cur++;
|
||||||
bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
|
bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
|
||||||
bad_usb->buf_start = i + 1;
|
bad_usb->buf_start = i + 1;
|
||||||
delay_val = ducky_parse_line(bad_usb, bad_usb->line);
|
delay_val = ducky_parse_line(
|
||||||
|
bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error));
|
||||||
|
|
||||||
if(delay_val < 0) {
|
if(delay_val < 0) {
|
||||||
bad_usb->st.error_line = bad_usb->st.line_cur;
|
bad_usb->st.error_line = bad_usb->st.line_cur;
|
||||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
|
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
|
||||||
@@ -602,6 +632,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
|||||||
furi_string_set(bad_usb->file_path, file_path);
|
furi_string_set(bad_usb->file_path, file_path);
|
||||||
|
|
||||||
bad_usb->st.state = BadUsbStateInit;
|
bad_usb->st.state = BadUsbStateInit;
|
||||||
|
bad_usb->st.error[0] = '\0';
|
||||||
|
|
||||||
bad_usb->thread = furi_thread_alloc();
|
bad_usb->thread = furi_thread_alloc();
|
||||||
furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
|
furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ typedef struct {
|
|||||||
uint16_t line_nb;
|
uint16_t line_nb;
|
||||||
uint32_t delay_remain;
|
uint32_t delay_remain;
|
||||||
uint16_t error_line;
|
uint16_t error_line;
|
||||||
|
char error[64];
|
||||||
} BadUsbState;
|
} BadUsbState;
|
||||||
|
|
||||||
BadUsbScript* bad_usb_script_open(FuriString* file_path);
|
BadUsbScript* bad_usb_script_open(FuriString* file_path);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
furi_string_reset(disp_str);
|
furi_string_reset(disp_str);
|
||||||
|
canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error);
|
||||||
} else if(model->state.state == BadUsbStateIdle) {
|
} else if(model->state.state == BadUsbStateIdle) {
|
||||||
canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18);
|
canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18);
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
canvas_set_font(canvas, FontBigNumbers);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "../gpio_app_i.h"
|
#include "../gpio_app_i.h"
|
||||||
#include "furi_hal_power.h"
|
#include "furi_hal_power.h"
|
||||||
#include "furi_hal_usb.h"
|
#include "furi_hal_usb.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
enum GpioItem {
|
enum GpioItem {
|
||||||
GpioItemUsbUart,
|
GpioItemUsbUart,
|
||||||
@@ -88,6 +89,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|||||||
} else if(event.event == GpioStartEventUsbUart) {
|
} else if(event.event == GpioStartEventUsbUart) {
|
||||||
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
|
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
|
||||||
if(!furi_hal_usb_is_locked()) {
|
if(!furi_hal_usb_is_locked()) {
|
||||||
|
DOLPHIN_DEED(DolphinDeedGpioUartBridge);
|
||||||
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
|
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
|
||||||
} else {
|
} else {
|
||||||
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);
|
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e
|
|||||||
uint32_t record_count;
|
uint32_t record_count;
|
||||||
if(infrared_brute_force_start(
|
if(infrared_brute_force_start(
|
||||||
brute_force, infrared_custom_event_get_value(event.event), &record_count)) {
|
brute_force, infrared_custom_event_get_value(event.event), &record_count)) {
|
||||||
DOLPHIN_DEED(DolphinDeedIrBruteForce);
|
DOLPHIN_DEED(DolphinDeedIrSend);
|
||||||
infrared_scene_universal_common_show_popup(infrared, record_count);
|
infrared_scene_universal_common_show_popup(infrared, record_count);
|
||||||
} else {
|
} else {
|
||||||
scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases);
|
scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ void nfc_scene_detect_reader_callback(void* context) {
|
|||||||
|
|
||||||
void nfc_scene_detect_reader_on_enter(void* context) {
|
void nfc_scene_detect_reader_on_enter(void* context) {
|
||||||
Nfc* nfc = context;
|
Nfc* nfc = context;
|
||||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
|
||||||
|
|
||||||
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
|
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
|
||||||
detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);
|
detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "../nfc_i.h"
|
#include "../nfc_i.h"
|
||||||
|
|
||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexSave,
|
|
||||||
SubmenuIndexInfo,
|
SubmenuIndexInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15,7 +14,6 @@ void nfc_scene_emv_menu_on_enter(void* context) {
|
|||||||
Nfc* nfc = context;
|
Nfc* nfc = context;
|
||||||
Submenu* submenu = nfc->submenu;
|
Submenu* submenu = nfc->submenu;
|
||||||
|
|
||||||
submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc);
|
|
||||||
submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
|
submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
|
||||||
submenu_set_selected_item(
|
submenu_set_selected_item(
|
||||||
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
|
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
|
||||||
@@ -28,13 +26,7 @@ bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SubmenuIndexSave) {
|
if(event.event == SubmenuIndexInfo) {
|
||||||
nfc->dev->format = NfcDeviceSaveFormatBankCard;
|
|
||||||
// Clear device name
|
|
||||||
nfc_device_set_name(nfc->dev, "");
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
|
||||||
consumed = true;
|
|
||||||
} else if(event.event == SubmenuIndexInfo) {
|
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,33 @@ void nfc_scene_emv_read_success_on_enter(void* context) {
|
|||||||
nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc);
|
nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc);
|
||||||
|
|
||||||
FuriString* temp_str;
|
FuriString* temp_str;
|
||||||
temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
|
if(emv_data->name[0] != '\0') {
|
||||||
for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
|
temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
|
||||||
furi_string_cat_printf(
|
} else {
|
||||||
temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
|
temp_str = furi_string_alloc_printf("\e#Unknown Bank Card\n");
|
||||||
|
}
|
||||||
|
if(emv_data->number_len) {
|
||||||
|
for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
|
||||||
|
furi_string_cat_printf(
|
||||||
|
temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
|
||||||
|
}
|
||||||
|
furi_string_trim(temp_str);
|
||||||
|
} else if(emv_data->aid_len) {
|
||||||
|
furi_string_cat_printf(temp_str, "Can't parse data from app\n");
|
||||||
|
// Parse AID name
|
||||||
|
FuriString* aid_name;
|
||||||
|
aid_name = furi_string_alloc();
|
||||||
|
if(nfc_emv_parser_get_aid_name(
|
||||||
|
nfc->dev->storage, emv_data->aid, emv_data->aid_len, aid_name)) {
|
||||||
|
furi_string_cat_printf(temp_str, "AID: %s", furi_string_get_cstr(aid_name));
|
||||||
|
} else {
|
||||||
|
furi_string_cat_printf(temp_str, "AID: ");
|
||||||
|
for(uint8_t i = 0; i < emv_data->aid_len; i++) {
|
||||||
|
furi_string_cat_printf(temp_str, "%02X", emv_data->aid[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
furi_string_free(aid_name);
|
||||||
}
|
}
|
||||||
furi_string_trim(temp_str);
|
|
||||||
|
|
||||||
// Add expiration date
|
// Add expiration date
|
||||||
if(emv_data->exp_mon) {
|
if(emv_data->exp_mon) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "../nfc_i.h"
|
#include "../nfc_i.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexSave,
|
SubmenuIndexSave,
|
||||||
@@ -35,6 +36,8 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
|
|||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == SubmenuIndexSave) {
|
if(event.event == SubmenuIndexSave) {
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcMfcAdd);
|
||||||
|
|
||||||
scene_manager_set_scene_state(
|
scene_manager_set_scene_state(
|
||||||
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave);
|
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave);
|
||||||
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
|
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ void nfc_scene_mf_ultralight_read_auth_result_widget_callback(
|
|||||||
|
|
||||||
void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
|
void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
|
||||||
Nfc* nfc = context;
|
Nfc* nfc = context;
|
||||||
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
|
||||||
|
|
||||||
// Setup dialog view
|
// Setup dialog view
|
||||||
FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
|
FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
|
||||||
@@ -38,6 +37,7 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
|
|||||||
widget_add_string_element(
|
widget_add_string_element(
|
||||||
widget, 0, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
|
widget, 0, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
|
||||||
if(mf_ul_data->auth_success) {
|
if(mf_ul_data->auth_success) {
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
|
||||||
furi_string_printf(
|
furi_string_printf(
|
||||||
temp_str,
|
temp_str,
|
||||||
"Password: %02X %02X %02X %02X",
|
"Password: %02X %02X %02X %02X",
|
||||||
@@ -54,6 +54,8 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
|
|||||||
config_pages->auth_data.pack.raw[1]);
|
config_pages->auth_data.pack.raw[1]);
|
||||||
widget_add_string_element(
|
widget_add_string_element(
|
||||||
widget, 0, 39, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
|
widget, 0, 39, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
|
||||||
|
} else {
|
||||||
|
DOLPHIN_DEED(DolphinDeedNfcMfulError);
|
||||||
}
|
}
|
||||||
furi_string_printf(
|
furi_string_printf(
|
||||||
temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4);
|
temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
|
|||||||
|
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == NfcCustomEventByteInputDone) {
|
if(event.event == NfcCustomEventByteInputDone) {
|
||||||
DOLPHIN_DEED(DolphinDeedNfcAdd);
|
DOLPHIN_DEED(DolphinDeedNfcAddSave);
|
||||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
|
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
|
||||||
nfc->dev->dev_data.nfc_data = nfc->dev_edit_data;
|
nfc->dev->dev_data.nfc_data = nfc->dev_edit_data;
|
||||||
if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) {
|
if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) {
|
||||||
|
|||||||
@@ -5,6 +5,5 @@ App(
|
|||||||
provides=[
|
provides=[
|
||||||
"music_player",
|
"music_player",
|
||||||
"snake_game",
|
"snake_game",
|
||||||
"bt_hid",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
App(
|
App(
|
||||||
appid="bt_hid",
|
appid="bt_hid",
|
||||||
name="Bluetooth Remote",
|
name="Bluetooth Remote",
|
||||||
apptype=FlipperAppType.PLUGIN,
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
entry_point="bt_hid_app",
|
entry_point="bt_hid_app",
|
||||||
stack_size=1 * 1024,
|
stack_size=1 * 1024,
|
||||||
cdefines=["APP_BLE_HID"],
|
|
||||||
requires=[
|
|
||||||
"bt",
|
|
||||||
"gui",
|
|
||||||
],
|
|
||||||
order=10,
|
|
||||||
fap_icon="bt_remote_10px.png",
|
|
||||||
fap_category="Tools",
|
fap_category="Tools",
|
||||||
|
fap_icon="bt_remote_10px.png",
|
||||||
|
fap_icon_assets="assets",
|
||||||
)
|
)
|
||||||
|
|||||||
BIN
applications/plugins/bt_hid_app/assets/Arr_dwn_7x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Arr_up_7x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Ble_connected_15x15.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Button_18x18.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Circles_47x47.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/plugins/bt_hid_app/assets/Left_mouse_icon_9x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Like_def_11x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Like_pressed_17x17.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/plugins/bt_hid_app/assets/Ok_btn_pressed_13x13.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Pressed_Button_13x13.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Right_mouse_icon_9x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Space_65x18.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Voldwn_6x6.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/plugins/bt_hid_app/assets/Volup_8x6.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
@@ -1,6 +1,7 @@
|
|||||||
#include "bt_hid.h"
|
#include "bt_hid.h"
|
||||||
#include <furi_hal_bt.h>
|
#include <furi_hal_bt.h>
|
||||||
#include <notification/notification_messages.h>
|
#include <notification/notification_messages.h>
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
#define TAG "BtHidApp"
|
#define TAG "BtHidApp"
|
||||||
|
|
||||||
@@ -8,6 +9,7 @@ enum BtDebugSubmenuIndex {
|
|||||||
BtHidSubmenuIndexKeynote,
|
BtHidSubmenuIndexKeynote,
|
||||||
BtHidSubmenuIndexKeyboard,
|
BtHidSubmenuIndexKeyboard,
|
||||||
BtHidSubmenuIndexMedia,
|
BtHidSubmenuIndexMedia,
|
||||||
|
BtHidSubmenuIndexTikTok,
|
||||||
BtHidSubmenuIndexMouse,
|
BtHidSubmenuIndexMouse,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,6 +28,9 @@ void bt_hid_submenu_callback(void* context, uint32_t index) {
|
|||||||
} else if(index == BtHidSubmenuIndexMouse) {
|
} else if(index == BtHidSubmenuIndexMouse) {
|
||||||
app->view_id = BtHidViewMouse;
|
app->view_id = BtHidViewMouse;
|
||||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse);
|
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse);
|
||||||
|
} else if(index == BtHidSubmenuIndexTikTok) {
|
||||||
|
app->view_id = BtHidViewTikTok;
|
||||||
|
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +69,7 @@ void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
|
|||||||
bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected);
|
bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected);
|
||||||
bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected);
|
bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected);
|
||||||
bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected);
|
bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected);
|
||||||
|
bt_hid_tiktok_set_connected_status(bt_hid->bt_hid_tiktok, connected);
|
||||||
}
|
}
|
||||||
|
|
||||||
BtHid* bt_hid_app_alloc() {
|
BtHid* bt_hid_app_alloc() {
|
||||||
@@ -90,6 +96,8 @@ BtHid* bt_hid_app_alloc() {
|
|||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app);
|
app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app);
|
||||||
submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
|
submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
|
||||||
|
submenu_add_item(
|
||||||
|
app->submenu, "TikTok Controller", BtHidSubmenuIndexTikTok, bt_hid_submenu_callback, app);
|
||||||
submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app);
|
submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app);
|
||||||
view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit);
|
view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit);
|
||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
@@ -126,6 +134,13 @@ BtHid* bt_hid_app_alloc() {
|
|||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media));
|
app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media));
|
||||||
|
|
||||||
|
// TikTok view
|
||||||
|
app->bt_hid_tiktok = bt_hid_tiktok_alloc();
|
||||||
|
view_set_previous_callback(
|
||||||
|
bt_hid_tiktok_get_view(app->bt_hid_tiktok), bt_hid_exit_confirm_view);
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
app->view_dispatcher, BtHidViewTikTok, bt_hid_tiktok_get_view(app->bt_hid_tiktok));
|
||||||
|
|
||||||
// Mouse view
|
// Mouse view
|
||||||
app->bt_hid_mouse = bt_hid_mouse_alloc();
|
app->bt_hid_mouse = bt_hid_mouse_alloc();
|
||||||
view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view);
|
view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view);
|
||||||
@@ -158,6 +173,8 @@ void bt_hid_app_free(BtHid* app) {
|
|||||||
bt_hid_media_free(app->bt_hid_media);
|
bt_hid_media_free(app->bt_hid_media);
|
||||||
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse);
|
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse);
|
||||||
bt_hid_mouse_free(app->bt_hid_mouse);
|
bt_hid_mouse_free(app->bt_hid_mouse);
|
||||||
|
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
|
||||||
|
bt_hid_tiktok_free(app->bt_hid_tiktok);
|
||||||
view_dispatcher_free(app->view_dispatcher);
|
view_dispatcher_free(app->view_dispatcher);
|
||||||
|
|
||||||
// Close records
|
// Close records
|
||||||
@@ -185,6 +202,8 @@ int32_t bt_hid_app(void* p) {
|
|||||||
}
|
}
|
||||||
furi_hal_bt_start_advertising();
|
furi_hal_bt_start_advertising();
|
||||||
|
|
||||||
|
DOLPHIN_DEED(DolphinDeedPluginStart);
|
||||||
|
|
||||||
view_dispatcher_run(app->view_dispatcher);
|
view_dispatcher_run(app->view_dispatcher);
|
||||||
|
|
||||||
bt_set_status_changed_callback(app->bt, NULL, NULL);
|
bt_set_status_changed_callback(app->bt, NULL, NULL);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "views/bt_hid_keyboard.h"
|
#include "views/bt_hid_keyboard.h"
|
||||||
#include "views/bt_hid_media.h"
|
#include "views/bt_hid_media.h"
|
||||||
#include "views/bt_hid_mouse.h"
|
#include "views/bt_hid_mouse.h"
|
||||||
|
#include "views/bt_hid_tiktok.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Bt* bt;
|
Bt* bt;
|
||||||
@@ -25,6 +26,7 @@ typedef struct {
|
|||||||
BtHidKeyboard* bt_hid_keyboard;
|
BtHidKeyboard* bt_hid_keyboard;
|
||||||
BtHidMedia* bt_hid_media;
|
BtHidMedia* bt_hid_media;
|
||||||
BtHidMouse* bt_hid_mouse;
|
BtHidMouse* bt_hid_mouse;
|
||||||
|
BtHidTikTok* bt_hid_tiktok;
|
||||||
uint32_t view_id;
|
uint32_t view_id;
|
||||||
} BtHid;
|
} BtHid;
|
||||||
|
|
||||||
@@ -34,5 +36,6 @@ typedef enum {
|
|||||||
BtHidViewKeyboard,
|
BtHidViewKeyboard,
|
||||||
BtHidViewMedia,
|
BtHidViewMedia,
|
||||||
BtHidViewMouse,
|
BtHidViewMouse,
|
||||||
|
BtHidViewTikTok,
|
||||||
BtHidViewExitConfirm,
|
BtHidViewExitConfirm,
|
||||||
} BtHidView;
|
} BtHidView;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
#include <gui/icon_i.h>
|
#include <gui/icon_i.h>
|
||||||
|
|
||||||
|
#include "bt_hid_icons.h"
|
||||||
|
|
||||||
struct BtHidKeyboard {
|
struct BtHidKeyboard {
|
||||||
View* view;
|
View* view;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <furi_hal_usb_hid.h>
|
#include <furi_hal_usb_hid.h>
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
#include "bt_hid_icons.h"
|
||||||
|
|
||||||
struct BtHidKeynote {
|
struct BtHidKeynote {
|
||||||
View* view;
|
View* view;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <furi_hal_usb_hid.h>
|
#include <furi_hal_usb_hid.h>
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
#include "bt_hid_icons.h"
|
||||||
|
|
||||||
struct BtHidMedia {
|
struct BtHidMedia {
|
||||||
View* view;
|
View* view;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <furi_hal_usb_hid.h>
|
#include <furi_hal_usb_hid.h>
|
||||||
#include <gui/elements.h>
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
#include "bt_hid_icons.h"
|
||||||
|
|
||||||
struct BtHidMouse {
|
struct BtHidMouse {
|
||||||
View* view;
|
View* view;
|
||||||
};
|
};
|
||||||
|
|||||||
207
applications/plugins/bt_hid_app/views/bt_hid_tiktok.c
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
#include "bt_hid_tiktok.h"
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal_bt_hid.h>
|
||||||
|
#include <furi_hal_usb_hid.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
#include "bt_hid_icons.h"
|
||||||
|
|
||||||
|
struct BtHidTikTok {
|
||||||
|
View* view;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool left_pressed;
|
||||||
|
bool up_pressed;
|
||||||
|
bool right_pressed;
|
||||||
|
bool down_pressed;
|
||||||
|
bool ok_pressed;
|
||||||
|
bool connected;
|
||||||
|
} BtHidTikTokModel;
|
||||||
|
|
||||||
|
static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
BtHidTikTokModel* model = context;
|
||||||
|
|
||||||
|
// Header
|
||||||
|
if(model->connected) {
|
||||||
|
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
|
||||||
|
} else {
|
||||||
|
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
|
||||||
|
}
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok");
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
// Keypad circles
|
||||||
|
canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47);
|
||||||
|
|
||||||
|
// Up
|
||||||
|
if(model->up_pressed) {
|
||||||
|
canvas_set_bitmap_mode(canvas, 1);
|
||||||
|
canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
|
||||||
|
canvas_set_bitmap_mode(canvas, 0);
|
||||||
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
}
|
||||||
|
canvas_draw_icon(canvas, 96, 11, &I_Arr_up_7x9);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
|
// Down
|
||||||
|
if(model->down_pressed) {
|
||||||
|
canvas_set_bitmap_mode(canvas, 1);
|
||||||
|
canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);
|
||||||
|
canvas_set_bitmap_mode(canvas, 0);
|
||||||
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
}
|
||||||
|
canvas_draw_icon(canvas, 96, 44, &I_Arr_dwn_7x9);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
|
// Left
|
||||||
|
if(model->left_pressed) {
|
||||||
|
canvas_set_bitmap_mode(canvas, 1);
|
||||||
|
canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);
|
||||||
|
canvas_set_bitmap_mode(canvas, 0);
|
||||||
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
}
|
||||||
|
canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
|
// Right
|
||||||
|
if(model->right_pressed) {
|
||||||
|
canvas_set_bitmap_mode(canvas, 1);
|
||||||
|
canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);
|
||||||
|
canvas_set_bitmap_mode(canvas, 0);
|
||||||
|
canvas_set_color(canvas, ColorWhite);
|
||||||
|
}
|
||||||
|
canvas_draw_icon(canvas, 111, 29, &I_Volup_8x6);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
|
||||||
|
// Ok
|
||||||
|
if(model->ok_pressed) {
|
||||||
|
canvas_draw_icon(canvas, 91, 23, &I_Like_pressed_17x17);
|
||||||
|
} else {
|
||||||
|
canvas_draw_icon(canvas, 94, 27, &I_Like_def_11x9);
|
||||||
|
}
|
||||||
|
// Exit
|
||||||
|
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bt_hid_tiktok_process_press(BtHidTikTok* bt_hid_tiktok, InputEvent* event) {
|
||||||
|
with_view_model(
|
||||||
|
bt_hid_tiktok->view,
|
||||||
|
BtHidTikTokModel * model,
|
||||||
|
{
|
||||||
|
if(event->key == InputKeyUp) {
|
||||||
|
model->up_pressed = true;
|
||||||
|
} else if(event->key == InputKeyDown) {
|
||||||
|
model->down_pressed = true;
|
||||||
|
} else if(event->key == InputKeyLeft) {
|
||||||
|
model->left_pressed = true;
|
||||||
|
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT);
|
||||||
|
} else if(event->key == InputKeyRight) {
|
||||||
|
model->right_pressed = true;
|
||||||
|
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT);
|
||||||
|
} else if(event->key == InputKeyOk) {
|
||||||
|
model->ok_pressed = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bt_hid_tiktok_process_release(BtHidTikTok* bt_hid_tiktok, InputEvent* event) {
|
||||||
|
with_view_model(
|
||||||
|
bt_hid_tiktok->view,
|
||||||
|
BtHidTikTokModel * model,
|
||||||
|
{
|
||||||
|
if(event->key == InputKeyUp) {
|
||||||
|
model->up_pressed = false;
|
||||||
|
} else if(event->key == InputKeyDown) {
|
||||||
|
model->down_pressed = false;
|
||||||
|
} else if(event->key == InputKeyLeft) {
|
||||||
|
model->left_pressed = false;
|
||||||
|
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT);
|
||||||
|
} else if(event->key == InputKeyRight) {
|
||||||
|
model->right_pressed = false;
|
||||||
|
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT);
|
||||||
|
} else if(event->key == InputKeyOk) {
|
||||||
|
model->ok_pressed = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
BtHidTikTok* bt_hid_tiktok = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event->type == InputTypePress) {
|
||||||
|
bt_hid_tiktok_process_press(bt_hid_tiktok, event);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event->type == InputTypeRelease) {
|
||||||
|
bt_hid_tiktok_process_release(bt_hid_tiktok, event);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event->type == InputTypeShort) {
|
||||||
|
if(event->key == InputKeyOk) {
|
||||||
|
furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT);
|
||||||
|
furi_delay_ms(50);
|
||||||
|
furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT);
|
||||||
|
furi_delay_ms(50);
|
||||||
|
furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT);
|
||||||
|
furi_delay_ms(50);
|
||||||
|
furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event->key == InputKeyUp) {
|
||||||
|
// Emulate up swipe
|
||||||
|
furi_hal_bt_hid_mouse_scroll(-6);
|
||||||
|
furi_hal_bt_hid_mouse_scroll(-12);
|
||||||
|
furi_hal_bt_hid_mouse_scroll(-19);
|
||||||
|
furi_hal_bt_hid_mouse_scroll(-12);
|
||||||
|
furi_hal_bt_hid_mouse_scroll(-6);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event->key == InputKeyDown) {
|
||||||
|
// Emulate down swipe
|
||||||
|
furi_hal_bt_hid_mouse_scroll(6);
|
||||||
|
furi_hal_bt_hid_mouse_scroll(12);
|
||||||
|
furi_hal_bt_hid_mouse_scroll(19);
|
||||||
|
furi_hal_bt_hid_mouse_scroll(12);
|
||||||
|
furi_hal_bt_hid_mouse_scroll(6);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event->key == InputKeyBack) {
|
||||||
|
furi_hal_bt_hid_consumer_key_release_all();
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
BtHidTikTok* bt_hid_tiktok_alloc() {
|
||||||
|
BtHidTikTok* bt_hid_tiktok = malloc(sizeof(BtHidTikTok));
|
||||||
|
bt_hid_tiktok->view = view_alloc();
|
||||||
|
view_set_context(bt_hid_tiktok->view, bt_hid_tiktok);
|
||||||
|
view_allocate_model(bt_hid_tiktok->view, ViewModelTypeLocking, sizeof(BtHidTikTokModel));
|
||||||
|
view_set_draw_callback(bt_hid_tiktok->view, bt_hid_tiktok_draw_callback);
|
||||||
|
view_set_input_callback(bt_hid_tiktok->view, bt_hid_tiktok_input_callback);
|
||||||
|
|
||||||
|
return bt_hid_tiktok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok) {
|
||||||
|
furi_assert(bt_hid_tiktok);
|
||||||
|
view_free(bt_hid_tiktok->view);
|
||||||
|
free(bt_hid_tiktok);
|
||||||
|
}
|
||||||
|
|
||||||
|
View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok) {
|
||||||
|
furi_assert(bt_hid_tiktok);
|
||||||
|
return bt_hid_tiktok->view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected) {
|
||||||
|
furi_assert(bt_hid_tiktok);
|
||||||
|
with_view_model(
|
||||||
|
bt_hid_tiktok->view, BtHidTikTokModel * model, { model->connected = connected; }, true);
|
||||||
|
}
|
||||||
13
applications/plugins/bt_hid_app/views/bt_hid_tiktok.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
typedef struct BtHidTikTok BtHidTikTok;
|
||||||
|
|
||||||
|
BtHidTikTok* bt_hid_tiktok_alloc();
|
||||||
|
|
||||||
|
void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok);
|
||||||
|
|
||||||
|
View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok);
|
||||||
|
|
||||||
|
void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected);
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
#include <notification/notification.h>
|
#include <notification/notification.h>
|
||||||
#include <notification/notification_messages.h>
|
#include <notification/notification_messages.h>
|
||||||
|
|
||||||
@@ -346,6 +347,8 @@ int32_t snake_game_app(void* p) {
|
|||||||
|
|
||||||
notification_message_block(notification, &sequence_display_backlight_enforce_on);
|
notification_message_block(notification, &sequence_display_backlight_enforce_on);
|
||||||
|
|
||||||
|
DOLPHIN_DEED(DolphinDeedPluginGameStart);
|
||||||
|
|
||||||
SnakeEvent event;
|
SnakeEvent event;
|
||||||
for(bool processing = true; processing;) {
|
for(bool processing = true; processing;) {
|
||||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||||
|
|||||||
@@ -18,13 +18,15 @@ static const DolphinDeedWeight dolphin_deed_weights[] = {
|
|||||||
{1, DolphinAppNfc}, // DolphinDeedNfcRead
|
{1, DolphinAppNfc}, // DolphinDeedNfcRead
|
||||||
{3, DolphinAppNfc}, // DolphinDeedNfcReadSuccess
|
{3, DolphinAppNfc}, // DolphinDeedNfcReadSuccess
|
||||||
{3, DolphinAppNfc}, // DolphinDeedNfcSave
|
{3, DolphinAppNfc}, // DolphinDeedNfcSave
|
||||||
|
{1, DolphinAppNfc}, // DolphinDeedNfcDetectReader
|
||||||
{2, DolphinAppNfc}, // DolphinDeedNfcEmulate
|
{2, DolphinAppNfc}, // DolphinDeedNfcEmulate
|
||||||
{2, DolphinAppNfc}, // DolphinDeedNfcAdd
|
{2, DolphinAppNfc}, // DolphinDeedNfcMfcAdd
|
||||||
|
{1, DolphinAppNfc}, // DolphinDeedNfcMfulError
|
||||||
|
{1, DolphinAppNfc}, // DolphinDeedNfcAddSave
|
||||||
|
|
||||||
{1, DolphinAppIr}, // DolphinDeedIrSend
|
{1, DolphinAppIr}, // DolphinDeedIrSend
|
||||||
{3, DolphinAppIr}, // DolphinDeedIrLearnSuccess
|
{3, DolphinAppIr}, // DolphinDeedIrLearnSuccess
|
||||||
{3, DolphinAppIr}, // DolphinDeedIrSave
|
{3, DolphinAppIr}, // DolphinDeedIrSave
|
||||||
{2, DolphinAppIr}, // DolphinDeedIrBruteForce
|
|
||||||
|
|
||||||
{1, DolphinAppIbutton}, // DolphinDeedIbuttonRead
|
{1, DolphinAppIbutton}, // DolphinDeedIbuttonRead
|
||||||
{3, DolphinAppIbutton}, // DolphinDeedIbuttonReadSuccess
|
{3, DolphinAppIbutton}, // DolphinDeedIbuttonReadSuccess
|
||||||
@@ -34,16 +36,24 @@ static const DolphinDeedWeight dolphin_deed_weights[] = {
|
|||||||
|
|
||||||
{3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript
|
{3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript
|
||||||
{3, DolphinAppU2f}, // DolphinDeedU2fAuthorized
|
{3, DolphinAppU2f}, // DolphinDeedU2fAuthorized
|
||||||
|
|
||||||
|
{1, DolphinAppGpio}, // DolphinDeedGpioUartBridge
|
||||||
|
|
||||||
|
{1, DolphinAppPlugin}, // DolphinDeedPluginStart
|
||||||
|
{1, DolphinAppPlugin}, // DolphinDeedPluginGameStart
|
||||||
|
{10, DolphinAppPlugin}, // DolphinDeedPluginGameWin
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint8_t dolphin_deed_limits[] = {
|
static uint8_t dolphin_deed_limits[] = {
|
||||||
15, // DolphinAppSubGhz
|
20, // DolphinAppSubGhz
|
||||||
15, // DolphinAppRfid
|
20, // DolphinAppRfid
|
||||||
15, // DolphinAppNfc
|
20, // DolphinAppNfc
|
||||||
15, // DolphinAppIr
|
20, // DolphinAppIr
|
||||||
15, // DolphinAppIbutton
|
20, // DolphinAppIbutton
|
||||||
15, // DolphinAppBadusb
|
20, // DolphinAppBadusb
|
||||||
15, // DolphinAppU2f
|
20, // DolphinAppU2f
|
||||||
|
20, // DolphinAppGpio
|
||||||
|
20, // DolphinAppPlugin
|
||||||
};
|
};
|
||||||
|
|
||||||
_Static_assert(COUNT_OF(dolphin_deed_weights) == DolphinDeedMAX, "dolphin_deed_weights size error");
|
_Static_assert(COUNT_OF(dolphin_deed_weights) == DolphinDeedMAX, "dolphin_deed_weights size error");
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ typedef enum {
|
|||||||
DolphinAppIbutton,
|
DolphinAppIbutton,
|
||||||
DolphinAppBadusb,
|
DolphinAppBadusb,
|
||||||
DolphinAppU2f,
|
DolphinAppU2f,
|
||||||
|
DolphinAppGpio,
|
||||||
|
DolphinAppPlugin,
|
||||||
DolphinAppMAX,
|
DolphinAppMAX,
|
||||||
} DolphinApp;
|
} DolphinApp;
|
||||||
|
|
||||||
@@ -34,13 +36,15 @@ typedef enum {
|
|||||||
DolphinDeedNfcRead,
|
DolphinDeedNfcRead,
|
||||||
DolphinDeedNfcReadSuccess,
|
DolphinDeedNfcReadSuccess,
|
||||||
DolphinDeedNfcSave,
|
DolphinDeedNfcSave,
|
||||||
|
DolphinDeedNfcDetectReader,
|
||||||
DolphinDeedNfcEmulate,
|
DolphinDeedNfcEmulate,
|
||||||
DolphinDeedNfcAdd,
|
DolphinDeedNfcMfcAdd,
|
||||||
|
DolphinDeedNfcMfulError,
|
||||||
|
DolphinDeedNfcAddSave,
|
||||||
|
|
||||||
DolphinDeedIrSend,
|
DolphinDeedIrSend,
|
||||||
DolphinDeedIrLearnSuccess,
|
DolphinDeedIrLearnSuccess,
|
||||||
DolphinDeedIrSave,
|
DolphinDeedIrSave,
|
||||||
DolphinDeedIrBruteForce,
|
|
||||||
|
|
||||||
DolphinDeedIbuttonRead,
|
DolphinDeedIbuttonRead,
|
||||||
DolphinDeedIbuttonReadSuccess,
|
DolphinDeedIbuttonReadSuccess,
|
||||||
@@ -52,6 +56,12 @@ typedef enum {
|
|||||||
|
|
||||||
DolphinDeedU2fAuthorized,
|
DolphinDeedU2fAuthorized,
|
||||||
|
|
||||||
|
DolphinDeedGpioUartBridge,
|
||||||
|
|
||||||
|
DolphinDeedPluginStart,
|
||||||
|
DolphinDeedPluginGameStart,
|
||||||
|
DolphinDeedPluginGameWin,
|
||||||
|
|
||||||
DolphinDeedMAX,
|
DolphinDeedMAX,
|
||||||
|
|
||||||
DolphinDeedTestLeft,
|
DolphinDeedTestLeft,
|
||||||
|
|||||||
BIN
assets/dolphin/external/L1_Painting_128x64/frame_0.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_1.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_10.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_11.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_2.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_3.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_4.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_5.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_6.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_7.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_8.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_9.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
32
assets/dolphin/external/L1_Painting_128x64/meta.txt
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
Filetype: Flipper Animation
|
||||||
|
Version: 1
|
||||||
|
|
||||||
|
Width: 128
|
||||||
|
Height: 64
|
||||||
|
Passive frames: 9
|
||||||
|
Active frames: 13
|
||||||
|
Frames order: 0 1 2 3 4 5 2 3 4 10 6 7 8 7 8 7 8 7 8 9 10 11
|
||||||
|
Active cycles: 1
|
||||||
|
Frame rate: 2
|
||||||
|
Duration: 3600
|
||||||
|
Active cooldown: 7
|
||||||
|
|
||||||
|
Bubble slots: 1
|
||||||
|
|
||||||
|
Slot: 0
|
||||||
|
X: 57
|
||||||
|
Y: 24
|
||||||
|
Text: No mistakes,
|
||||||
|
AlignH: Left
|
||||||
|
AlignV: Center
|
||||||
|
StartFrame: 11
|
||||||
|
EndFrame: 14
|
||||||
|
|
||||||
|
Slot: 0
|
||||||
|
X: 57
|
||||||
|
Y: 21
|
||||||
|
Text: only happy\n accidents
|
||||||
|
AlignH: Left
|
||||||
|
AlignV: Center
|
||||||
|
StartFrame: 15
|
||||||
|
EndFrame: 18
|
||||||
7
assets/dolphin/external/manifest.txt
vendored
@@ -85,6 +85,13 @@ Min level: 1
|
|||||||
Max level: 3
|
Max level: 3
|
||||||
Weight: 3
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L1_Painting_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 7
|
||||||
|
Min level: 1
|
||||||
|
Max level: 3
|
||||||
|
Weight: 6
|
||||||
|
|
||||||
Name: L3_Hijack_radio_128x64
|
Name: L3_Hijack_radio_128x64
|
||||||
Min butthurt: 0
|
Min butthurt: 0
|
||||||
Max butthurt: 8
|
Max butthurt: 8
|
||||||
|
|||||||
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_0.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_1.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_10.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_11.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_2.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_3.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_4.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_5.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_6.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_7.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_8.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_9.bm
Normal file
32
assets/resources/dolphin/L1_Painting_128x64/meta.txt
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
Filetype: Flipper Animation
|
||||||
|
Version: 1
|
||||||
|
|
||||||
|
Width: 128
|
||||||
|
Height: 64
|
||||||
|
Passive frames: 9
|
||||||
|
Active frames: 13
|
||||||
|
Frames order: 0 1 2 3 4 5 2 3 4 10 6 7 8 7 8 7 8 7 8 9 10 11
|
||||||
|
Active cycles: 1
|
||||||
|
Frame rate: 2
|
||||||
|
Duration: 3600
|
||||||
|
Active cooldown: 7
|
||||||
|
|
||||||
|
Bubble slots: 1
|
||||||
|
|
||||||
|
Slot: 0
|
||||||
|
X: 57
|
||||||
|
Y: 24
|
||||||
|
Text: No mistakes,
|
||||||
|
AlignH: Left
|
||||||
|
AlignV: Center
|
||||||
|
StartFrame: 11
|
||||||
|
EndFrame: 14
|
||||||
|
|
||||||
|
Slot: 0
|
||||||
|
X: 57
|
||||||
|
Y: 21
|
||||||
|
Text: only happy\n accidents
|
||||||
|
AlignH: Left
|
||||||
|
AlignV: Center
|
||||||
|
StartFrame: 15
|
||||||
|
EndFrame: 18
|
||||||
@@ -85,6 +85,13 @@ Min level: 1
|
|||||||
Max level: 3
|
Max level: 3
|
||||||
Weight: 3
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L1_Painting_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 7
|
||||||
|
Min level: 1
|
||||||
|
Max level: 3
|
||||||
|
Weight: 6
|
||||||
|
|
||||||
Name: L3_Hijack_radio_128x64
|
Name: L3_Hijack_radio_128x64
|
||||||
Min butthurt: 0
|
Min butthurt: 0
|
||||||
Max butthurt: 8
|
Max butthurt: 8
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Only 2 parameters are mandatory: ***appid*** and ***apptype***, others are optio
|
|||||||
| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and application bundles |
|
| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and application bundles |
|
||||||
|
|
||||||
* **name**: Name that is displayed in menus.
|
* **name**: Name that is displayed in menus.
|
||||||
* **entry_point**: C function to be used as application's entry point.
|
* **entry_point**: C function to be used as application's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` in order to use them as entry points.
|
||||||
* **flags**: Internal flags for system apps. Do not use.
|
* **flags**: Internal flags for system apps. Do not use.
|
||||||
* **cdefines**: C preprocessor definitions to declare globally for other apps when current application is included in active build configuration.
|
* **cdefines**: C preprocessor definitions to declare globally for other apps when current application is included in active build configuration.
|
||||||
* **requires**: List of application IDs to also include in build configuration, when current application is referenced in list of applications to build.
|
* **requires**: List of application IDs to also include in build configuration, when current application is referenced in list of applications to build.
|
||||||
@@ -55,7 +55,7 @@ The following parameters are used only for [FAPs](./AppsOnSDCard.md):
|
|||||||
* **fap_author**: string, may be empty. Application's author.
|
* **fap_author**: string, may be empty. Application's author.
|
||||||
* **fap_weburl**: string, may be empty. Application's homepage.
|
* **fap_weburl**: string, may be empty. Application's homepage.
|
||||||
* **fap_icon_assets**: string. If present, defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details.
|
* **fap_icon_assets**: string. If present, defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details.
|
||||||
* **fap_extbuild**: provides support for parts of application sources to be build by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list.
|
* **fap_extbuild**: provides support for parts of application sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list.
|
||||||
Note that commands are executed at the firmware root folder's root, and all intermediate files must be placed in a application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**.
|
Note that commands are executed at the firmware root folder's root, and all intermediate files must be placed in a application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**.
|
||||||
|
|
||||||
Example for building an app from Rust sources:
|
Example for building an app from Rust sources:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[fbt](./fbt.md) has support for building applications as FAP files. FAP are essentially .elf executables with extra metadata and resources bundled in.
|
[fbt](./fbt.md) has support for building applications as FAP files. FAP are essentially .elf executables with extra metadata and resources bundled in.
|
||||||
|
|
||||||
FAPs are built with `faps` **`fbt`** target. They can also be deployed to `dist` folder with `plugin_dist` **`fbt`** target.
|
FAPs are built with `faps` target. They can also be deployed to `dist` folder with `fap_dist` target.
|
||||||
|
|
||||||
FAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning).
|
FAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning).
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ To build your application as a FAP, just create a folder with your app's source
|
|||||||
|
|
||||||
* To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest.
|
* To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest.
|
||||||
* To build your app, then upload it over USB & run it on Flipper, use `./fbt launch_app APPSRC=applications/path/to/app`. This command is configured in default [VSCode profile](../.vscode/ReadMe.md) as "Launch App on Flipper" build action (Ctrl+Shift+B menu).
|
* To build your app, then upload it over USB & run it on Flipper, use `./fbt launch_app APPSRC=applications/path/to/app`. This command is configured in default [VSCode profile](../.vscode/ReadMe.md) as "Launch App on Flipper" build action (Ctrl+Shift+B menu).
|
||||||
* To build all FAPs, run `./fbt plugin_dist`.
|
* To build all FAPs, run `./fbt faps` or `./fbt fap_dist`.
|
||||||
|
|
||||||
|
|
||||||
## FAP assets
|
## FAP assets
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option.
|
|||||||
### High-level (what you most likely need)
|
### High-level (what you most likely need)
|
||||||
|
|
||||||
- `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified
|
- `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified
|
||||||
- `plugin_dist` - build external plugins & publish to `dist` folder
|
- `fap_dist` - build external plugins & publish to `dist` folder
|
||||||
- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card
|
- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card
|
||||||
- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper
|
- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper
|
||||||
- `flash` - flash attached device with OpenOCD over ST-Link
|
- `flash` - flash attached device with OpenOCD over ST-Link
|
||||||
@@ -56,6 +56,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option.
|
|||||||
- `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration
|
- `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration
|
||||||
- `lint`, `format` - run clang-format on C source code to check and reformat it according to `.clang-format` specs
|
- `lint`, `format` - run clang-format on C source code to check and reformat it according to `.clang-format` specs
|
||||||
- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests
|
- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests
|
||||||
|
- `cli` - start Flipper CLI session over USB
|
||||||
|
|
||||||
### Firmware targets
|
### Firmware targets
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Import("ENV", "fw_build_meta")
|
|||||||
from SCons.Errors import UserError
|
from SCons.Errors import UserError
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from fbt.util import (
|
from fbt_extra.util import (
|
||||||
should_gen_cdb_and_link_dir,
|
should_gen_cdb_and_link_dir,
|
||||||
link_elf_dir_as_latest,
|
link_elf_dir_as_latest,
|
||||||
)
|
)
|
||||||
@@ -141,6 +141,10 @@ else:
|
|||||||
if extra_int_apps := GetOption("extra_int_apps"):
|
if extra_int_apps := GetOption("extra_int_apps"):
|
||||||
fwenv.Append(APPS=extra_int_apps.split(","))
|
fwenv.Append(APPS=extra_int_apps.split(","))
|
||||||
|
|
||||||
|
|
||||||
|
if fwenv["FAP_EXAMPLES"]:
|
||||||
|
fwenv.Append(APPDIRS=[("applications/examples", False)])
|
||||||
|
|
||||||
fwenv.LoadApplicationManifests()
|
fwenv.LoadApplicationManifests()
|
||||||
fwenv.PrepareApplicationsBuild()
|
fwenv.PrepareApplicationsBuild()
|
||||||
|
|
||||||
@@ -316,10 +320,13 @@ if fwenv["IS_BASE_FIRMWARE"]:
|
|||||||
"-D__inline__=inline",
|
"-D__inline__=inline",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
Depends(sdk_source, (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"]))
|
# Depends(sdk_source, (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"]))
|
||||||
|
Depends(sdk_source, fwenv.ProcessSdkDepends("sdk_origin.d"))
|
||||||
|
|
||||||
sdk_tree = fwenv.SDKTree("sdk/sdk.opts", "sdk_origin")
|
fwenv["SDK_DIR"] = fwenv.Dir("sdk")
|
||||||
AlwaysBuild(sdk_tree)
|
sdk_tree = fwenv.SDKTree(fwenv["SDK_DIR"], "sdk_origin")
|
||||||
|
fw_artifacts.append(sdk_tree)
|
||||||
|
# AlwaysBuild(sdk_tree)
|
||||||
Alias("sdk_tree", sdk_tree)
|
Alias("sdk_tree", sdk_tree)
|
||||||
|
|
||||||
sdk_apicheck = fwenv.SDKSymUpdater(fwenv.subst("$SDK_DEFINITION"), "sdk_origin")
|
sdk_apicheck = fwenv.SDKSymUpdater(fwenv.subst("$SDK_DEFINITION"), "sdk_origin")
|
||||||
@@ -329,7 +336,7 @@ if fwenv["IS_BASE_FIRMWARE"]:
|
|||||||
Alias("sdk_check", sdk_apicheck)
|
Alias("sdk_check", sdk_apicheck)
|
||||||
|
|
||||||
sdk_apisyms = fwenv.SDKSymGenerator(
|
sdk_apisyms = fwenv.SDKSymGenerator(
|
||||||
"assets/compiled/symbols.h", fwenv.subst("$SDK_DEFINITION")
|
"assets/compiled/symbols.h", fwenv["SDK_DEFINITION"]
|
||||||
)
|
)
|
||||||
Alias("api_syms", sdk_apisyms)
|
Alias("api_syms", sdk_apisyms)
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ static bool protocol_awid_can_be_decoded(uint8_t* data) {
|
|||||||
|
|
||||||
// Avoid detection for invalid formats
|
// Avoid detection for invalid formats
|
||||||
uint8_t len = bit_lib_get_bits(data, 8, 8);
|
uint8_t len = bit_lib_get_bits(data, 8, 8);
|
||||||
if(len != 26 && len != 50 && len != 37 && len != 34) break;
|
if(len != 26 && len != 50 && len != 37 && len != 34 && len != 36) break;
|
||||||
|
|
||||||
result = true;
|
result = true;
|
||||||
} while(false);
|
} while(false);
|
||||||
@@ -207,7 +207,7 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {
|
|||||||
|
|
||||||
// Fix incorrect length byte
|
// Fix incorrect length byte
|
||||||
if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 &&
|
if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 &&
|
||||||
protocol->data[0] != 34) {
|
protocol->data[0] != 34 && protocol->data[0] != 36 ) {
|
||||||
protocol->data[0] = 26;
|
protocol->data[0] = 26;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {
|
|||||||
|
|
||||||
const ProtocolBase protocol_awid = {
|
const ProtocolBase protocol_awid = {
|
||||||
.name = "AWID",
|
.name = "AWID",
|
||||||
.manufacturer = "AWIG",
|
.manufacturer = "AWID",
|
||||||
.data_size = AWID_DECODED_DATA_SIZE,
|
.data_size = AWID_DECODED_DATA_SIZE,
|
||||||
.features = LFRFIDFeatureASK,
|
.features = LFRFIDFeatureASK,
|
||||||
.validate_count = 3,
|
.validate_count = 3,
|
||||||
|
|||||||
@@ -639,35 +639,7 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
|
|||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
|
// Leave for backward compatibility
|
||||||
bool saved = false;
|
|
||||||
EmvData* data = &dev->dev_data.emv_data;
|
|
||||||
uint32_t data_temp = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
// Write Bank card specific data
|
|
||||||
if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break;
|
|
||||||
if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break;
|
|
||||||
if(!flipper_format_write_string_cstr(file, "Name", data->name)) break;
|
|
||||||
if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break;
|
|
||||||
if(data->exp_mon) {
|
|
||||||
uint8_t exp_data[2] = {data->exp_mon, data->exp_year};
|
|
||||||
if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break;
|
|
||||||
}
|
|
||||||
if(data->country_code) {
|
|
||||||
data_temp = data->country_code;
|
|
||||||
if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break;
|
|
||||||
}
|
|
||||||
if(data->currency_code) {
|
|
||||||
data_temp = data->currency_code;
|
|
||||||
if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break;
|
|
||||||
}
|
|
||||||
saved = true;
|
|
||||||
} while(false);
|
|
||||||
|
|
||||||
return saved;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
|
bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
EmvData* data = &dev->dev_data.emv_data;
|
EmvData* data = &dev->dev_data.emv_data;
|
||||||
@@ -1071,7 +1043,7 @@ static bool nfc_device_save_file(
|
|||||||
if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
|
if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
|
||||||
// Write nfc device type
|
// Write nfc device type
|
||||||
if(!flipper_format_write_comment_cstr(
|
if(!flipper_format_write_comment_cstr(
|
||||||
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card"))
|
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic"))
|
||||||
break;
|
break;
|
||||||
nfc_device_prepare_format_string(dev, temp_str);
|
nfc_device_prepare_format_string(dev, temp_str);
|
||||||
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
|
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
|
||||||
@@ -1086,8 +1058,6 @@ static bool nfc_device_save_file(
|
|||||||
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
|
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
|
||||||
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
|
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
|
||||||
if(!nfc_device_save_mifare_df_data(file, dev)) break;
|
if(!nfc_device_save_mifare_df_data(file, dev)) break;
|
||||||
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
|
|
||||||
if(!nfc_device_save_bank_card_data(file, dev)) break;
|
|
||||||
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
|
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
|
||||||
// Save data
|
// Save data
|
||||||
if(!nfc_device_save_mifare_classic_data(file, dev)) break;
|
if(!nfc_device_save_mifare_classic_data(file, dev)) break;
|
||||||
|
|||||||
@@ -233,31 +233,51 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
|
|||||||
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
|
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
// Bank cards require strong field to start application. If we find AID, try at least several
|
||||||
// Read card
|
// times to start EMV application
|
||||||
|
uint8_t start_application_attempts = 0;
|
||||||
|
while(start_application_attempts < 3) {
|
||||||
|
if(nfc_worker->state != NfcWorkerStateRead) break;
|
||||||
|
start_application_attempts++;
|
||||||
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
|
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
|
||||||
if(!emv_read_bank_card(tx_rx, &emv_app)) break;
|
if(emv_read_bank_card(tx_rx, &emv_app)) {
|
||||||
// Copy data
|
FURI_LOG_D(TAG, "Bank card number read from %d attempt", start_application_attempts);
|
||||||
// TODO Set EmvData to reader or like in mifare ultralight!
|
break;
|
||||||
result->number_len = emv_app.card_number_len;
|
} else if(emv_app.aid_len && !emv_app.app_started) {
|
||||||
memcpy(result->number, emv_app.card_number, result->number_len);
|
FURI_LOG_D(
|
||||||
|
TAG,
|
||||||
|
"AID found but failed to start EMV app from %d attempt",
|
||||||
|
start_application_attempts);
|
||||||
|
furi_hal_nfc_sleep();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "Failed to find AID");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Copy data
|
||||||
|
if(emv_app.aid_len) {
|
||||||
result->aid_len = emv_app.aid_len;
|
result->aid_len = emv_app.aid_len;
|
||||||
memcpy(result->aid, emv_app.aid, result->aid_len);
|
memcpy(result->aid, emv_app.aid, result->aid_len);
|
||||||
if(emv_app.name_found) {
|
|
||||||
memcpy(result->name, emv_app.name, sizeof(emv_app.name));
|
|
||||||
}
|
|
||||||
if(emv_app.exp_month) {
|
|
||||||
result->exp_mon = emv_app.exp_month;
|
|
||||||
result->exp_year = emv_app.exp_year;
|
|
||||||
}
|
|
||||||
if(emv_app.country_code) {
|
|
||||||
result->country_code = emv_app.country_code;
|
|
||||||
}
|
|
||||||
if(emv_app.currency_code) {
|
|
||||||
result->currency_code = emv_app.currency_code;
|
|
||||||
}
|
|
||||||
read_success = true;
|
read_success = true;
|
||||||
} while(false);
|
}
|
||||||
|
if(emv_app.card_number_len) {
|
||||||
|
result->number_len = emv_app.card_number_len;
|
||||||
|
memcpy(result->number, emv_app.card_number, result->number_len);
|
||||||
|
}
|
||||||
|
if(emv_app.name_found) {
|
||||||
|
memcpy(result->name, emv_app.name, sizeof(emv_app.name));
|
||||||
|
}
|
||||||
|
if(emv_app.exp_month) {
|
||||||
|
result->exp_mon = emv_app.exp_month;
|
||||||
|
result->exp_year = emv_app.exp_year;
|
||||||
|
}
|
||||||
|
if(emv_app.country_code) {
|
||||||
|
result->country_code = emv_app.country_code;
|
||||||
|
}
|
||||||
|
if(emv_app.currency_code) {
|
||||||
|
result->currency_code = emv_app.currency_code;
|
||||||
|
}
|
||||||
|
|
||||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||||
reader_analyzer_stop(nfc_worker->reader_analyzer);
|
reader_analyzer_stop(nfc_worker->reader_analyzer);
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
static bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
||||||
bool app_aid_found = false;
|
bool app_aid_found = false;
|
||||||
const uint8_t emv_select_ppse_cmd[] = {
|
const uint8_t emv_select_ppse_cmd[] = {
|
||||||
0x00, 0xA4, // SELECT ppse
|
0x00, 0xA4, // SELECT ppse
|
||||||
@@ -212,8 +212,8 @@ bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
|||||||
return app_aid_found;
|
return app_aid_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
static bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
||||||
bool select_app_success = false;
|
app->app_started = false;
|
||||||
const uint8_t emv_select_header[] = {
|
const uint8_t emv_select_header[] = {
|
||||||
0x00,
|
0x00,
|
||||||
0xA4, // SELECT application
|
0xA4, // SELECT application
|
||||||
@@ -236,7 +236,7 @@ bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
|||||||
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||||
emv_trace(tx_rx, "Start application answer:");
|
emv_trace(tx_rx, "Start application answer:");
|
||||||
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
|
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
|
||||||
select_app_success = true;
|
app->app_started = true;
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
|
FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
|
||||||
}
|
}
|
||||||
@@ -244,7 +244,7 @@ bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
|||||||
FURI_LOG_E(TAG, "Failed to start application");
|
FURI_LOG_E(TAG, "Failed to start application");
|
||||||
}
|
}
|
||||||
|
|
||||||
return select_app_success;
|
return app->app_started;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
|
static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
|
||||||
@@ -367,14 +367,6 @@ static bool emv_read_files(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
|
|||||||
return card_num_read;
|
return card_num_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
|
|
||||||
furi_assert(tx_rx);
|
|
||||||
furi_assert(emv_app);
|
|
||||||
memset(emv_app, 0, sizeof(EmvApplication));
|
|
||||||
|
|
||||||
return emv_select_ppse(tx_rx, emv_app);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
|
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
|
||||||
furi_assert(tx_rx);
|
furi_assert(tx_rx);
|
||||||
furi_assert(emv_app);
|
furi_assert(emv_app);
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ typedef struct {
|
|||||||
uint8_t priority;
|
uint8_t priority;
|
||||||
uint8_t aid[16];
|
uint8_t aid[16];
|
||||||
uint8_t aid_len;
|
uint8_t aid_len;
|
||||||
|
bool app_started;
|
||||||
char name[32];
|
char name[32];
|
||||||
bool name_found;
|
bool name_found;
|
||||||
uint8_t card_number[10];
|
uint8_t card_number[10];
|
||||||
@@ -68,15 +69,6 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
|
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
|
||||||
|
|
||||||
/** Search for EMV Application
|
|
||||||
*
|
|
||||||
* @param tx_rx FuriHalNfcTxRxContext instance
|
|
||||||
* @param emv_app EmvApplication instance
|
|
||||||
*
|
|
||||||
* @return true on success
|
|
||||||
*/
|
|
||||||
bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
|
|
||||||
|
|
||||||
/** Emulate bank card
|
/** Emulate bank card
|
||||||
* @note Answer to application selection and PDOL
|
* @note Answer to application selection and PDOL
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -41,25 +41,3 @@ def link_dir(target_path, source_path, is_windows):
|
|||||||
|
|
||||||
def single_quote(arg_list):
|
def single_quote(arg_list):
|
||||||
return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
|
return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
|
||||||
|
|
||||||
|
|
||||||
def link_elf_dir_as_latest(env, elf_node):
|
|
||||||
elf_dir = elf_node.Dir(".")
|
|
||||||
latest_dir = env.Dir("#build/latest")
|
|
||||||
print(f"Setting {elf_dir} as latest built dir (./build/latest/)")
|
|
||||||
return link_dir(latest_dir.abspath, elf_dir.abspath, env["PLATFORM"] == "win32")
|
|
||||||
|
|
||||||
|
|
||||||
def should_gen_cdb_and_link_dir(env, requested_targets):
|
|
||||||
explicitly_building_updater = False
|
|
||||||
# Hacky way to check if updater-related targets were requested
|
|
||||||
for build_target in requested_targets:
|
|
||||||
if "updater" in str(build_target):
|
|
||||||
explicitly_building_updater = True
|
|
||||||
|
|
||||||
is_updater = not env["IS_BASE_FIRMWARE"]
|
|
||||||
# If updater is explicitly requested, link to the latest updater
|
|
||||||
# Otherwise, link to firmware
|
|
||||||
return (is_updater and explicitly_building_updater) or (
|
|
||||||
not is_updater and not explicitly_building_updater
|
|
||||||
)
|
|
||||||
41
scripts/fbt_tools/fbt_debugopts.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
def generate(env, **kw):
|
||||||
|
env.SetDefault(
|
||||||
|
OPENOCD_GDB_PIPE=[
|
||||||
|
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
||||||
|
],
|
||||||
|
GDBOPTS_BASE=[
|
||||||
|
"-ex",
|
||||||
|
"target extended-remote ${GDBREMOTE}",
|
||||||
|
"-ex",
|
||||||
|
"set confirm off",
|
||||||
|
"-ex",
|
||||||
|
"set pagination off",
|
||||||
|
],
|
||||||
|
GDBOPTS_BLACKMAGIC=[
|
||||||
|
"-ex",
|
||||||
|
"monitor swdp_scan",
|
||||||
|
"-ex",
|
||||||
|
"monitor debug_bmp enable",
|
||||||
|
"-ex",
|
||||||
|
"attach 1",
|
||||||
|
"-ex",
|
||||||
|
"set mem inaccessible-by-default off",
|
||||||
|
],
|
||||||
|
GDBPYOPTS=[
|
||||||
|
"-ex",
|
||||||
|
"source debug/FreeRTOS/FreeRTOS.py",
|
||||||
|
"-ex",
|
||||||
|
"source debug/flipperapps.py",
|
||||||
|
"-ex",
|
||||||
|
"source debug/PyCortexMDebug/PyCortexMDebug.py",
|
||||||
|
"-ex",
|
||||||
|
"svd_load ${SVD_FILE}",
|
||||||
|
"-ex",
|
||||||
|
"compare-sections",
|
||||||
|
],
|
||||||
|
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def exists(env):
|
||||||
|
return True
|
||||||
@@ -136,7 +136,6 @@ def generate(env):
|
|||||||
"CoproBuilder": Builder(
|
"CoproBuilder": Builder(
|
||||||
action=Action(
|
action=Action(
|
||||||
[
|
[
|
||||||
Mkdir("$TARGET"),
|
|
||||||
'${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" '
|
'${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" '
|
||||||
"copro ${COPRO_CUBE_DIR} "
|
"copro ${COPRO_CUBE_DIR} "
|
||||||
"${TARGET} ${COPRO_MCU_FAMILY} "
|
"${TARGET} ${COPRO_MCU_FAMILY} "
|
||||||
@@ -145,7 +144,7 @@ def generate(env):
|
|||||||
'--stack_file="${COPRO_STACK_BIN}" '
|
'--stack_file="${COPRO_STACK_BIN}" '
|
||||||
"--stack_addr=${COPRO_STACK_ADDR} ",
|
"--stack_addr=${COPRO_STACK_ADDR} ",
|
||||||
],
|
],
|
||||||
"",
|
"\tCOPRO\t${TARGET}",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -6,12 +6,10 @@ import SCons.Warnings
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
from fbt.elfmanifest import assemble_manifest_data
|
from fbt.elfmanifest import assemble_manifest_data
|
||||||
from fbt.appmanifest import FlipperManifestException
|
from fbt.appmanifest import FlipperApplication, FlipperManifestException
|
||||||
from fbt.sdk import SdkCache
|
from fbt.sdk import SdkCache
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from site_scons.fbt.appmanifest import FlipperApplication
|
|
||||||
|
|
||||||
|
|
||||||
def BuildAppElf(env, app):
|
def BuildAppElf(env, app):
|
||||||
ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR")
|
ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR")
|
||||||
@@ -111,7 +109,7 @@ def BuildAppElf(env, app):
|
|||||||
)
|
)
|
||||||
|
|
||||||
app_elf_raw = app_env.Program(
|
app_elf_raw = app_env.Program(
|
||||||
os.path.join(app_work_dir, f"{app.appid}_d"),
|
os.path.join(ext_apps_work_dir, f"{app.appid}_d"),
|
||||||
app_sources,
|
app_sources,
|
||||||
APP_ENTRY=app.entry_point,
|
APP_ENTRY=app.entry_point,
|
||||||
)
|
)
|
||||||
@@ -180,7 +178,7 @@ def validate_app_imports(target, source, env):
|
|||||||
if unresolved_syms:
|
if unresolved_syms:
|
||||||
SCons.Warnings.warn(
|
SCons.Warnings.warn(
|
||||||
SCons.Warnings.LinkWarning,
|
SCons.Warnings.LinkWarning,
|
||||||
f"{source[0].path}: app won't run. Unresolved symbols: {unresolved_syms}",
|
f"\033[93m{source[0].path}: app won't run. Unresolved symbols: \033[95m{unresolved_syms}\033[0m",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
44
scripts/fbt_tools/fbt_help.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
targets_help = """Configuration variables:
|
||||||
|
"""
|
||||||
|
|
||||||
|
tail_help = """
|
||||||
|
|
||||||
|
TASKS:
|
||||||
|
Building:
|
||||||
|
firmware_all, fw_dist:
|
||||||
|
Build firmware; create distribution package
|
||||||
|
faps, fap_dist:
|
||||||
|
Build all FAP apps
|
||||||
|
fap_{APPID}, launch_app APPSRC={APPID}:
|
||||||
|
Build FAP app with appid={APPID}; upload & start it over USB
|
||||||
|
|
||||||
|
Flashing & debugging:
|
||||||
|
flash, flash_blackmagic, jflash:
|
||||||
|
Flash firmware to target using debug probe
|
||||||
|
flash_usb, flash_usb_full:
|
||||||
|
Install firmware using self-update package
|
||||||
|
debug, debug_other, blackmagic:
|
||||||
|
Start GDB
|
||||||
|
|
||||||
|
Other:
|
||||||
|
cli:
|
||||||
|
Open a Flipper CLI session over USB
|
||||||
|
firmware_cdb, updater_cdb:
|
||||||
|
Generate сompilation_database.json
|
||||||
|
lint, lint_py:
|
||||||
|
run linters
|
||||||
|
format, format_py:
|
||||||
|
run code formatters
|
||||||
|
|
||||||
|
For more targets & info, see documentation/fbt.md
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def generate(env, **kw):
|
||||||
|
vars = kw["vars"]
|
||||||
|
basic_help = vars.GenerateHelpText(env)
|
||||||
|
env.Help(targets_help + basic_help + tail_help)
|
||||||
|
|
||||||
|
|
||||||
|
def exists(env):
|
||||||
|
return True
|
||||||