Merge branch 'dev' into ntag-auto-pwd-capture

This commit is contained in:
gornekich
2022-10-13 13:04:38 +05:00
committed by GitHub
118 changed files with 919 additions and 398 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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");

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
} }

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)) {

View File

@@ -5,6 +5,5 @@ App(
provides=[ provides=[
"music_player", "music_player",
"snake_game", "snake_game",
"bt_hid",
], ],
) )

View File

@@ -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",
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}; };

View File

@@ -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;
}; };

View File

@@ -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;
}; };

View File

@@ -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;
}; };

View 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);
}

View 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);

View File

@@ -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);

View File

@@ -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");

View File

@@ -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,

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View 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

View File

@@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
* *

View File

@@ -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
)

View 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

View File

@@ -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}",
) )
), ),
} }

View File

@@ -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",
) )

View 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

Some files were not shown because too many files have changed in this diff Show More