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
+3
View File
@@ -62,6 +62,8 @@ jobs:
- name: 'Download build artifacts'
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;
chmod 600 ./deploy_key;
rsync -avzP \
@@ -97,3 +99,4 @@ jobs:
${{ secrets.AMAP_MARIADB_PORT }} \
${{ secrets.AMAP_MARIADB_DATABASE }} \
artifacts/flipper-z-f7-firmware-$SUFFIX.elf.map.all
+7 -17
View File
@@ -56,14 +56,14 @@ jobs:
- name: 'Bundle scripts'
if: ${{ !github.event.pull_request.head.repo.fork }}
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'
run: |
set -e
for TARGET in ${TARGETS}; do
FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \
copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
done
- name: 'Move upload files'
@@ -74,17 +74,6 @@ jobs:
mv dist/${TARGET}-*/* artifacts/
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"
run: |
git diff --exit-code
@@ -97,8 +86,7 @@ jobs:
- name: 'Bundle core2 firmware'
if: ${{ !github.event.pull_request.head.repo.fork }}
run: |
FBT_TOOLCHAIN_PATH=/opt ./fbt copro_dist
tar czpf "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" -C assets core2_firmware
cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz"
- name: 'Copy .map file'
run: |
@@ -107,6 +95,8 @@ jobs:
- name: 'Upload artifacts to update server'
if: ${{ !github.event.pull_request.head.repo.fork }}
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;
chmod 600 ./deploy_key;
rsync -avzP --delete --mkpath \
@@ -174,6 +164,6 @@ jobs:
run: |
set -e
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
done
+1
View File
@@ -27,6 +27,7 @@ jobs:
- name: 'Check protobuf branch'
run: |
git submodule update --init
SUB_PATH="assets/protobuf";
SUB_BRANCH="dev";
SUB_COMMITS_MIN=40;
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
- name: 'Check code formatting'
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
if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request
+1 -1
View File
@@ -26,4 +26,4 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- 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
+4 -2
View File
@@ -57,11 +57,11 @@ jobs:
- name: 'Generate compile_comands.json'
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'
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 analyze \
@.pvsoptions \
@@ -76,6 +76,8 @@ jobs:
- name: 'Upload artifacts to update server'
if: ${{ !github.event.pull_request.head.repo.fork }}
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;
chmod 600 ./deploy_key;
rsync -avrzP --mkpath \
+20 -43
View File
@@ -7,7 +7,6 @@
# construction of certain targets behind command-line options.
import os
import subprocess
DefaultEnvironment(tools=[])
@@ -15,17 +14,22 @@ EnsurePythonVersion(3, 8)
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
# This environment is created only for loading options & validating file/dir existence
fbt_variables = SConscript("site_scons/commandline.scons")
cmd_environment = Environment(tools=[], variables=fbt_variables)
Help(fbt_variables.GenerateHelpText(cmd_environment))
cmd_environment = Environment(
toolpath=["#/scripts/fbt_tools"],
tools=[
("fbt_help", {"vars": fbt_variables}),
],
variables=fbt_variables,
)
# Building basic environment - tools, utility methods, cross-compilation
# settings, gcc flags for Cortex-M4, basic builders and more
coreenv = SConscript(
"site_scons/environ.scons",
exports={"VAR_ENV": cmd_environment},
toolpath=["#/scripts/fbt_tools"],
)
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
distenv = coreenv.Clone(
tools=["fbt_dist", "openocd", "blackmagic", "jflash"],
OPENOCD_GDB_PIPE=[
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
tools=[
"fbt_dist",
"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,
)
@@ -166,7 +142,7 @@ basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
distenv.Default(basic_dist)
dist_dir = distenv.GetProjetDirName()
plugin_dist = [
fap_dist = [
distenv.Install(
f"#/dist/{dist_dir}/apps/debug_elf",
firmware_env["FW_EXTAPPS"]["debug"].values(),
@@ -176,9 +152,9 @@ plugin_dist = [
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
),
]
Depends(plugin_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
Alias("plugin_dist", plugin_dist)
# distenv.Default(plugin_dist)
Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
Alias("fap_dist", fap_dist)
# distenv.Default(fap_dist)
plugin_resources_dist = list(
distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
@@ -189,9 +165,10 @@ distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist)
# Target for bundling core2 package for qFlipper
copro_dist = distenv.CoproBuilder(
distenv.Dir("assets/core2_firmware"),
"#/build/core2_firmware.tgz",
[],
)
distenv.AlwaysBuild(copro_dist)
distenv.Alias("copro_dist", copro_dist)
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
@@ -4,6 +4,8 @@
#include <gui/gui.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"
typedef struct {
+35 -4
View File
@@ -231,7 +231,8 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) {
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);
const char* line_tmp = furi_string_get_cstr(line);
bool state = false;
@@ -261,6 +262,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
if((state) && (delay_val > 0)) {
return (int32_t)delay_val;
}
if(error != NULL) {
snprintf(error, error_len, "Invalid number %s", line_tmp);
}
return SCRIPT_STATE_ERROR;
} else if(
(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
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
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;
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
// STRING
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
state = ducky_string(line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid string %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
// ALTCHAR
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
ducky_numlock_on();
state = ducky_altchar(line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid altchar %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(
(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];
ducky_numlock_on();
state = ducky_altstring(line_tmp);
if(!state && error != NULL) {
snprintf(error, error_len, "Invalid altstring %s", line_tmp);
}
return (state) ? (0) : SCRIPT_STATE_ERROR;
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
// REPEAT
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
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;
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
// SYSRQ
@@ -304,7 +323,12 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
} else {
// Special keys + modifiers
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) {
// It's a modifier key
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);
return (0);
}
if(error != NULL) {
strncpy(error, "Unknown error", error_len);
}
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) {
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
return 0;
} 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->buf_len = bad_usb->buf_len + 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) {
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);
@@ -602,6 +632,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
furi_string_set(bad_usb->file_path, file_path);
bad_usb->st.state = BadUsbStateInit;
bad_usb->st.error[0] = '\0';
bad_usb->thread = furi_thread_alloc();
furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
@@ -25,6 +25,7 @@ typedef struct {
uint16_t line_nb;
uint32_t delay_remain;
uint16_t error_line;
char error[64];
} BadUsbState;
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, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(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) {
canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18);
canvas_set_font(canvas, FontBigNumbers);
@@ -1,6 +1,7 @@
#include "../gpio_app_i.h"
#include "furi_hal_power.h"
#include "furi_hal_usb.h"
#include <dolphin/dolphin.h>
enum GpioItem {
GpioItemUsbUart,
@@ -88,6 +89,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == GpioStartEventUsbUart) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart);
if(!furi_hal_usb_is_locked()) {
DOLPHIN_DEED(DolphinDeedGpioUartBridge);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart);
} else {
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc);
@@ -70,7 +70,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e
uint32_t record_count;
if(infrared_brute_force_start(
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);
} else {
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) {
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_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);
@@ -1,7 +1,6 @@
#include "../nfc_i.h"
enum SubmenuIndex {
SubmenuIndexSave,
SubmenuIndexInfo,
};
@@ -15,7 +14,6 @@ void nfc_scene_emv_menu_on_enter(void* context) {
Nfc* nfc = context;
Submenu* submenu = nfc->submenu;
submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc);
submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
submenu_set_selected_item(
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
@@ -28,13 +26,7 @@ bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) {
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) {
nfc->dev->format = NfcDeviceSaveFormatBankCard;
// Clear device name
nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
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);
FuriString* temp_str;
temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
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]);
if(emv_data->name[0] != '\0') {
temp_str = furi_string_alloc_printf("\e#%s\n", emv_data->name);
} else {
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
if(emv_data->exp_mon) {
@@ -1,4 +1,5 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
enum SubmenuIndex {
SubmenuIndexSave,
@@ -35,6 +36,8 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexSave) {
DOLPHIN_DEED(DolphinDeedNfcMfcAdd);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave);
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
@@ -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) {
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup dialog view
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, 0, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
if(mf_ul_data->auth_success) {
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
furi_string_printf(
temp_str,
"Password: %02X %02X %02X %02X",
@@ -54,6 +54,8 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
config_pages->auth_data.pack.raw[1]);
widget_add_string_element(
widget, 0, 39, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str));
} else {
DOLPHIN_DEED(DolphinDeedNfcMfulError);
}
furi_string_printf(
temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4);
@@ -30,7 +30,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) {
DOLPHIN_DEED(DolphinDeedNfcAdd);
DOLPHIN_DEED(DolphinDeedNfcAddSave);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
nfc->dev->dev_data.nfc_data = nfc->dev_edit_data;
if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) {
-1
View File
@@ -5,6 +5,5 @@ App(
provides=[
"music_player",
"snake_game",
"bt_hid",
],
)
@@ -1,15 +1,10 @@
App(
appid="bt_hid",
name="Bluetooth Remote",
apptype=FlipperAppType.PLUGIN,
apptype=FlipperAppType.EXTERNAL,
entry_point="bt_hid_app",
stack_size=1 * 1024,
cdefines=["APP_BLE_HID"],
requires=[
"bt",
"gui",
],
order=10,
fap_icon="bt_remote_10px.png",
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

+19
View File
@@ -1,6 +1,7 @@
#include "bt_hid.h"
#include <furi_hal_bt.h>
#include <notification/notification_messages.h>
#include <dolphin/dolphin.h>
#define TAG "BtHidApp"
@@ -8,6 +9,7 @@ enum BtDebugSubmenuIndex {
BtHidSubmenuIndexKeynote,
BtHidSubmenuIndexKeyboard,
BtHidSubmenuIndexMedia,
BtHidSubmenuIndexTikTok,
BtHidSubmenuIndexMouse,
};
@@ -26,6 +28,9 @@ void bt_hid_submenu_callback(void* context, uint32_t index) {
} else if(index == BtHidSubmenuIndexMouse) {
app->view_id = 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_media_set_connected_status(bt_hid->bt_hid_media, 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() {
@@ -90,6 +96,8 @@ BtHid* bt_hid_app_alloc() {
submenu_add_item(
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, "TikTok Controller", BtHidSubmenuIndexTikTok, 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_dispatcher_add_view(
@@ -126,6 +134,13 @@ BtHid* bt_hid_app_alloc() {
view_dispatcher_add_view(
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
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);
@@ -158,6 +173,8 @@ void bt_hid_app_free(BtHid* app) {
bt_hid_media_free(app->bt_hid_media);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse);
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);
// Close records
@@ -185,6 +202,8 @@ int32_t bt_hid_app(void* p) {
}
furi_hal_bt_start_advertising();
DOLPHIN_DEED(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher);
bt_set_status_changed_callback(app->bt, NULL, NULL);
+3
View File
@@ -13,6 +13,7 @@
#include "views/bt_hid_keyboard.h"
#include "views/bt_hid_media.h"
#include "views/bt_hid_mouse.h"
#include "views/bt_hid_tiktok.h"
typedef struct {
Bt* bt;
@@ -25,6 +26,7 @@ typedef struct {
BtHidKeyboard* bt_hid_keyboard;
BtHidMedia* bt_hid_media;
BtHidMouse* bt_hid_mouse;
BtHidTikTok* bt_hid_tiktok;
uint32_t view_id;
} BtHid;
@@ -34,5 +36,6 @@ typedef enum {
BtHidViewKeyboard,
BtHidViewMedia,
BtHidViewMouse,
BtHidViewTikTok,
BtHidViewExitConfirm,
} BtHidView;
@@ -5,6 +5,8 @@
#include <gui/elements.h>
#include <gui/icon_i.h>
#include "bt_hid_icons.h"
struct BtHidKeyboard {
View* view;
};
@@ -4,6 +4,8 @@
#include <furi_hal_usb_hid.h>
#include <gui/elements.h>
#include "bt_hid_icons.h"
struct BtHidKeynote {
View* view;
};
@@ -4,6 +4,8 @@
#include <furi_hal_usb_hid.h>
#include <gui/elements.h>
#include "bt_hid_icons.h"
struct BtHidMedia {
View* view;
};
@@ -4,6 +4,8 @@
#include <furi_hal_usb_hid.h>
#include <gui/elements.h>
#include "bt_hid_icons.h"
struct BtHidMouse {
View* view;
};
@@ -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);
}
@@ -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 <input/input.h>
#include <stdlib.h>
#include <dolphin/dolphin.h>
#include <notification/notification.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);
DOLPHIN_DEED(DolphinDeedPluginGameStart);
SnakeEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
@@ -18,13 +18,15 @@ static const DolphinDeedWeight dolphin_deed_weights[] = {
{1, DolphinAppNfc}, // DolphinDeedNfcRead
{3, DolphinAppNfc}, // DolphinDeedNfcReadSuccess
{3, DolphinAppNfc}, // DolphinDeedNfcSave
{1, DolphinAppNfc}, // DolphinDeedNfcDetectReader
{2, DolphinAppNfc}, // DolphinDeedNfcEmulate
{2, DolphinAppNfc}, // DolphinDeedNfcAdd
{2, DolphinAppNfc}, // DolphinDeedNfcMfcAdd
{1, DolphinAppNfc}, // DolphinDeedNfcMfulError
{1, DolphinAppNfc}, // DolphinDeedNfcAddSave
{1, DolphinAppIr}, // DolphinDeedIrSend
{3, DolphinAppIr}, // DolphinDeedIrLearnSuccess
{3, DolphinAppIr}, // DolphinDeedIrSave
{2, DolphinAppIr}, // DolphinDeedIrBruteForce
{1, DolphinAppIbutton}, // DolphinDeedIbuttonRead
{3, DolphinAppIbutton}, // DolphinDeedIbuttonReadSuccess
@@ -34,16 +36,24 @@ static const DolphinDeedWeight dolphin_deed_weights[] = {
{3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript
{3, DolphinAppU2f}, // DolphinDeedU2fAuthorized
{1, DolphinAppGpio}, // DolphinDeedGpioUartBridge
{1, DolphinAppPlugin}, // DolphinDeedPluginStart
{1, DolphinAppPlugin}, // DolphinDeedPluginGameStart
{10, DolphinAppPlugin}, // DolphinDeedPluginGameWin
};
static uint8_t dolphin_deed_limits[] = {
15, // DolphinAppSubGhz
15, // DolphinAppRfid
15, // DolphinAppNfc
15, // DolphinAppIr
15, // DolphinAppIbutton
15, // DolphinAppBadusb
15, // DolphinAppU2f
20, // DolphinAppSubGhz
20, // DolphinAppRfid
20, // DolphinAppNfc
20, // DolphinAppIr
20, // DolphinAppIbutton
20, // DolphinAppBadusb
20, // DolphinAppU2f
20, // DolphinAppGpio
20, // DolphinAppPlugin
};
_Static_assert(COUNT_OF(dolphin_deed_weights) == DolphinDeedMAX, "dolphin_deed_weights size error");
@@ -14,6 +14,8 @@ typedef enum {
DolphinAppIbutton,
DolphinAppBadusb,
DolphinAppU2f,
DolphinAppGpio,
DolphinAppPlugin,
DolphinAppMAX,
} DolphinApp;
@@ -34,13 +36,15 @@ typedef enum {
DolphinDeedNfcRead,
DolphinDeedNfcReadSuccess,
DolphinDeedNfcSave,
DolphinDeedNfcDetectReader,
DolphinDeedNfcEmulate,
DolphinDeedNfcAdd,
DolphinDeedNfcMfcAdd,
DolphinDeedNfcMfulError,
DolphinDeedNfcAddSave,
DolphinDeedIrSend,
DolphinDeedIrLearnSuccess,
DolphinDeedIrSave,
DolphinDeedIrBruteForce,
DolphinDeedIbuttonRead,
DolphinDeedIbuttonReadSuccess,
@@ -52,6 +56,12 @@ typedef enum {
DolphinDeedU2fAuthorized,
DolphinDeedGpioUartBridge,
DolphinDeedPluginStart,
DolphinDeedPluginGameStart,
DolphinDeedPluginGameWin,
DolphinDeedMAX,
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

+32
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
+7
View File
@@ -85,6 +85,13 @@ Min level: 1
Max level: 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
Min butthurt: 0
Max butthurt: 8
@@ -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
View File
@@ -85,6 +85,13 @@ Min level: 1
Max level: 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
Min butthurt: 0
Max butthurt: 8
+2 -2
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 |
* **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.
* **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.
@@ -55,7 +55,7 @@ The following parameters are used only for [FAPs](./AppsOnSDCard.md):
* **fap_author**: string, may be empty. Application's author.
* **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_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`**.
Example for building an app from Rust sources:
+2 -2
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.
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).
@@ -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 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
+2 -1
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)
- `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
- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper
- `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
- `lint`, `format` - run clang-format on C source code to check and reformat it according to `.clang-format` specs
- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests
- `cli` - start Flipper CLI session over USB
### Firmware targets
+12 -5
View File
@@ -3,7 +3,7 @@ Import("ENV", "fw_build_meta")
from SCons.Errors import UserError
import itertools
from fbt.util import (
from fbt_extra.util import (
should_gen_cdb_and_link_dir,
link_elf_dir_as_latest,
)
@@ -141,6 +141,10 @@ else:
if extra_int_apps := GetOption("extra_int_apps"):
fwenv.Append(APPS=extra_int_apps.split(","))
if fwenv["FAP_EXAMPLES"]:
fwenv.Append(APPDIRS=[("applications/examples", False)])
fwenv.LoadApplicationManifests()
fwenv.PrepareApplicationsBuild()
@@ -316,10 +320,13 @@ if fwenv["IS_BASE_FIRMWARE"]:
"-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")
AlwaysBuild(sdk_tree)
fwenv["SDK_DIR"] = fwenv.Dir("sdk")
sdk_tree = fwenv.SDKTree(fwenv["SDK_DIR"], "sdk_origin")
fw_artifacts.append(sdk_tree)
# AlwaysBuild(sdk_tree)
Alias("sdk_tree", sdk_tree)
sdk_apicheck = fwenv.SDKSymUpdater(fwenv.subst("$SDK_DEFINITION"), "sdk_origin")
@@ -329,7 +336,7 @@ if fwenv["IS_BASE_FIRMWARE"]:
Alias("sdk_check", sdk_apicheck)
sdk_apisyms = fwenv.SDKSymGenerator(
"assets/compiled/symbols.h", fwenv.subst("$SDK_DEFINITION")
"assets/compiled/symbols.h", fwenv["SDK_DEFINITION"]
)
Alias("api_syms", sdk_apisyms)
+3 -3
View File
@@ -81,7 +81,7 @@ static bool protocol_awid_can_be_decoded(uint8_t* data) {
// Avoid detection for invalid formats
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;
} while(false);
@@ -207,7 +207,7 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {
// Fix incorrect length byte
if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 &&
protocol->data[0] != 34) {
protocol->data[0] != 34 && protocol->data[0] != 36 ) {
protocol->data[0] = 26;
}
@@ -232,7 +232,7 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {
const ProtocolBase protocol_awid = {
.name = "AWID",
.manufacturer = "AWIG",
.manufacturer = "AWID",
.data_size = AWID_DECODED_DATA_SIZE,
.features = LFRFIDFeatureASK,
.validate_count = 3,
+2 -32
View File
@@ -639,35 +639,7 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
return parsed;
}
static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
EmvData* data = &dev->dev_data.emv_data;
uint32_t data_temp = 0;
do {
// Write Bank card specific data
if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break;
if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break;
if(!flipper_format_write_string_cstr(file, "Name", data->name)) break;
if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break;
if(data->exp_mon) {
uint8_t exp_data[2] = {data->exp_mon, data->exp_year};
if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break;
}
if(data->country_code) {
data_temp = data->country_code;
if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break;
}
if(data->currency_code) {
data_temp = data->currency_code;
if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break;
}
saved = true;
} while(false);
return saved;
}
// Leave for backward compatibility
bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) {
bool parsed = false;
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;
// Write nfc device type
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;
nfc_device_prepare_format_string(dev, temp_str);
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;
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
if(!nfc_device_save_mifare_df_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
if(!nfc_device_save_bank_card_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
// Save data
if(!nfc_device_save_mifare_classic_data(file, dev)) break;
+41 -21
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);
}
do {
// Read card
// Bank cards require strong field to start application. If we find AID, try at least several
// 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(!emv_read_bank_card(tx_rx, &emv_app)) break;
// Copy data
// TODO Set EmvData to reader or like in mifare ultralight!
result->number_len = emv_app.card_number_len;
memcpy(result->number, emv_app.card_number, result->number_len);
if(emv_read_bank_card(tx_rx, &emv_app)) {
FURI_LOG_D(TAG, "Bank card number read from %d attempt", start_application_attempts);
break;
} else if(emv_app.aid_len && !emv_app.app_started) {
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;
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;
} 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)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
+5 -13
View File
@@ -182,7 +182,7 @@ static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app
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;
const uint8_t emv_select_ppse_cmd[] = {
0x00, 0xA4, // SELECT ppse
@@ -212,8 +212,8 @@ bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
return app_aid_found;
}
bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool select_app_success = false;
static bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
app->app_started = false;
const uint8_t emv_select_header[] = {
0x00,
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)) {
emv_trace(tx_rx, "Start application answer:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
select_app_success = true;
app->app_started = true;
} else {
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");
}
return select_app_success;
return app->app_started;
}
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;
}
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) {
furi_assert(tx_rx);
furi_assert(emv_app);
+1 -9
View File
@@ -45,6 +45,7 @@ typedef struct {
uint8_t priority;
uint8_t aid[16];
uint8_t aid_len;
bool app_started;
char name[32];
bool name_found;
uint8_t card_number[10];
@@ -68,15 +69,6 @@ typedef struct {
*/
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
* @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):
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
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
@@ -136,7 +136,6 @@ def generate(env):
"CoproBuilder": Builder(
action=Action(
[
Mkdir("$TARGET"),
'${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" '
"copro ${COPRO_CUBE_DIR} "
"${TARGET} ${COPRO_MCU_FAMILY} "
@@ -145,7 +144,7 @@ def generate(env):
'--stack_file="${COPRO_STACK_BIN}" '
"--stack_addr=${COPRO_STACK_ADDR} ",
],
"",
"\tCOPRO\t${TARGET}",
)
),
}
@@ -6,12 +6,10 @@ import SCons.Warnings
import os
import pathlib
from fbt.elfmanifest import assemble_manifest_data
from fbt.appmanifest import FlipperManifestException
from fbt.appmanifest import FlipperApplication, FlipperManifestException
from fbt.sdk import SdkCache
import itertools
from site_scons.fbt.appmanifest import FlipperApplication
def BuildAppElf(env, app):
ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR")
@@ -111,7 +109,7 @@ def BuildAppElf(env, app):
)
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_ENTRY=app.entry_point,
)
@@ -180,7 +178,7 @@ def validate_app_imports(target, source, env):
if unresolved_syms:
SCons.Warnings.warn(
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
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