1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 05:06:30 +04:00

Compare commits

..

79 Commits

Author SHA1 Message Date
MX
da18305119 Merge branch 'dev' into release 2023-10-18 00:57:24 +03:00
MX
c71d04a660 Merge branch 'dev' into release 2023-10-11 01:39:19 +03:00
MX
f3f68fd5d3 Merge branch 'dev' into release 2023-09-12 18:59:41 +03:00
MX
d4ce47c941 Merge branch 'dev' into release 2023-09-12 18:56:27 +03:00
MX
7413a78013 Merge branch 'dev' into release 2023-09-12 01:19:43 +03:00
MX
83967d6e06 Merge branch 'dev' into release 2023-09-12 00:46:22 +03:00
MX
4d1c6a8d20 Merge branch 'dev' into release 2023-09-05 06:11:28 +03:00
MX
c95a0a0171 Merge branch 'dev' into release 2023-09-04 23:19:37 +03:00
MX
feba1dbc24 Merge branch 'dev' into release 2023-08-20 05:51:14 +03:00
MX
5b578cf69d Merge branch 'dev' into release 2023-08-17 04:44:44 +03:00
MX
dafea08581 Merge branch 'dev' into release 2023-08-17 04:35:36 +03:00
MX
5c50224571 del submodules 2023-08-17 04:22:18 +03:00
MX
b510df47f9 Merge branch 'dev' into release 2023-08-17 04:21:28 +03:00
MX
2aec3ec5fd Merge branch 'dev' into release 2023-08-17 04:16:57 +03:00
MX
4b8c017302 Merge branch 'dev' into release 2023-08-05 19:09:50 +03:00
MX
b579bca227 Merge branch 'dev' into release 2023-07-25 05:35:30 +03:00
MX
3a676f7afa Merge branch 'dev' into release 2023-07-24 03:18:54 +03:00
MX
ca479303a1 Merge branch 'dev' into release 2023-07-20 01:32:12 +03:00
MX
5e649d8c41 Merge branch 'dev' into release 2023-07-20 01:25:14 +03:00
MX
0bc626ba1d Merge branch 'dev' into release 2023-07-09 00:59:02 +03:00
MX
3821ee7709 Merge branch 'dev' into release 2023-07-08 22:53:03 +03:00
MX
cb17189b15 Merge branch 'dev' into release 2023-06-25 01:40:11 +03:00
MX
80e8167054 Merge branch 'dev' into release 2023-06-25 00:55:18 +03:00
MX
eede5ed29e Merge branch 'dev' into release 2023-06-14 19:18:00 +03:00
MX
360fef7777 Merge branch 'dev' into release 2023-06-12 05:22:36 +03:00
MX
b83da5d3cb Merge branch 'dev' into release 2023-06-12 04:58:39 +03:00
MX
ae5d28fbc5 Merge branch 'dev' into release 2023-06-12 04:33:34 +03:00
MX
f4cd7c0100 Merge branch 'dev' into release 2023-06-03 23:47:45 +03:00
MX
7b68fd30ec Merge branch 'dev' into release 2023-06-03 22:38:42 +03:00
MX
ac222f1b0c Merge branch 'dev' into release 2023-06-03 02:23:14 +03:00
MX
b0e8e68909 Merge branch 'dev' into release 2023-06-02 19:52:05 +03:00
MX
ef5f6e2f70 Merge branch 'dev' into release 2023-05-28 07:01:32 +03:00
MX
30f6da3fa3 Merge branch 'dev' into release 2023-05-18 03:17:03 +03:00
MX
3a47154cdb Merge branch 'dev' into release 2023-05-18 02:54:58 +03:00
MX
d953d35991 Merge branch 'dev' into release 2023-05-05 23:33:23 +03:00
MX
4b74d13e10 Merge branch 'dev' into release 2023-05-02 03:40:11 +03:00
MX
98bf353287 Merge branch 'dev' into release 2023-04-27 22:15:19 +03:00
MX
eb8c751b31 Merge branch 'dev' into release 2023-04-27 19:58:25 +03:00
MX
9abad8704f Merge branch 'dev' into release 2023-04-27 04:02:39 +03:00
MX
6716c0f792 Merge branch 'dev' into release 2023-04-21 15:59:36 +03:00
MX
35f7ec6c07 Merge branch 'dev' into release 2023-04-14 04:08:34 +03:00
MX
a524fd7674 Merge branch 'dev' into release 2023-04-07 11:50:22 +03:00
MX
ff27fd3094 Merge branch 'dev' into release 2023-04-07 01:08:58 +03:00
MX
977ac09fe6 Merge branch 'dev' into release 2023-04-07 01:07:08 +03:00
MX
1a88e01899 Merge branch 'dev' into release 2023-04-02 03:12:58 +03:00
MX
6e710c5164 Merge branch 'dev' into release 2023-03-28 04:54:07 +03:00
MX
fd56ac3400 Merge branch 'dev' into release 2023-03-23 00:42:20 +03:00
MX
1d801c38f9 Merge branch 'dev' into release 2023-03-21 03:25:06 +03:00
MX
8afdb5b7b4 Merge branch 'dev' into release 2023-03-21 03:06:21 +03:00
MX
83624b1dee Merge branch 'dev' into release 2023-03-21 02:59:07 +03:00
MX
56adcf1ad8 Merge branch 'dev' into release 2023-03-18 06:44:59 +03:00
MX
ad27f87a0c Fix submodules 2023-03-17 04:18:15 +03:00
MX
346cf299ee Merge branch 'dev' into release 2023-03-17 04:15:27 +03:00
MX
05489fda7d Merge branch 'dev' into release 2023-03-09 05:08:50 +03:00
MX
1b12526357 Merge branch 'dev' into release 2023-03-01 03:11:36 +03:00
MX
802035d92e Merge branch 'dev' into release 2023-02-26 06:51:31 +03:00
MX
a24d0f1958 Merge branch 'dev' into release 2023-02-24 06:12:29 +03:00
MX
da68f2e4ed Merge branch 'dev' into release 2023-02-13 22:07:34 +03:00
MX
8f16dbb8e7 Merge branch 'dev' into release 2023-02-13 01:18:46 +03:00
MX
49e458f1b5 Merge branch 'dev' into release 2023-02-12 05:07:09 +03:00
MX
5cf46d2aa9 Merge branch 'dev' into release 2023-02-11 04:56:15 +03:00
MX
beedf54e75 Merge branch 'dev' into release 2023-02-11 04:35:02 +03:00
MX
70ccb89c3d fix submodule update 2023-02-04 23:52:13 +03:00
MX
5ea43a2a4b Merge branch 'dev' into release 2023-02-04 23:50:09 +03:00
MX
41f60dbbf4 Merge branch 'dev' into release 2023-01-19 22:45:38 +03:00
MX
827341ec08 Merge branch 'dev' into release 2023-01-19 01:53:01 +03:00
MX
5c36043d03 Merge branch 'dev' into release 2022-12-31 03:21:55 +03:00
MX
cf5811f8d9 Merge branch 'dev' into release 2022-12-31 02:59:26 +03:00
MX
ec6a169bf8 Merge branch 'dev' into release 2022-12-19 21:57:35 +03:00
MX
f1dec87c1b Merge branch 'dev' into release 2022-12-19 20:52:17 +03:00
MX
ab29951a99 Merge branch 'dev' into release 2022-12-19 19:26:06 +03:00
MX
bbe9f88bbe Merge branch 'dev' into release 2022-12-11 01:34:50 +03:00
MX
9188bf0013 Merge branch 'dev' into release 2022-12-10 04:38:04 +03:00
MX
f33ed59567 Merge branch 'dev' into release 2022-12-03 01:04:48 +03:00
MX
3fd8c80861 Merge branch 'dev' into release 2022-12-03 00:59:18 +03:00
MX
7b8ac3a5a0 Merge branch 'dev' into release 2022-11-30 21:05:08 +03:00
MX
6fef957001 Merge branch 'dev' into release 2022-11-24 04:02:59 +03:00
MX
0de1c9df89 Merge branch 'dev' into release 2022-11-24 03:45:52 +03:00
MX
a0e8cfbe97 Merge branch 'dev' into release 2022-11-24 03:41:01 +03:00
1004 changed files with 70859 additions and 45229 deletions

View File

@@ -22,8 +22,7 @@ steps:
- export DIST_SUFFIX=${DRONE_TAG}c - export DIST_SUFFIX=${DRONE_TAG}c
- export WORKFLOW_BRANCH_OR_TAG=release-cfw - export WORKFLOW_BRANCH_OR_TAG=release-cfw
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1 - rm -rf assets/resources/apps/
- rm -rf applications/main/clock_app/resources/apps/
- rm -rf build/ - rm -rf build/
- rm -rf dist/ - rm -rf dist/
- rm -rf .sconsign.dblite - rm -rf .sconsign.dblite
@@ -43,11 +42,10 @@ steps:
- export DIST_SUFFIX=${DRONE_TAG} - export DIST_SUFFIX=${DRONE_TAG}
- export WORKFLOW_BRANCH_OR_TAG=release-cfw - export WORKFLOW_BRANCH_OR_TAG=release-cfw
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz - tar zxvf all-the-apps-base.tgz
- cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/ - cp -R base_pack_build/artifacts-base/* assets/resources/apps/
- cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/ - cp -R base_pack_build/apps_data/* assets/resources/apps_data/
- rm -rf base_pack_build - rm -rf base_pack_build
- rm -rf all-the-apps-base.tgz - rm -rf all-the-apps-base.tgz
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
@@ -66,12 +64,11 @@ steps:
commands: commands:
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz
- tar zxvf all-the-apps-extra.tgz - tar zxvf all-the-apps-extra.tgz
- cp -R extra_pack_build/artifacts-extra/* applications/main/clock_app/resources/apps/ - cp -R extra_pack_build/artifacts-extra/* assets/resources/apps/
- rm -rf extra_pack_build - rm -rf extra_pack_build
- export DIST_SUFFIX=${DRONE_TAG}e - export DIST_SUFFIX=${DRONE_TAG}e
- export WORKFLOW_BRANCH_OR_TAG=release-cfw - export WORKFLOW_BRANCH_OR_TAG=release-cfw
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package - ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-extra-apps - mkdir artifacts-extra-apps
@@ -90,7 +87,6 @@ steps:
- export DIST_SUFFIX=${DRONE_TAG}r - export DIST_SUFFIX=${DRONE_TAG}r
- export WORKFLOW_BRANCH_OR_TAG=release-cfw-rgb - export WORKFLOW_BRANCH_OR_TAG=release-cfw-rgb
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package - ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-rgb-patch - mkdir artifacts-rgb-patch
@@ -109,17 +105,16 @@ steps:
- git checkout -- . - git checkout -- .
- rm -f assets/dolphin/external/manifest.txt - rm -f assets/dolphin/external/manifest.txt
- cp .ci_files/anims_ofw.txt assets/dolphin/external/manifest.txt - cp .ci_files/anims_ofw.txt assets/dolphin/external/manifest.txt
- rm -rf applications/main/clock_app/resources/apps/ - rm -rf assets/resources/apps/
- export DIST_SUFFIX=${DRONE_TAG}n - export DIST_SUFFIX=${DRONE_TAG}n
- export WORKFLOW_BRANCH_OR_TAG=no-custom-anims - export WORKFLOW_BRANCH_OR_TAG=no-custom-anims
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package - ./fbt COMPACT=1 DEBUG=0 updater_package
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz - tar zxvf all-the-apps-base.tgz
- cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/ - cp -R base_pack_build/artifacts-base/* assets/resources/apps/
- cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/ - cp -R base_pack_build/apps_data/* assets/resources/apps_data/
- rm -rf base_pack_build - rm -rf base_pack_build
- rm -rf all-the-apps-base.tgz - rm -rf all-the-apps-base.tgz
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
@@ -396,8 +391,7 @@ steps:
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}c - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}c
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw - export WORKFLOW_BRANCH_OR_TAG=dev-cfw
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1 - rm -rf assets/resources/apps/
- rm -rf applications/main/clock_app/resources/apps/
- rm -rf build/ - rm -rf build/
- rm -rf dist/ - rm -rf dist/
- rm -rf .sconsign.dblite - rm -rf .sconsign.dblite
@@ -418,11 +412,10 @@ steps:
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER} - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw - export WORKFLOW_BRANCH_OR_TAG=dev-cfw
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz - tar zxvf all-the-apps-base.tgz
- cp -R base_pack_build/artifacts-base/* applications/main/clock_app/resources/apps/ - cp -R base_pack_build/artifacts-base/* assets/resources/apps/
- cp -R base_pack_build/apps_data/* applications/main/clock_app/resources/apps_data/ - cp -R base_pack_build/apps_data/* assets/resources/apps_data/
- rm -rf base_pack_build - rm -rf base_pack_build
- rm -rf all-the-apps-base.tgz - rm -rf all-the-apps-base.tgz
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
@@ -441,12 +434,11 @@ steps:
commands: commands:
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz - wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz
- tar zxvf all-the-apps-extra.tgz - tar zxvf all-the-apps-extra.tgz
- cp -R extra_pack_build/artifacts-extra/* applications/main/clock_app/resources/apps/ - cp -R extra_pack_build/artifacts-extra/* assets/resources/apps/
- rm -rf extra_pack_build - rm -rf extra_pack_build
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw - export WORKFLOW_BRANCH_OR_TAG=dev-cfw
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package - ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-extra-apps - mkdir artifacts-extra-apps
@@ -465,7 +457,6 @@ steps:
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}r - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}r
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw-rgb - export WORKFLOW_BRANCH_OR_TAG=dev-cfw-rgb
- export FORCE_NO_DIRTY=yes - export FORCE_NO_DIRTY=yes
- export FBT_GIT_SUBMODULE_SHALLOW=1
- rm -f build/f7-firmware-C/toolbox/version.* - rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package - ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-rgb-patch - mkdir artifacts-rgb-patch

View File

@@ -1 +1 @@
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* --ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/*

View File

@@ -67,22 +67,22 @@ if GetOption("fullenv") or any(
# Target for self-update package # Target for self-update package
dist_basic_arguments = [ dist_basic_arguments = [
"--bundlever", "--bundlever",
"${UPDATE_VERSION_STRING}", '"${UPDATE_VERSION_STRING}"',
] ]
dist_radio_arguments = [ dist_radio_arguments = [
"--radio", "--radio",
"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}", '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"',
"--radiotype", "--radiotype",
"${COPRO_STACK_TYPE}", "${COPRO_STACK_TYPE}",
"${COPRO_DISCLAIMER}", "${COPRO_DISCLAIMER}",
"--obdata", "--obdata",
"${ROOT_DIR.abspath}/${COPRO_OB_DATA}", '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"',
"--stackversion", "--stackversion",
"${COPRO_CUBE_VERSION}", "${COPRO_CUBE_VERSION}",
] ]
dist_resource_arguments = [ dist_resource_arguments = [
"-r", "-r",
firmware_env.subst("${RESOURCES_ROOT}"), '"${ROOT_DIR.abspath}/assets/resources"',
] ]
dist_splash_arguments = ( dist_splash_arguments = (
[ [
@@ -95,7 +95,7 @@ if GetOption("fullenv") or any(
selfupdate_dist = distenv.DistCommand( selfupdate_dist = distenv.DistCommand(
"updater_package", "updater_package",
(distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES_MANIFEST"]), (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]),
DIST_EXTRA=[ DIST_EXTRA=[
*dist_basic_arguments, *dist_basic_arguments,
*dist_radio_arguments, *dist_radio_arguments,
@@ -128,8 +128,7 @@ if GetOption("fullenv") or any(
# Installation over USB & CLI # Installation over USB & CLI
usb_update_package = distenv.AddUsbFlashTarget( usb_update_package = distenv.AddUsbFlashTarget(
"#build/usbinstall.flag", "#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist)
(firmware_env["FW_RESOURCES_MANIFEST"], selfupdate_dist),
) )
distenv.Alias("flash_usb_full", usb_update_package) distenv.Alias("flash_usb_full", usb_update_package)
@@ -167,25 +166,17 @@ Depends(
list(app_artifact.validator for app_artifact in external_app_list), list(app_artifact.validator for app_artifact in external_app_list),
) )
Alias("fap_dist", fap_dist) Alias("fap_dist", fap_dist)
# distenv.Default(fap_dist)
distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_dist)
# Copy all faps to device # Copy all faps to device
fap_deploy = distenv.PhonyTarget( fap_deploy = distenv.PhonyTarget(
"fap_deploy", "fap_deploy",
[ "${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps",
[ source=Dir("#/assets/resources/apps"),
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/storage.py",
"-p",
"${FLIP_PORT}",
"send",
"${SOURCE}",
"/ext/apps",
]
],
source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")),
) )
Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"])
# Target for bundling core2 package for qFlipper # Target for bundling core2 package for qFlipper
@@ -323,7 +314,9 @@ distenv.PhonyTarget(
) )
# Start Flipper CLI via PySerial's miniterm # Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}") distenv.PhonyTarget(
"cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}"
)
# Update WiFi devboard firmware # Update WiFi devboard firmware
distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")

View File

@@ -71,7 +71,7 @@ static void direct_draw_run(DirectDraw* instance) {
size_t counter = 0; size_t counter = 0;
float fps = 0; float fps = 0;
furi_thread_set_current_priority(FuriThreadPriorityIdle); vTaskPrioritySet(furi_thread_get_current_id(), FuriThreadPriorityIdle);
do { do {
size_t elapsed = DWT->CYCCNT - start; size_t elapsed = DWT->CYCCNT - start;

View File

@@ -21,51 +21,22 @@ static void rpc_debug_app_tick_event_callback(void* context) {
scene_manager_handle_tick_event(app->scene_manager); scene_manager_handle_tick_event(app->scene_manager);
} }
static void static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) {
rpc_debug_app_format_hex(const uint8_t* data, size_t data_size, char* buf, size_t buf_size) {
if(data == NULL || data_size == 0) {
strncpy(buf, "<Data empty>", buf_size);
return;
}
const size_t byte_width = 3;
const size_t line_width = 7;
data_size = MIN(data_size, buf_size / (byte_width + 1));
for(size_t i = 0; i < data_size; ++i) {
char* p = buf + (i * byte_width);
char sep = !((i + 1) % line_width) ? '\n' : ' ';
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
}
buf[buf_size - 1] = '\0';
}
static void rpc_debug_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {
furi_assert(context); furi_assert(context);
RpcDebugApp* app = context; RpcDebugApp* app = context;
furi_assert(app->rpc); furi_assert(app->rpc);
if(event->type == RpcAppEventTypeSessionClose) { if(event == RpcAppEventSessionClose) {
scene_manager_stop(app->scene_manager); scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher); view_dispatcher_stop(app->view_dispatcher);
rpc_system_app_set_callback(app->rpc, NULL, NULL); rpc_system_app_set_callback(app->rpc, NULL, NULL);
app->rpc = NULL; app->rpc = NULL;
} else if(event->type == RpcAppEventTypeAppExit) { } else if(event == RpcAppEventAppExit) {
scene_manager_stop(app->scene_manager); scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher); view_dispatcher_stop(app->view_dispatcher);
rpc_system_app_confirm(app->rpc, true); rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true);
} else if(event->type == RpcAppEventTypeDataExchange) {
furi_assert(event->data.type == RpcAppSystemEventDataTypeBytes);
rpc_debug_app_format_hex(
event->data.bytes.ptr, event->data.bytes.size, app->text_store, TEXT_STORE_SIZE);
view_dispatcher_send_custom_event(
app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
} else { } else {
rpc_system_app_confirm(app->rpc, false); rpc_system_app_confirm(app->rpc, event, false);
} }
} }

View File

@@ -1,5 +1,40 @@
#include "../rpc_debug_app.h" #include "../rpc_debug_app.h"
static void rpc_debug_app_scene_start_format_hex(
const uint8_t* data,
size_t data_size,
char* buf,
size_t buf_size) {
furi_assert(data);
furi_assert(buf);
const size_t byte_width = 3;
const size_t line_width = 7;
data_size = MIN(data_size, buf_size / (byte_width + 1));
for(size_t i = 0; i < data_size; ++i) {
char* p = buf + (i * byte_width);
char sep = !((i + 1) % line_width) ? '\n' : ' ';
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
}
buf[buf_size - 1] = '\0';
}
static void rpc_debug_app_scene_receive_data_exchange_callback(
const uint8_t* data,
size_t data_size,
void* context) {
RpcDebugApp* app = context;
if(data) {
rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE);
} else {
strncpy(app->text_store, "<Data empty>", TEXT_STORE_SIZE);
}
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
}
void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) { void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
RpcDebugApp* app = context; RpcDebugApp* app = context;
strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE); strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE);
@@ -7,6 +42,8 @@ void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
text_box_set_text(app->text_box, app->text_store); text_box_set_text(app->text_box, app->text_store);
text_box_set_font(app->text_box, TextBoxFontHex); text_box_set_font(app->text_box, TextBoxFontHex);
rpc_system_app_set_data_exchange_callback(
app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox); view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox);
} }
@@ -16,7 +53,6 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventRpcDataExchange) { if(event.event == RpcDebugAppCustomEventRpcDataExchange) {
rpc_system_app_confirm(app->rpc, true);
notification_message(app->notifications, &sequence_blink_cyan_100); notification_message(app->notifications, &sequence_blink_cyan_100);
notification_message(app->notifications, &sequence_display_backlight_on); notification_message(app->notifications, &sequence_display_backlight_on);
text_box_set_text(app->text_box, app->text_store); text_box_set_text(app->text_box, app->text_store);
@@ -30,4 +66,5 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana
void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) { void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) {
RpcDebugApp* app = context; RpcDebugApp* app = context;
text_box_reset(app->text_box); text_box_reset(app->text_box);
rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL);
} }

View File

@@ -5,7 +5,6 @@ App(
cdefines=["APP_UNIT_TESTS"], cdefines=["APP_UNIT_TESTS"],
requires=["system_settings"], requires=["system_settings"],
provides=["delay_test"], provides=["delay_test"],
resources="resources",
order=100, order=100,
) )

View File

@@ -28,7 +28,7 @@ void bt_test_alloc() {
} }
void bt_test_free() { void bt_test_free() {
furi_check(bt_test); furi_assert(bt_test);
free(bt_test->nvm_ram_buff_ref); free(bt_test->nvm_ram_buff_ref);
free(bt_test->nvm_ram_buff_dut); free(bt_test->nvm_ram_buff_dut);
bt_keys_storage_free(bt_test->bt_keys_storage); bt_keys_storage_free(bt_test->bt_keys_storage);
@@ -89,7 +89,7 @@ static void bt_test_keys_remove_test_file() {
} }
MU_TEST(bt_test_keys_storage_serial_profile) { MU_TEST(bt_test_keys_storage_serial_profile) {
furi_check(bt_test); furi_assert(bt_test);
bt_test_keys_remove_test_file(); bt_test_keys_remove_test_file();
bt_test_keys_storage_profile(); bt_test_keys_storage_profile();

View File

@@ -27,7 +27,7 @@ static void infrared_test_alloc() {
} }
static void infrared_test_free() { static void infrared_test_free() {
furi_check(test); furi_assert(test);
infrared_free_decoder(test->decoder_handler); infrared_free_decoder(test->decoder_handler);
infrared_free_encoder(test->encoder_handler); infrared_free_encoder(test->encoder_handler);
flipper_format_free(test->ff); flipper_format_free(test->ff);

View File

@@ -22,7 +22,7 @@ MU_TEST(manifest_iteration_test) {
ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(storage); ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(storage);
do { do {
// Open manifest file // Open manifest file
if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("unit_tests/Manifest_test"))) { if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("unit_tests/Manifest"))) {
result = false; result = false;
break; break;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,458 +0,0 @@
#ifdef FW_CFG_unit_tests
#include <lib/nfc/nfc.h>
#include <lib/nfc/helpers/iso14443_crc.h>
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
#include <furi/furi.h>
#define NFC_MAX_BUFFER_SIZE (256)
typedef enum {
NfcTransportLogLevelWarning,
NfcTransportLogLevelInfo,
} NfcTransportLogLevel;
FuriMessageQueue* poller_queue = NULL;
FuriMessageQueue* listener_queue = NULL;
typedef enum {
NfcMessageTypeTx,
NfcMessageTypeTimeout,
NfcMessageTypeAbort,
} NfcMessageType;
typedef struct {
uint16_t data_bits;
uint8_t data[NFC_MAX_BUFFER_SIZE];
} NfcMessageData;
typedef struct {
NfcMessageType type;
NfcMessageData data;
} NfcMessage;
typedef enum {
NfcStateIdle,
NfcStateReady,
NfcStateReset,
} NfcState;
typedef enum {
Iso14443_3aColResStatusIdle,
Iso14443_3aColResStatusInProgress,
Iso14443_3aColResStatusDone,
} Iso14443_3aColResStatus;
typedef struct {
Iso14443_3aSensResp sens_resp;
Iso14443_3aSddResp sdd_resp[2];
Iso14443_3aSelResp sel_resp[2];
} Iso14443_3aColResData;
struct Nfc {
NfcState state;
Iso14443_3aColResStatus col_res_status;
Iso14443_3aColResData col_res_data;
NfcEventCallback callback;
void* context;
NfcMode mode;
FuriThread* worker_thread;
};
static void nfc_test_print(
NfcTransportLogLevel log_level,
const char* message,
uint8_t* buffer,
uint16_t bits) {
FuriString* str = furi_string_alloc();
size_t bytes = (bits + 7) / 8;
for(size_t i = 0; i < bytes; i++) {
furi_string_cat_printf(str, " %02X", buffer[i]);
}
if(log_level == NfcTransportLogLevelWarning) {
FURI_LOG_W(message, "%s", furi_string_get_cstr(str));
} else {
FURI_LOG_I(message, "%s", furi_string_get_cstr(str));
}
furi_string_free(str);
}
static void nfc_prepare_col_res_data(
Nfc* instance,
uint8_t* uid,
uint8_t uid_len,
uint8_t* atqa,
uint8_t sak) {
memcpy(instance->col_res_data.sens_resp.sens_resp, atqa, 2);
if(uid_len == 7) {
instance->col_res_data.sdd_resp[0].nfcid[0] = 0x88;
memcpy(&instance->col_res_data.sdd_resp[0].nfcid[1], uid, 3);
uint8_t bss = 0;
for(size_t i = 0; i < 4; i++) {
bss ^= instance->col_res_data.sdd_resp[0].nfcid[i];
}
instance->col_res_data.sdd_resp[0].bss = bss;
instance->col_res_data.sel_resp[0].sak = 0x04;
memcpy(instance->col_res_data.sdd_resp[1].nfcid, &uid[3], 4);
bss = 0;
for(size_t i = 0; i < 4; i++) {
bss ^= instance->col_res_data.sdd_resp[1].nfcid[i];
}
instance->col_res_data.sdd_resp[1].bss = bss;
instance->col_res_data.sel_resp[1].sak = sak;
} else {
furi_crash("Not supporting not 7 bytes");
}
}
Nfc* nfc_alloc() {
Nfc* instance = malloc(sizeof(Nfc));
return instance;
}
void nfc_free(Nfc* instance) {
furi_check(instance);
free(instance);
}
void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) {
UNUSED(instance);
UNUSED(tech);
instance->mode = mode;
}
void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) {
UNUSED(instance);
UNUSED(fdt_poll_fc);
}
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {
UNUSED(instance);
UNUSED(fdt_listen_fc);
}
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {
UNUSED(instance);
UNUSED(mask_rx_time_fc);
}
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {
UNUSED(instance);
UNUSED(fdt_poll_poll_us);
}
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {
UNUSED(instance);
UNUSED(guard_time_us);
}
NfcError nfc_iso14443a_listener_set_col_res_data(
Nfc* instance,
uint8_t* uid,
uint8_t uid_len,
uint8_t* atqa,
uint8_t sak) {
furi_check(instance);
furi_check(uid);
furi_check(atqa);
nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak);
return NfcErrorNone;
}
static int32_t nfc_worker_poller(void* context) {
Nfc* instance = context;
furi_check(instance->callback);
instance->state = NfcStateReady;
NfcCommand command = NfcCommandContinue;
NfcEvent event = {};
while(true) {
event.type = NfcEventTypePollerReady;
command = instance->callback(event, instance->context);
if(command == NfcCommandStop) {
break;
}
}
instance->state = NfcStateIdle;
return 0;
}
static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, uint16_t rx_bits) {
furi_check(instance->col_res_status != Iso14443_3aColResStatusDone);
BitBuffer* tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
bool processed = false;
if((rx_bits == 7) && (rx_data[0] == 0x52)) {
instance->col_res_status = Iso14443_3aColResStatusInProgress;
bit_buffer_copy_bytes(
tx_buffer,
instance->col_res_data.sens_resp.sens_resp,
sizeof(instance->col_res_data.sens_resp.sens_resp));
nfc_listener_tx(instance, tx_buffer);
processed = true;
} else if(rx_bits == 2 * 8) {
if((rx_data[0] == 0x93) && (rx_data[1] == 0x20)) {
bit_buffer_copy_bytes(
tx_buffer,
(const uint8_t*)&instance->col_res_data.sdd_resp[0],
sizeof(Iso14443_3aSddResp));
nfc_listener_tx(instance, tx_buffer);
processed = true;
} else if((rx_data[0] == 0x95) && (rx_data[1] == 0x20)) {
bit_buffer_copy_bytes(
tx_buffer,
(const uint8_t*)&instance->col_res_data.sdd_resp[1],
sizeof(Iso14443_3aSddResp));
nfc_listener_tx(instance, tx_buffer);
processed = true;
}
} else if(rx_bits == 9 * 8) {
if((rx_data[0] == 0x93) && (rx_data[1] == 0x70)) {
bit_buffer_set_size_bytes(tx_buffer, 1);
bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[0].sak);
iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);
nfc_listener_tx(instance, tx_buffer);
processed = true;
} else if((rx_data[0] == 0x95) && (rx_data[1] == 0x70)) {
bit_buffer_set_size_bytes(tx_buffer, 1);
bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[1].sak);
iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);
nfc_listener_tx(instance, tx_buffer);
instance->col_res_status = Iso14443_3aColResStatusDone;
NfcEvent event = {.type = NfcEventTypeListenerActivated};
instance->callback(event, instance->context);
processed = true;
}
}
if(!processed) {
NfcMessage message = {.type = NfcMessageTypeTimeout};
furi_message_queue_put(poller_queue, &message, FuriWaitForever);
}
bit_buffer_free(tx_buffer);
}
static int32_t nfc_worker_listener(void* context) {
Nfc* instance = context;
furi_check(instance->callback);
NfcMessage message = {};
NfcEventData event_data = {};
event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
NfcEvent nfc_event = {.data = event_data};
while(true) {
furi_message_queue_get(listener_queue, &message, FuriWaitForever);
bit_buffer_copy_bits(event_data.buffer, message.data.data, message.data.data_bits);
if((message.data.data[0] == 0x52) && (message.data.data_bits == 7)) {
instance->col_res_status = Iso14443_3aColResStatusIdle;
}
if(message.type == NfcMessageTypeAbort) {
break;
} else if(message.type == NfcMessageTypeTx) {
nfc_test_print(
NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits);
if(instance->col_res_status != Iso14443_3aColResStatusDone) {
nfc_worker_listener_pass_col_res(
instance, message.data.data, message.data.data_bits);
} else {
instance->state = NfcStateReady;
nfc_event.type = NfcEventTypeRxEnd;
instance->callback(nfc_event, instance->context);
}
}
}
instance->state = NfcStateIdle;
instance->col_res_status = Iso14443_3aColResStatusIdle;
memset(&instance->col_res_data, 0, sizeof(instance->col_res_data));
bit_buffer_free(nfc_event.data.buffer);
return 0;
}
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
furi_check(instance);
furi_check(instance->worker_thread == NULL);
if(instance->mode == NfcModeListener) {
furi_check(listener_queue == NULL);
// Check that poller didn't start
furi_check(poller_queue == NULL);
} else {
furi_check(poller_queue == NULL);
// Check that poller is started after listener
furi_check(listener_queue);
}
instance->callback = callback;
instance->context = context;
if(instance->mode == NfcModeListener) {
listener_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));
} else {
poller_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));
}
instance->worker_thread = furi_thread_alloc();
furi_thread_set_context(instance->worker_thread, instance);
furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHigh);
furi_thread_set_stack_size(instance->worker_thread, 8 * 1024);
if(instance->mode == NfcModeListener) {
furi_thread_set_name(instance->worker_thread, "NfcWorkerListener");
furi_thread_set_callback(instance->worker_thread, nfc_worker_listener);
} else {
furi_thread_set_name(instance->worker_thread, "NfcWorkerPoller");
furi_thread_set_callback(instance->worker_thread, nfc_worker_poller);
}
furi_thread_start(instance->worker_thread);
}
void nfc_stop(Nfc* instance) {
furi_check(instance);
furi_check(instance->worker_thread);
if(instance->mode == NfcModeListener) {
NfcMessage message = {.type = NfcMessageTypeAbort};
furi_message_queue_put(listener_queue, &message, FuriWaitForever);
furi_thread_join(instance->worker_thread);
furi_message_queue_free(listener_queue);
listener_queue = NULL;
furi_thread_free(instance->worker_thread);
instance->worker_thread = NULL;
} else {
furi_thread_join(instance->worker_thread);
furi_message_queue_free(poller_queue);
poller_queue = NULL;
furi_thread_free(instance->worker_thread);
instance->worker_thread = NULL;
}
}
// Called from worker thread
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {
furi_check(instance);
furi_check(poller_queue);
furi_check(listener_queue);
furi_check(tx_buffer);
NfcMessage message = {};
message.type = NfcMessageTypeTx;
message.data.data_bits = bit_buffer_get_size(tx_buffer);
bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
furi_message_queue_put(poller_queue, &message, FuriWaitForever);
return NfcErrorNone;
}
NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) {
return nfc_listener_tx(instance, tx_buffer);
}
NfcError
nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {
furi_check(instance);
furi_check(tx_buffer);
furi_check(rx_buffer);
furi_check(poller_queue);
furi_check(listener_queue);
UNUSED(fwt);
NfcError error = NfcErrorNone;
NfcMessage message = {};
message.type = NfcMessageTypeTx;
message.data.data_bits = bit_buffer_get_size(tx_buffer);
bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
// Tx
furi_check(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk);
// Rx
FuriStatus status = furi_message_queue_get(poller_queue, &message, 50);
if(status == FuriStatusErrorTimeout) {
error = NfcErrorTimeout;
} else if(message.type == NfcMessageTypeTx) {
bit_buffer_copy_bits(rx_buffer, message.data.data, message.data.data_bits);
nfc_test_print(
NfcTransportLogLevelWarning, "TAG", message.data.data, message.data.data_bits);
} else if(message.type == NfcMessageTypeTimeout) {
error = NfcErrorTimeout;
}
return error;
}
NfcError nfc_iso14443a_poller_trx_custom_parity(
Nfc* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
}
// Technology specific API
NfcError nfc_iso14443a_poller_trx_short_frame(
Nfc* instance,
NfcIso14443aShortFrame frame,
BitBuffer* rx_buffer,
uint32_t fwt) {
UNUSED(frame);
BitBuffer* tx_buffer = bit_buffer_alloc(32);
bit_buffer_set_size(tx_buffer, 7);
bit_buffer_set_byte(tx_buffer, 0, 0x52);
NfcError error = nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
bit_buffer_free(tx_buffer);
return error;
}
NfcError nfc_iso14443a_poller_trx_sdd_frame(
Nfc* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
}
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
UNUSED(instance);
return NfcErrorNone;
}
#endif

View File

@@ -1,66 +0,0 @@
Filetype: Flipper NFC device
Version: 3
# Nfc device type can be UID, Mifare Ultralight, Mifare Classic
Device type: NTAG213
# UID, ATQA and SAK are common for all formats
UID: 04 AC 6B 72 BA 6C 80
ATQA: 00 44
SAK: 00
# Mifare Ultralight specific data
Data format version: 1
Signature: 2D AE BC AF 84 B8 85 87 C2 FB FE 76 13 58 86 72 8E 1D 3C B5 DA 24 23 44 E5 63 4D 4C 82 FB D7 18
Mifare version: 00 04 04 02 01 00 0F 03
Counter 0: 0
Tearing 0: 00
Counter 1: 0
Tearing 1: 00
Counter 2: 0
Tearing 2: 00
Pages total: 45
Pages read: 45
Page 0: 04 AC 6B 4B
Page 1: 72 BA 6C 80
Page 2: 24 48 00 00
Page 3: E1 10 12 00
Page 4: 00 00 41 50
Page 5: 00 00 31 31
Page 6: 00 20 09 28
Page 7: 00 03 31 59
Page 8: 91 DF D3 00
Page 9: 00 00 00 00
Page 10: 00 00 00 00
Page 11: 00 00 00 00
Page 12: 00 00 00 00
Page 13: 00 00 00 00
Page 14: 00 00 00 00
Page 15: 00 00 00 00
Page 16: 00 00 00 00
Page 17: 00 00 00 00
Page 18: 00 00 00 00
Page 19: 00 00 00 00
Page 20: 00 00 00 00
Page 21: 00 00 00 00
Page 22: 00 00 00 00
Page 23: 00 00 00 00
Page 24: 00 00 00 00
Page 25: 00 00 00 00
Page 26: 00 00 00 00
Page 27: 00 00 00 00
Page 28: 00 00 00 00
Page 29: 00 00 00 00
Page 30: 00 00 00 00
Page 31: 00 00 00 00
Page 32: 00 00 00 00
Page 33: 00 00 00 00
Page 34: 00 00 00 00
Page 35: 00 00 00 00
Page 36: 00 00 00 00
Page 37: 00 00 00 00
Page 38: 00 00 00 00
Page 39: 00 00 00 00
Page 40: 00 00 00 BD
Page 41: 04 00 00 04
Page 42: C0 05 00 00
Page 43: 95 3F 52 FF
Page 44: 00 00 00 00
Failed authentication attempts: 0

View File

@@ -1,156 +0,0 @@
Filetype: Flipper NFC device
Version: 3
# Nfc device type can be UID, Mifare Ultralight, Mifare Classic
Device type: NTAG215
# UID, ATQA and SAK are common for all formats
UID: 04 51 5C FA 6F 73 81
ATQA: 00 44
SAK: 00
# Mifare Ultralight specific data
Data format version: 1
Signature: 42 21 E4 6C 79 6A 81 5E EA 0D 93 6D 85 EE 4B 0C 2A 00 D5 77 F1 C5 67 F3 63 75 F8 EB 86 48 5E 6B
Mifare version: 00 04 04 02 01 00 11 03
Counter 0: 0
Tearing 0: 00
Counter 1: 0
Tearing 1: 00
Counter 2: 00
Tearing 2: 00
Pages total: 135
Pages read: 135
Page 0: 04 51 5C 81
Page 1: FA 6F 73 81
Page 2: 67 48 0F E0
Page 3: F1 10 FF EE
Page 4: A5 00 00 00
Page 5: 90 42 74 71
Page 6: FD 8F 50 61
Page 7: C5 65 1B 54
Page 8: EF 68 D0 8E
Page 9: 3D 35 DB 83
Page 10: D3 00 29 F6
Page 11: 42 2A A5 5C
Page 12: F1 69 0A FC
Page 13: B6 44 E9 6B
Page 14: 77 41 88 81
Page 15: 86 31 CB AD
Page 16: B1 DE F1 AB
Page 17: DF 96 C2 C5
Page 18: C1 26 99 96
Page 19: 85 AF 9F 0E
Page 20: 58 FE ED DC
Page 21: 0A 0A 00 01
Page 22: 03 C1 05 02
Page 23: 38 39 34 33
Page 24: 49 2D 4E 5C
Page 25: 5B 21 0F 44
Page 26: 3F 3F 76 69
Page 27: B4 72 D8 38
Page 28: A0 35 53 51
Page 29: 53 EB A6 7C
Page 30: 3E 8B 97 C0
Page 31: 00 7A 45 13
Page 32: 3A 8B D4 0F
Page 33: 31 C2 32 CC
Page 34: B4 24 A6 1B
Page 35: D3 F5 4A 1F
Page 36: CD 8F 1D 64
Page 37: 01 F4 DF C2
Page 38: 11 16 C2 C5
Page 39: 30 6D 49 AF
Page 40: 10 D4 7C 3C
Page 41: 6E 36 4E 08
Page 42: 95 76 BC 84
Page 43: 35 50 DD F0
Page 44: 21 0F EE D9
Page 45: 85 19 54 5F
Page 46: 3E A9 04 20
Page 47: 1B 97 E4 39
Page 48: FF 0A 45 F6
Page 49: 13 D4 3E DD
Page 50: 97 42 FC 67
Page 51: 6A AC 78 96
Page 52: D1 DA 25 23
Page 53: BF 4D B3 76
Page 54: F1 21 ED 15
Page 55: BD 55 11 C4
Page 56: 4E 8C E9 23
Page 57: C0 C4 6D 5A
Page 58: 58 25 FF 95
Page 59: 3C 2B 7A 57
Page 60: 66 BE A0 61
Page 61: BC FC 4A 31
Page 62: 4D AC EE 81
Page 63: BE 1A 86 04
Page 64: F6 D7 5E B3
Page 65: E7 A8 A2 86
Page 66: E9 40 AB 47
Page 67: C8 36 E4 3E
Page 68: A7 4D D3 EA
Page 69: 83 9A 64 F7
Page 70: 96 6B 5D BF
Page 71: 4E A2 A6 0F
Page 72: BD 3D BE 7C
Page 73: 22 0C 68 51
Page 74: 0F 9A B8 AE
Page 75: 38 2C C4 CD
Page 76: 53 D8 DD 18
Page 77: A6 5D 35 87
Page 78: C9 6D 99 59
Page 79: 61 9F B6 DC
Page 80: E6 22 0F 99
Page 81: 39 82 79 60
Page 82: 58 2E BE F7
Page 83: EF F7 95 62
Page 84: D5 06 1B 58
Page 85: 65 05 A9 08
Page 86: 75 ED 5D 90
Page 87: 5A E1 7E C9
Page 88: 35 D6 29 BB
Page 89: D0 67 6C F9
Page 90: A0 FF 0B 93
Page 91: 22 EA A3 3F
Page 92: E2 BD BD 58
Page 93: BE 93 D9 94
Page 94: 41 CC 7E 40
Page 95: E6 8C 5A 43
Page 96: 65 C1 24 94
Page 97: B9 97 61 13
Page 98: AD 74 FF 21
Page 99: 0F EC F6 03
Page 100: 89 5D 89 E5
Page 101: 8D 11 F8 D7
Page 102: 33 43 79 2E
Page 103: 23 E5 29 B5
Page 104: 53 98 13 FF
Page 105: E8 79 8B 33
Page 106: 45 6C 34 38
Page 107: 3B 69 28 D7
Page 108: D2 80 B0 2F
Page 109: D0 18 D5 DD
Page 110: 6C 2D D9 97
Page 111: CA 78 B4 A2
Page 112: B7 3E B8 79
Page 113: A2 BE 54 E4
Page 114: C8 28 0C 4A
Page 115: 81 E7 EC 1C
Page 116: 39 93 6F 70
Page 117: 75 77 5C FC
Page 118: 66 58 0C 1C
Page 119: 9F 70 2E C8
Page 120: 52 4A 52 BD
Page 121: 56 D5 6A 15
Page 122: 54 1B 33 90
Page 123: 44 11 C1 07
Page 124: 11 5C BA 80
Page 125: 10 14 20 9A
Page 126: 4A D8 E6 36
Page 127: DA B8 59 E5
Page 128: 5E 48 95 DA
Page 129: 96 6A 26 85
Page 130: 01 00 0F BD
Page 131: 00 00 00 04
Page 132: 5F 00 00 00
Page 133: 00 00 00 00
Page 134: 00 00 00 00
Failed authentication attempts: 0

View File

@@ -1,252 +0,0 @@
Filetype: Flipper NFC device
Version: 2
# Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card
Device type: NTAG216
# UID, ATQA and SAK are common for all formats
UID: 04 D9 65 0A 32 5E 80
ATQA: 44 00
SAK: 00
# Mifare Ultralight specific data
Data format version: 1
Signature: 48 2A F2 01 0F F2 F5 A7 9A D5 79 6E CB 14 54 48 98 D1 57 5D 8A 23 A9 B0 E8 20 02 3E CD C8 16 DB
Mifare version: 00 04 04 02 01 00 13 03
Counter 0: 0
Tearing 0: 00
Counter 1: 0
Tearing 1: 00
Counter 2: 0
Tearing 2: 00
Pages total: 231
Pages read: 231
Page 0: 04 D9 65 30
Page 1: 0A 32 5E 80
Page 2: E6 48 00 00
Page 3: E1 10 6D 00
Page 4: 03 37 D1 01
Page 5: 33 55 04 6D
Page 6: 2E 79 6F 75
Page 7: 74 75 62 65
Page 8: 2E 63 6F 6D
Page 9: 2F 77 61 74
Page 10: 63 68 3F 76
Page 11: 3D 62 78 71
Page 12: 4C 73 72 6C
Page 13: 61 6B 4B 38
Page 14: 26 66 65 61
Page 15: 74 75 72 65
Page 16: 3D 79 6F 75
Page 17: 74 75 2E 62
Page 18: 65 FE 00 00
Page 19: 00 00 00 00
Page 20: 00 00 00 00
Page 21: 00 00 00 00
Page 22: 00 00 00 00
Page 23: 00 00 00 00
Page 24: 00 00 00 00
Page 25: 00 00 00 00
Page 26: 00 00 00 00
Page 27: 00 00 00 00
Page 28: 00 00 00 00
Page 29: 00 00 00 00
Page 30: 00 00 00 00
Page 31: 00 00 00 00
Page 32: 00 00 00 00
Page 33: 00 00 00 00
Page 34: 00 00 00 00
Page 35: 00 00 00 00
Page 36: 00 00 00 00
Page 37: 00 00 00 00
Page 38: 00 00 00 00
Page 39: 00 00 00 00
Page 40: 00 00 00 00
Page 41: 00 00 00 00
Page 42: 00 00 00 00
Page 43: 00 00 00 00
Page 44: 00 00 00 00
Page 45: 00 00 00 00
Page 46: 00 00 00 00
Page 47: 00 00 00 00
Page 48: 00 00 00 00
Page 49: 00 00 00 00
Page 50: 00 00 00 00
Page 51: 00 00 00 00
Page 52: 00 00 00 00
Page 53: 00 00 00 00
Page 54: 00 00 00 00
Page 55: 00 00 00 00
Page 56: 00 00 00 00
Page 57: 00 00 00 00
Page 58: 00 00 00 00
Page 59: 00 00 00 00
Page 60: 00 00 00 00
Page 61: 00 00 00 00
Page 62: 00 00 00 00
Page 63: 00 00 00 00
Page 64: 00 00 00 00
Page 65: 00 00 00 00
Page 66: 00 00 00 00
Page 67: 00 00 00 00
Page 68: 00 00 00 00
Page 69: 00 00 00 00
Page 70: 00 00 00 00
Page 71: 00 00 00 00
Page 72: 00 00 00 00
Page 73: 00 00 00 00
Page 74: 00 00 00 00
Page 75: 00 00 00 00
Page 76: 00 00 00 00
Page 77: 00 00 00 00
Page 78: 00 00 00 00
Page 79: 00 00 00 00
Page 80: 00 00 00 00
Page 81: 00 00 00 00
Page 82: 00 00 00 00
Page 83: 00 00 00 00
Page 84: 00 00 00 00
Page 85: 00 00 00 00
Page 86: 00 00 00 00
Page 87: 00 00 00 00
Page 88: 00 00 00 00
Page 89: 00 00 00 00
Page 90: 00 00 00 00
Page 91: 00 00 00 00
Page 92: 00 00 00 00
Page 93: 00 00 00 00
Page 94: 00 00 00 00
Page 95: 00 00 00 00
Page 96: 00 00 00 00
Page 97: 00 00 00 00
Page 98: 00 00 00 00
Page 99: 00 00 00 00
Page 100: 00 00 00 00
Page 101: 00 00 00 00
Page 102: 00 00 00 00
Page 103: 00 00 00 00
Page 104: 00 00 00 00
Page 105: 00 00 00 00
Page 106: 00 00 00 00
Page 107: 00 00 00 00
Page 108: 00 00 00 00
Page 109: 00 00 00 00
Page 110: 00 00 00 00
Page 111: 00 00 00 00
Page 112: 00 00 00 00
Page 113: 00 00 00 00
Page 114: 00 00 00 00
Page 115: 00 00 00 00
Page 116: 00 00 00 00
Page 117: 00 00 00 00
Page 118: 00 00 00 00
Page 119: 00 00 00 00
Page 120: 00 00 00 00
Page 121: 00 00 00 00
Page 122: 00 00 00 00
Page 123: 00 00 00 00
Page 124: 00 00 00 00
Page 125: 00 00 00 00
Page 126: 00 00 00 00
Page 127: 00 00 00 00
Page 128: 00 00 00 00
Page 129: 00 00 00 00
Page 130: 00 00 00 00
Page 131: 00 00 00 00
Page 132: 00 00 00 00
Page 133: 00 00 00 00
Page 134: 00 00 00 00
Page 135: 00 00 00 00
Page 136: 00 00 00 00
Page 137: 00 00 00 00
Page 138: 00 00 00 00
Page 139: 00 00 00 00
Page 140: 00 00 00 00
Page 141: 00 00 00 00
Page 142: 00 00 00 00
Page 143: 00 00 00 00
Page 144: 00 00 00 00
Page 145: 00 00 00 00
Page 146: 00 00 00 00
Page 147: 00 00 00 00
Page 148: 00 00 00 00
Page 149: 00 00 00 00
Page 150: 00 00 00 00
Page 151: 00 00 00 00
Page 152: 00 00 00 00
Page 153: 00 00 00 00
Page 154: 00 00 00 00
Page 155: 00 00 00 00
Page 156: 00 00 00 00
Page 157: 00 00 00 00
Page 158: 00 00 00 00
Page 159: 00 00 00 00
Page 160: 00 00 00 00
Page 161: 00 00 00 00
Page 162: 00 00 00 00
Page 163: 00 00 00 00
Page 164: 00 00 00 00
Page 165: 00 00 00 00
Page 166: 00 00 00 00
Page 167: 00 00 00 00
Page 168: 00 00 00 00
Page 169: 00 00 00 00
Page 170: 00 00 00 00
Page 171: 00 00 00 00
Page 172: 00 00 00 00
Page 173: 00 00 00 00
Page 174: 00 00 00 00
Page 175: 00 00 00 00
Page 176: 00 00 00 00
Page 177: 00 00 00 00
Page 178: 00 00 00 00
Page 179: 00 00 00 00
Page 180: 00 00 00 00
Page 181: 00 00 00 00
Page 182: 00 00 00 00
Page 183: 00 00 00 00
Page 184: 00 00 00 00
Page 185: 00 00 00 00
Page 186: 00 00 00 00
Page 187: 00 00 00 00
Page 188: 00 00 00 00
Page 189: 00 00 00 00
Page 190: 00 00 00 00
Page 191: 00 00 00 00
Page 192: 00 00 00 00
Page 193: 00 00 00 00
Page 194: 00 00 00 00
Page 195: 00 00 00 00
Page 196: 00 00 00 00
Page 197: 00 00 00 00
Page 198: 00 00 00 00
Page 199: 00 00 00 00
Page 200: 00 00 00 00
Page 201: 00 00 00 00
Page 202: 00 00 00 00
Page 203: 00 00 00 00
Page 204: 00 00 00 00
Page 205: 00 00 00 00
Page 206: 00 00 00 00
Page 207: 00 00 00 00
Page 208: 00 00 00 00
Page 209: 00 00 00 00
Page 210: 00 00 00 00
Page 211: 00 00 00 00
Page 212: 00 00 00 00
Page 213: 00 00 00 00
Page 214: 00 00 00 00
Page 215: 00 00 00 00
Page 216: 00 00 00 00
Page 217: 00 00 00 00
Page 218: 00 00 00 00
Page 219: 00 00 00 00
Page 220: 00 00 00 00
Page 221: 00 00 00 00
Page 222: 00 00 00 00
Page 223: 00 00 00 00
Page 224: 00 00 00 00
Page 225: 00 00 00 00
Page 226: 00 00 00 BD
Page 227: 04 00 00 FF
Page 228: 00 05 00 00
Page 229: 00 00 00 00
Page 230: 00 00 00 00
Failed authentication attempts: 0

View File

@@ -1,41 +0,0 @@
Filetype: Flipper NFC device
Version: 3
# Nfc device type can be UID, Mifare Ultralight, Mifare Classic
Device type: Mifare Ultralight 11
# UID, ATQA and SAK are common for all formats
UID: 04 15 74 F2 B0 5E 81
ATQA: 00 44
SAK: 00
# Mifare Ultralight specific data
Data format version: 1
Signature: A4 37 7D E5 8C 2F 88 D8 04 60 41 6E 3A C8 CD DB 19 94 26 12 C5 D0 12 B0 EB 88 05 72 89 F2 A5 61
Mifare version: 00 04 03 01 01 00 0B 03
Counter 0: 0
Tearing 0: BD
Counter 1: 0
Tearing 1: BD
Counter 2: 0
Tearing 2: BD
Pages total: 20
Pages read: 20
Page 0: 04 15 74 ED
Page 1: F2 B0 5E 81
Page 2: 9D 48 F8 FF
Page 3: C1 31 3E 3F
Page 4: B0 00 F0 02
Page 5: 2F B3 45 A0
Page 6: D4 9C 02 F2
Page 7: 4A B1 ED FF
Page 8: C8 01 00 02
Page 9: 4F B3 46 70
Page 10: EE F6 60 B0
Page 11: B6 C6 12 1B
Page 12: B9 1E 49 C3
Page 13: 49 DF 7A 57
Page 14: 08 52 2A 11
Page 15: 28 0A 28 59
Page 16: 00 00 00 FF
Page 17: 00 05 00 00
Page 18: FF FF FF FF
Page 19: 00 00 00 00
Failed authentication attempts: 0

View File

@@ -1,62 +0,0 @@
Filetype: Flipper NFC device
Version: 3
# Nfc device type can be UID, Mifare Ultralight, Mifare Classic
Device type: Mifare Ultralight 21
# UID, ATQA and SAK are common for all formats
UID: 34 BF AB B1 AE 73 D6
ATQA: 00 44
SAK: 00
# Mifare Ultralight specific data
Data format version: 1
Signature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Mifare version: 00 34 21 01 01 00 0E 03
Counter 0: 0
Tearing 0: 00
Counter 1: 0
Tearing 1: 00
Counter 2: 0
Tearing 2: 00
Pages total: 41
Pages read: 41
Page 0: 34 BF AB A8
Page 1: B1 AE 73 D6
Page 2: BA 00 70 08
Page 3: FF FF FF FC
Page 4: 45 D9 BB A0
Page 5: 5D 9D FA 00
Page 6: 80 70 38 40
Page 7: 12 30 02 00
Page 8: 00 00 00 00
Page 9: 00 00 00 00
Page 10: AC A1 0D E4
Page 11: 80 70 38 40
Page 12: 00 57 A0 01
Page 13: 00 08 C1 40
Page 14: 00 00 00 00
Page 15: AC A1 0D E4
Page 16: 00 00 00 00
Page 17: 00 00 00 00
Page 18: 00 00 00 00
Page 19: 00 00 00 00
Page 20: 00 00 00 00
Page 21: 00 00 00 00
Page 22: 00 00 00 00
Page 23: 00 00 00 00
Page 24: 00 00 00 00
Page 25: 00 00 00 00
Page 26: 00 00 00 00
Page 27: 00 00 00 00
Page 28: 00 00 00 00
Page 29: 00 00 00 00
Page 30: 00 00 00 00
Page 31: 00 00 00 00
Page 32: 00 00 00 00
Page 33: 00 00 00 00
Page 34: 00 00 00 00
Page 35: 00 00 00 00
Page 36: 00 00 00 BD
Page 37: 00 00 00 FF
Page 38: 00 05 00 00
Page 39: FF FF FF FF
Page 40: 00 00 00 00
Failed authentication attempts: 0

View File

@@ -18,8 +18,6 @@
#include <cli/cli.h> #include <cli/cli.h>
#include <loader/loader.h> #include <loader/loader.h>
#include <protobuf_version.h> #include <protobuf_version.h>
#include <FreeRTOS.h>
#include <semphr.h> #include <semphr.h>
LIST_DEF(MsgList, PB_Main, M_POD_OPLIST) LIST_DEF(MsgList, PB_Main, M_POD_OPLIST)
@@ -38,7 +36,7 @@ typedef struct {
FuriStreamBuffer* output_stream; FuriStreamBuffer* output_stream;
SemaphoreHandle_t close_session_semaphore; SemaphoreHandle_t close_session_semaphore;
SemaphoreHandle_t terminate_semaphore; SemaphoreHandle_t terminate_semaphore;
uint32_t timeout; TickType_t timeout;
} RpcSessionContext; } RpcSessionContext;
static RpcSessionContext rpc_session[TEST_RPC_SESSIONS]; static RpcSessionContext rpc_session[TEST_RPC_SESSIONS];
@@ -546,7 +544,7 @@ static bool test_rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_
RpcSessionContext* session_context = istream->state; RpcSessionContext* session_context = istream->state;
size_t bytes_received = 0; size_t bytes_received = 0;
uint32_t now = furi_get_tick(); TickType_t now = xTaskGetTickCount();
int32_t time_left = session_context->timeout - now; int32_t time_left = session_context->timeout - now;
time_left = MAX(time_left, 0); time_left = MAX(time_left, 0);
bytes_received = bytes_received =
@@ -690,7 +688,7 @@ static void test_rpc_decode_and_compare(MsgList_t expected_msg_list, uint8_t ses
furi_check(!MsgList_empty_p(expected_msg_list)); furi_check(!MsgList_empty_p(expected_msg_list));
furi_check(session < TEST_RPC_SESSIONS); furi_check(session < TEST_RPC_SESSIONS);
rpc_session[session].timeout = furi_get_tick() + MAX_RECEIVE_OUTPUT_TIMEOUT; rpc_session[session].timeout = xTaskGetTickCount() + MAX_RECEIVE_OUTPUT_TIMEOUT;
pb_istream_t istream = { pb_istream_t istream = {
.callback = test_rpc_pb_stream_read, .callback = test_rpc_pb_stream_read,
.state = &rpc_session[session], .state = &rpc_session[session],
@@ -714,7 +712,7 @@ static void test_rpc_decode_and_compare(MsgList_t expected_msg_list, uint8_t ses
pb_release(&PB_Main_msg, &result); pb_release(&PB_Main_msg, &result);
} }
rpc_session[session].timeout = furi_get_tick() + 50; rpc_session[session].timeout = xTaskGetTickCount() + 50;
if(pb_decode_ex(&istream, &PB_Main_msg, &result, PB_DECODE_DELIMITED)) { if(pb_decode_ex(&istream, &PB_Main_msg, &result, PB_DECODE_DELIMITED)) {
mu_fail("decoded more than expected"); mu_fail("decoded more than expected");
} }

View File

@@ -65,8 +65,8 @@ const UnitTest unit_tests[] = {
void minunit_print_progress() { void minunit_print_progress() {
static const char progress[] = {'\\', '|', '/', '-'}; static const char progress[] = {'\\', '|', '/', '-'};
static uint8_t progress_counter = 0; static uint8_t progress_counter = 0;
static uint32_t last_tick = 0; static TickType_t last_tick = 0;
uint32_t current_tick = furi_get_tick(); TickType_t current_tick = xTaskGetTickCount();
if(current_tick - last_tick > 20) { if(current_tick - last_tick > 20) {
last_tick = current_tick; last_tick = current_tick;
printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]); printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]);

View File

@@ -5,7 +5,6 @@ App(
entry_point="example_plugins_app", entry_point="example_plugins_app",
stack_size=2 * 1024, stack_size=2 * 1024,
fap_category="Examples", fap_category="Examples",
sources=["*.c", "!plugin*.c"],
) )
App( App(
@@ -22,7 +21,6 @@ App(
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.PLUGIN,
entry_point="example_plugin1_ep", entry_point="example_plugin1_ep",
requires=["example_plugins", "example_plugins_multi"], requires=["example_plugins", "example_plugins_multi"],
sources=["plugin1.c"],
) )
App( App(
@@ -30,5 +28,4 @@ App(
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.PLUGIN,
entry_point="example_plugin2_ep", entry_point="example_plugin2_ep",
requires=["example_plugins_multi"], requires=["example_plugins_multi"],
sources=["plugin2.c"],
) )

View File

@@ -5,7 +5,6 @@ App(
entry_point="example_advanced_plugins_app", entry_point="example_advanced_plugins_app",
stack_size=2 * 1024, stack_size=2 * 1024,
fap_category="Examples", fap_category="Examples",
sources=["*.c*", "!plugin*.c"],
) )
App( App(

View File

@@ -95,17 +95,18 @@ void archive_free(ArchiveApp* archive) {
} }
void archive_show_loading_popup(ArchiveApp* context, bool show) { void archive_show_loading_popup(ArchiveApp* context, bool show) {
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
ViewStack* view_stack = context->view_stack; ViewStack* view_stack = context->view_stack;
Loading* loading = context->loading; Loading* loading = context->loading;
if(show) { if(show) {
// Raise timer priority so that animations can play // Raise timer priority so that animations can play
furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
view_stack_add_view(view_stack, loading_get_view(loading)); view_stack_add_view(view_stack, loading_get_view(loading));
} else { } else {
view_stack_remove_view(view_stack, loading_get_view(loading)); view_stack_remove_view(view_stack, loading_get_view(loading));
// Restore default timer priority // Restore default timer priority
furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
} }
} }

View File

@@ -6,7 +6,6 @@ App(
stack_size=2 * 1024, stack_size=2 * 1024,
icon="A_BadUsb_14", icon="A_BadUsb_14",
order=70, order=70,
resources="resources",
fap_libs=["assets"], fap_libs=["assets"],
fap_icon="icon.png", fap_icon="icon.png",
fap_category="USB", fap_category="USB",

View File

@@ -6,7 +6,6 @@ App(
icon="A_Clock_14", icon="A_Clock_14",
stack_size=2 * 1024, stack_size=2 * 1024,
order=81, order=81,
resources="resources",
fap_icon="icon.png", fap_icon="icon.png",
fap_category="Tools", fap_category="Tools",
) )

View File

@@ -17,6 +17,5 @@ App(
apptype=FlipperAppType.STARTUP, apptype=FlipperAppType.STARTUP,
targets=["f7"], targets=["f7"],
entry_point="ibutton_on_system_start", entry_point="ibutton_on_system_start",
sources=["ibutton_cli.c"],
order=60, order=60,
) )

View File

@@ -39,23 +39,21 @@ static void ibutton_make_app_folder(iButton* ibutton) {
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
} }
static void ibutton_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) {
furi_assert(context); furi_assert(context);
iButton* ibutton = context; iButton* ibutton = context;
if(event->type == RpcAppEventTypeSessionClose) { if(event == RpcAppEventSessionClose) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);
rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
ibutton->rpc = NULL; ibutton->rpc = NULL;
} else if(event->type == RpcAppEventTypeAppExit) { } else if(event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
} else if(event->type == RpcAppEventTypeLoadFile) { } else if(event == RpcAppEventLoadFile) {
furi_assert(event->data.type == RpcAppSystemEventDataTypeString); view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
furi_string_set(ibutton->file_path, event->data.string);
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoadFile);
} else { } else {
rpc_system_app_confirm(ibutton->rpc, false); rpc_system_app_confirm(ibutton->rpc, event, false);
} }
} }

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
typedef enum { enum iButtonCustomEvent {
// Reserve first 100 events for button types and indexes, starting from 0 // Reserve first 100 events for button types and indexes, starting from 0
iButtonCustomEventReserved = 100, iButtonCustomEventReserved = 100,
@@ -10,12 +10,8 @@ typedef enum {
iButtonCustomEventByteEditResult, iButtonCustomEventByteEditResult,
iButtonCustomEventWorkerEmulated, iButtonCustomEventWorkerEmulated,
iButtonCustomEventWorkerRead, iButtonCustomEventWorkerRead,
iButtonCustomEventWorkerWriteOK,
iButtonCustomEventWorkerWriteSameKey,
iButtonCustomEventWorkerWriteNoDetect,
iButtonCustomEventWorkerWriteCannotWrite,
iButtonCustomEventRpcLoadFile, iButtonCustomEventRpcLoad,
iButtonCustomEventRpcExit, iButtonCustomEventRpcExit,
iButtonCustomEventRpcSessionClose, iButtonCustomEventRpcSessionClose,
} iButtonCustomEvent; };

View File

@@ -23,23 +23,28 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
consumed = true; consumed = true;
if(event.event == iButtonCustomEventRpcLoadFile) { if(event.event == iButtonCustomEventRpcLoad) {
bool result = false; bool result = false;
const char* file_path = rpc_system_app_get_data(ibutton->rpc);
if(ibutton_load_key(ibutton)) { if(file_path && (furi_string_empty(ibutton->file_path))) {
popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop); furi_string_set(ibutton->file_path, file_path);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); if(ibutton_load_key(ibutton)) {
ibutton_worker_emulate_start(ibutton->worker, ibutton->key); popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
result = true; ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
ibutton_worker_emulate_start(ibutton->worker, ibutton->key);
result = true;
}
} }
rpc_system_app_confirm(ibutton->rpc, result); rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result);
} else if(event.event == iButtonCustomEventRpcExit) { } else if(event.event == iButtonCustomEventRpcExit) {
rpc_system_app_confirm(ibutton->rpc, true); rpc_system_app_confirm(ibutton->rpc, RpcAppEventAppExit, true);
scene_manager_stop(ibutton->scene_manager); scene_manager_stop(ibutton->scene_manager);
view_dispatcher_stop(ibutton->view_dispatcher); view_dispatcher_stop(ibutton->view_dispatcher);

View File

@@ -5,26 +5,9 @@ typedef enum {
iButtonSceneWriteStateBlinkYellow, iButtonSceneWriteStateBlinkYellow,
} iButtonSceneWriteState; } iButtonSceneWriteState;
static inline iButtonCustomEvent
ibutton_scene_write_to_custom_event(iButtonWorkerWriteResult result) {
switch(result) {
case iButtonWorkerWriteOK:
return iButtonCustomEventWorkerWriteOK;
case iButtonWorkerWriteSameKey:
return iButtonCustomEventWorkerWriteSameKey;
case iButtonWorkerWriteNoDetect:
return iButtonCustomEventWorkerWriteNoDetect;
case iButtonWorkerWriteCannotWrite:
return iButtonCustomEventWorkerWriteCannotWrite;
default:
furi_crash();
}
}
static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult result) { static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult result) {
iButton* ibutton = context; iButton* ibutton = context;
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
ibutton->view_dispatcher, ibutton_scene_write_to_custom_event(result));
} }
void ibutton_scene_write_on_enter(void* context) { void ibutton_scene_write_on_enter(void* context) {
@@ -78,14 +61,16 @@ bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
consumed = true; consumed = true;
if((event.event == iButtonCustomEventWorkerWriteOK) || if((event.event == iButtonWorkerWriteOK) || (event.event == iButtonWorkerWriteSameKey)) {
(event.event == iButtonCustomEventWorkerWriteSameKey)) {
scene_manager_next_scene(scene_manager, iButtonSceneWriteSuccess); scene_manager_next_scene(scene_manager, iButtonSceneWriteSuccess);
} else if(event.event == iButtonCustomEventWorkerWriteNoDetect) { } else if(event.event == iButtonWorkerWriteNoDetect) {
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink); ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink);
} else if(event.event == iButtonCustomEventWorkerWriteCannotWrite) { } else if(event.event == iButtonWorkerWriteCannotWrite) {
ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink); ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink);
} }
} else if(event.type == SceneManagerEventTypeTick) {
consumed = true;
} }
return consumed; return consumed;

View File

@@ -7,8 +7,6 @@ App(
icon="A_Infrared_14", icon="A_Infrared_14",
stack_size=3 * 1024, stack_size=3 * 1024,
order=40, order=40,
sources=["*.c", "!infrared_cli.c"],
resources="resources",
fap_libs=["assets"], fap_libs=["assets"],
fap_icon="icon.png", fap_icon="icon.png",
fap_category="Infrared", fap_category="Infrared",
@@ -19,10 +17,5 @@ App(
apptype=FlipperAppType.STARTUP, apptype=FlipperAppType.STARTUP,
targets=["f7"], targets=["f7"],
entry_point="infrared_on_system_start", entry_point="infrared_on_system_start",
sources=[
"infrared_cli.c",
"infrared_brute_force.c",
"infrared_signal.c",
],
order=20, order=20,
) )

View File

@@ -1,85 +1,69 @@
#include "infrared_app_i.h" #include "infrared_i.h"
#include <string.h> #include <string.h>
#include <toolbox/path.h>
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#define TAG "InfraredApp"
#define INFRARED_TX_MIN_INTERVAL_MS 50U #define INFRARED_TX_MIN_INTERVAL_MS 50U
static const NotificationSequence* static const NotificationSequence* infrared_notification_sequences[] = {
infrared_notification_sequences[InfraredNotificationMessageCount] = { &sequence_success,
&sequence_success, &sequence_set_only_green_255,
&sequence_set_only_green_255, &sequence_reset_green,
&sequence_reset_green, &sequence_solid_yellow,
&sequence_solid_yellow, &sequence_reset_rgb,
&sequence_reset_rgb, &sequence_blink_start_cyan,
&sequence_blink_start_cyan, &sequence_blink_start_magenta,
&sequence_blink_start_magenta, &sequence_blink_stop,
&sequence_blink_stop,
}; };
static void infrared_make_app_folder(InfraredApp* infrared) { static void infrared_make_app_folder(Infrared* infrared) {
if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) { if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) {
infrared_show_error_message(infrared, "Cannot create\napp folder"); dialog_message_show_storage_error(infrared->dialogs, "Cannot create\napp folder");
} }
} }
static bool infrared_custom_event_callback(void* context, uint32_t event) { static bool infrared_custom_event_callback(void* context, uint32_t event) {
furi_assert(context); furi_assert(context);
InfraredApp* infrared = context; Infrared* infrared = context;
return scene_manager_handle_custom_event(infrared->scene_manager, event); return scene_manager_handle_custom_event(infrared->scene_manager, event);
} }
static bool infrared_back_event_callback(void* context) { static bool infrared_back_event_callback(void* context) {
furi_assert(context); furi_assert(context);
InfraredApp* infrared = context; Infrared* infrared = context;
return scene_manager_handle_back_event(infrared->scene_manager); return scene_manager_handle_back_event(infrared->scene_manager);
} }
static void infrared_tick_event_callback(void* context) { static void infrared_tick_event_callback(void* context) {
furi_assert(context); furi_assert(context);
InfraredApp* infrared = context; Infrared* infrared = context;
scene_manager_handle_tick_event(infrared->scene_manager); scene_manager_handle_tick_event(infrared->scene_manager);
} }
static void infrared_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) {
furi_assert(context); furi_assert(context);
InfraredApp* infrared = context; Infrared* infrared = context;
furi_assert(infrared->rpc_ctx); furi_assert(infrared->rpc_ctx);
if(event->type == RpcAppEventTypeSessionClose) { if(event == RpcAppEventSessionClose) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose); infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose);
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
infrared->rpc_ctx = NULL; infrared->rpc_ctx = NULL;
} else if(event->type == RpcAppEventTypeAppExit) { } else if(event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcExit); infrared->view_dispatcher, InfraredCustomEventTypeRpcExit);
} else if(event->type == RpcAppEventTypeLoadFile) { } else if(event == RpcAppEventLoadFile) {
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
furi_string_set(infrared->file_path, event->data.string);
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoadFile); infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad);
} else if(event->type == RpcAppEventTypeButtonPress) { } else if(event == RpcAppEventButtonPress) {
furi_assert( view_dispatcher_send_custom_event(
event->data.type == RpcAppSystemEventDataTypeString || infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress);
event->data.type == RpcAppSystemEventDataTypeInt32); } else if(event == RpcAppEventButtonRelease) {
if(event->data.type == RpcAppSystemEventDataTypeString) {
furi_string_set(infrared->button_name, event->data.string);
view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressName);
} else {
infrared->app_state.current_button_index = event->data.i32;
view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressIndex);
}
} else if(event->type == RpcAppEventTypeButtonRelease) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease);
} else { } else {
rpc_system_app_confirm(infrared->rpc_ctx, false); rpc_system_app_confirm(infrared->rpc_ctx, event, false);
} }
} }
@@ -125,11 +109,10 @@ static void infrared_find_vacant_remote_name(FuriString* name, const char* path)
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
} }
static InfraredApp* infrared_alloc() { static Infrared* infrared_alloc() {
InfraredApp* infrared = malloc(sizeof(InfraredApp)); Infrared* infrared = malloc(sizeof(Infrared));
infrared->file_path = furi_string_alloc(); infrared->file_path = furi_string_alloc();
infrared->button_name = furi_string_alloc();
InfraredAppState* app_state = &infrared->app_state; InfraredAppState* app_state = &infrared->app_state;
app_state->is_learning_new_remote = false; app_state->is_learning_new_remote = false;
@@ -156,7 +139,7 @@ static InfraredApp* infrared_alloc() {
infrared->worker = infrared_worker_alloc(); infrared->worker = infrared_worker_alloc();
infrared->remote = infrared_remote_alloc(); infrared->remote = infrared_remote_alloc();
infrared->current_signal = infrared_signal_alloc(); infrared->received_signal = infrared_signal_alloc();
infrared->brute_force = infrared_brute_force_alloc(); infrared->brute_force = infrared_brute_force_alloc();
infrared->submenu = submenu_alloc(); infrared->submenu = submenu_alloc();
@@ -167,16 +150,16 @@ static InfraredApp* infrared_alloc() {
view_dispatcher_add_view( view_dispatcher_add_view(
view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input)); view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input));
infrared->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
infrared->variable_item_list = variable_item_list_alloc(); infrared->variable_item_list = variable_item_list_alloc();
view_dispatcher_add_view( view_dispatcher_add_view(
infrared->view_dispatcher, infrared->view_dispatcher,
InfraredViewVariableItemList, InfraredViewVariableItemList,
variable_item_list_get_view(infrared->variable_item_list)); variable_item_list_get_view(infrared->variable_item_list));
infrared->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
infrared->button_menu = button_menu_alloc(); infrared->button_menu = button_menu_alloc();
view_dispatcher_add_view( view_dispatcher_add_view(
view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu)); view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu));
@@ -207,7 +190,7 @@ static InfraredApp* infrared_alloc() {
return infrared; return infrared;
} }
static void infrared_free(InfraredApp* infrared) { static void infrared_free(Infrared* infrared) {
furi_assert(infrared); furi_assert(infrared);
ViewDispatcher* view_dispatcher = infrared->view_dispatcher; ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
InfraredAppState* app_state = &infrared->app_state; InfraredAppState* app_state = &infrared->app_state;
@@ -224,12 +207,12 @@ static void infrared_free(InfraredApp* infrared) {
view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput); view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput);
text_input_free(infrared->text_input); text_input_free(infrared->text_input);
view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);
dialog_ex_free(infrared->dialog_ex);
view_dispatcher_remove_view(infrared->view_dispatcher, InfraredViewVariableItemList); view_dispatcher_remove_view(infrared->view_dispatcher, InfraredViewVariableItemList);
variable_item_list_free(infrared->variable_item_list); variable_item_list_free(infrared->variable_item_list);
view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx);
dialog_ex_free(infrared->dialog_ex);
view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu); view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu);
button_menu_free(infrared->button_menu); button_menu_free(infrared->button_menu);
@@ -255,7 +238,7 @@ static void infrared_free(InfraredApp* infrared) {
scene_manager_free(infrared->scene_manager); scene_manager_free(infrared->scene_manager);
infrared_brute_force_free(infrared->brute_force); infrared_brute_force_free(infrared->brute_force);
infrared_signal_free(infrared->current_signal); infrared_signal_free(infrared->received_signal);
infrared_remote_free(infrared->remote); infrared_remote_free(infrared->remote);
infrared_worker_free(infrared->worker); infrared_worker_free(infrared->worker);
@@ -269,67 +252,75 @@ static void infrared_free(InfraredApp* infrared) {
infrared->gui = NULL; infrared->gui = NULL;
furi_string_free(infrared->file_path); furi_string_free(infrared->file_path);
furi_string_free(infrared->button_name);
// Disable 5v power if was enabled for external module
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
free(infrared); free(infrared);
} }
bool infrared_add_remote_with_button( bool infrared_add_remote_with_button(
const InfraredApp* infrared, Infrared* infrared,
const char* button_name, const char* button_name,
const InfraredSignal* signal) { InfraredSignal* signal) {
InfraredRemote* remote = infrared->remote; InfraredRemote* remote = infrared->remote;
FuriString* new_name = furi_string_alloc_set(INFRARED_DEFAULT_REMOTE_NAME); FuriString *new_name, *new_path;
FuriString* new_path = furi_string_alloc_set(INFRARED_APP_FOLDER); new_name = furi_string_alloc_set(INFRARED_DEFAULT_REMOTE_NAME);
new_path = furi_string_alloc_set(INFRARED_APP_FOLDER);
infrared_find_vacant_remote_name(new_name, furi_string_get_cstr(new_path)); infrared_find_vacant_remote_name(new_name, furi_string_get_cstr(new_path));
furi_string_cat_printf( furi_string_cat_printf(
new_path, "/%s%s", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION); new_path, "/%s%s", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION);
bool success = false; infrared_remote_reset(remote);
infrared_remote_set_name(remote, furi_string_get_cstr(new_name));
infrared_remote_set_path(remote, furi_string_get_cstr(new_path));
do { furi_string_free(new_name);
if(!infrared_remote_create(remote, furi_string_get_cstr(new_path))) break; furi_string_free(new_path);
if(!infrared_remote_append_signal(remote, signal, button_name)) break; return infrared_remote_add_button(remote, button_name, signal);
success = true; }
} while(false);
bool infrared_rename_current_remote(Infrared* infrared, const char* name) {
InfraredRemote* remote = infrared->remote;
const char* remote_path = infrared_remote_get_path(remote);
if(!strcmp(infrared_remote_get_name(remote), name)) {
return true;
}
FuriString* new_name;
new_name = furi_string_alloc_set(name);
infrared_find_vacant_remote_name(new_name, remote_path);
FuriString* new_path;
new_path = furi_string_alloc_set(infrared_remote_get_path(remote));
if(furi_string_end_with(new_path, INFRARED_APP_EXTENSION)) {
size_t filename_start = furi_string_search_rchar(new_path, '/');
furi_string_left(new_path, filename_start);
}
furi_string_cat_printf(
new_path, "/%s%s", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION);
Storage* storage = furi_record_open(RECORD_STORAGE);
FS_Error status = storage_common_rename(
storage, infrared_remote_get_path(remote), furi_string_get_cstr(new_path));
infrared_remote_set_name(remote, furi_string_get_cstr(new_name));
infrared_remote_set_path(remote, furi_string_get_cstr(new_path));
furi_string_free(new_name); furi_string_free(new_name);
furi_string_free(new_path); furi_string_free(new_path);
return success; furi_record_close(RECORD_STORAGE);
return (status == FSE_OK || status == FSE_EXIST);
} }
bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name) { void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
InfraredRemote* remote = infrared->remote;
const char* old_path = infrared_remote_get_path(remote);
if(!strcmp(infrared_remote_get_name(remote), new_name)) {
return true;
}
FuriString* new_name_fstr = furi_string_alloc_set(new_name);
FuriString* new_path_fstr = furi_string_alloc_set(old_path);
infrared_find_vacant_remote_name(new_name_fstr, old_path);
if(furi_string_end_with(new_path_fstr, INFRARED_APP_EXTENSION)) {
path_extract_dirname(old_path, new_path_fstr);
}
path_append(new_path_fstr, furi_string_get_cstr(new_name_fstr));
furi_string_cat(new_path_fstr, INFRARED_APP_EXTENSION);
const bool success = infrared_remote_rename(remote, furi_string_get_cstr(new_path_fstr));
furi_string_free(new_name_fstr);
furi_string_free(new_path_fstr);
return success;
}
void infrared_tx_start(InfraredApp* infrared) {
if(infrared->app_state.is_transmitting) { if(infrared->app_state.is_transmitting) {
return; return;
} }
@@ -340,12 +331,12 @@ void infrared_tx_start(InfraredApp* infrared) {
return; return;
} }
if(infrared_signal_is_raw(infrared->current_signal)) { if(infrared_signal_is_raw(signal)) {
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(infrared->current_signal); InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
infrared_worker_set_raw_signal( infrared_worker_set_raw_signal(
infrared->worker, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); infrared->worker, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
} else { } else {
const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal); InfraredMessage* message = infrared_signal_get_message(signal);
infrared_worker_set_decoded_signal(infrared->worker, message); infrared_worker_set_decoded_signal(infrared->worker, message);
} }
@@ -359,20 +350,20 @@ void infrared_tx_start(InfraredApp* infrared) {
infrared->app_state.is_transmitting = true; infrared->app_state.is_transmitting = true;
} }
void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) { void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote)); furi_assert(button_index < infrared_remote_get_button_count(infrared->remote));
if(infrared_remote_load_signal(infrared->remote, infrared->current_signal, button_index)) { InfraredRemoteButton* button = infrared_remote_get_button(infrared->remote, button_index);
infrared_tx_start(infrared); InfraredSignal* signal = infrared_remote_button_get_signal(button);
} else {
infrared_show_error_message( infrared_tx_start_signal(infrared, signal);
infrared,
"Failed to load\n\"%s\"",
infrared_remote_get_signal_name(infrared->remote, button_index));
}
} }
void infrared_tx_stop(InfraredApp* infrared) { void infrared_tx_start_received(Infrared* infrared) {
infrared_tx_start_signal(infrared, infrared->received_signal);
}
void infrared_tx_stop(Infrared* infrared) {
if(!infrared->app_state.is_transmitting) { if(!infrared->app_state.is_transmitting) {
return; return;
} }
@@ -386,65 +377,53 @@ void infrared_tx_stop(InfraredApp* infrared) {
infrared->app_state.last_transmit_time = furi_get_tick(); infrared->app_state.last_transmit_time = furi_get_tick();
} }
void infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...) { void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, text);
vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, fmt, args); vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, text, args);
va_end(args); va_end(args);
} }
void infrared_text_store_clear(InfraredApp* infrared, uint32_t bank) { void infrared_text_store_clear(Infrared* infrared, uint32_t bank) {
memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE + 1); memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE + 1);
} }
void infrared_play_notification_message( void infrared_play_notification_message(Infrared* infrared, uint32_t message) {
const InfraredApp* infrared, furi_assert(message < sizeof(infrared_notification_sequences) / sizeof(NotificationSequence*));
InfraredNotificationMessage message) {
furi_assert(message < InfraredNotificationMessageCount);
notification_message(infrared->notifications, infrared_notification_sequences[message]); notification_message(infrared->notifications, infrared_notification_sequences[message]);
} }
void infrared_show_loading_popup(const InfraredApp* infrared, bool show) { void infrared_show_loading_popup(Infrared* infrared, bool show) {
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
ViewStack* view_stack = infrared->view_stack; ViewStack* view_stack = infrared->view_stack;
Loading* loading = infrared->loading; Loading* loading = infrared->loading;
if(show) { if(show) {
// Raise timer priority so that animations can play // Raise timer priority so that animations can play
furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
view_stack_add_view(view_stack, loading_get_view(loading)); view_stack_add_view(view_stack, loading_get_view(loading));
} else { } else {
view_stack_remove_view(view_stack, loading_get_view(loading)); view_stack_remove_view(view_stack, loading_get_view(loading));
// Restore default timer priority // Restore default timer priority
furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
} }
} }
void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
FuriString* message = furi_string_alloc_vprintf(fmt, args);
dialog_message_show_storage_error(infrared->dialogs, furi_string_get_cstr(message));
furi_string_free(message);
va_end(args);
}
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
furi_assert(context); furi_assert(context);
InfraredApp* infrared = context; Infrared* infrared = context;
if(infrared_worker_signal_is_decoded(received_signal)) { if(infrared_worker_signal_is_decoded(received_signal)) {
infrared_signal_set_message( infrared_signal_set_message(
infrared->current_signal, infrared_worker_get_decoded_signal(received_signal)); infrared->received_signal, infrared_worker_get_decoded_signal(received_signal));
} else { } else {
const uint32_t* timings; const uint32_t* timings;
size_t timings_size; size_t timings_size;
infrared_worker_get_raw_signal(received_signal, &timings, &timings_size); infrared_worker_get_raw_signal(received_signal, &timings, &timings_size);
infrared_signal_set_raw_signal( infrared_signal_set_raw_signal(
infrared->current_signal, infrared->received_signal,
timings, timings,
timings_size, timings_size,
INFRARED_COMMON_CARRIER_FREQUENCY, INFRARED_COMMON_CARRIER_FREQUENCY,
@@ -457,20 +436,20 @@ void infrared_signal_received_callback(void* context, InfraredWorkerSignal* rece
void infrared_text_input_callback(void* context) { void infrared_text_input_callback(void* context) {
furi_assert(context); furi_assert(context);
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone); infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone);
} }
void infrared_popup_closed_callback(void* context) { void infrared_popup_closed_callback(void* context) {
furi_assert(context); furi_assert(context);
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypePopupClosed); infrared->view_dispatcher, InfraredCustomEventTypePopupClosed);
} }
int32_t infrared_app(void* p) { int32_t infrared_app(void* p) {
InfraredApp* infrared = infrared_alloc(); Infrared* infrared = infrared_alloc();
infrared_make_app_folder(infrared); infrared_make_app_folder(infrared);
@@ -486,15 +465,13 @@ int32_t infrared_app(void* p) {
rpc_system_app_send_started(infrared->rpc_ctx); rpc_system_app_send_started(infrared->rpc_ctx);
is_rpc_mode = true; is_rpc_mode = true;
} else { } else {
const char* file_path = (const char*)p; furi_string_set(infrared->file_path, (const char*)p);
is_remote_loaded = infrared_remote_load(infrared->remote, file_path); is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path);
if(!is_remote_loaded) { if(!is_remote_loaded) {
infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path); dialog_message_show_storage_error(
infrared->dialogs, "Failed to load\nselected remote");
return -1; return -1;
} }
furi_string_set(infrared->file_path, file_path);
} }
} }

View File

@@ -0,0 +1,3 @@
#pragma once
typedef struct Infrared Infrared;

View File

@@ -1,15 +0,0 @@
/**
* @file infrared_app.h
* @brief Infrared application - start here.
*
* @see infrared_app_i.h for the main application data structure and functions.
* @see infrared_signal.h for the infrared signal library - loading, storing and transmitting signals.
* @see infrared_remote.hl for the infrared remote library - loading, storing and manipulating remotes.
* @see infrared_brute_force.h for the infrared brute force - loading and transmitting multiple signals.
*/
#pragma once
/**
* @brief InfraredApp opaque type declaration.
*/
typedef struct InfraredApp InfraredApp;

View File

@@ -1,292 +0,0 @@
/**
* @file infrared_app_i.h
* @brief Main Infrared application types and functions.
*/
#pragma once
#include <gui/gui.h>
#include <gui/view.h>
#include <assets_icons.h>
#include <gui/view_stack.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/popup.h>
#include <gui/modules/loading.h>
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/text_input.h>
#include <gui/modules/button_menu.h>
#include <gui/modules/button_panel.h>
#include <gui/modules/variable_item_list.h>
#include <storage/storage.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <infrared_worker.h>
#include "infrared_app.h"
#include "infrared_remote.h"
#include "infrared_brute_force.h"
#include "infrared_custom_event.h"
#include "scenes/infrared_scene.h"
#include "views/infrared_progress_view.h"
#include "views/infrared_debug_view.h"
#include "views/infrared_move_view.h"
#include "rpc/rpc_app.h"
#define INFRARED_FILE_NAME_SIZE 100
#define INFRARED_TEXT_STORE_NUM 2
#define INFRARED_TEXT_STORE_SIZE 128
#define INFRARED_MAX_BUTTON_NAME_LENGTH 22
#define INFRARED_MAX_REMOTE_NAME_LENGTH 22
#define INFRARED_APP_FOLDER ANY_PATH("infrared")
#define INFRARED_APP_EXTENSION ".ir"
#define INFRARED_DEFAULT_REMOTE_NAME "Remote"
#define INFRARED_LOG_TAG "InfraredApp"
/**
* @brief Enumeration of invalid remote button indices.
*/
typedef enum {
InfraredButtonIndexNone = -1, /**< No button is currently selected. */
} InfraredButtonIndex;
/**
* @brief Enumeration of editing targets.
*/
typedef enum {
InfraredEditTargetNone, /**< No editing target is selected. */
InfraredEditTargetRemote, /**< Whole remote is selected as editing target. */
InfraredEditTargetButton, /**< Single button is selected as editing target. */
} InfraredEditTarget;
/**
* @brief Enumeration of editing modes.
*/
typedef enum {
InfraredEditModeNone, /**< No editing mode is selected. */
InfraredEditModeRename, /**< Rename mode is selected. */
InfraredEditModeDelete, /**< Delete mode is selected. */
} InfraredEditMode;
/**
* @brief Infrared application state type.
*/
typedef struct {
bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */
bool is_debug_enabled; /**< Whether to enable or disable debugging features. */
bool is_transmitting; /**< Whether a signal is currently being transmitted. */
InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */
InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */
int32_t current_button_index; /**< Selected button index (move destination). */
int32_t prev_button_index; /**< Previous button index (move source). */
uint32_t last_transmit_time; /**< Lat time a signal was transmitted. */
} InfraredAppState;
/**
* @brief Infrared application type.
*/
struct InfraredApp {
SceneManager* scene_manager; /**< Pointer to a SceneManager instance. */
ViewDispatcher* view_dispatcher; /**< Pointer to a ViewDispatcher instance. */
Gui* gui; /**< Pointer to a Gui instance. */
Storage* storage; /**< Pointer to a Storage instance. */
DialogsApp* dialogs; /**< Pointer to a DialogsApp instance. */
NotificationApp* notifications; /**< Pointer to a NotificationApp instance. */
InfraredWorker* worker; /**< Used to send or receive signals. */
InfraredRemote* remote; /**< Holds the currently loaded remote. */
InfraredSignal* current_signal; /**< Holds the currently loaded signal. */
InfraredBruteForce* brute_force; /**< Used for the Universal Remote feature. */
Submenu* submenu; /**< Standard view for displaying application menus. */
TextInput* text_input; /**< Standard view for receiving user text input. */
DialogEx* dialog_ex; /**< Standard view for displaying dialogs. */
ButtonMenu* button_menu; /**< Custom view for interacting with IR remotes. */
Popup* popup; /**< Standard view for displaying messages. */
VariableItemList* variable_item_list;
ViewStack* view_stack; /**< Standard view for displaying stacked interfaces. */
InfraredDebugView* debug_view; /**< Custom view for displaying debug information. */
InfraredMoveView* move_view; /**< Custom view for rearranging buttons in a remote. */
ButtonPanel* button_panel; /**< Standard view for displaying control panels. */
Loading* loading; /**< Standard view for informing about long operations. */
InfraredProgressView* progress; /**< Custom view for showing brute force progress. */
FuriString* file_path; /**< Full path to the currently loaded file. */
FuriString* button_name; /** Name of the button requested in RPC mode. */
/** Arbitrary text storage for various inputs. */
char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1];
InfraredAppState app_state; /**< Application state. */
void* rpc_ctx; /**< Pointer to the RPC context object. */
};
/**
* @brief Enumeration of all used view types.
*/
typedef enum {
InfraredViewSubmenu,
InfraredViewTextInput,
InfraredViewDialogEx,
InfraredViewButtonMenu,
InfraredViewPopup,
InfraredViewStack,
InfraredViewDebugView,
InfraredViewMove,
InfraredViewVariableItemList,
} InfraredView;
/**
* @brief Enumeration of all notification message types.
*/
typedef enum {
InfraredNotificationMessageSuccess, /**< Play a short happy tune. */
InfraredNotificationMessageGreenOn, /**< Turn green LED on. */
InfraredNotificationMessageGreenOff, /**< Turn green LED off. */
InfraredNotificationMessageYellowOn, /**< Turn yellow LED on. */
InfraredNotificationMessageYellowOff, /**< Turn yellow LED off. */
InfraredNotificationMessageBlinkStartRead, /**< Blink the LED to indicate receiver mode. */
InfraredNotificationMessageBlinkStartSend, /**< Blink the LED to indicate transmitter mode. */
InfraredNotificationMessageBlinkStop, /**< Stop blinking the LED. */
InfraredNotificationMessageCount, /**< Special value equal to the message type count. */
} InfraredNotificationMessage;
/**
* @brief Add a new remote with a single signal.
*
* The filename will be automatically generated depending on
* the names and number of other files in the infrared data directory.
*
* @param[in] infrared pointer to the application instance.
* @param[in] name pointer to a zero-terminated string containing the signal name.
* @param[in] signal pointer to the signal to be added.
* @return true if the remote was successfully created, false otherwise.
*/
bool infrared_add_remote_with_button(
const InfraredApp* infrared,
const char* name,
const InfraredSignal* signal);
/**
* @brief Rename the currently loaded remote.
*
* @param[in] infrared pointer to the application instance.
* @param[in] new_name pointer to a zero-terminated string containing the new remote name.
* @return true if the remote was successfully renamed, false otherwise.
*/
bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name);
/**
* @brief Begin transmission of the currently loaded signal.
*
* The signal will be repeated indefinitely until stopped.
*
* @param[in,out] infrared pointer to the application instance.
*/
void infrared_tx_start(InfraredApp* infrared);
/**
* @brief Load a signal under the given index and begin transmission.
*
* The signal will be repeated indefinitely until stopped.
*
* @param[in,out] infrared pointer to the application instance.
* @param[in] button_index index of the signal to be loaded.
* @returns true if the signal could be loaded, false otherwise.
*/
void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index);
/**
* @brief Stop transmission of the currently loaded signal.
*
* @param[in,out] infrared pointer to the application instance.
*/
void infrared_tx_stop(InfraredApp* infrared);
/**
* @brief Set the internal text store with formatted text.
*
* @param[in,out] infrared pointer to the application instance.
* @param[in] bank index of text store bank (0 or 1).
* @param[in] fmt pointer to a zero-terminated string containing the format text.
* @param[in] ... additional arguments.
*/
void infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...)
_ATTRIBUTE((__format__(__printf__, 3, 4)));
/**
* @brief Clear the internal text store.
*
* @param[in,out] infrared pointer to the application instance.
* @param[in] bank index of text store bank (0 or 1).
*/
void infrared_text_store_clear(InfraredApp* infrared, uint32_t bank);
/**
* @brief Play a sound and/or blink the LED.
*
* @param[in] infrared pointer to the application instance.
* @param[in] message type of the message to play.
*/
void infrared_play_notification_message(
const InfraredApp* infrared,
InfraredNotificationMessage message);
/**
* @brief Show a loading pop-up screen.
*
* In order for this to work, a Stack view must be currently active and
* the main view must be added to it.
*
* @param[in] infrared pointer to the application instance.
* @param[in] show whether to show or hide the pop-up.
*/
void infrared_show_loading_popup(const InfraredApp* infrared, bool show);
/**
* @brief Show a formatted error messsage.
*
* @param[in] infrared pointer to the application instance.
* @param[in] fmt pointer to a zero-terminated string containing the format text.
* @param[in] ... additional arguments.
*/
void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...)
_ATTRIBUTE((__format__(__printf__, 2, 3)));
/**
* @brief Common received signal callback.
*
* Called when the worker has received a complete infrared signal.
*
* @param[in,out] context pointer to the user-specified context object.
* @param[in] received_signal pointer to the received signal.
*/
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal);
/**
* @brief Common text input callback.
*
* Called when the input has been accepted by the user.
*
* @param[in,out] context pointer to the user-specified context object.
*/
void infrared_text_input_callback(void* context);
/**
* @brief Common popup close callback.
*
* Called when the popup has been closed either by the user or after a timeout.
*
* @param[in,out] context pointer to the user-specified context object.
*/
void infrared_popup_closed_callback(void* context);

View File

@@ -111,7 +111,7 @@ bool infrared_brute_force_start(
return success; return success;
} }
bool infrared_brute_force_is_started(const InfraredBruteForce* brute_force) { bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) {
return brute_force->is_started; return brute_force->is_started;
} }
@@ -128,10 +128,8 @@ void infrared_brute_force_stop(InfraredBruteForce* brute_force) {
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) {
furi_assert(brute_force->is_started); furi_assert(brute_force->is_started);
const bool success = infrared_signal_search_by_name_and_read( const bool success = infrared_signal_search_and_read(
brute_force->current_signal, brute_force->current_signal, brute_force->ff, brute_force->current_record_name);
brute_force->ff,
furi_string_get_cstr(brute_force->current_record_name));
if(success) { if(success) {
infrared_signal_transmit(brute_force->current_signal); infrared_signal_transmit(brute_force->current_signal);
} }

View File

@@ -1,110 +1,23 @@
/**
* @file infrared_brute_force.h
* @brief Infrared signal brute-forcing library.
*
* The BruteForce library is used to send large quantities of signals,
* sorted by a category. It is used to implement the Universal Remote
* feature.
*/
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
/**
* @brief InfraredBruteForce opaque type declaration.
*/
typedef struct InfraredBruteForce InfraredBruteForce; typedef struct InfraredBruteForce InfraredBruteForce;
/**
* @brief Create a new InfraredBruteForce instance.
*
* @returns pointer to the created instance.
*/
InfraredBruteForce* infrared_brute_force_alloc(); InfraredBruteForce* infrared_brute_force_alloc();
/**
* @brief Delete an InfraredBruteForce instance.
*
* @param[in,out] brute_force pointer to the instance to be deleted.
*/
void infrared_brute_force_free(InfraredBruteForce* brute_force); void infrared_brute_force_free(InfraredBruteForce* brute_force);
/**
* @brief Set an InfraredBruteForce instance to use a signal database contained in a file.
*
* @param[in,out] brute_force pointer to the instance to be configured.
* @param[in] db_filename pointer to a zero-terminated string containing a full path to the database file.
*/
void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename); void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename);
/**
* @brief Build a signal dictionary from a previously set database file.
*
* This function must be called each time after setting the database via
* a infrared_brute_force_set_db_filename() call.
*
* @param[in,out] brute_force pointer to the instance to be updated.
* @returns true on success, false otherwise.
*/
bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force);
/**
* @brief Start transmitting signals from a category stored in an InfraredBruteForce's instance dictionary.
*
* @param[in,out] brute_force pointer to the instance to be started.
* @param[in] index index of the signal category in the dictionary.
* @returns true on success, false otherwise.
*/
bool infrared_brute_force_start( bool infrared_brute_force_start(
InfraredBruteForce* brute_force, InfraredBruteForce* brute_force,
uint32_t index, uint32_t index,
uint32_t* record_count); uint32_t* record_count);
bool infrared_brute_force_is_started(InfraredBruteForce* brute_force);
/**
* @brief Determine whether the transmission was started.
*
* @param[in] brute_force pointer to the instance to be tested.
* @returns true if transmission was started, false otherwise.
*/
bool infrared_brute_force_is_started(const InfraredBruteForce* brute_force);
/**
* @brief Stop transmitting the signals.
*
* @param[in] brute_force pointer to the instance to be stopped.
*/
void infrared_brute_force_stop(InfraredBruteForce* brute_force); void infrared_brute_force_stop(InfraredBruteForce* brute_force);
/**
* @brief Send the next signal from the chosen category.
*
* This function is called repeatedly until no more signals are left
* in the chosen signal category.
*
* @warning Transmission must be started first by calling infrared_brute_force_start()
* before calling this function.
*
* @param[in,out] brute_force pointer to the instance to be used.
* @returns true if the next signal existed and could be transmitted, false otherwise.
*/
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force); bool infrared_brute_force_send_next(InfraredBruteForce* brute_force);
void infrared_brute_force_reset(InfraredBruteForce* brute_force);
/**
* @brief Add a signal category to an InfraredBruteForce instance's dictionary.
*
* @param[in,out] brute_force pointer to the instance to be updated.
* @param[in] index index of the category to be added.
* @param[in] name name of the category to be added.
*/
void infrared_brute_force_add_record( void infrared_brute_force_add_record(
InfraredBruteForce* brute_force, InfraredBruteForce* brute_force,
uint32_t index, uint32_t index,
const char* name); const char* name);
/**
* @brief Reset an InfraredBruteForce instance.
*
* @param[in,out] brute_force pointer to the instance to be reset.
*/
void infrared_brute_force_reset(InfraredBruteForce* brute_force);

View File

@@ -202,7 +202,7 @@ static bool
} }
static bool infrared_cli_decode_raw_signal( static bool infrared_cli_decode_raw_signal(
const InfraredRawSignal* raw_signal, InfraredRawSignal* raw_signal,
InfraredDecoderHandler* decoder, InfraredDecoderHandler* decoder,
FlipperFormat* output_file, FlipperFormat* output_file,
const char* signal_name) { const char* signal_name) {
@@ -274,7 +274,7 @@ static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* o
continue; continue;
} }
} }
const InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal); InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
printf( printf(
"Raw signal: %s, %zu samples\r\n", "Raw signal: %s, %zu samples\r\n",
furi_string_get_cstr(tmp), furi_string_get_cstr(tmp),

View File

@@ -15,10 +15,9 @@ enum InfraredCustomEventType {
InfraredCustomEventTypeButtonSelected, InfraredCustomEventTypeButtonSelected,
InfraredCustomEventTypeBackPressed, InfraredCustomEventTypeBackPressed,
InfraredCustomEventTypeRpcLoadFile, InfraredCustomEventTypeRpcLoad,
InfraredCustomEventTypeRpcExit, InfraredCustomEventTypeRpcExit,
InfraredCustomEventTypeRpcButtonPressName, InfraredCustomEventTypeRpcButtonPress,
InfraredCustomEventTypeRpcButtonPressIndex,
InfraredCustomEventTypeRpcButtonRelease, InfraredCustomEventTypeRpcButtonRelease,
InfraredCustomEventTypeRpcSessionClose, InfraredCustomEventTypeRpcSessionClose,
}; };

View File

@@ -0,0 +1,149 @@
#pragma once
#include <gui/gui.h>
#include <gui/view.h>
#include <assets_icons.h>
#include <gui/view_stack.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/popup.h>
#include <gui/modules/loading.h>
#include <gui/modules/submenu.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/text_input.h>
#include <gui/modules/button_menu.h>
#include <gui/modules/button_panel.h>
#include <storage/storage.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <infrared_worker.h>
#include "infrared.h"
#include "infrared_remote.h"
#include "infrared_brute_force.h"
#include "infrared_custom_event.h"
#include "scenes/infrared_scene.h"
#include "views/infrared_progress_view.h"
#include "views/infrared_debug_view.h"
#include "views/infrared_move_view.h"
#include "rpc/rpc_app.h"
#define INFRARED_FILE_NAME_SIZE 100
#define INFRARED_TEXT_STORE_NUM 2
#define INFRARED_TEXT_STORE_SIZE 128
#define INFRARED_MAX_BUTTON_NAME_LENGTH 22
#define INFRARED_MAX_REMOTE_NAME_LENGTH 22
#define INFRARED_APP_FOLDER ANY_PATH("infrared")
#define INFRARED_APP_EXTENSION ".ir"
#define INFRARED_DEFAULT_REMOTE_NAME "Remote"
#define INFRARED_LOG_TAG "InfraredApp"
typedef enum {
InfraredButtonIndexNone = -1,
} InfraredButtonIndex;
typedef enum {
InfraredEditTargetNone,
InfraredEditTargetRemote,
InfraredEditTargetButton,
} InfraredEditTarget;
typedef enum {
InfraredEditModeNone,
InfraredEditModeRename,
InfraredEditModeDelete,
} InfraredEditMode;
typedef struct {
bool is_learning_new_remote;
bool is_debug_enabled;
bool is_transmitting;
InfraredEditTarget edit_target : 8;
InfraredEditMode edit_mode : 8;
int32_t current_button_index;
int32_t current_button_index_move_orig;
uint32_t last_transmit_time;
} InfraredAppState;
struct Infrared {
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
Gui* gui;
Storage* storage;
DialogsApp* dialogs;
NotificationApp* notifications;
InfraredWorker* worker;
InfraredRemote* remote;
InfraredSignal* received_signal;
InfraredBruteForce* brute_force;
Submenu* submenu;
TextInput* text_input;
VariableItemList* variable_item_list;
DialogEx* dialog_ex;
ButtonMenu* button_menu;
Popup* popup;
ViewStack* view_stack;
InfraredDebugView* debug_view;
InfraredMoveView* move_view;
ButtonPanel* button_panel;
Loading* loading;
InfraredProgressView* progress;
FuriString* file_path;
char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1];
InfraredAppState app_state;
void* rpc_ctx;
};
typedef enum {
InfraredViewSubmenu,
InfraredViewTextInput,
InfraredViewVariableItemList,
InfraredViewDialogEx,
InfraredViewButtonMenu,
InfraredViewPopup,
InfraredViewStack,
InfraredViewDebugView,
InfraredViewMove,
} InfraredView;
typedef enum {
InfraredNotificationMessageSuccess,
InfraredNotificationMessageGreenOn,
InfraredNotificationMessageGreenOff,
InfraredNotificationMessageYellowOn,
InfraredNotificationMessageYellowOff,
InfraredNotificationMessageBlinkStartRead,
InfraredNotificationMessageBlinkStartSend,
InfraredNotificationMessageBlinkStop,
} InfraredNotificationMessage;
bool infrared_add_remote_with_button(Infrared* infrared, const char* name, InfraredSignal* signal);
bool infrared_rename_current_remote(Infrared* infrared, const char* name);
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal);
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index);
void infrared_tx_start_received(Infrared* infrared);
void infrared_tx_stop(Infrared* infrared);
void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...);
void infrared_text_store_clear(Infrared* infrared, uint32_t bank);
void infrared_play_notification_message(Infrared* infrared, uint32_t message);
void infrared_show_loading_popup(Infrared* infrared, bool show);
void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal);
void infrared_text_input_callback(void* context);
void infrared_popup_closed_callback(void* context);

View File

@@ -1,427 +1,197 @@
#include "infrared_remote.h" #include "infrared_remote.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <m-array.h> #include <m-array.h>
#include <toolbox/m_cstr_dup.h>
#include <toolbox/path.h> #include <toolbox/path.h>
#include <storage/storage.h> #include <storage/storage.h>
#include <core/common_defines.h>
#define TAG "InfraredRemote" #define TAG "InfraredRemote"
#define INFRARED_FILE_HEADER "IR signals file" ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST);
#define INFRARED_FILE_VERSION (1)
ARRAY_DEF(StringArray, const char*, M_CSTR_DUP_OPLIST); //-V575
struct InfraredRemote { struct InfraredRemote {
StringArray_t signal_names; InfraredButtonArray_t buttons;
FuriString* name; FuriString* name;
FuriString* path; FuriString* path;
}; };
typedef struct { static void infrared_remote_clear_buttons(InfraredRemote* remote) {
InfraredRemote* remote; InfraredButtonArray_it_t it;
FlipperFormat* ff_in; for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
FlipperFormat* ff_out; InfraredButtonArray_next(it)) {
FuriString* signal_name; infrared_remote_button_free(*InfraredButtonArray_cref(it));
InfraredSignal* signal; }
size_t signal_index; InfraredButtonArray_reset(remote->buttons);
} InfraredBatch; }
typedef struct {
size_t signal_index;
const char* signal_name;
const InfraredSignal* signal;
} InfraredBatchTarget;
typedef bool (
*InfraredBatchCallback)(const InfraredBatch* batch, const InfraredBatchTarget* target);
InfraredRemote* infrared_remote_alloc() { InfraredRemote* infrared_remote_alloc() {
InfraredRemote* remote = malloc(sizeof(InfraredRemote)); InfraredRemote* remote = malloc(sizeof(InfraredRemote));
StringArray_init(remote->signal_names); InfraredButtonArray_init(remote->buttons);
remote->name = furi_string_alloc(); remote->name = furi_string_alloc();
remote->path = furi_string_alloc(); remote->path = furi_string_alloc();
return remote; return remote;
} }
void infrared_remote_free(InfraredRemote* remote) { void infrared_remote_free(InfraredRemote* remote) {
StringArray_clear(remote->signal_names); infrared_remote_clear_buttons(remote);
InfraredButtonArray_clear(remote->buttons);
furi_string_free(remote->path); furi_string_free(remote->path);
furi_string_free(remote->name); furi_string_free(remote->name);
free(remote); free(remote);
} }
void infrared_remote_reset(InfraredRemote* remote) { void infrared_remote_reset(InfraredRemote* remote) {
StringArray_reset(remote->signal_names); infrared_remote_clear_buttons(remote);
furi_string_reset(remote->name); furi_string_reset(remote->name);
furi_string_reset(remote->path); furi_string_reset(remote->path);
} }
const char* infrared_remote_get_name(const InfraredRemote* remote) { void infrared_remote_set_name(InfraredRemote* remote, const char* name) {
furi_string_set(remote->name, name);
}
const char* infrared_remote_get_name(InfraredRemote* remote) {
return furi_string_get_cstr(remote->name); return furi_string_get_cstr(remote->name);
} }
static void infrared_remote_set_path(InfraredRemote* remote, const char* path) { void infrared_remote_set_path(InfraredRemote* remote, const char* path) {
furi_string_set(remote->path, path); furi_string_set(remote->path, path);
path_extract_filename(remote->path, remote->name, true);
} }
const char* infrared_remote_get_path(const InfraredRemote* remote) { const char* infrared_remote_get_path(InfraredRemote* remote) {
return furi_string_get_cstr(remote->path); return furi_string_get_cstr(remote->path);
} }
size_t infrared_remote_get_signal_count(const InfraredRemote* remote) { size_t infrared_remote_get_button_count(InfraredRemote* remote) {
return StringArray_size(remote->signal_names); return InfraredButtonArray_size(remote->buttons);
} }
const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index) { InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) {
furi_assert(index < infrared_remote_get_signal_count(remote)); furi_assert(index < InfraredButtonArray_size(remote->buttons));
return *StringArray_cget(remote->signal_names, index); return *InfraredButtonArray_get(remote->buttons, index);
} }
bool infrared_remote_load_signal( bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) {
const InfraredRemote* remote, for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
InfraredSignal* signal, InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
size_t index) { if(!strcmp(infrared_remote_button_get_name(button), name)) {
furi_assert(index < infrared_remote_get_signal_count(remote));
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
bool success = false;
do {
const char* path = furi_string_get_cstr(remote->path);
if(!flipper_format_buffered_file_open_existing(ff, path)) break;
if(!infrared_signal_search_by_index_and_read(signal, ff, index)) {
const char* signal_name = infrared_remote_get_signal_name(remote, index);
FURI_LOG_E(TAG, "Failed to load signal '%s' from file '%s'", signal_name, path);
break;
}
success = true;
} while(false);
flipper_format_free(ff);
furi_record_close(RECORD_STORAGE);
return success;
}
bool infrared_remote_get_signal_index(
const InfraredRemote* remote,
const char* name,
size_t* index) {
uint32_t i = 0;
StringArray_it_t it;
for(StringArray_it(it, remote->signal_names); !StringArray_end_p(it);
StringArray_next(it), ++i) {
if(strcmp(*StringArray_cref(it), name) == 0) {
*index = i; *index = i;
return true; return true;
} }
} }
return false; return false;
} }
bool infrared_remote_append_signal( bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) {
InfraredRemote* remote, InfraredRemoteButton* button = infrared_remote_button_alloc();
const InfraredSignal* signal, infrared_remote_button_set_name(button, name);
const char* name) { infrared_remote_button_set_signal(button, signal);
InfraredButtonArray_push_back(remote->buttons, button);
return infrared_remote_store(remote);
}
bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index) {
furi_assert(index < InfraredButtonArray_size(remote->buttons));
InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, index);
infrared_remote_button_set_name(button, new_name);
return infrared_remote_store(remote);
}
bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) {
furi_assert(index < InfraredButtonArray_size(remote->buttons));
InfraredRemoteButton* button;
InfraredButtonArray_pop_at(&button, remote->buttons, index);
infrared_remote_button_free(button);
return infrared_remote_store(remote);
}
void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest) {
furi_assert(index_orig < InfraredButtonArray_size(remote->buttons));
furi_assert(index_dest < InfraredButtonArray_size(remote->buttons));
InfraredRemoteButton* button;
InfraredButtonArray_pop_at(&button, remote->buttons, index_orig);
InfraredButtonArray_push_at(remote->buttons, index_dest, button);
}
bool infrared_remote_store(InfraredRemote* remote) {
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_file_alloc(storage); FlipperFormat* ff = flipper_format_file_alloc(storage);
bool success = false;
const char* path = furi_string_get_cstr(remote->path); const char* path = furi_string_get_cstr(remote->path);
do { FURI_LOG_I(TAG, "store file: \'%s\'", path);
if(!flipper_format_file_open_append(ff, path)) break;
if(!infrared_signal_save(signal, ff, name)) break;
StringArray_push_back(remote->signal_names, name); bool success = flipper_format_file_open_always(ff, path) &&
success = true; flipper_format_write_header_cstr(ff, "IR signals file", 1);
} while(false); if(success) {
InfraredButtonArray_it_t it;
flipper_format_free(ff); for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
furi_record_close(RECORD_STORAGE); InfraredButtonArray_next(it)) {
InfraredRemoteButton* button = *InfraredButtonArray_cref(it);
return success; success = infrared_signal_save(
} infrared_remote_button_get_signal(button),
ff,
static bool infrared_remote_batch_start( infrared_remote_button_get_name(button));
InfraredRemote* remote, if(!success) {
InfraredBatchCallback batch_callback,
const InfraredBatchTarget* target) {
FuriString* tmp = furi_string_alloc();
Storage* storage = furi_record_open(RECORD_STORAGE);
InfraredBatch batch_context = {
.remote = remote,
.ff_in = flipper_format_buffered_file_alloc(storage),
.ff_out = flipper_format_buffered_file_alloc(storage),
.signal_name = furi_string_alloc(),
.signal = infrared_signal_alloc(),
.signal_index = 0,
};
const char* path_in = furi_string_get_cstr(remote->path);
const char* path_out;
FS_Error status;
do {
furi_string_printf(tmp, "%s.temp%08x.swp", path_in, rand());
path_out = furi_string_get_cstr(tmp);
status = storage_common_stat(storage, path_out, NULL);
} while(status == FSE_OK || status == FSE_EXIST);
bool success = false;
do {
if(!flipper_format_buffered_file_open_existing(batch_context.ff_in, path_in)) break;
if(!flipper_format_buffered_file_open_always(batch_context.ff_out, path_out)) break;
if(!flipper_format_write_header_cstr(
batch_context.ff_out, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION))
break;
const size_t signal_count = infrared_remote_get_signal_count(remote);
for(; batch_context.signal_index < signal_count; ++batch_context.signal_index) {
if(!infrared_signal_read(
batch_context.signal, batch_context.ff_in, batch_context.signal_name))
break; break;
if(!batch_callback(&batch_context, target)) break; }
} }
if(batch_context.signal_index != signal_count) break;
if(!flipper_format_buffered_file_close(batch_context.ff_out)) break;
if(!flipper_format_buffered_file_close(batch_context.ff_in)) break;
const FS_Error status = storage_common_rename(storage, path_out, path_in);
success = (status == FSE_OK || status == FSE_EXIST);
} while(false);
infrared_signal_free(batch_context.signal);
furi_string_free(batch_context.signal_name);
flipper_format_free(batch_context.ff_out);
flipper_format_free(batch_context.ff_in);
furi_string_free(tmp);
furi_record_close(RECORD_STORAGE);
return success;
}
static bool infrared_remote_insert_signal_callback(
const InfraredBatch* batch,
const InfraredBatchTarget* target) {
// Insert a signal under the specified index
if(batch->signal_index == target->signal_index) {
if(!infrared_signal_save(target->signal, batch->ff_out, target->signal_name)) return false;
StringArray_push_at(
batch->remote->signal_names, target->signal_index, target->signal_name);
} }
// Write the rest normally
return infrared_signal_save(
batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name));
}
bool infrared_remote_insert_signal(
InfraredRemote* remote,
const InfraredSignal* signal,
const char* name,
size_t index) {
if(index >= infrared_remote_get_signal_count(remote)) {
return infrared_remote_append_signal(remote, signal, name);
}
const InfraredBatchTarget insert_target = {
.signal_index = index,
.signal_name = name,
.signal = signal,
};
return infrared_remote_batch_start(
remote, infrared_remote_insert_signal_callback, &insert_target);
}
static bool infrared_remote_rename_signal_callback(
const InfraredBatch* batch,
const InfraredBatchTarget* target) {
const char* signal_name;
if(batch->signal_index == target->signal_index) {
// Rename the signal at requested index
signal_name = target->signal_name;
StringArray_set_at(batch->remote->signal_names, batch->signal_index, signal_name);
} else {
// Use the original name otherwise
signal_name = furi_string_get_cstr(batch->signal_name);
}
return infrared_signal_save(batch->signal, batch->ff_out, signal_name);
}
bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name) {
furi_assert(index < infrared_remote_get_signal_count(remote));
const InfraredBatchTarget rename_target = {
.signal_index = index,
.signal_name = new_name,
.signal = NULL,
};
return infrared_remote_batch_start(
remote, infrared_remote_rename_signal_callback, &rename_target);
}
static bool infrared_remote_delete_signal_callback(
const InfraredBatch* batch,
const InfraredBatchTarget* target) {
if(batch->signal_index == target->signal_index) {
// Do not save the signal to be deleted, remove it from the signal name list instead
StringArray_remove_v(
batch->remote->signal_names, batch->signal_index, batch->signal_index + 1);
} else {
// Pass other signals through
return infrared_signal_save(
batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name));
}
return true;
}
bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index) {
furi_assert(index < infrared_remote_get_signal_count(remote));
const InfraredBatchTarget delete_target = {
.signal_index = index,
.signal_name = NULL,
.signal = NULL,
};
return infrared_remote_batch_start(
remote, infrared_remote_delete_signal_callback, &delete_target);
}
bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index) {
const size_t signal_count = infrared_remote_get_signal_count(remote);
furi_assert(index < signal_count);
furi_assert(new_index < signal_count);
if(index == new_index) return true;
InfraredSignal* signal = infrared_signal_alloc();
char* signal_name = strdup(infrared_remote_get_signal_name(remote, index));
bool success = false;
do {
if(!infrared_remote_load_signal(remote, signal, index)) break;
if(!infrared_remote_delete_signal(remote, index)) break;
if(!infrared_remote_insert_signal(remote, signal, signal_name, new_index)) break;
success = true;
} while(false);
free(signal_name);
infrared_signal_free(signal);
return success;
}
bool infrared_remote_create(InfraredRemote* remote, const char* path) {
FURI_LOG_I(TAG, "Creating new file: '%s'", path);
infrared_remote_reset(remote);
infrared_remote_set_path(remote, path);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_file_alloc(storage);
bool success = false;
do {
if(!flipper_format_file_open_always(ff, path)) break;
if(!flipper_format_write_header_cstr(ff, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION))
break;
success = true;
} while(false);
flipper_format_free(ff); flipper_format_free(ff);
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
return success; return success;
} }
bool infrared_remote_load(InfraredRemote* remote, const char* path) { bool infrared_remote_load(InfraredRemote* remote, FuriString* path) {
FURI_LOG_I(TAG, "Loading file: '%s'", path);
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
FuriString* tmp = furi_string_alloc(); FuriString* buf;
buf = furi_string_alloc();
FURI_LOG_I(TAG, "load file: \'%s\'", furi_string_get_cstr(path));
bool success = false; bool success = false;
do { do {
if(!flipper_format_buffered_file_open_existing(ff, path)) break; if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(path))) break;
uint32_t version; uint32_t version;
if(!flipper_format_read_header(ff, tmp, &version)) break; if(!flipper_format_read_header(ff, buf, &version)) break;
if(!furi_string_equal(buf, "IR signals file") || (version != 1)) break;
if(!furi_string_equal(tmp, INFRARED_FILE_HEADER) || (version != INFRARED_FILE_VERSION)) path_extract_filename(path, buf, true);
break; infrared_remote_clear_buttons(remote);
infrared_remote_set_name(remote, furi_string_get_cstr(buf));
infrared_remote_set_path(remote, furi_string_get_cstr(path));
infrared_remote_set_path(remote, path); for(bool can_read = true; can_read;) {
StringArray_reset(remote->signal_names); InfraredRemoteButton* button = infrared_remote_button_alloc();
can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf);
while(infrared_signal_read_name(ff, tmp)) { if(can_read) {
StringArray_push_back(remote->signal_names, furi_string_get_cstr(tmp)); infrared_remote_button_set_name(button, furi_string_get_cstr(buf));
InfraredButtonArray_push_back(remote->buttons, button);
} else {
infrared_remote_button_free(button);
}
} }
success = true; success = true;
} while(false); } while(false);
furi_string_free(tmp); furi_string_free(buf);
flipper_format_free(ff); flipper_format_free(ff);
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
return success;
}
bool infrared_remote_rename(InfraredRemote* remote, const char* new_path) {
const char* old_path = infrared_remote_get_path(remote);
Storage* storage = furi_record_open(RECORD_STORAGE);
const FS_Error status = storage_common_rename(storage, old_path, new_path);
furi_record_close(RECORD_STORAGE);
const bool success = (status == FSE_OK || status == FSE_EXIST);
if(success) {
infrared_remote_set_path(remote, new_path);
}
return success; return success;
} }
bool infrared_remote_remove(InfraredRemote* remote) { bool infrared_remote_remove(InfraredRemote* remote) {
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
const FS_Error status = storage_common_remove(storage, infrared_remote_get_path(remote));
FS_Error status = storage_common_remove(storage, furi_string_get_cstr(remote->path));
infrared_remote_reset(remote);
furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_STORAGE);
return (status == FSE_OK || status == FSE_NOT_EXIST);
const bool success = (status == FSE_OK || status == FSE_NOT_EXIST);
if(success) {
infrared_remote_reset(remote);
}
return success;
} }

View File

@@ -1,229 +1,30 @@
/**
* @file infrared_remote.h
* @brief Infrared remote library.
*
* An infrared remote contains zero or more infrared signals which
* have a (possibly non-unique) name each.
*
* The current implementation does load only the names into the memory,
* while the signals themselves are loaded on-demand one by one. In theory,
* this should allow for quite large remotes with relatively bulky signals.
*/
#pragma once #pragma once
#include "infrared_signal.h" #include <stdbool.h>
#include "infrared_remote_button.h"
/**
* @brief InfraredRemote opaque type declaration.
*/
typedef struct InfraredRemote InfraredRemote; typedef struct InfraredRemote InfraredRemote;
/**
* @brief Create a new InfraredRemote instance.
*
* @returns pointer to the created instance.
*/
InfraredRemote* infrared_remote_alloc(); InfraredRemote* infrared_remote_alloc();
/**
* @brief Delete an InfraredRemote instance.
*
* @param[in,out] remote pointer to the instance to be deleted.
*/
void infrared_remote_free(InfraredRemote* remote); void infrared_remote_free(InfraredRemote* remote);
/**
* @brief Reset an InfraredRemote instance.
*
* Resetting a remote clears its signal name list and
* the associated file path.
*
* @param[in,out] remote pointer to the instance to be deleted.
*/
void infrared_remote_reset(InfraredRemote* remote); void infrared_remote_reset(InfraredRemote* remote);
/** void infrared_remote_set_name(InfraredRemote* remote, const char* name);
* @brief Get an InfraredRemote instance's name. const char* infrared_remote_get_name(InfraredRemote* remote);
*
* The name is deduced from the file path.
*
* The return value remains valid unless one of the following functions is called:
* - infrared_remote_reset()
* - infrared_remote_load()
* - infrared_remote_create()
*
* @param[in] remote pointer to the instance to be queried.
* @returns pointer to a zero-terminated string containing the name.
*/
const char* infrared_remote_get_name(const InfraredRemote* remote);
/** void infrared_remote_set_path(InfraredRemote* remote, const char* path);
* @brief Get an InfraredRemote instance's file path. const char* infrared_remote_get_path(InfraredRemote* remote);
*
* Same return value validity considerations as infrared_remote_get_name().
*
* @param[in] remote pointer to the instance to be queried.
* @returns pointer to a zero-terminated string containing the path.
*/
const char* infrared_remote_get_path(const InfraredRemote* remote);
/** size_t infrared_remote_get_button_count(InfraredRemote* remote);
* @brief Get the number of signals listed in an InfraredRemote instance. InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index);
* bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index);
* @param[in] remote pointer to the instance to be queried.
* @returns number of signals, zero or more
*/
size_t infrared_remote_get_signal_count(const InfraredRemote* remote);
/** bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal);
* @brief Get the name of a signal listed in an InfraredRemote instance. bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index);
* bool infrared_remote_delete_button(InfraredRemote* remote, size_t index);
* @param[in] remote pointer to the instance to be queried. void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest);
* @param[in] index index of the signal in question. Must be less than the total signal count.
*/
const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index);
/** bool infrared_remote_store(InfraredRemote* remote);
* @brief Get the index of a signal listed in an InfraredRemote instance by its name. bool infrared_remote_load(InfraredRemote* remote, FuriString* path);
*
* @param[in] remote pointer to the instance to be queried.
* @param[in] name pointer to a zero-terminated string containig the name of the signal in question.
* @param[out] index pointer to the variable to hold the signal index.
* @returns true if a signal with the given name was found, false otherwise.
*/
bool infrared_remote_get_signal_index(
const InfraredRemote* remote,
const char* name,
size_t* index);
/**
* @brief Load a signal listed in an InfraredRemote instance.
*
* As mentioned above, the signals are loaded on-demand. The user code must call this function
* each time it wants to interact with a new signal.
*
* @param[in] remote pointer to the instance to load from.
* @param[out] signal pointer to the signal to load into. Must be allocated.
* @param[in] index index of the signal to be loaded. Must be less than the total signal count.
* @return true if the signal was successfully loaded, false otherwise.
*/
bool infrared_remote_load_signal(
const InfraredRemote* remote,
InfraredSignal* signal,
size_t index);
/**
* @brief Append a signal to the file associated with an InfraredRemote instance.
*
* The file path must be somehow initialised first by calling either infrared_remote_load() or
* infrared_remote_create(). As the name suggests, the signal will be put in the end of the file.
*
* @param[in,out] remote pointer to the instance to append to.
* @param[in] signal pointer to the signal to be appended.
* @param[in] name pointer to a zero-terminated string containing the name of the signal.
* @returns true if the signal was successfully appended, false otherwise.
*/
bool infrared_remote_append_signal(
InfraredRemote* remote,
const InfraredSignal* signal,
const char* name);
/**
* @brief Insert a signal to the file associated with an InfraredRemote instance.
*
* Same behaviour as infrared_remote_append_signal(), but the user code can decide where to
* put the signal in the file.
*
* Index values equal to or greater than the total signal count will result in behaviour
* identical to infrared_remote_append_signal().
*
* @param[in,out] remote pointer to the instance to insert to.
* @param[in] signal pointer to the signal to be inserted.
* @param[in] name pointer to a zero-terminated string containing the name of the signal.
* @param[in] index the index under which the signal shall be inserted.
* @returns true if the signal was successfully inserted, false otherwise.
*/
bool infrared_remote_insert_signal(
InfraredRemote* remote,
const InfraredSignal* signal,
const char* name,
size_t index);
/**
* @brief Rename a signal in the file associated with an InfraredRemote instance.
*
* Only changes the signal's name, but neither its position nor contents.
*
* @param[in,out] remote pointer to the instance to be modified.
* @param[in] index index of the signal to be renamed. Must be less than the total signal count.
* @param[in] new_name pointer to a zero-terminated string containig the signal's new name.
* @returns true if the signal was successfully renamed, false otherwise.
*/
bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name);
/**
* @brief Change a signal's position in the file associated with an InfraredRemote instance.
*
* Only changes the signal's position (index), but neither its name nor contents.
*
* @param[in,out] remote pointer to the instance to be modified.
* @param[in] index index of the signal to be moved. Must be less than the total signal count.
* @param[in] new_index index of the signal to be moved. Must be less than the total signal count.
*/
bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index);
/**
* @brief Delete a signal in the file associated with an InfraredRemote instance.
*
* @param[in,out] remote pointer to the instance to be modified.
* @param[in] index index of the signal to be deleted. Must be less than the total signal count.
* @returns true if the signal was successfully deleted, false otherwise.
*/
bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index);
/**
* @brief Create a new file and associate it with an InfraredRemote instance.
*
* The instance will be reset and given a new empty file with just the header.
*
* @param[in,out] remote pointer to the instance to be assigned with a new file.
* @param[in] path pointer to a zero-terminated string containing the full file path.
* @returns true if the file was successfully created, false otherwise.
*/
bool infrared_remote_create(InfraredRemote* remote, const char* path);
/**
* @brief Associate an InfraredRemote instance with a file and load the signal names from it.
*
* The instance will be reset and fill its signal name list from the given file.
* The file must already exist and be valid.
*
* @param[in,out] remote pointer to the instance to be assigned with an existing file.
* @param[in] path pointer to a zero-terminated string containing the full file path.
* @returns true if the file was successfully loaded, false otherwise.
*/
bool infrared_remote_load(InfraredRemote* remote, const char* path);
/**
* @brief Rename the file associated with an InfraredRemote instance.
*
* Only renames the file, no signals are added, moved or deleted.
*
* @param[in,out] remote pointer to the instance to be modified.
* @param[in] new_path pointer to a zero-terminated string containing the new full file path.
* @returns true if the file was successfully renamed, false otherwise.
*/
bool infrared_remote_rename(InfraredRemote* remote, const char* new_path);
/**
* @brief Remove the file associated with an InfraredRemote instance.
*
* This operation is irreversible and fully deletes the remote file
* from the underlying filesystem.
* After calling this function, the instance becomes invalid until
* infrared_remote_create() or infrared_remote_load() are successfully executed.
*
* @param[in,out] remote pointer to the instance to be modified.
* @returns true if the file was successfully removed, false otherwise.
*/
bool infrared_remote_remove(InfraredRemote* remote); bool infrared_remote_remove(InfraredRemote* remote);

View File

@@ -0,0 +1,37 @@
#include "infrared_remote_button.h"
#include <stdlib.h>
struct InfraredRemoteButton {
FuriString* name;
InfraredSignal* signal;
};
InfraredRemoteButton* infrared_remote_button_alloc() {
InfraredRemoteButton* button = malloc(sizeof(InfraredRemoteButton));
button->name = furi_string_alloc();
button->signal = infrared_signal_alloc();
return button;
}
void infrared_remote_button_free(InfraredRemoteButton* button) {
furi_string_free(button->name);
infrared_signal_free(button->signal);
free(button);
}
void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name) {
furi_string_set(button->name, name);
}
const char* infrared_remote_button_get_name(InfraredRemoteButton* button) {
return furi_string_get_cstr(button->name);
}
void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) {
infrared_signal_set_signal(button->signal, signal);
}
InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button) {
return button->signal;
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "infrared_signal.h"
typedef struct InfraredRemoteButton InfraredRemoteButton;
InfraredRemoteButton* infrared_remote_button_alloc();
void infrared_remote_button_free(InfraredRemoteButton* button);
void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name);
const char* infrared_remote_button_get_name(InfraredRemoteButton* button);
void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal);
InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button);

View File

@@ -8,8 +8,6 @@
#define TAG "InfraredSignal" #define TAG "InfraredSignal"
#define INFRARED_SIGNAL_NAME_KEY "name"
struct InfraredSignal { struct InfraredSignal {
bool is_raw; bool is_raw;
union { union {
@@ -26,7 +24,7 @@ static void infrared_signal_clear_timings(InfraredSignal* signal) {
} }
} }
static bool infrared_signal_is_message_valid(const InfraredMessage* message) { static bool infrared_signal_is_message_valid(InfraredMessage* message) {
if(!infrared_is_protocol_valid(message->protocol)) { if(!infrared_is_protocol_valid(message->protocol)) {
FURI_LOG_E(TAG, "Unknown protocol"); FURI_LOG_E(TAG, "Unknown protocol");
return false; return false;
@@ -59,7 +57,7 @@ static bool infrared_signal_is_message_valid(const InfraredMessage* message) {
return true; return true;
} }
static bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) { static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) {
if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) { if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) {
FURI_LOG_E( FURI_LOG_E(
TAG, TAG,
@@ -85,8 +83,7 @@ static bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) {
return true; return true;
} }
static inline bool static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) {
infrared_signal_save_message(const InfraredMessage* message, FlipperFormat* ff) {
const char* protocol_name = infrared_get_protocol_name(message->protocol); const char* protocol_name = infrared_get_protocol_name(message->protocol);
return flipper_format_write_string_cstr(ff, "type", "parsed") && return flipper_format_write_string_cstr(ff, "type", "parsed") &&
flipper_format_write_string_cstr(ff, "protocol", protocol_name) && flipper_format_write_string_cstr(ff, "protocol", protocol_name) &&
@@ -94,7 +91,7 @@ static inline bool
flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4);
} }
static inline bool infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) { static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) {
furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT);
return flipper_format_write_string_cstr(ff, "type", "raw") && return flipper_format_write_string_cstr(ff, "type", "raw") &&
flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) &&
@@ -183,11 +180,11 @@ void infrared_signal_free(InfraredSignal* signal) {
free(signal); free(signal);
} }
bool infrared_signal_is_raw(const InfraredSignal* signal) { bool infrared_signal_is_raw(InfraredSignal* signal) {
return signal->is_raw; return signal->is_raw;
} }
bool infrared_signal_is_valid(const InfraredSignal* signal) { bool infrared_signal_is_valid(InfraredSignal* signal) {
return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) : return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :
infrared_signal_is_message_valid(&signal->payload.message); infrared_signal_is_message_valid(&signal->payload.message);
} }
@@ -236,7 +233,7 @@ void infrared_signal_set_raw_signal(
memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t)); memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));
} }
const InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal) { InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
furi_assert(signal->is_raw); furi_assert(signal->is_raw);
return &signal->payload.raw; return &signal->payload.raw;
} }
@@ -248,14 +245,14 @@ void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage*
signal->payload.message = *message; signal->payload.message = *message;
} }
const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal) { InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) {
furi_assert(!signal->is_raw); furi_assert(!signal->is_raw);
return &signal->payload.message; return &signal->payload.message;
} }
bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name) { bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
if(!flipper_format_write_comment_cstr(ff, "") || if(!flipper_format_write_comment_cstr(ff, "") ||
!flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_NAME_KEY, name)) { !flipper_format_write_string_cstr(ff, "name", name)) {
return false; return false;
} else if(signal->is_raw) { } else if(signal->is_raw) {
return infrared_signal_save_raw(&signal->payload.raw, ff); return infrared_signal_save_raw(&signal->payload.raw, ff);
@@ -265,61 +262,46 @@ bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const
} }
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) {
FuriString* tmp = furi_string_alloc();
bool success = false; bool success = false;
do { do {
if(!infrared_signal_read_name(ff, name)) break; if(!flipper_format_read_string(ff, "name", tmp)) break;
furi_string_set(name, tmp);
if(!infrared_signal_read_body(signal, ff)) break; if(!infrared_signal_read_body(signal, ff)) break;
success = true;
} while(0);
success = true; //-V779 furi_string_free(tmp);
return success;
}
bool infrared_signal_search_and_read(
InfraredSignal* signal,
FlipperFormat* ff,
const FuriString* name) {
bool success = false;
FuriString* tmp = furi_string_alloc();
do {
bool is_name_found = false;
while(flipper_format_read_string(ff, "name", tmp)) {
is_name_found = furi_string_equal(name, tmp);
if(is_name_found) break;
}
if(!is_name_found) break; //-V547
if(!infrared_signal_read_body(signal, ff)) break; //-V779
success = true;
} while(false); } while(false);
return success;
}
bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name) {
return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name);
}
bool infrared_signal_search_by_name_and_read(
InfraredSignal* signal,
FlipperFormat* ff,
const char* name) {
bool success = false;
FuriString* tmp = furi_string_alloc();
while(infrared_signal_read_name(ff, tmp)) {
if(furi_string_equal(tmp, name)) {
success = infrared_signal_read_body(signal, ff);
break;
}
}
furi_string_free(tmp); furi_string_free(tmp);
return success; return success;
} }
bool infrared_signal_search_by_index_and_read( void infrared_signal_transmit(InfraredSignal* signal) {
InfraredSignal* signal,
FlipperFormat* ff,
size_t index) {
bool success = false;
FuriString* tmp = furi_string_alloc();
for(uint32_t i = 0; infrared_signal_read_name(ff, tmp); ++i) {
if(i == index) {
success = infrared_signal_read_body(signal, ff);
break;
}
}
furi_string_free(tmp);
return success;
}
void infrared_signal_transmit(const InfraredSignal* signal) {
if(signal->is_raw) { if(signal->is_raw) {
const InfraredRawSignal* raw_signal = &signal->payload.raw; InfraredRawSignal* raw_signal = &signal->payload.raw;
infrared_send_raw_ext( infrared_send_raw_ext(
raw_signal->timings, raw_signal->timings,
raw_signal->timings_size, raw_signal->timings_size,
@@ -327,7 +309,7 @@ void infrared_signal_transmit(const InfraredSignal* signal) {
raw_signal->frequency, raw_signal->frequency,
raw_signal->duty_cycle); raw_signal->duty_cycle);
} else { } else {
const InfraredMessage* message = &signal->payload.message; InfraredMessage* message = &signal->payload.message;
infrared_send(message, 1); infrared_send(message, 1);
} }
} }

View File

@@ -1,205 +1,45 @@
/**
* @file infrared_signal.h
* @brief Infrared signal library.
*
* Infrared signals may be of two types:
* - known to the infrared signal decoder, or *parsed* signals
* - the rest, or *raw* signals, which are treated merely as a set of timings.
*/
#pragma once #pragma once
#include <flipper_format/flipper_format.h> #include <stddef.h>
#include <infrared/encoder_decoder/infrared.h> #include <stdint.h>
#include <stdbool.h>
#include <infrared.h>
#include <flipper_format/flipper_format.h>
/**
* @brief InfraredSignal opaque type declaration.
*/
typedef struct InfraredSignal InfraredSignal; typedef struct InfraredSignal InfraredSignal;
/**
* @brief Raw signal type definition.
*
* Measurement units used:
* - time: microseconds (uS)
* - frequency: Hertz (Hz)
* - duty_cycle: no units, fraction between 0 and 1.
*/
typedef struct { typedef struct {
size_t timings_size; /**< Number of elements in the timings array. */ size_t timings_size;
uint32_t* timings; /**< Pointer to an array of timings describing the signal. */ uint32_t* timings;
uint32_t frequency; /**< Carrier frequency of the signal. */ uint32_t frequency;
float duty_cycle; /**< Duty cycle of the signal. */ float duty_cycle;
} InfraredRawSignal; } InfraredRawSignal;
/**
* @brief Create a new InfraredSignal instance.
*
* @returns pointer to the instance created.
*/
InfraredSignal* infrared_signal_alloc(); InfraredSignal* infrared_signal_alloc();
/**
* @brief Delete an InfraredSignal instance.
*
* @param[in,out] signal pointer to the instance to be deleted.
*/
void infrared_signal_free(InfraredSignal* signal); void infrared_signal_free(InfraredSignal* signal);
/** bool infrared_signal_is_raw(InfraredSignal* signal);
* @brief Test whether an InfraredSignal instance holds a raw signal. bool infrared_signal_is_valid(InfraredSignal* signal);
*
* @param[in] signal pointer to the instance to be tested.
* @returns true if the instance holds a raw signal, false otherwise.
*/
bool infrared_signal_is_raw(const InfraredSignal* signal);
/**
* @brief Test whether an InfraredSignal instance holds any signal.
*
* @param[in] signal pointer to the instance to be tested.
* @returns true if the instance holds raw signal, false otherwise.
*/
bool infrared_signal_is_valid(const InfraredSignal* signal);
/**
* @brief Set an InfraredInstance to hold the signal from another one.
*
* Any instance's previous contents will be automatically deleted before
* copying the source instance's contents.
*
* @param[in,out] signal pointer to the destination instance.
* @param[in] other pointer to the source instance.
*/
void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other); void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other);
/**
* @brief Set an InfraredInstance to hold a raw signal.
*
* Any instance's previous contents will be automatically deleted before
* copying the raw signal.
*
* After this call, infrared_signal_is_raw() will return true.
*
* @param[in,out] signal pointer to the destination instance.
* @param[in] timings pointer to an array containing the raw signal timings.
* @param[in] timings_size number of elements in the timings array.
* @param[in] frequency signal carrier frequency, in Hertz.
* @param[in] duty_cycle signal duty cycle, fraction between 0 and 1.
*/
void infrared_signal_set_raw_signal( void infrared_signal_set_raw_signal(
InfraredSignal* signal, InfraredSignal* signal,
const uint32_t* timings, const uint32_t* timings,
size_t timings_size, size_t timings_size,
uint32_t frequency, uint32_t frequency,
float duty_cycle); float duty_cycle);
InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal);
/**
* @brief Get the raw signal held by an InfraredSignal instance.
*
* @warning the instance MUST hold a *raw* signal, otherwise undefined behaviour will occur.
*
* @param[in] signal pointer to the instance to be queried.
* @returns pointer to the raw signal structure held by the instance.
*/
const InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal);
/**
* @brief Set an InfraredInstance to hold a parsed signal.
*
* Any instance's previous contents will be automatically deleted before
* copying the raw signal.
*
* After this call, infrared_signal_is_raw() will return false.
*
* @param[in,out] signal pointer to the destination instance.
* @param[in] message pointer to the message containing the parsed signal.
*/
void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message); void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message);
InfraredMessage* infrared_signal_get_message(InfraredSignal* signal);
/** bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name);
* @brief Get the parsed signal held by an InfraredSignal instance.
*
* @warning the instance MUST hold a *parsed* signal, otherwise undefined behaviour will occur.
*
* @param[in] signal pointer to the instance to be queried.
* @returns pointer to the parsed signal structure held by the instance.
*/
const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal);
/**
* @brief Read a signal from a FlipperFormat file into an InfraredSignal instance.
*
* The file must be allocated and open prior to this call. The seek position determines
* which signal will be read (if there is more than one in the file). Calling this function
* repeatedly will result in all signals in the file to be read until no more are left.
*
* @param[in,out] signal pointer to the instance to be read into.
* @param[in,out] ff pointer to the FlipperFormat file instance to read from.
* @param[out] name pointer to the string to hold the signal name. Must be properly allocated.
* @returns true if a signal was successfully read, false otherwise (e.g. no more signals to read).
*/
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name);
bool infrared_signal_search_and_read(
/**
* @brief Read a signal name from a FlipperFormat file.
*
* Same behaviour as infrared_signal_read(), but only the name is read.
*
* @param[in,out] ff pointer to the FlipperFormat file instance to read from.
* @param[out] name pointer to the string to hold the signal name. Must be properly allocated.
* @returns true if a signal name was successfully read, false otherwise (e.g. no more signals to read).
*/
bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name);
/**
* @brief Read a signal with a particular name from a FlipperFormat file into an InfraredSignal instance.
*
* This function will look for a signal with the given name and if found, attempt to read it.
* Same considerations apply as to infrared_signal_read().
*
* @param[in,out] signal pointer to the instance to be read into.
* @param[in,out] ff pointer to the FlipperFormat file instance to read from.
* @param[in] name pointer to a zero-terminated string containing the requested signal name.
* @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found).
*/
bool infrared_signal_search_by_name_and_read(
InfraredSignal* signal, InfraredSignal* signal,
FlipperFormat* ff, FlipperFormat* ff,
const char* name); const FuriString* name);
/** void infrared_signal_transmit(InfraredSignal* signal);
* @brief Read a signal with a particular index from a FlipperFormat file into an InfraredSignal instance.
*
* This function will look for a signal with the given index and if found, attempt to read it.
* Same considerations apply as to infrared_signal_read().
*
* @param[in,out] signal pointer to the instance to be read into.
* @param[in,out] ff pointer to the FlipperFormat file instance to read from.
* @param[in] index the requested signal index.
* @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found).
*/
bool infrared_signal_search_by_index_and_read(
InfraredSignal* signal,
FlipperFormat* ff,
size_t index);
/**
* @brief Save a signal contained in an InfraredSignal instance to a FlipperFormat file.
*
* The file must be allocated and open prior to this call. Additionally, an appropriate header
* must be already written into the file.
*
* @param[in] signal pointer to the instance holding the signal to be saved.
* @param[in,out] ff pointer to the FlipperFormat file instance to write to.
* @param[in] name pointer to a zero-terminated string contating the name of the signal.
*/
bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name);
/**
* @brief Transmit a signal contained in an InfraredSignal instance.
*
* The transmission happens once per call using the built-in hardware (via HAL calls).
*
* @param[in] signal pointer to the instance holding the signal to be transmitted.
*/
void infrared_signal_transmit(const InfraredSignal* signal);

View File

@@ -1,21 +1,20 @@
#include "../../infrared_app_i.h" #include "../../infrared_i.h"
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void infrared_scene_universal_common_item_callback(void* context, uint32_t index) { void infrared_scene_universal_common_item_callback(void* context, uint32_t index) {
InfraredApp* infrared = context; Infrared* infrared = context;
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index); uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);
view_dispatcher_send_custom_event(infrared->view_dispatcher, event); view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
} }
static void infrared_scene_universal_common_progress_back_callback(void* context) { static void infrared_scene_universal_common_progress_back_callback(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1); uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1);
view_dispatcher_send_custom_event(infrared->view_dispatcher, event); view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
} }
static void static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint32_t record_count) {
infrared_scene_universal_common_show_popup(InfraredApp* infrared, uint32_t record_count) {
ViewStack* view_stack = infrared->view_stack; ViewStack* view_stack = infrared->view_stack;
InfraredProgressView* progress = infrared->progress; InfraredProgressView* progress = infrared->progress;
infrared_progress_view_set_progress_total(progress, record_count); infrared_progress_view_set_progress_total(progress, record_count);
@@ -25,7 +24,7 @@ static void
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend);
} }
static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { static void infrared_scene_universal_common_hide_popup(Infrared* infrared) {
ViewStack* view_stack = infrared->view_stack; ViewStack* view_stack = infrared->view_stack;
InfraredProgressView* progress = infrared->progress; InfraredProgressView* progress = infrared->progress;
view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress)); view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress));
@@ -33,12 +32,12 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) {
} }
void infrared_scene_universal_common_on_enter(void* context) { void infrared_scene_universal_common_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel)); view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));
} }
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
InfraredBruteForce* brute_force = infrared->brute_force; InfraredBruteForce* brute_force = infrared->brute_force;
bool consumed = false; bool consumed = false;
@@ -85,7 +84,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e
} }
void infrared_scene_universal_common_on_exit(void* context) { void infrared_scene_universal_common_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel; ButtonPanel* button_panel = infrared->button_panel;
view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel)); view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel));
infrared_brute_force_reset(infrared->brute_force); infrared_brute_force_reset(infrared->brute_force);

View File

@@ -1,12 +1,12 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) { static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, result); view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
} }
void infrared_scene_ask_back_on_enter(void* context) { void infrared_scene_ask_back_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
DialogEx* dialog_ex = infrared->dialog_ex; DialogEx* dialog_ex = infrared->dialog_ex;
if(infrared->app_state.is_learning_new_remote) { if(infrared->app_state.is_learning_new_remote) {
@@ -28,7 +28,7 @@ void infrared_scene_ask_back_on_enter(void* context) {
} }
bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
@@ -54,6 +54,6 @@ bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_ask_back_on_exit(void* context) { void infrared_scene_ask_back_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
dialog_ex_reset(infrared->dialog_ex); dialog_ex_reset(infrared->dialog_ex);
} }

View File

@@ -1,12 +1,12 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) { static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, result); view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
} }
void infrared_scene_ask_retry_on_enter(void* context) { void infrared_scene_ask_retry_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
DialogEx* dialog_ex = infrared->dialog_ex; DialogEx* dialog_ex = infrared->dialog_ex;
dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop); dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop);
@@ -23,7 +23,7 @@ void infrared_scene_ask_retry_on_enter(void* context) {
} }
bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
@@ -43,6 +43,6 @@ bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_ask_retry_on_exit(void* context) { void infrared_scene_ask_retry_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
dialog_ex_reset(infrared->dialog_ex); dialog_ex_reset(infrared->dialog_ex);
} }

View File

@@ -1,7 +1,7 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
void infrared_scene_debug_on_enter(void* context) { void infrared_scene_debug_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
InfraredWorker* worker = infrared->worker; InfraredWorker* worker = infrared->worker;
infrared_worker_rx_set_received_signal_callback( infrared_worker_rx_set_received_signal_callback(
@@ -14,16 +14,16 @@ void infrared_scene_debug_on_enter(void* context) {
} }
bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypeSignalReceived) { if(event.event == InfraredCustomEventTypeSignalReceived) {
InfraredDebugView* debug_view = infrared->debug_view; InfraredDebugView* debug_view = infrared->debug_view;
InfraredSignal* signal = infrared->current_signal; InfraredSignal* signal = infrared->received_signal;
if(infrared_signal_is_raw(signal)) { if(infrared_signal_is_raw(signal)) {
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
infrared_debug_view_set_text(debug_view, "RAW\n%d samples\n", raw->timings_size); infrared_debug_view_set_text(debug_view, "RAW\n%d samples\n", raw->timings_size);
printf("RAW, %zu samples:\r\n", raw->timings_size); printf("RAW, %zu samples:\r\n", raw->timings_size);
@@ -33,7 +33,7 @@ bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {
printf("\r\n"); printf("\r\n");
} else { } else {
const InfraredMessage* message = infrared_signal_get_message(signal); InfraredMessage* message = infrared_signal_get_message(signal);
infrared_debug_view_set_text( infrared_debug_view_set_text(
debug_view, debug_view,
"%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n",
@@ -61,7 +61,7 @@ bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_debug_on_exit(void* context) { void infrared_scene_debug_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
InfraredWorker* worker = infrared->worker; InfraredWorker* worker = infrared->worker;
infrared_worker_rx_stop(worker); infrared_worker_rx_stop(worker);
infrared_worker_rx_enable_blink_on_receiving(worker, false); infrared_worker_rx_enable_blink_on_receiving(worker, false);

View File

@@ -1,4 +1,4 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include <furi_hal_infrared.h> #include <furi_hal_infrared.h>
uint8_t value_index_ir; uint8_t value_index_ir;
@@ -10,7 +10,7 @@ const char* const infrared_debug_cfg_variables_text[] = {
}; };
static void infrared_scene_debug_settings_changed(VariableItem* item) { static void infrared_scene_debug_settings_changed(VariableItem* item) {
InfraredApp* infrared = variable_item_get_context(item); Infrared* infrared = variable_item_get_context(item);
value_index_ir = variable_item_get_current_value_index(item); value_index_ir = variable_item_get_current_value_index(item);
UNUSED(infrared); UNUSED(infrared);
@@ -35,12 +35,12 @@ static void infrared_scene_debug_settings_power_changed(VariableItem* item) {
} }
static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) { static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index); view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
} }
void infrared_scene_debug_settings_on_enter(void* context) { void infrared_scene_debug_settings_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
VariableItemList* variable_item_list = infrared->variable_item_list; VariableItemList* variable_item_list = infrared->variable_item_list;
@@ -72,7 +72,7 @@ void infrared_scene_debug_settings_on_enter(void* context) {
} }
bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
UNUSED(infrared); UNUSED(infrared);
UNUSED(event); UNUSED(event);
@@ -80,6 +80,6 @@ bool infrared_scene_debug_settings_on_event(void* context, SceneManagerEvent eve
} }
void infrared_scene_debug_settings_on_exit(void* context) { void infrared_scene_debug_settings_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
variable_item_list_reset(infrared->variable_item_list); variable_item_list_reset(infrared->variable_item_list);
} }

View File

@@ -1,4 +1,4 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
typedef enum { typedef enum {
SubmenuIndexAddButton, SubmenuIndexAddButton,
@@ -10,12 +10,12 @@ typedef enum {
} SubmenuIndex; } SubmenuIndex;
static void infrared_scene_edit_submenu_callback(void* context, uint32_t index) { static void infrared_scene_edit_submenu_callback(void* context, uint32_t index) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index); view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
} }
void infrared_scene_edit_on_enter(void* context) { void infrared_scene_edit_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Submenu* submenu = infrared->submenu; Submenu* submenu = infrared->submenu;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
@@ -64,7 +64,7 @@ void infrared_scene_edit_on_enter(void* context) {
} }
bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
@@ -106,6 +106,6 @@ bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_edit_on_exit(void* context) { void infrared_scene_edit_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
submenu_reset(infrared->submenu); submenu_reset(infrared->submenu);
} }

View File

@@ -1,12 +1,12 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
static void infrared_scene_edit_button_select_submenu_callback(void* context, uint32_t index) { static void infrared_scene_edit_button_select_submenu_callback(void* context, uint32_t index) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index); view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
} }
void infrared_scene_edit_button_select_on_enter(void* context) { void infrared_scene_edit_button_select_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Submenu* submenu = infrared->submenu; Submenu* submenu = infrared->submenu;
InfraredRemote* remote = infrared->remote; InfraredRemote* remote = infrared->remote;
InfraredAppState* app_state = &infrared->app_state; InfraredAppState* app_state = &infrared->app_state;
@@ -16,16 +16,16 @@ void infrared_scene_edit_button_select_on_enter(void* context) {
"Delete Button:"; "Delete Button:";
submenu_set_header(submenu, header); submenu_set_header(submenu, header);
const size_t button_count = infrared_remote_get_signal_count(remote); const size_t button_count = infrared_remote_get_button_count(remote);
for(size_t i = 0; i < button_count; ++i) { for(size_t i = 0; i < button_count; ++i) {
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
submenu_add_item( submenu_add_item(
submenu, submenu,
infrared_remote_get_signal_name(remote, i), infrared_remote_button_get_name(button),
i, i,
infrared_scene_edit_button_select_submenu_callback, infrared_scene_edit_button_select_submenu_callback,
context); context);
} }
if(button_count && app_state->current_button_index != InfraredButtonIndexNone) { if(button_count && app_state->current_button_index != InfraredButtonIndexNone) {
submenu_set_selected_item(submenu, app_state->current_button_index); submenu_set_selected_item(submenu, app_state->current_button_index);
app_state->current_button_index = InfraredButtonIndexNone; app_state->current_button_index = InfraredButtonIndexNone;
@@ -35,7 +35,7 @@ void infrared_scene_edit_button_select_on_enter(void* context) {
} }
bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
InfraredAppState* app_state = &infrared->app_state; InfraredAppState* app_state = &infrared->app_state;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
@@ -48,7 +48,7 @@ bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent
} else if(edit_mode == InfraredEditModeDelete) { } else if(edit_mode == InfraredEditModeDelete) {
scene_manager_next_scene(scene_manager, InfraredSceneEditDelete); scene_manager_next_scene(scene_manager, InfraredSceneEditDelete);
} else { } else {
furi_crash(); furi_assert(0);
} }
consumed = true; consumed = true;
} }
@@ -57,6 +57,6 @@ bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent
} }
void infrared_scene_edit_button_select_on_exit(void* context) { void infrared_scene_edit_button_select_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
submenu_reset(infrared->submenu); submenu_reset(infrared->submenu);
} }

View File

@@ -1,49 +1,42 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
static void static void
infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) { infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, result); view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
} }
void infrared_scene_edit_delete_on_enter(void* context) { void infrared_scene_edit_delete_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
DialogEx* dialog_ex = infrared->dialog_ex; DialogEx* dialog_ex = infrared->dialog_ex;
InfraredRemote* remote = infrared->remote; InfraredRemote* remote = infrared->remote;
const InfraredEditTarget edit_target = infrared->app_state.edit_target; const InfraredEditTarget edit_target = infrared->app_state.edit_target;
if(edit_target == InfraredEditTargetButton) { if(edit_target == InfraredEditTargetButton) {
int32_t current_button_index = infrared->app_state.current_button_index;
furi_assert(current_button_index != InfraredButtonIndexNone);
dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop);
InfraredRemoteButton* current_button =
infrared_remote_get_button(remote, current_button_index);
InfraredSignal* signal = infrared_remote_button_get_signal(current_button);
const int32_t current_button_index = infrared->app_state.current_button_index; if(infrared_signal_is_raw(signal)) {
furi_check(current_button_index != InfraredButtonIndexNone); const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
if(!infrared_remote_load_signal(remote, infrared->current_signal, current_button_index)) {
infrared_show_error_message(
infrared,
"Failed to load\n\"%s\"",
infrared_remote_get_signal_name(remote, current_button_index));
scene_manager_previous_scene(infrared->scene_manager);
return;
}
if(infrared_signal_is_raw(infrared->current_signal)) {
const InfraredRawSignal* raw =
infrared_signal_get_raw_signal(infrared->current_signal);
infrared_text_store_set( infrared_text_store_set(
infrared, infrared,
0, 0,
"%s\nRAW\n%zu samples", "%s\nRAW\n%ld samples",
infrared_remote_get_signal_name(remote, current_button_index), infrared_remote_button_get_name(current_button),
raw->timings_size); raw->timings_size);
} else { } else {
const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal); const InfraredMessage* message = infrared_signal_get_message(signal);
infrared_text_store_set( infrared_text_store_set(
infrared, infrared,
0, 0,
"%s\n%s\nA=0x%0*lX C=0x%0*lX", "%s\n%s\nA=0x%0*lX C=0x%0*lX",
infrared_remote_get_signal_name(remote, current_button_index), infrared_remote_button_get_name(current_button),
infrared_get_protocol_name(message->protocol), infrared_get_protocol_name(message->protocol),
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
message->address, message->address,
@@ -56,11 +49,11 @@ void infrared_scene_edit_delete_on_enter(void* context) {
infrared_text_store_set( infrared_text_store_set(
infrared, infrared,
0, 0,
"%s\n with %zu buttons", "%s\n with %lu buttons",
infrared_remote_get_name(remote), infrared_remote_get_name(remote),
infrared_remote_get_signal_count(remote)); infrared_remote_get_button_count(remote));
} else { } else {
furi_crash(); furi_assert(0);
} }
dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter); dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter);
@@ -70,14 +63,11 @@ void infrared_scene_edit_delete_on_enter(void* context) {
dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback); dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback);
dialog_ex_set_context(dialog_ex, context); dialog_ex_set_context(dialog_ex, context);
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
view_stack_add_view(infrared->view_stack, dialog_ex_get_view(infrared->dialog_ex));
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
} }
bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
@@ -93,24 +83,18 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event)
if(edit_target == InfraredEditTargetButton) { if(edit_target == InfraredEditTargetButton) {
furi_assert(app_state->current_button_index != InfraredButtonIndexNone); furi_assert(app_state->current_button_index != InfraredButtonIndexNone);
infrared_show_loading_popup(infrared, true); success = infrared_remote_delete_button(remote, app_state->current_button_index);
success = infrared_remote_delete_signal(remote, app_state->current_button_index);
infrared_show_loading_popup(infrared, false);
app_state->current_button_index = InfraredButtonIndexNone; app_state->current_button_index = InfraredButtonIndexNone;
} else if(edit_target == InfraredEditTargetRemote) { } else if(edit_target == InfraredEditTargetRemote) {
success = infrared_remote_remove(remote); success = infrared_remote_remove(remote);
app_state->current_button_index = InfraredButtonIndexNone; app_state->current_button_index = InfraredButtonIndexNone;
} else { } else {
furi_crash(); furi_assert(0);
} }
if(success) { if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone);
} else { } else {
infrared_show_error_message(
infrared,
"Failed to\ndelete %s",
edit_target == InfraredEditTargetButton ? "button" : "file");
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
scene_manager_search_and_switch_to_previous_scene_one_of( scene_manager_search_and_switch_to_previous_scene_one_of(
scene_manager, possible_scenes, COUNT_OF(possible_scenes)); scene_manager, possible_scenes, COUNT_OF(possible_scenes));
@@ -123,6 +107,6 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event)
} }
void infrared_scene_edit_delete_on_exit(void* context) { void infrared_scene_edit_delete_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_stack_remove_view(infrared->view_stack, dialog_ex_get_view(infrared->dialog_ex)); UNUSED(infrared);
} }

View File

@@ -1,7 +1,7 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
void infrared_scene_edit_delete_done_on_enter(void* context) { void infrared_scene_edit_delete_done_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
@@ -16,7 +16,7 @@ void infrared_scene_edit_delete_done_on_enter(void* context) {
} }
bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
@@ -33,7 +33,7 @@ bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent e
view_dispatcher_stop(infrared->view_dispatcher); view_dispatcher_stop(infrared->view_dispatcher);
} }
} else { } else {
furi_crash(); furi_assert(0);
} }
consumed = true; consumed = true;
} }
@@ -43,6 +43,6 @@ bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent e
} }
void infrared_scene_edit_delete_done_on_exit(void* context) { void infrared_scene_edit_delete_done_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
UNUSED(infrared); UNUSED(infrared);
} }

View File

@@ -1,69 +1,44 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
static void infrared_scene_edit_move_button_callback( static void infrared_scene_move_button(uint32_t index_old, uint32_t index_new, void* context) {
uint32_t index_old, InfraredRemote* remote = context;
uint32_t index_new, furi_assert(remote);
void* context) { infrared_remote_move_button(remote, index_old, index_new);
InfraredApp* infrared = context; }
furi_assert(infrared);
infrared->app_state.prev_button_index = index_old; static const char* infrared_scene_get_btn_name(uint32_t index, void* context) {
infrared->app_state.current_button_index = index_new; InfraredRemote* remote = context;
furi_assert(remote);
view_dispatcher_send_custom_event( InfraredRemoteButton* button = infrared_remote_get_button(remote, index);
infrared->view_dispatcher, InfraredCustomEventTypeButtonSelected); return (infrared_remote_button_get_name(button));
} }
void infrared_scene_edit_move_on_enter(void* context) { void infrared_scene_edit_move_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
InfraredRemote* remote = infrared->remote; InfraredRemote* remote = infrared->remote;
for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) { infrared_move_view_set_callback(infrared->move_view, infrared_scene_move_button);
infrared_move_view_add_item(
infrared->move_view, infrared_remote_get_signal_name(remote, i));
}
infrared_move_view_set_callback( uint32_t btn_count = infrared_remote_get_button_count(remote);
infrared->move_view, infrared_scene_edit_move_button_callback, infrared); infrared_move_view_list_init(
infrared->move_view, btn_count, infrared_scene_get_btn_name, remote);
infrared_move_view_list_update(infrared->move_view);
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove);
view_stack_add_view(infrared->view_stack, infrared_move_view_get_view(infrared->move_view));
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
} }
bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { UNUSED(event);
if(event.event == InfraredCustomEventTypeButtonSelected) { UNUSED(infrared);
infrared_show_loading_popup(infrared, true);
const bool button_moved = infrared_remote_move_signal(
infrared->remote,
infrared->app_state.prev_button_index,
infrared->app_state.current_button_index);
infrared_show_loading_popup(infrared, false);
if(!button_moved) {
infrared_show_error_message(
infrared,
"Failed to move\n\"%s\"",
infrared_remote_get_signal_name(
infrared->remote, infrared->app_state.current_button_index));
scene_manager_search_and_switch_to_previous_scene(
infrared->scene_manager, InfraredSceneRemoteList);
}
consumed = true;
}
}
return consumed; return consumed;
} }
void infrared_scene_edit_move_on_exit(void* context) { void infrared_scene_edit_move_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_stack_remove_view(infrared->view_stack, infrared_move_view_get_view(infrared->move_view)); InfraredRemote* remote = infrared->remote;
infrared_move_view_reset(infrared->move_view); infrared_remote_store(remote);
} }

View File

@@ -1,10 +1,10 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include <string.h> #include <string.h>
#include <toolbox/path.h> #include <toolbox/path.h>
void infrared_scene_edit_rename_on_enter(void* context) { void infrared_scene_edit_rename_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
InfraredRemote* remote = infrared->remote; InfraredRemote* remote = infrared->remote;
TextInput* text_input = infrared->text_input; TextInput* text_input = infrared->text_input;
size_t enter_name_length = 0; size_t enter_name_length = 0;
@@ -14,12 +14,14 @@ void infrared_scene_edit_rename_on_enter(void* context) {
text_input_set_header_text(text_input, "Name the button"); text_input_set_header_text(text_input, "Name the button");
const int32_t current_button_index = infrared->app_state.current_button_index; const int32_t current_button_index = infrared->app_state.current_button_index;
furi_check(current_button_index != InfraredButtonIndexNone); furi_assert(current_button_index != InfraredButtonIndexNone);
InfraredRemoteButton* current_button =
infrared_remote_get_button(remote, current_button_index);
enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH; enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH;
strncpy( strncpy(
infrared->text_store[0], infrared->text_store[0],
infrared_remote_get_signal_name(remote, current_button_index), infrared_remote_button_get_name(current_button),
enter_name_length); enter_name_length);
} else if(edit_target == InfraredEditTargetRemote) { } else if(edit_target == InfraredEditTargetRemote) {
@@ -42,7 +44,7 @@ void infrared_scene_edit_rename_on_enter(void* context) {
furi_string_free(folder_path); furi_string_free(folder_path);
} else { } else {
furi_crash(); furi_assert(0);
} }
text_input_set_result_callback( text_input_set_result_callback(
@@ -53,14 +55,11 @@ void infrared_scene_edit_rename_on_enter(void* context) {
enter_name_length, enter_name_length,
false); false);
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
view_stack_add_view(infrared->view_stack, text_input_get_view(infrared->text_input));
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
} }
bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
InfraredRemote* remote = infrared->remote; InfraredRemote* remote = infrared->remote;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
InfraredAppState* app_state = &infrared->app_state; InfraredAppState* app_state = &infrared->app_state;
@@ -73,24 +72,18 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event)
if(edit_target == InfraredEditTargetButton) { if(edit_target == InfraredEditTargetButton) {
const int32_t current_button_index = app_state->current_button_index; const int32_t current_button_index = app_state->current_button_index;
furi_assert(current_button_index != InfraredButtonIndexNone); furi_assert(current_button_index != InfraredButtonIndexNone);
infrared_show_loading_popup(infrared, true); success = infrared_remote_rename_button(
success = infrared_remote_rename_signal( remote, infrared->text_store[0], current_button_index);
remote, current_button_index, infrared->text_store[0]);
infrared_show_loading_popup(infrared, false);
app_state->current_button_index = InfraredButtonIndexNone; app_state->current_button_index = InfraredButtonIndexNone;
} else if(edit_target == InfraredEditTargetRemote) { } else if(edit_target == InfraredEditTargetRemote) {
success = infrared_rename_current_remote(infrared, infrared->text_store[0]); success = infrared_rename_current_remote(infrared, infrared->text_store[0]);
} else { } else {
furi_crash(); furi_assert(0);
} }
if(success) { if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone);
} else { } else {
infrared_show_error_message(
infrared,
"Failed to\nrename %s",
edit_target == InfraredEditTargetButton ? "button" : "file");
scene_manager_search_and_switch_to_previous_scene( scene_manager_search_and_switch_to_previous_scene(
scene_manager, InfraredSceneRemoteList); scene_manager, InfraredSceneRemoteList);
} }
@@ -102,11 +95,9 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event)
} }
void infrared_scene_edit_rename_on_exit(void* context) { void infrared_scene_edit_rename_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
TextInput* text_input = infrared->text_input; TextInput* text_input = infrared->text_input;
view_stack_remove_view(infrared->view_stack, text_input_get_view(text_input));
void* validator_context = text_input_get_validator_callback_context(text_input); void* validator_context = text_input_get_validator_callback_context(text_input);
text_input_set_validator(text_input, NULL, NULL); text_input_set_validator(text_input, NULL, NULL);

View File

@@ -1,7 +1,7 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
void infrared_scene_edit_rename_done_on_enter(void* context) { void infrared_scene_edit_rename_done_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
@@ -16,7 +16,7 @@ void infrared_scene_edit_rename_done_on_enter(void* context) {
} }
bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -33,6 +33,6 @@ bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent e
} }
void infrared_scene_edit_rename_done_on_exit(void* context) { void infrared_scene_edit_rename_done_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
UNUSED(infrared); UNUSED(infrared);
} }

View File

@@ -1,7 +1,7 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
void infrared_scene_error_databases_on_enter(void* context) { void infrared_scene_error_databases_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
popup_set_icon(popup, 5, 11, &I_SDQuestion_35x43); popup_set_icon(popup, 5, 11, &I_SDQuestion_35x43);
@@ -16,7 +16,7 @@ void infrared_scene_error_databases_on_enter(void* context) {
} }
bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -31,7 +31,7 @@ bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent ev
} }
void infrared_scene_error_databases_on_exit(void* context) { void infrared_scene_error_databases_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
popup_reset(infrared->popup); popup_reset(infrared->popup);
infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOff); infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOff);
} }

View File

@@ -1,8 +1,8 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void infrared_scene_learn_on_enter(void* context) { void infrared_scene_learn_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
InfraredWorker* worker = infrared->worker; InfraredWorker* worker = infrared->worker;
@@ -21,7 +21,7 @@ void infrared_scene_learn_on_enter(void* context) {
} }
bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -37,7 +37,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_learn_on_exit(void* context) { void infrared_scene_learn_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL); infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
infrared_worker_rx_stop(infrared->worker); infrared_worker_rx_stop(infrared->worker);

View File

@@ -1,7 +1,7 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
void infrared_scene_learn_done_on_enter(void* context) { void infrared_scene_learn_done_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
@@ -21,7 +21,7 @@ void infrared_scene_learn_done_on_enter(void* context) {
} }
bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -38,7 +38,7 @@ bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event)
} }
void infrared_scene_learn_done_on_exit(void* context) { void infrared_scene_learn_done_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
infrared->app_state.is_learning_new_remote = false; infrared->app_state.is_learning_new_remote = false;
popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop); popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop);
} }

View File

@@ -1,16 +1,16 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
void infrared_scene_learn_enter_name_on_enter(void* context) { void infrared_scene_learn_enter_name_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
TextInput* text_input = infrared->text_input; TextInput* text_input = infrared->text_input;
InfraredSignal* signal = infrared->current_signal; InfraredSignal* signal = infrared->received_signal;
if(infrared_signal_is_raw(signal)) { if(infrared_signal_is_raw(signal)) {
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
infrared_text_store_set(infrared, 0, "RAW_%zu", raw->timings_size); infrared_text_store_set(infrared, 0, "RAW_%d", raw->timings_size);
} else { } else {
const InfraredMessage* message = infrared_signal_get_message(signal); InfraredMessage* message = infrared_signal_get_message(signal);
infrared_text_store_set( infrared_text_store_set(
infrared, infrared,
0, 0,
@@ -28,32 +28,31 @@ void infrared_scene_learn_enter_name_on_enter(void* context) {
infrared->text_store[0], infrared->text_store[0],
INFRARED_MAX_BUTTON_NAME_LENGTH, INFRARED_MAX_BUTTON_NAME_LENGTH,
true); true);
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
} }
bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
InfraredSignal* signal = infrared->current_signal; InfraredSignal* signal = infrared->received_signal;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InfraredCustomEventTypeTextEditDone) { if(event.event == InfraredCustomEventTypeTextEditDone) {
const char* signal_name = infrared->text_store[0]; bool success = false;
const bool success = if(infrared->app_state.is_learning_new_remote) {
infrared->app_state.is_learning_new_remote ? success =
infrared_add_remote_with_button(infrared, signal_name, signal) : infrared_add_remote_with_button(infrared, infrared->text_store[0], signal);
infrared_remote_append_signal(infrared->remote, signal, signal_name); } else {
success =
infrared_remote_add_button(infrared->remote, infrared->text_store[0], signal);
}
if(success) { if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); scene_manager_next_scene(scene_manager, InfraredSceneLearnDone);
dolphin_deed(DolphinDeedIrSave); dolphin_deed(DolphinDeedIrSave);
} else { } else {
infrared_show_error_message( dialog_message_show_storage_error(infrared->dialogs, "Failed to save file");
infrared,
"Failed to\n%s",
infrared->app_state.is_learning_new_remote ? "create file" : "add signal");
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
scene_manager_search_and_switch_to_previous_scene_one_of( scene_manager_search_and_switch_to_previous_scene_one_of(
scene_manager, possible_scenes, COUNT_OF(possible_scenes)); scene_manager, possible_scenes, COUNT_OF(possible_scenes));
@@ -66,6 +65,6 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e
} }
void infrared_scene_learn_enter_name_on_exit(void* context) { void infrared_scene_learn_enter_name_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
UNUSED(infrared); UNUSED(infrared);
} }

View File

@@ -1,26 +1,26 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
static void static void
infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, result); view_dispatcher_send_custom_event(infrared->view_dispatcher, result);
} }
void infrared_scene_learn_success_on_enter(void* context) { void infrared_scene_learn_success_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
DialogEx* dialog_ex = infrared->dialog_ex; DialogEx* dialog_ex = infrared->dialog_ex;
InfraredSignal* signal = infrared->current_signal; InfraredSignal* signal = infrared->received_signal;
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);
if(infrared_signal_is_raw(signal)) { if(infrared_signal_is_raw(signal)) {
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter); dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter);
infrared_text_store_set(infrared, 0, "%zu samples", raw->timings_size); infrared_text_store_set(infrared, 0, "%d samples", raw->timings_size);
dialog_ex_set_text(dialog_ex, infrared->text_store[0], 75, 23, AlignLeft, AlignTop); dialog_ex_set_text(dialog_ex, infrared->text_store[0], 75, 23, AlignLeft, AlignTop);
} else { } else {
const InfraredMessage* message = infrared_signal_get_message(signal); InfraredMessage* message = infrared_signal_get_message(signal);
uint8_t addr_digits = uint8_t addr_digits =
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4); ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4);
uint8_t cmd_digits = uint8_t cmd_digits =
@@ -56,7 +56,7 @@ void infrared_scene_learn_success_on_enter(void* context) {
} }
bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
const bool is_transmitter_idle = !infrared->app_state.is_transmitting; const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
bool consumed = false; bool consumed = false;
@@ -84,7 +84,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even
consumed = true; consumed = true;
} else if(event.event == DialogExPressCenter) { } else if(event.event == DialogExPressCenter) {
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
infrared_tx_start(infrared); infrared_tx_start_received(infrared);
consumed = true; consumed = true;
} else if(event.event == DialogExReleaseCenter) { } else if(event.event == DialogExReleaseCenter) {
infrared_tx_stop(infrared); infrared_tx_stop(infrared);
@@ -96,7 +96,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even
} }
void infrared_scene_learn_success_on_exit(void* context) { void infrared_scene_learn_success_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
dialog_ex_reset(infrared->dialog_ex); dialog_ex_reset(infrared->dialog_ex);
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
} }

View File

@@ -1,14 +1,14 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
typedef enum { typedef enum {
ButtonIndexLearn = -2, ButtonIndexPlus = -2,
ButtonIndexEdit = -1, ButtonIndexEdit = -1,
ButtonIndexNA = 0, ButtonIndexNA = 0,
} ButtonIndex; } ButtonIndex;
static void static void
infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) { infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) {
InfraredApp* infrared = context; Infrared* infrared = context;
uint16_t custom_type; uint16_t custom_type;
if(type == InputTypePress) { if(type == InputTypePress) {
@@ -26,15 +26,17 @@ static void
} }
void infrared_scene_remote_on_enter(void* context) { void infrared_scene_remote_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
InfraredRemote* remote = infrared->remote; InfraredRemote* remote = infrared->remote;
ButtonMenu* button_menu = infrared->button_menu; ButtonMenu* button_menu = infrared->button_menu;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) { size_t button_count = infrared_remote_get_button_count(remote);
for(size_t i = 0; i < button_count; ++i) {
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
button_menu_add_item( button_menu_add_item(
button_menu, button_menu,
infrared_remote_get_signal_name(remote, i), infrared_remote_button_get_name(button),
i, i,
infrared_scene_remote_button_menu_callback, infrared_scene_remote_button_menu_callback,
ButtonMenuItemTypeCommon, ButtonMenuItemTypeCommon,
@@ -44,7 +46,7 @@ void infrared_scene_remote_on_enter(void* context) {
button_menu_add_item( button_menu_add_item(
button_menu, button_menu,
"+", "+",
ButtonIndexLearn, ButtonIndexPlus,
infrared_scene_remote_button_menu_callback, infrared_scene_remote_button_menu_callback,
ButtonMenuItemTypeControl, ButtonMenuItemTypeControl,
context); context);
@@ -66,7 +68,7 @@ void infrared_scene_remote_on_enter(void* context) {
} }
bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
const bool is_transmitter_idle = !infrared->app_state.is_transmitting; const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
bool consumed = false; bool consumed = false;
@@ -95,7 +97,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
if(is_transmitter_idle) { if(is_transmitter_idle) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
scene_manager, InfraredSceneRemote, (unsigned)button_index); scene_manager, InfraredSceneRemote, (unsigned)button_index);
if(button_index == ButtonIndexLearn) { if(button_index == ButtonIndexPlus) {
infrared->app_state.is_learning_new_remote = false; infrared->app_state.is_learning_new_remote = false;
scene_manager_next_scene(scene_manager, InfraredSceneLearn); scene_manager_next_scene(scene_manager, InfraredSceneLearn);
consumed = true; consumed = true;
@@ -114,6 +116,6 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_remote_on_exit(void* context) { void infrared_scene_remote_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
button_menu_reset(infrared->button_menu); button_menu_reset(infrared->button_menu);
} }

View File

@@ -1,34 +1,31 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
void infrared_scene_remote_list_on_enter(void* context) { void infrared_scene_remote_list_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
ViewDispatcher* view_dispatcher = infrared->view_dispatcher; ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack);
DialogsFileBrowserOptions browser_options; DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px); dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px);
browser_options.base_path = INFRARED_APP_FOLDER; browser_options.base_path = INFRARED_APP_FOLDER;
while(dialog_file_browser_show( bool success = dialog_file_browser_show(
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options)) { infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options);
const char* file_path = furi_string_get_cstr(infrared->file_path);
if(success) {
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack);
infrared_show_loading_popup(infrared, true); infrared_show_loading_popup(infrared, true);
const bool remote_loaded = infrared_remote_load(infrared->remote, file_path); success = infrared_remote_load(infrared->remote, infrared->file_path);
infrared_show_loading_popup(infrared, false); infrared_show_loading_popup(infrared, false);
if(remote_loaded) {
scene_manager_next_scene(scene_manager, InfraredSceneRemote);
return;
} else {
infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path);
}
} }
scene_manager_previous_scene(scene_manager); if(success) {
scene_manager_next_scene(scene_manager, InfraredSceneRemote);
} else {
scene_manager_previous_scene(scene_manager);
}
} }
bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) {

View File

@@ -1,8 +1,6 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include <gui/canvas.h> #include <gui/canvas.h>
#define TAG "InfraredApp"
typedef enum { typedef enum {
InfraredRpcStateIdle, InfraredRpcStateIdle,
InfraredRpcStateLoaded, InfraredRpcStateLoaded,
@@ -10,7 +8,7 @@ typedef enum {
} InfraredRpcState; } InfraredRpcState;
void infrared_scene_rpc_on_enter(void* context) { void infrared_scene_rpc_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Popup* popup = infrared->popup; Popup* popup = infrared->popup;
popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom); popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom);
@@ -29,7 +27,7 @@ void infrared_scene_rpc_on_enter(void* context) {
} }
bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
@@ -40,11 +38,12 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
view_dispatcher_stop(infrared->view_dispatcher); view_dispatcher_stop(infrared->view_dispatcher);
} else if(event.event == InfraredCustomEventTypePopupClosed) { } else if(event.event == InfraredCustomEventTypePopupClosed) {
view_dispatcher_stop(infrared->view_dispatcher); view_dispatcher_stop(infrared->view_dispatcher);
} else if(event.event == InfraredCustomEventTypeRpcLoadFile) { } else if(event.event == InfraredCustomEventTypeRpcLoad) {
bool result = false; bool result = false;
if(state == InfraredRpcStateIdle) { const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
result = infrared_remote_load( if(arg && (state == InfraredRpcStateIdle)) {
infrared->remote, furi_string_get_cstr(infrared->file_path)); furi_string_set(infrared->file_path, arg);
result = infrared_remote_load(infrared->remote, infrared->file_path);
if(result) { if(result) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
@@ -56,35 +55,20 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
popup_set_text( popup_set_text(
infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop);
rpc_system_app_confirm(infrared->rpc_ctx, result); rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result);
} else if( } else if(event.event == InfraredCustomEventTypeRpcButtonPress) {
event.event == InfraredCustomEventTypeRpcButtonPressName ||
event.event == InfraredCustomEventTypeRpcButtonPressIndex) {
bool result = false; bool result = false;
if(state == InfraredRpcStateLoaded) { const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
if(event.event == InfraredCustomEventTypeRpcButtonPressName) { if(arg && (state == InfraredRpcStateLoaded)) {
const char* button_name = furi_string_get_cstr(infrared->button_name); size_t button_index = 0;
size_t index; if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) {
const bool index_found = infrared_tx_start_button_index(infrared, button_index);
infrared_remote_get_signal_index(infrared->remote, button_name, &index); result = true;
infrared->app_state.current_button_index =
index_found ? (signed)index : InfraredButtonIndexNone;
FURI_LOG_D(TAG, "Sending signal with name \"%s\"", button_name);
} else {
FURI_LOG_D(
TAG,
"Sending signal with index \"%ld\"",
infrared->app_state.current_button_index);
}
if(infrared->app_state.current_button_index != InfraredButtonIndexNone) {
infrared_tx_start_button_index(
infrared, infrared->app_state.current_button_index);
scene_manager_set_scene_state( scene_manager_set_scene_state(
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending); infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending);
result = true;
} }
} }
rpc_system_app_confirm(infrared->rpc_ctx, result); rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result);
} else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) {
bool result = false; bool result = false;
if(state == InfraredRpcStateSending) { if(state == InfraredRpcStateSending) {
@@ -93,11 +77,11 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
} }
rpc_system_app_confirm(infrared->rpc_ctx, result); rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result);
} else if(event.event == InfraredCustomEventTypeRpcExit) { } else if(event.event == InfraredCustomEventTypeRpcExit) {
scene_manager_stop(infrared->scene_manager); scene_manager_stop(infrared->scene_manager);
view_dispatcher_stop(infrared->view_dispatcher); view_dispatcher_stop(infrared->view_dispatcher);
rpc_system_app_confirm(infrared->rpc_ctx, true); rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true);
} else if(event.event == InfraredCustomEventTypeRpcSessionClose) { } else if(event.event == InfraredCustomEventTypeRpcSessionClose) {
scene_manager_stop(infrared->scene_manager); scene_manager_stop(infrared->scene_manager);
view_dispatcher_stop(infrared->view_dispatcher); view_dispatcher_stop(infrared->view_dispatcher);
@@ -107,7 +91,7 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_rpc_on_exit(void* context) { void infrared_scene_rpc_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) == if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) ==
InfraredRpcStateSending) { InfraredRpcStateSending) {
infrared_tx_stop(infrared); infrared_tx_stop(infrared);

View File

@@ -1,4 +1,4 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
enum SubmenuIndex { enum SubmenuIndex {
SubmenuIndexUniversalRemotes, SubmenuIndexUniversalRemotes,
@@ -10,12 +10,12 @@ enum SubmenuIndex {
}; };
static void infrared_scene_start_submenu_callback(void* context, uint32_t index) { static void infrared_scene_start_submenu_callback(void* context, uint32_t index) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index); view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
} }
void infrared_scene_start_on_enter(void* context) { void infrared_scene_start_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Submenu* submenu = infrared->submenu; Submenu* submenu = infrared->submenu;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
@@ -68,7 +68,7 @@ void infrared_scene_start_on_enter(void* context) {
} }
bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
@@ -107,6 +107,6 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_start_on_exit(void* context) { void infrared_scene_start_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
submenu_reset(infrared->submenu); submenu_reset(infrared->submenu);
} }

View File

@@ -1,4 +1,4 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
typedef enum { typedef enum {
SubmenuIndexUniversalTV, SubmenuIndexUniversalTV,
@@ -9,12 +9,12 @@ typedef enum {
} SubmenuIndex; } SubmenuIndex;
static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) { static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) {
InfraredApp* infrared = context; Infrared* infrared = context;
view_dispatcher_send_custom_event(infrared->view_dispatcher, index); view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
} }
void infrared_scene_universal_on_enter(void* context) { void infrared_scene_universal_on_enter(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
Submenu* submenu = infrared->submenu; Submenu* submenu = infrared->submenu;
submenu_add_item( submenu_add_item(
@@ -59,7 +59,7 @@ void infrared_scene_universal_on_enter(void* context) {
} }
bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
InfraredApp* infrared = context; Infrared* infrared = context;
SceneManager* scene_manager = infrared->scene_manager; SceneManager* scene_manager = infrared->scene_manager;
bool consumed = false; bool consumed = false;
@@ -87,6 +87,6 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
} }
void infrared_scene_universal_on_exit(void* context) { void infrared_scene_universal_on_exit(void* context) {
InfraredApp* infrared = context; Infrared* infrared = context;
submenu_reset(infrared->submenu); submenu_reset(infrared->submenu);
} }

View File

@@ -1,11 +1,11 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h" #include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_ac_on_enter(void* context) { void infrared_scene_universal_ac_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context); infrared_scene_universal_common_on_enter(context);
InfraredApp* infrared = context; Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel; ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force; InfraredBruteForce* brute_force = infrared->brute_force;

View File

@@ -1,11 +1,11 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h" #include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_audio_on_enter(void* context) { void infrared_scene_universal_audio_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context); infrared_scene_universal_common_on_enter(context);
InfraredApp* infrared = context; Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel; ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force; InfraredBruteForce* brute_force = infrared->brute_force;

View File

@@ -1,11 +1,11 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h" #include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_fan_on_enter(void* context) { void infrared_scene_universal_fan_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context); infrared_scene_universal_common_on_enter(context);
InfraredApp* infrared = context; Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel; ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force; InfraredBruteForce* brute_force = infrared->brute_force;

View File

@@ -1,11 +1,11 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h" #include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_projector_on_enter(void* context) { void infrared_scene_universal_projector_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context); infrared_scene_universal_common_on_enter(context);
InfraredApp* infrared = context; Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel; ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force; InfraredBruteForce* brute_force = infrared->brute_force;

View File

@@ -1,11 +1,11 @@
#include "../infrared_app_i.h" #include "../infrared_i.h"
#include "common/infrared_scene_universal_common.h" #include "common/infrared_scene_universal_common.h"
void infrared_scene_universal_tv_on_enter(void* context) { void infrared_scene_universal_tv_on_enter(void* context) {
infrared_scene_universal_common_on_enter(context); infrared_scene_universal_common_on_enter(context);
InfraredApp* infrared = context; Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel; ButtonPanel* button_panel = infrared->button_panel;
InfraredBruteForce* brute_force = infrared->brute_force; InfraredBruteForce* brute_force = infrared->brute_force;

View File

@@ -1,11 +1,10 @@
#include "infrared_move_view.h" #include "infrared_move_view.h"
#include <m-array.h>
#include <gui/canvas.h> #include <gui/canvas.h>
#include <gui/elements.h> #include <gui/elements.h>
#include <toolbox/m_cstr_dup.h> #include <stdlib.h>
#include <string.h>
#define LIST_ITEMS 4U #define LIST_ITEMS 4U
#define LIST_LINE_H 13U #define LIST_LINE_H 13U
@@ -14,41 +13,42 @@
struct InfraredMoveView { struct InfraredMoveView {
View* view; View* view;
InfraredMoveCallback callback; InfraredMoveCallback move_cb;
void* callback_context; void* cb_context;
}; };
ARRAY_DEF(InfraredMoveViewItemArray, const char*, M_CSTR_DUP_OPLIST); //-V575
typedef struct { typedef struct {
InfraredMoveViewItemArray_t labels; const char** btn_names;
uint32_t btn_number;
int32_t list_offset; int32_t list_offset;
int32_t current_idx; int32_t item_idx;
int32_t start_idx;
bool is_moving; bool is_moving;
InfraredMoveGetItemCallback get_item_cb;
} InfraredMoveViewModel; } InfraredMoveViewModel;
static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) { static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
InfraredMoveViewModel* model = _model; InfraredMoveViewModel* model = _model;
UNUSED(model);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontPrimary); canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned( elements_multiline_text_aligned(
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move"); canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move");
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels); bool show_scrollbar = model->btn_number > LIST_ITEMS;
const bool show_scrollbar = btn_number > LIST_ITEMS;
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
for(uint32_t i = 0; i < MIN(btn_number, LIST_ITEMS); i++) { for(uint32_t i = 0; i < MIN(model->btn_number, LIST_ITEMS); i++) {
int32_t idx = CLAMP((uint32_t)(i + model->list_offset), btn_number, 0U); int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->btn_number, 0u);
uint8_t x_offset = (model->is_moving && model->current_idx == idx) ? MOVE_X_OFFSET : 0; uint8_t x_offset = (model->is_moving && model->item_idx == idx) ? MOVE_X_OFFSET : 0;
uint8_t y_offset = HEADER_H + i * LIST_LINE_H; uint8_t y_offset = HEADER_H + i * LIST_LINE_H;
uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1); uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
if(model->current_idx == idx) { if(model->item_idx == idx) {
canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H); canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
@@ -60,12 +60,7 @@ static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1); canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1);
} }
canvas_draw_str_aligned( canvas_draw_str_aligned(
canvas, canvas, x_offset + 3, y_offset + 3, AlignLeft, AlignTop, model->btn_names[idx]);
x_offset + 3,
y_offset + 3,
AlignLeft,
AlignTop,
*InfraredMoveViewItemArray_cget(model->labels, idx));
} }
if(show_scrollbar) { if(show_scrollbar) {
@@ -74,22 +69,22 @@ static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
canvas_width(canvas), canvas_width(canvas),
HEADER_H, HEADER_H,
canvas_height(canvas) - HEADER_H, canvas_height(canvas) - HEADER_H,
model->current_idx, model->item_idx,
btn_number); model->btn_number);
} }
} }
static void update_list_offset(InfraredMoveViewModel* model) { static void update_list_offset(InfraredMoveViewModel* model) {
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels); int32_t bounds = model->btn_number > (LIST_ITEMS - 1) ? 2 : model->btn_number;
const int32_t bounds = btn_number > (LIST_ITEMS - 1) ? 2 : btn_number;
if((btn_number > (LIST_ITEMS - 1)) && (model->current_idx >= ((int32_t)btn_number - 1))) { if((model->btn_number > (LIST_ITEMS - 1)) &&
model->list_offset = model->current_idx - (LIST_ITEMS - 1); (model->item_idx >= ((int32_t)model->btn_number - 1))) {
} else if(model->list_offset < model->current_idx - bounds) { model->list_offset = model->item_idx - (LIST_ITEMS - 1);
model->list_offset = } else if(model->list_offset < model->item_idx - bounds) {
CLAMP(model->current_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)btn_number - bounds, 0); model->list_offset = CLAMP(
} else if(model->list_offset > model->current_idx - bounds) { model->item_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)model->btn_number - bounds, 0);
model->list_offset = CLAMP(model->current_idx - 1, (int32_t)btn_number - bounds, 0); } else if(model->list_offset > model->item_idx - bounds) {
model->list_offset = CLAMP(model->item_idx - 1, (int32_t)model->btn_number - bounds, 0);
} }
} }
@@ -100,130 +95,117 @@ static bool infrared_move_view_input_callback(InputEvent* event, void* context)
if(((event->type == InputTypeShort || event->type == InputTypeRepeat)) && if(((event->type == InputTypeShort || event->type == InputTypeRepeat)) &&
((event->key == InputKeyUp) || (event->key == InputKeyDown))) { ((event->key == InputKeyUp) || (event->key == InputKeyDown))) {
bool is_moving = false;
uint32_t index_old = 0;
uint32_t index_new = 0;
with_view_model( with_view_model(
move_view->view, move_view->view,
InfraredMoveViewModel * model, InfraredMoveViewModel * model,
{ {
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels); is_moving = model->is_moving;
const int32_t item_idx_prev = model->current_idx; index_old = model->item_idx;
if(event->key == InputKeyUp) { if(event->key == InputKeyUp) {
if(model->current_idx <= 0) { if(model->item_idx <= 0) {
model->current_idx = btn_number; model->item_idx = model->btn_number;
} }
model->current_idx--; model->item_idx--;
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
model->current_idx++; model->item_idx++;
if(model->current_idx >= (int32_t)(btn_number)) { if(model->item_idx >= (int32_t)(model->btn_number)) {
model->current_idx = 0; model->item_idx = 0;
} }
} }
index_new = model->item_idx;
if(model->is_moving) {
InfraredMoveViewItemArray_swap_at(
model->labels, item_idx_prev, model->current_idx);
}
update_list_offset(model); update_list_offset(model);
}, },
true); !is_moving);
if((is_moving) && (move_view->move_cb)) {
move_view->move_cb(index_old, index_new, move_view->cb_context);
infrared_move_view_list_update(move_view);
}
consumed = true; consumed = true;
} else if((event->key == InputKeyOk) && (event->type == InputTypeShort)) {
with_view_model(
move_view->view,
InfraredMoveViewModel * model,
{
if(!model->is_moving) {
model->start_idx = model->current_idx;
} else if(move_view->callback) {
move_view->callback(
model->start_idx, model->current_idx, move_view->callback_context);
}
model->is_moving = !(model->is_moving);
},
true);
consumed = true;
} else if(event->key == InputKeyBack) {
with_view_model(
move_view->view,
InfraredMoveViewModel * model,
{
if(model->is_moving && move_view->callback) {
move_view->callback(
model->start_idx, model->current_idx, move_view->callback_context);
}
model->is_moving = false;
},
false);
// Not consuming, Back event is passed thru
} }
if((event->key == InputKeyOk) && (event->type == InputTypeShort)) {
with_view_model(
move_view->view,
InfraredMoveViewModel * model,
{ model->is_moving = !(model->is_moving); },
true);
consumed = true;
}
return consumed; return consumed;
} }
void infrared_move_view_set_callback( static void infrared_move_view_on_exit(void* context) {
InfraredMoveView* move_view, furi_assert(context);
InfraredMoveCallback callback, InfraredMoveView* move_view = context;
void* context) {
furi_assert(move_view);
move_view->callback = callback;
move_view->callback_context = context;
}
void infrared_move_view_add_item(InfraredMoveView* move_view, const char* label) {
with_view_model(
move_view->view,
InfraredMoveViewModel * model,
{ InfraredMoveViewItemArray_push_back(model->labels, label); },
true);
}
void infrared_move_view_reset(InfraredMoveView* move_view) {
with_view_model( with_view_model(
move_view->view, move_view->view,
InfraredMoveViewModel * model, InfraredMoveViewModel * model,
{ {
InfraredMoveViewItemArray_reset(model->labels); if(model->btn_names) {
model->list_offset = 0; free(model->btn_names);
model->start_idx = 0; model->btn_names = NULL;
model->current_idx = 0; }
model->is_moving = false; model->btn_number = 0;
model->get_item_cb = NULL;
}, },
false); false);
move_view->callback_context = NULL; move_view->cb_context = NULL;
}
void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback) {
furi_assert(move_view);
move_view->move_cb = callback;
}
void infrared_move_view_list_init(
InfraredMoveView* move_view,
uint32_t item_count,
InfraredMoveGetItemCallback load_cb,
void* context) {
furi_assert(move_view);
move_view->cb_context = context;
with_view_model(
move_view->view,
InfraredMoveViewModel * model,
{
furi_assert(model->btn_names == NULL);
model->btn_names = malloc(sizeof(char*) * item_count);
model->btn_number = item_count;
model->get_item_cb = load_cb;
},
false);
}
void infrared_move_view_list_update(InfraredMoveView* move_view) {
furi_assert(move_view);
with_view_model(
move_view->view,
InfraredMoveViewModel * model,
{
for(uint32_t i = 0; i < model->btn_number; i++) {
if(!model->get_item_cb) break;
model->btn_names[i] = model->get_item_cb(i, move_view->cb_context);
}
},
true);
} }
InfraredMoveView* infrared_move_view_alloc(void) { InfraredMoveView* infrared_move_view_alloc(void) {
InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView)); InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView));
move_view->view = view_alloc(); move_view->view = view_alloc();
view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel)); view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel));
view_set_draw_callback(move_view->view, infrared_move_view_draw_callback); view_set_draw_callback(move_view->view, infrared_move_view_draw_callback);
view_set_input_callback(move_view->view, infrared_move_view_input_callback); view_set_input_callback(move_view->view, infrared_move_view_input_callback);
view_set_exit_callback(move_view->view, infrared_move_view_on_exit);
view_set_context(move_view->view, move_view); view_set_context(move_view->view, move_view);
with_view_model(
move_view->view,
InfraredMoveViewModel * model,
{ InfraredMoveViewItemArray_init(model->labels); },
true);
return move_view; return move_view;
} }
void infrared_move_view_free(InfraredMoveView* move_view) { void infrared_move_view_free(InfraredMoveView* move_view) {
with_view_model(
move_view->view,
InfraredMoveViewModel * model,
{ InfraredMoveViewItemArray_clear(model->labels); },
true);
view_free(move_view->view); view_free(move_view->view);
free(move_view); free(move_view);
} }

View File

@@ -6,17 +6,20 @@ typedef struct InfraredMoveView InfraredMoveView;
typedef void (*InfraredMoveCallback)(uint32_t index_old, uint32_t index_new, void* context); typedef void (*InfraredMoveCallback)(uint32_t index_old, uint32_t index_new, void* context);
typedef const char* (*InfraredMoveGetItemCallback)(uint32_t index, void* context);
InfraredMoveView* infrared_move_view_alloc(void); InfraredMoveView* infrared_move_view_alloc(void);
void infrared_move_view_free(InfraredMoveView* debug_view); void infrared_move_view_free(InfraredMoveView* debug_view);
View* infrared_move_view_get_view(InfraredMoveView* debug_view); View* infrared_move_view_get_view(InfraredMoveView* debug_view);
void infrared_move_view_set_callback( void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback);
void infrared_move_view_list_init(
InfraredMoveView* move_view, InfraredMoveView* move_view,
InfraredMoveCallback callback, uint32_t item_count,
InfraredMoveGetItemCallback load_cb,
void* context); void* context);
void infrared_move_view_add_item(InfraredMoveView* move_view, const char* label); void infrared_move_view_list_update(InfraredMoveView* move_view);
void infrared_move_view_reset(InfraredMoveView* move_view);

View File

@@ -17,6 +17,5 @@ App(
targets=["f7"], targets=["f7"],
apptype=FlipperAppType.STARTUP, apptype=FlipperAppType.STARTUP,
entry_point="lfrfid_on_system_start", entry_point="lfrfid_on_system_start",
sources=["lfrfid_cli.c"],
order=50, order=50,
) )

View File

@@ -13,23 +13,21 @@ static bool lfrfid_debug_back_event_callback(void* context) {
return scene_manager_handle_back_event(app->scene_manager); return scene_manager_handle_back_event(app->scene_manager);
} }
static void rpc_command_callback(const RpcAppSystemEvent* event, void* context) { static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
furi_assert(context); furi_assert(context);
LfRfid* app = (LfRfid*)context; LfRfid* app = (LfRfid*)context;
if(event->type == RpcAppEventTypeSessionClose) { if(rpc_event == RpcAppEventSessionClose) {
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose); view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose);
// Detach RPC // Detach RPC
rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL);
app->rpc_ctx = NULL; app->rpc_ctx = NULL;
} else if(event->type == RpcAppEventTypeAppExit) { } else if(rpc_event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit); view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit);
} else if(event->type == RpcAppEventTypeLoadFile) { } else if(rpc_event == RpcAppEventLoadFile) {
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
furi_string_set(app->file_path, event->data.string);
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile); view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile);
} else { } else {
rpc_system_app_confirm(app->rpc_ctx, false); rpc_system_app_confirm(app->rpc_ctx, rpc_event, false);
} }
} }

View File

@@ -24,15 +24,17 @@ bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
consumed = true; consumed = true;
if(event.event == LfRfidEventExit) { if(event.event == LfRfidEventExit) {
rpc_system_app_confirm(app->rpc_ctx, true); rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true);
scene_manager_stop(app->scene_manager); scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher); view_dispatcher_stop(app->view_dispatcher);
} else if(event.event == LfRfidEventRpcSessionClose) { } else if(event.event == LfRfidEventRpcSessionClose) {
scene_manager_stop(app->scene_manager); scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher); view_dispatcher_stop(app->view_dispatcher);
} else if(event.event == LfRfidEventRpcLoadFile) { } else if(event.event == LfRfidEventRpcLoadFile) {
const char* arg = rpc_system_app_get_data(app->rpc_ctx);
bool result = false; bool result = false;
if(app->rpc_state == LfRfidRpcStateIdle) { if(arg && (app->rpc_state == LfRfidRpcStateIdle)) {
furi_string_set(app->file_path, arg);
if(lfrfid_load_key_data(app, app->file_path, false)) { if(lfrfid_load_key_data(app, app->file_path, false)) {
lfrfid_worker_start_thread(app->lfworker); lfrfid_worker_start_thread(app->lfworker);
lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);
@@ -46,7 +48,7 @@ bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) {
result = true; result = true;
} }
} }
rpc_system_app_confirm(app->rpc_ctx, result); rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result);
} }
} }
return consumed; return consumed;

View File

@@ -7,78 +7,15 @@ App(
icon="A_NFC_14", icon="A_NFC_14",
stack_size=5 * 1024, stack_size=5 * 1024,
order=30, order=30,
resources="resources",
sources=[
"*.c",
"!plugins",
"!nfc_cli.c",
],
fap_libs=["assets"], fap_libs=["assets"],
fap_icon="icon.png", fap_icon="icon.png",
fap_category="NFC", fap_category="NFC",
) )
# Parser plugins
App(
appid="all_in_one_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="all_in_one_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/all_in_one.c"],
)
App(
appid="opal_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="opal_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/opal.c"],
)
App(
appid="myki_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="myki_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/myki.c"],
)
App(
appid="troika_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="troika_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/troika.c"],
)
App(
appid="plantain_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="plantain_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/plantain.c"],
)
App(
appid="two_cities_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="two_cities_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/two_cities.c"],
)
App( App(
appid="nfc_start", appid="nfc_start",
targets=["f7"], targets=["f7"],
apptype=FlipperAppType.STARTUP, apptype=FlipperAppType.STARTUP,
entry_point="nfc_on_system_start", entry_point="nfc_on_system_start",
sources=["nfc_cli.c"],
order=30, order=30,
) )

View File

@@ -1,212 +0,0 @@
#include "mf_classic_key_cache.h"
#include <furi/furi.h>
#include <storage/storage.h>
#define NFC_APP_KEYS_EXTENSION ".keys"
#define NFC_APP_KEY_CACHE_FOLDER "/ext/nfc/.cache"
static const char* mf_classic_key_cache_file_header = "Flipper NFC keys";
static const uint32_t mf_classic_key_cache_file_version = 1;
struct MfClassicKeyCache {
MfClassicDeviceKeys keys;
MfClassicKeyType current_key_type;
uint8_t current_sector;
};
static void nfc_get_key_cache_file_path(const uint8_t* uid, size_t uid_len, FuriString* path) {
furi_string_printf(path, "%s/", NFC_APP_KEY_CACHE_FOLDER);
for(size_t i = 0; i < uid_len; i++) {
furi_string_cat_printf(path, "%02X", uid[i]);
}
furi_string_cat_printf(path, "%s", NFC_APP_KEYS_EXTENSION);
}
MfClassicKeyCache* mf_classic_key_cache_alloc() {
MfClassicKeyCache* instance = malloc(sizeof(MfClassicKeyCache));
return instance;
}
void mf_classic_key_cache_free(MfClassicKeyCache* instance) {
furi_assert(instance);
free(instance);
}
bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data) {
UNUSED(instance);
furi_assert(data);
size_t uid_len = 0;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
FuriString* file_path = furi_string_alloc();
nfc_get_key_cache_file_path(uid, uid_len, file_path);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
FuriString* temp_str = furi_string_alloc();
bool save_success = false;
do {
if(!storage_simply_mkdir(storage, NFC_APP_KEY_CACHE_FOLDER)) break;
if(!storage_simply_remove(storage, furi_string_get_cstr(file_path))) break;
if(!flipper_format_buffered_file_open_always(ff, furi_string_get_cstr(file_path))) break;
if(!flipper_format_write_header_cstr(
ff, mf_classic_key_cache_file_header, mf_classic_key_cache_file_version))
break;
if(!flipper_format_write_string_cstr(
ff, "Mifare Classic type", mf_classic_get_device_name(data, NfcDeviceNameTypeShort)))
break;
if(!flipper_format_write_hex_uint64(ff, "Key A map", &data->key_a_mask, 1)) break;
if(!flipper_format_write_hex_uint64(ff, "Key B map", &data->key_b_mask, 1)) break;
uint8_t sector_num = mf_classic_get_total_sectors_num(data->type);
bool key_save_success = true;
for(size_t i = 0; (i < sector_num) && (key_save_success); i++) {
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i);
if(FURI_BIT(data->key_a_mask, i)) {
furi_string_printf(temp_str, "Key A sector %d", i);
key_save_success = flipper_format_write_hex(
ff, furi_string_get_cstr(temp_str), sec_tr->key_a.data, sizeof(MfClassicKey));
}
if(!key_save_success) break;
if(FURI_BIT(data->key_b_mask, i)) {
furi_string_printf(temp_str, "Key B sector %d", i);
key_save_success = flipper_format_write_hex(
ff, furi_string_get_cstr(temp_str), sec_tr->key_b.data, sizeof(MfClassicKey));
}
}
save_success = key_save_success;
} while(false);
flipper_format_free(ff);
furi_string_free(temp_str);
furi_string_free(file_path);
furi_record_close(RECORD_STORAGE);
return save_success;
}
bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len) {
furi_assert(instance);
furi_assert(uid);
mf_classic_key_cache_reset(instance);
FuriString* file_path = furi_string_alloc();
nfc_get_key_cache_file_path(uid, uid_len, file_path);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
FuriString* temp_str = furi_string_alloc();
bool load_success = false;
do {
if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(file_path))) break;
uint32_t version = 0;
if(!flipper_format_read_header(ff, temp_str, &version)) break;
if(furi_string_cmp_str(temp_str, mf_classic_key_cache_file_header)) break;
if(version != mf_classic_key_cache_file_version) break;
if(!flipper_format_read_hex_uint64(ff, "Key A map", &instance->keys.key_a_mask, 1)) break;
if(!flipper_format_read_hex_uint64(ff, "Key B map", &instance->keys.key_b_mask, 1)) break;
bool key_read_success = true;
for(size_t i = 0; (i < MF_CLASSIC_TOTAL_SECTORS_MAX) && (key_read_success); i++) {
if(FURI_BIT(instance->keys.key_a_mask, i)) {
furi_string_printf(temp_str, "Key A sector %d", i);
key_read_success = flipper_format_read_hex(
ff,
furi_string_get_cstr(temp_str),
instance->keys.key_a[i].data,
sizeof(MfClassicKey));
}
if(!key_read_success) break;
if(FURI_BIT(instance->keys.key_b_mask, i)) {
furi_string_printf(temp_str, "Key B sector %d", i);
key_read_success = flipper_format_read_hex(
ff,
furi_string_get_cstr(temp_str),
instance->keys.key_b[i].data,
sizeof(MfClassicKey));
}
}
load_success = key_read_success;
} while(false);
flipper_format_buffered_file_close(ff);
flipper_format_free(ff);
furi_string_free(temp_str);
furi_string_free(file_path);
furi_record_close(RECORD_STORAGE);
return load_success;
}
void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data) {
furi_assert(instance);
furi_assert(data);
mf_classic_key_cache_reset(instance);
instance->keys.key_a_mask = data->key_a_mask;
instance->keys.key_b_mask = data->key_b_mask;
for(size_t i = 0; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) {
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i);
if(FURI_BIT(data->key_a_mask, i)) {
instance->keys.key_a[i] = sec_tr->key_a;
}
if(FURI_BIT(data->key_b_mask, i)) {
instance->keys.key_b[i] = sec_tr->key_b;
}
}
}
bool mf_classic_key_cahce_get_next_key(
MfClassicKeyCache* instance,
uint8_t* sector_num,
MfClassicKey* key,
MfClassicKeyType* key_type) {
furi_assert(instance);
furi_assert(sector_num);
furi_assert(key);
furi_assert(key_type);
bool next_key_found = false;
for(uint8_t i = instance->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) {
if(FURI_BIT(instance->keys.key_a_mask, i)) {
FURI_BIT_CLEAR(instance->keys.key_a_mask, i);
*key = instance->keys.key_a[i];
*key_type = MfClassicKeyTypeA;
*sector_num = i;
next_key_found = true;
break;
}
if(FURI_BIT(instance->keys.key_b_mask, i)) {
FURI_BIT_CLEAR(instance->keys.key_b_mask, i);
*key = instance->keys.key_b[i];
*key_type = MfClassicKeyTypeB;
*sector_num = i;
next_key_found = true;
instance->current_sector = i;
break;
}
}
return next_key_found;
}
void mf_classic_key_cache_reset(MfClassicKeyCache* instance) {
furi_assert(instance);
instance->current_key_type = MfClassicKeyTypeA;
instance->current_sector = 0;
instance->keys.key_a_mask = 0;
instance->keys.key_b_mask = 0;
}

View File

@@ -1,31 +0,0 @@
#pragma once
#include <nfc/protocols/mf_classic/mf_classic.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct MfClassicKeyCache MfClassicKeyCache;
MfClassicKeyCache* mf_classic_key_cache_alloc();
void mf_classic_key_cache_free(MfClassicKeyCache* instance);
bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len);
void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data);
bool mf_classic_key_cahce_get_next_key(
MfClassicKeyCache* instance,
uint8_t* sector_num,
MfClassicKey* key,
MfClassicKeyType* key_type);
bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data);
void mf_classic_key_cache_reset(MfClassicKeyCache* instance);
#ifdef __cplusplus
}
#endif

View File

@@ -1,58 +0,0 @@
#include "mf_ultralight_auth.h"
#include <furi.h>
#include <mbedtls/sha1.h>
MfUltralightAuth* mf_ultralight_auth_alloc() {
MfUltralightAuth* instance = malloc(sizeof(MfUltralightAuth));
return instance;
}
void mf_ultralight_auth_free(MfUltralightAuth* instance) {
furi_assert(instance);
free(instance);
}
void mf_ultralight_auth_reset(MfUltralightAuth* instance) {
furi_assert(instance);
instance->type = MfUltralightAuthTypeNone;
memset(&instance->password, 0, sizeof(MfUltralightAuthPassword));
memset(&instance->pack, 0, sizeof(MfUltralightAuthPack));
}
bool mf_ultralight_generate_amiibo_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len) {
furi_assert(instance);
furi_assert(uid);
bool generated = false;
if(uid_len == 7) {
instance->password.data[0] = uid[1] ^ uid[3] ^ 0xAA;
instance->password.data[1] = uid[2] ^ uid[4] ^ 0x55;
instance->password.data[2] = uid[3] ^ uid[5] ^ 0xAA;
instance->password.data[3] = uid[4] ^ uid[6] ^ 0x55;
generated = true;
}
return generated;
}
bool mf_ultralight_generate_xiaomi_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len) {
furi_assert(instance);
furi_assert(uid);
uint8_t hash[20];
bool generated = false;
if(uid_len == 7) {
mbedtls_sha1(uid, uid_len, hash);
instance->password.data[0] = (hash[hash[0] % 20]);
instance->password.data[1] = (hash[(hash[0] + 5) % 20]);
instance->password.data[2] = (hash[(hash[0] + 13) % 20]);
instance->password.data[3] = (hash[(hash[0] + 17) % 20]);
generated = true;
}
return generated;
}

View File

@@ -1,35 +0,0 @@
#pragma once
#include <lib/nfc/protocols/mf_ultralight/mf_ultralight.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MfUltralightAuthTypeNone,
MfUltralightAuthTypeReader,
MfUltralightAuthTypeManual,
MfUltralightAuthTypeXiaomi,
MfUltralightAuthTypeAmiibo,
} MfUltralightAuthType;
typedef struct {
MfUltralightAuthType type;
MfUltralightAuthPassword password;
MfUltralightAuthPack pack;
} MfUltralightAuth;
MfUltralightAuth* mf_ultralight_auth_alloc();
void mf_ultralight_auth_free(MfUltralightAuth* instance);
void mf_ultralight_auth_reset(MfUltralightAuth* instance);
bool mf_ultralight_generate_amiibo_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len);
bool mf_ultralight_generate_xiaomi_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len);
#ifdef __cplusplus
}
#endif

View File

@@ -1,83 +0,0 @@
#include "mf_user_dict.h"
#include <nfc/helpers/nfc_dict.h>
#include <nfc/protocols/mf_classic/mf_classic.h>
#include <furi/furi.h>
#define NFC_APP_FOLDER ANY_PATH("nfc")
#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc")
struct MfUserDict {
size_t keys_num;
MfClassicKey* keys_arr;
};
MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) {
MfUserDict* instance = malloc(sizeof(MfUserDict));
NfcDict* dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict);
size_t dict_keys_num = nfc_dict_get_total_keys(dict);
instance->keys_num = MIN(max_keys_to_load, dict_keys_num);
if(instance->keys_num > 0) {
instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey));
for(size_t i = 0; i < instance->keys_num; i++) {
bool key_loaded =
nfc_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey));
furi_assert(key_loaded);
}
}
nfc_dict_free(dict);
return instance;
}
void mf_user_dict_free(MfUserDict* instance) {
furi_assert(instance);
if(instance->keys_num > 0) {
free(instance->keys_arr);
}
free(instance);
}
size_t mf_user_dict_get_keys_cnt(MfUserDict* instance) {
furi_assert(instance);
return instance->keys_num;
}
void mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str) {
furi_assert(instance);
furi_assert(str);
furi_assert(index < instance->keys_num);
furi_assert(instance->keys_arr);
furi_string_reset(str);
for(size_t i = 0; i < sizeof(MfClassicKey); i++) {
furi_string_cat_printf(str, "%02X", instance->keys_arr[index].data[i]);
}
}
bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) {
furi_assert(instance);
furi_assert(index < instance->keys_num);
furi_assert(instance->keys_arr);
NfcDict* dict = nfc_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict);
bool key_delete_success =
nfc_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey));
nfc_dict_free(dict);
if(key_delete_success) {
instance->keys_num--;
}
return key_delete_success;
}

View File

@@ -1,23 +0,0 @@
#pragma once
#include <furi/core/string.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct MfUserDict MfUserDict;
MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load);
void mf_user_dict_free(MfUserDict* instance);
size_t mf_user_dict_get_keys_cnt(MfUserDict* instance);
void mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str);
bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index);
#ifdef __cplusplus
}
#endif

View File

@@ -1,173 +0,0 @@
#include "mfkey32_logger.h"
#include <m-array.h>
#include <nfc/helpers/nfc_util.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#define MFKEY32_LOGGER_MAX_NONCES_SAVED (100)
typedef struct {
bool is_filled;
uint32_t cuid;
uint8_t sector_num;
MfClassicKeyType key_type;
uint32_t nt0;
uint32_t nr0;
uint32_t ar0;
uint32_t nt1;
uint32_t nr1;
uint32_t ar1;
} Mfkey32LoggerParams;
ARRAY_DEF(Mfkey32LoggerParams, Mfkey32LoggerParams, M_POD_OPLIST);
struct Mfkey32Logger {
uint32_t cuid;
Mfkey32LoggerParams_t params_arr;
size_t nonces_saves;
size_t params_collected;
};
Mfkey32Logger* mfkey32_logger_alloc(uint32_t cuid) {
Mfkey32Logger* instance = malloc(sizeof(Mfkey32Logger));
instance->cuid = cuid;
Mfkey32LoggerParams_init(instance->params_arr);
return instance;
}
void mfkey32_logger_free(Mfkey32Logger* instance) {
furi_assert(instance);
furi_assert(instance->params_arr);
Mfkey32LoggerParams_clear(instance->params_arr);
free(instance);
}
static bool mfkey32_logger_add_nonce_to_existing_params(
Mfkey32Logger* instance,
MfClassicAuthContext* auth_context) {
bool nonce_added = false;
do {
if(Mfkey32LoggerParams_size(instance->params_arr) == 0) break;
Mfkey32LoggerParams_it_t it;
for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it);
Mfkey32LoggerParams_next(it)) {
Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it);
if(params->is_filled) continue;
uint8_t sector_num = mf_classic_get_sector_by_block(auth_context->block_num);
if(params->sector_num != sector_num) continue;
if(params->key_type != auth_context->key_type) continue;
params->nt1 = nfc_util_bytes2num(auth_context->nt.data, sizeof(MfClassicNt));
params->nr1 = nfc_util_bytes2num(auth_context->nr.data, sizeof(MfClassicNr));
params->ar1 = nfc_util_bytes2num(auth_context->ar.data, sizeof(MfClassicAr));
params->is_filled = true;
instance->params_collected++;
nonce_added = true;
break;
}
} while(false);
return nonce_added;
}
void mfkey32_logger_add_nonce(Mfkey32Logger* instance, MfClassicAuthContext* auth_context) {
furi_assert(instance);
furi_assert(auth_context);
bool nonce_added = mfkey32_logger_add_nonce_to_existing_params(instance, auth_context);
if(!nonce_added && (instance->nonces_saves < MFKEY32_LOGGER_MAX_NONCES_SAVED)) {
uint8_t sector_num = mf_classic_get_sector_by_block(auth_context->block_num);
Mfkey32LoggerParams params = {
.is_filled = false,
.cuid = instance->cuid,
.sector_num = sector_num,
.key_type = auth_context->key_type,
.nt0 = nfc_util_bytes2num(auth_context->nt.data, sizeof(MfClassicNt)),
.nr0 = nfc_util_bytes2num(auth_context->nr.data, sizeof(MfClassicNr)),
.ar0 = nfc_util_bytes2num(auth_context->ar.data, sizeof(MfClassicAr)),
};
Mfkey32LoggerParams_push_back(instance->params_arr, params);
instance->nonces_saves++;
}
}
size_t mfkey32_logger_get_params_num(Mfkey32Logger* instance) {
furi_assert(instance);
return instance->params_collected;
}
bool mfkey32_logger_save_params(Mfkey32Logger* instance, const char* path) {
furi_assert(instance);
furi_assert(path);
furi_assert(instance->params_collected > 0);
furi_assert(instance->params_arr);
bool params_saved = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* stream = buffered_file_stream_alloc(storage);
FuriString* temp_str = furi_string_alloc();
do {
if(!buffered_file_stream_open(stream, path, FSAM_WRITE, FSOM_OPEN_APPEND)) break;
bool params_write_success = true;
Mfkey32LoggerParams_it_t it;
for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it);
Mfkey32LoggerParams_next(it)) {
Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it);
if(!params->is_filled) continue;
furi_string_printf(
temp_str,
"Sec %d key %c cuid %08lx nt0 %08lx nr0 %08lx ar0 %08lx nt1 %08lx nr1 %08lx ar1 %08lx\n",
params->sector_num,
params->key_type == MfClassicKeyTypeA ? 'A' : 'B',
params->cuid,
params->nt0,
params->nr0,
params->ar0,
params->nt1,
params->nr1,
params->ar1);
if(!stream_write_string(stream, temp_str)) {
params_write_success = false;
break;
}
}
if(!params_write_success) break;
params_saved = true;
} while(false);
furi_string_free(temp_str);
buffered_file_stream_close(stream);
stream_free(stream);
furi_record_close(RECORD_STORAGE);
return params_saved;
}
void mfkey32_logger_get_params_data(Mfkey32Logger* instance, FuriString* str) {
furi_assert(instance);
furi_assert(str);
furi_assert(instance->params_collected > 0);
furi_string_reset(str);
Mfkey32LoggerParams_it_t it;
for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it);
Mfkey32LoggerParams_next(it)) {
Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it);
if(!params->is_filled) continue;
char key_char = params->key_type == MfClassicKeyTypeA ? 'A' : 'B';
furi_string_cat_printf(str, "Sector %d, key %c\n", params->sector_num, key_char);
}
}

View File

@@ -1,25 +0,0 @@
#pragma once
#include <nfc/protocols/mf_classic/mf_classic.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Mfkey32Logger Mfkey32Logger;
Mfkey32Logger* mfkey32_logger_alloc(uint32_t cuid);
void mfkey32_logger_free(Mfkey32Logger* instance);
void mfkey32_logger_add_nonce(Mfkey32Logger* instance, MfClassicAuthContext* auth_context);
size_t mfkey32_logger_get_params_num(Mfkey32Logger* instance);
bool mfkey32_logger_save_params(Mfkey32Logger* instance, const char* path);
void mfkey32_logger_get_params_data(Mfkey32Logger* instance, FuriString* str);
#ifdef __cplusplus
}
#endif

View File

@@ -1,33 +1,17 @@
#pragma once #pragma once
typedef enum { enum NfcCustomEvent {
// Reserve first 100 events for button types and indexes, starting from 0 // Reserve first 100 events for button types and indexes, starting from 0
NfcCustomEventReserved = 100, NfcCustomEventReserved = 100,
// Mf classic dict attack events
NfcCustomEventDictAttackComplete,
NfcCustomEventDictAttackSkip,
NfcCustomEventDictAttackDataUpdate,
NfcCustomEventCardDetected,
NfcCustomEventCardLost,
NfcCustomEventViewExit, NfcCustomEventViewExit,
NfcCustomEventWorkerExit, NfcCustomEventWorkerExit,
NfcCustomEventWorkerUpdate,
NfcCustomEventWrongCard,
NfcCustomEventTimerExpired,
NfcCustomEventByteInputDone, NfcCustomEventByteInputDone,
NfcCustomEventTextInputDone, NfcCustomEventTextInputDone,
NfcCustomEventDictAttackDone, NfcCustomEventDictAttackDone,
NfcCustomEventDictAttackSkip,
NfcCustomEventRpcLoadFile, NfcCustomEventRpcLoad,
NfcCustomEventRpcExit,
NfcCustomEventRpcSessionClose, NfcCustomEventRpcSessionClose,
NfcCustomEventUpdateLog,
NfcCustomEventPollerSuccess, NfcCustomEventSaveShadow,
NfcCustomEventPollerIncomplete, };
NfcCustomEventPollerFailure,
NfcCustomEventListenerUpdate,
} NfcCustomEvent;

View File

@@ -1,147 +0,0 @@
#include "nfc_supported_cards.h"
#include "../plugins/supported_cards/nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h>
#include <flipper_application/plugins/plugin_manager.h>
#include <loader/firmware_api/firmware_api.h>
#include <furi.h>
#include <path.h>
#define TAG "NfcSupportedCards"
#define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins")
#define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal"
typedef struct {
Storage* storage;
File* directory;
FuriString* file_path;
char file_name[256];
FlipperApplication* app;
} NfcSupportedCards;
static NfcSupportedCards* nfc_supported_cards_alloc() {
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
instance->storage = furi_record_open(RECORD_STORAGE);
instance->directory = storage_file_alloc(instance->storage);
instance->file_path = furi_string_alloc();
if(!storage_dir_open(instance->directory, NFC_SUPPORTED_CARDS_PLUGINS_PATH)) {
FURI_LOG_D(TAG, "Failed to open directory: %s", NFC_SUPPORTED_CARDS_PLUGINS_PATH);
}
return instance;
}
static void nfc_supported_cards_free(NfcSupportedCards* instance) {
if(instance->app) {
flipper_application_free(instance->app);
}
furi_string_free(instance->file_path);
storage_dir_close(instance->directory);
storage_file_free(instance->directory);
furi_record_close(RECORD_STORAGE);
free(instance);
}
static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCards* instance) {
const NfcSupportedCardsPlugin* plugin = NULL;
do {
if(!storage_file_is_open(instance->directory)) break;
if(!storage_dir_read(
instance->directory, NULL, instance->file_name, sizeof(instance->file_name)))
break;
furi_string_set(instance->file_path, instance->file_name);
if(!furi_string_end_with_str(instance->file_path, NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX))
continue;
path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path);
if(instance->app) flipper_application_free(instance->app);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
if(flipper_application_preload(instance->app, furi_string_get_cstr(instance->file_path)) !=
FlipperApplicationPreloadStatusSuccess)
continue;
if(!flipper_application_is_plugin(instance->app)) continue;
if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
continue;
const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);
if(descriptor == NULL) continue;
if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) continue;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) continue;
plugin = descriptor->entry_point;
} while(plugin == NULL); //-V654
return plugin;
}
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc) {
furi_assert(device);
furi_assert(nfc);
bool card_read = false;
NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
do {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547
const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779
if(plugin->protocol != protocol) continue;
if(plugin->verify) {
if(!plugin->verify(nfc)) continue;
}
if(plugin->read) {
card_read = plugin->read(nfc, device);
}
} while(!card_read);
nfc_supported_cards_free(supported_cards);
return card_read;
}
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
bool parsed = false;
NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
do {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547
const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779
if(plugin->protocol != protocol) continue;
if(plugin->parse) {
parsed = plugin->parse(device, parsed_data);
}
} while(!parsed);
nfc_supported_cards_free(supported_cards);
return parsed;
}

View File

@@ -1,50 +0,0 @@
/**
* @file nfc_supported_cards.h
* @brief Supported card plugin loader interface.
*
* @see nfc_supported_card_plugin.h for instructions on adding a new plugin.
*/
#pragma once
#include <core/string.h>
#include <nfc/nfc.h>
#include <nfc/nfc_device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Read the card using a custom procedure.
*
* This function will load all suitable supported card plugins one by one and
* try to execute the custom read procedure specified in each. Upon first success,
* no further attempts will be made and the function will return.
*
* @param[in,out] device pointer to a device instance to hold the read data.
* @param[in,out] nfc pointer to an Nfc instance.
* @returns true if the card was successfully read, false otherwise.
*
* @see NfcSupportedCardPluginRead for detailed description.
*/
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc);
/**
* @brief Parse raw data into human-readable representation.
*
* This function will load all suitable supported card plugins one by one and
* try to parse the data according to each implementation. Upon first success,
* no further attempts will be made and the function will return.
*
* @param[in] device pointer to a device instance holding the data is to be parsed.
* @param[out] parsed_data pointer to the string to contain the formatted result.
* @returns true if the card was successfully parsed, false otherwise.
*
* @see NfcSupportedCardPluginParse for detailed description.
*/
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data);
#ifdef __cplusplus
}
#endif

View File

@@ -1,108 +0,0 @@
#include "felica.h"
#include "felica_render.h"
#include <nfc/protocols/felica/felica_poller.h>
#include "nfc/nfc_app_i.h"
#include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h"
static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolFelica);
NfcApp* instance = context;
const FelicaPollerEvent* felica_event = event.event_data;
if(felica_event->type == FelicaPollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
return NfcCommandStop;
}
return NfcCommandContinue;
}
static void nfc_scene_read_on_enter_felica(NfcApp* instance) {
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance);
}
static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t event) {
if(event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
return true;
}
return false;
}
const NfcProtocolSupportBase nfc_protocol_support_felica = {
.features = NfcProtocolFeatureNone,
.scene_info =
{
.on_enter = nfc_scene_info_on_enter_felica,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read =
{
.on_enter = nfc_scene_read_on_enter_felica,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_success =
{
.on_enter = nfc_scene_read_success_on_enter_felica,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_saved_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_saved_menu_on_event_felica,
},
.scene_save_name =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_emulate =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
};

View File

@@ -1,5 +0,0 @@
#pragma once
#include "../nfc_protocol_support_base.h"
extern const NfcProtocolSupportBase nfc_protocol_support_felica;

View File

@@ -1,19 +0,0 @@
#include "felica_render.h"
void nfc_render_felica_info(
const FelicaData* data,
NfcProtocolFormatType format_type,
FuriString* str) {
furi_string_cat_printf(str, "IDm:");
for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {
furi_string_cat_printf(str, " %02X", data->idm.data[i]);
}
if(format_type == NfcProtocolFormatTypeFull) {
furi_string_cat_printf(str, "\nPMm:");
for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) {
furi_string_cat_printf(str, " %02X", data->pmm.data[i]);
}
}
}

View File

@@ -1,10 +0,0 @@
#pragma once
#include <nfc/protocols/felica/felica.h>
#include "../nfc_protocol_support_render_common.h"
void nfc_render_felica_info(
const FelicaData* data,
NfcProtocolFormatType format_type,
FuriString* str);

View File

@@ -1,145 +0,0 @@
#include "iso14443_3a.h"
#include "iso14443_3a_render.h"
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
#include "nfc/nfc_app_i.h"
#include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h"
static void nfc_scene_info_on_enter_iso14443_3a(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeFull, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static NfcCommand
nfc_scene_read_poller_callback_iso14443_3a(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3a);
NfcApp* instance = context;
const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolIso14443_3a, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
return NfcCommandStop;
}
return NfcCommandContinue;
}
static void nfc_scene_read_on_enter_iso14443_3a(NfcApp* instance) {
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_3a, instance);
}
static void nfc_scene_read_success_on_enter_iso14443_3a(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeShort, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static NfcCommand
nfc_scene_emulate_listener_callback_iso14443_3a(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(event.event_data);
NfcApp* nfc = context;
Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;
if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {
furi_string_cat_printf(nfc->text_box_store, "R:");
for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer);
i++) {
furi_string_cat_printf(
nfc->text_box_store,
" %02X",
bit_buffer_get_byte(iso14443_3a_event->data->buffer, i));
}
furi_string_push_back(nfc->text_box_store, '\n');
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);
}
}
return NfcCommandContinue;
}
static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) {
const Iso14443_3aData* data =
nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a);
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_3a, data);
nfc_listener_start(
instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance);
}
static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, uint32_t event) {
if(event == SubmenuIndexCommonEmulate) {
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
return true;
}
return false;
}
const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = {
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid,
.scene_info =
{
.on_enter = nfc_scene_info_on_enter_iso14443_3a,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read =
{
.on_enter = nfc_scene_read_on_enter_iso14443_3a,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_read_menu_on_event_iso14443_3a,
},
.scene_read_success =
{
.on_enter = nfc_scene_read_success_on_enter_iso14443_3a,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_saved_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_save_name =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_emulate =
{
.on_enter = nfc_scene_emulate_on_enter_iso14443_3a,
.on_event = nfc_protocol_support_common_on_event_empty,
},
};

View File

@@ -1,5 +0,0 @@
#pragma once
#include "../nfc_protocol_support_base.h"
extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a;

View File

@@ -1,34 +0,0 @@
#include "iso14443_3a_render.h"
void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size) {
for(size_t i = 0; i < size; i++) {
furi_string_cat_printf(str, " %02X", data[i]);
}
}
void nfc_render_iso14443_3a_info(
const Iso14443_3aData* data,
NfcProtocolFormatType format_type,
FuriString* str) {
if(format_type == NfcProtocolFormatTypeFull) {
const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3';
furi_string_cat_printf(str, "ISO 14443-%c (NFC-A)\n", iso_type);
}
nfc_render_iso14443_3a_brief(data, str);
if(format_type == NfcProtocolFormatTypeFull) {
nfc_render_iso14443_3a_extra(data, str);
}
}
void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str) {
furi_string_cat_printf(str, "UID:");
nfc_render_iso14443_3a_format_bytes(str, data->uid, data->uid_len);
}
void nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str) {
furi_string_cat_printf(str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]);
furi_string_cat_printf(str, "SAK: %02X", data->sak);
}

View File

@@ -1,16 +0,0 @@
#pragma once
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
#include "../nfc_protocol_support_render_common.h"
void nfc_render_iso14443_3a_info(
const Iso14443_3aData* data,
NfcProtocolFormatType format_type,
FuriString* str);
void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size);
void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str);
void nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str);

View File

@@ -1,113 +0,0 @@
#include "iso14443_3b.h"
#include "iso14443_3b_render.h"
#include <nfc/protocols/iso14443_3b/iso14443_3b_poller.h>
#include "nfc/nfc_app_i.h"
#include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h"
static void nfc_scene_info_on_enter_iso14443_3b(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeFull, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static NfcCommand
nfc_scene_read_poller_callback_iso14443_3b(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3b);
NfcApp* instance = context;
const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;
if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolIso14443_3b, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
return NfcCommandStop;
}
return NfcCommandContinue;
}
static void nfc_scene_read_on_enter_iso14443_3b(NfcApp* instance) {
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_3b, instance);
}
static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeShort, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event) {
if(event == SubmenuIndexCommonEdit) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
return true;
}
return false;
}
static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, uint32_t event) {
return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);
}
const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = {
.features = NfcProtocolFeatureNone,
.scene_info =
{
.on_enter = nfc_scene_info_on_enter_iso14443_3b,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read =
{
.on_enter = nfc_scene_read_on_enter_iso14443_3b,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_success =
{
.on_enter = nfc_scene_read_success_on_enter_iso14443_3b,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_saved_menu =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_scene_saved_menu_on_event_iso14443_3b,
},
.scene_save_name =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_emulate =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
};

View File

@@ -1,5 +0,0 @@
#pragma once
#include "../nfc_protocol_support_base.h"
extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b;

View File

@@ -1,7 +0,0 @@
#pragma once
#include <nfc/protocols/iso14443_3b/iso14443_3b.h>
#include "iso14443_3b.h"
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event);

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