mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-13 05:06:30 +04:00
Compare commits
79 Commits
nfcrefacto
...
unlshd-065
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da18305119 | ||
|
|
c71d04a660 | ||
|
|
f3f68fd5d3 | ||
|
|
d4ce47c941 | ||
|
|
7413a78013 | ||
|
|
83967d6e06 | ||
|
|
4d1c6a8d20 | ||
|
|
c95a0a0171 | ||
|
|
feba1dbc24 | ||
|
|
5b578cf69d | ||
|
|
dafea08581 | ||
|
|
5c50224571 | ||
|
|
b510df47f9 | ||
|
|
2aec3ec5fd | ||
|
|
4b8c017302 | ||
|
|
b579bca227 | ||
|
|
3a676f7afa | ||
|
|
ca479303a1 | ||
|
|
5e649d8c41 | ||
|
|
0bc626ba1d | ||
|
|
3821ee7709 | ||
|
|
cb17189b15 | ||
|
|
80e8167054 | ||
|
|
eede5ed29e | ||
|
|
360fef7777 | ||
|
|
b83da5d3cb | ||
|
|
ae5d28fbc5 | ||
|
|
f4cd7c0100 | ||
|
|
7b68fd30ec | ||
|
|
ac222f1b0c | ||
|
|
b0e8e68909 | ||
|
|
ef5f6e2f70 | ||
|
|
30f6da3fa3 | ||
|
|
3a47154cdb | ||
|
|
d953d35991 | ||
|
|
4b74d13e10 | ||
|
|
98bf353287 | ||
|
|
eb8c751b31 | ||
|
|
9abad8704f | ||
|
|
6716c0f792 | ||
|
|
35f7ec6c07 | ||
|
|
a524fd7674 | ||
|
|
ff27fd3094 | ||
|
|
977ac09fe6 | ||
|
|
1a88e01899 | ||
|
|
6e710c5164 | ||
|
|
fd56ac3400 | ||
|
|
1d801c38f9 | ||
|
|
8afdb5b7b4 | ||
|
|
83624b1dee | ||
|
|
56adcf1ad8 | ||
|
|
ad27f87a0c | ||
|
|
346cf299ee | ||
|
|
05489fda7d | ||
|
|
1b12526357 | ||
|
|
802035d92e | ||
|
|
a24d0f1958 | ||
|
|
da68f2e4ed | ||
|
|
8f16dbb8e7 | ||
|
|
49e458f1b5 | ||
|
|
5cf46d2aa9 | ||
|
|
beedf54e75 | ||
|
|
70ccb89c3d | ||
|
|
5ea43a2a4b | ||
|
|
41f60dbbf4 | ||
|
|
827341ec08 | ||
|
|
5c36043d03 | ||
|
|
cf5811f8d9 | ||
|
|
ec6a169bf8 | ||
|
|
f1dec87c1b | ||
|
|
ab29951a99 | ||
|
|
bbe9f88bbe | ||
|
|
9188bf0013 | ||
|
|
f33ed59567 | ||
|
|
3fd8c80861 | ||
|
|
7b8ac3a5a0 | ||
|
|
6fef957001 | ||
|
|
0de1c9df89 | ||
|
|
a0e8cfbe97 |
31
.drone.yml
31
.drone.yml
@@ -22,8 +22,7 @@ steps:
|
||||
- export DIST_SUFFIX=${DRONE_TAG}c
|
||||
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
|
||||
- export FORCE_NO_DIRTY=yes
|
||||
- export FBT_GIT_SUBMODULE_SHALLOW=1
|
||||
- rm -rf applications/main/clock_app/resources/apps/
|
||||
- rm -rf assets/resources/apps/
|
||||
- rm -rf build/
|
||||
- rm -rf dist/
|
||||
- rm -rf .sconsign.dblite
|
||||
@@ -43,11 +42,10 @@ steps:
|
||||
- export DIST_SUFFIX=${DRONE_TAG}
|
||||
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
|
||||
- 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
|
||||
- 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/apps_data/* applications/main/clock_app/resources/apps_data/
|
||||
- cp -R base_pack_build/artifacts-base/* assets/resources/apps/
|
||||
- cp -R base_pack_build/apps_data/* assets/resources/apps_data/
|
||||
- rm -rf base_pack_build
|
||||
- rm -rf all-the-apps-base.tgz
|
||||
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||
@@ -66,12 +64,11 @@ steps:
|
||||
commands:
|
||||
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/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
|
||||
- export DIST_SUFFIX=${DRONE_TAG}e
|
||||
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
|
||||
- export FORCE_NO_DIRTY=yes
|
||||
- export FBT_GIT_SUBMODULE_SHALLOW=1
|
||||
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||
- mkdir artifacts-extra-apps
|
||||
@@ -90,7 +87,6 @@ steps:
|
||||
- export DIST_SUFFIX=${DRONE_TAG}r
|
||||
- export WORKFLOW_BRANCH_OR_TAG=release-cfw-rgb
|
||||
- export FORCE_NO_DIRTY=yes
|
||||
- export FBT_GIT_SUBMODULE_SHALLOW=1
|
||||
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||
- mkdir artifacts-rgb-patch
|
||||
@@ -109,17 +105,16 @@ steps:
|
||||
- git checkout -- .
|
||||
- rm -f 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 WORKFLOW_BRANCH_OR_TAG=no-custom-anims
|
||||
- export FORCE_NO_DIRTY=yes
|
||||
- export FBT_GIT_SUBMODULE_SHALLOW=1
|
||||
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/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/apps_data/* applications/main/clock_app/resources/apps_data/
|
||||
- cp -R base_pack_build/artifacts-base/* assets/resources/apps/
|
||||
- cp -R base_pack_build/apps_data/* assets/resources/apps_data/
|
||||
- rm -rf base_pack_build
|
||||
- rm -rf all-the-apps-base.tgz
|
||||
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||
@@ -396,8 +391,7 @@ steps:
|
||||
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}c
|
||||
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
|
||||
- export FORCE_NO_DIRTY=yes
|
||||
- export FBT_GIT_SUBMODULE_SHALLOW=1
|
||||
- rm -rf applications/main/clock_app/resources/apps/
|
||||
- rm -rf assets/resources/apps/
|
||||
- rm -rf build/
|
||||
- rm -rf dist/
|
||||
- rm -rf .sconsign.dblite
|
||||
@@ -418,11 +412,10 @@ steps:
|
||||
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}
|
||||
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
|
||||
- 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
|
||||
- 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/apps_data/* applications/main/clock_app/resources/apps_data/
|
||||
- cp -R base_pack_build/artifacts-base/* assets/resources/apps/
|
||||
- cp -R base_pack_build/apps_data/* assets/resources/apps_data/
|
||||
- rm -rf base_pack_build
|
||||
- rm -rf all-the-apps-base.tgz
|
||||
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||
@@ -441,12 +434,11 @@ steps:
|
||||
commands:
|
||||
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/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
|
||||
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e
|
||||
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
|
||||
- export FORCE_NO_DIRTY=yes
|
||||
- export FBT_GIT_SUBMODULE_SHALLOW=1
|
||||
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||
- mkdir artifacts-extra-apps
|
||||
@@ -465,7 +457,6 @@ steps:
|
||||
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}r
|
||||
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw-rgb
|
||||
- export FORCE_NO_DIRTY=yes
|
||||
- export FBT_GIT_SUBMODULE_SHALLOW=1
|
||||
- rm -f build/f7-firmware-C/toolbox/version.*
|
||||
- ./fbt COMPACT=1 DEBUG=0 updater_package
|
||||
- mkdir artifacts-rgb-patch
|
||||
|
||||
@@ -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/*
|
||||
|
||||
35
SConstruct
35
SConstruct
@@ -67,22 +67,22 @@ if GetOption("fullenv") or any(
|
||||
# Target for self-update package
|
||||
dist_basic_arguments = [
|
||||
"--bundlever",
|
||||
"${UPDATE_VERSION_STRING}",
|
||||
'"${UPDATE_VERSION_STRING}"',
|
||||
]
|
||||
dist_radio_arguments = [
|
||||
"--radio",
|
||||
"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}",
|
||||
'"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"',
|
||||
"--radiotype",
|
||||
"${COPRO_STACK_TYPE}",
|
||||
"${COPRO_DISCLAIMER}",
|
||||
"--obdata",
|
||||
"${ROOT_DIR.abspath}/${COPRO_OB_DATA}",
|
||||
'"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"',
|
||||
"--stackversion",
|
||||
"${COPRO_CUBE_VERSION}",
|
||||
]
|
||||
dist_resource_arguments = [
|
||||
"-r",
|
||||
firmware_env.subst("${RESOURCES_ROOT}"),
|
||||
'"${ROOT_DIR.abspath}/assets/resources"',
|
||||
]
|
||||
dist_splash_arguments = (
|
||||
[
|
||||
@@ -95,7 +95,7 @@ if GetOption("fullenv") or any(
|
||||
|
||||
selfupdate_dist = distenv.DistCommand(
|
||||
"updater_package",
|
||||
(distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES_MANIFEST"]),
|
||||
(distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]),
|
||||
DIST_EXTRA=[
|
||||
*dist_basic_arguments,
|
||||
*dist_radio_arguments,
|
||||
@@ -128,8 +128,7 @@ if GetOption("fullenv") or any(
|
||||
|
||||
# Installation over USB & CLI
|
||||
usb_update_package = distenv.AddUsbFlashTarget(
|
||||
"#build/usbinstall.flag",
|
||||
(firmware_env["FW_RESOURCES_MANIFEST"], selfupdate_dist),
|
||||
"#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist)
|
||||
)
|
||||
distenv.Alias("flash_usb_full", usb_update_package)
|
||||
|
||||
@@ -167,25 +166,17 @@ Depends(
|
||||
list(app_artifact.validator for app_artifact in external_app_list),
|
||||
)
|
||||
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
|
||||
|
||||
fap_deploy = distenv.PhonyTarget(
|
||||
"fap_deploy",
|
||||
[
|
||||
[
|
||||
"${PYTHON3}",
|
||||
"${FBT_SCRIPT_DIR}/storage.py",
|
||||
"-p",
|
||||
"${FLIP_PORT}",
|
||||
"send",
|
||||
"${SOURCE}",
|
||||
"/ext/apps",
|
||||
]
|
||||
],
|
||||
source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")),
|
||||
"${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps",
|
||||
source=Dir("#/assets/resources/apps"),
|
||||
)
|
||||
Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"])
|
||||
|
||||
|
||||
# Target for bundling core2 package for qFlipper
|
||||
@@ -323,7 +314,9 @@ distenv.PhonyTarget(
|
||||
)
|
||||
|
||||
# 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
|
||||
distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
|
||||
|
||||
@@ -71,7 +71,7 @@ static void direct_draw_run(DirectDraw* instance) {
|
||||
size_t counter = 0;
|
||||
float fps = 0;
|
||||
|
||||
furi_thread_set_current_priority(FuriThreadPriorityIdle);
|
||||
vTaskPrioritySet(furi_thread_get_current_id(), FuriThreadPriorityIdle);
|
||||
|
||||
do {
|
||||
size_t elapsed = DWT->CYCCNT - start;
|
||||
|
||||
@@ -21,51 +21,22 @@ static void rpc_debug_app_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void
|
||||
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) {
|
||||
static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
RpcDebugApp* app = context;
|
||||
furi_assert(app->rpc);
|
||||
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
rpc_system_app_set_callback(app->rpc, NULL, NULL);
|
||||
app->rpc = NULL;
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
rpc_system_app_confirm(app->rpc, 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);
|
||||
rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true);
|
||||
} else {
|
||||
rpc_system_app_confirm(app->rpc, false);
|
||||
rpc_system_app_confirm(app->rpc, event, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
#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) {
|
||||
RpcDebugApp* app = context;
|
||||
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_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);
|
||||
}
|
||||
|
||||
@@ -16,7 +53,6 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == RpcDebugAppCustomEventRpcDataExchange) {
|
||||
rpc_system_app_confirm(app->rpc, true);
|
||||
notification_message(app->notifications, &sequence_blink_cyan_100);
|
||||
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||
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) {
|
||||
RpcDebugApp* app = context;
|
||||
text_box_reset(app->text_box);
|
||||
rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
cdefines=["APP_UNIT_TESTS"],
|
||||
requires=["system_settings"],
|
||||
provides=["delay_test"],
|
||||
resources="resources",
|
||||
order=100,
|
||||
)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ void bt_test_alloc() {
|
||||
}
|
||||
|
||||
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_dut);
|
||||
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) {
|
||||
furi_check(bt_test);
|
||||
furi_assert(bt_test);
|
||||
|
||||
bt_test_keys_remove_test_file();
|
||||
bt_test_keys_storage_profile();
|
||||
|
||||
@@ -27,7 +27,7 @@ static void infrared_test_alloc() {
|
||||
}
|
||||
|
||||
static void infrared_test_free() {
|
||||
furi_check(test);
|
||||
furi_assert(test);
|
||||
infrared_free_decoder(test->decoder_handler);
|
||||
infrared_free_encoder(test->encoder_handler);
|
||||
flipper_format_free(test->ff);
|
||||
|
||||
@@ -22,7 +22,7 @@ MU_TEST(manifest_iteration_test) {
|
||||
ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(storage);
|
||||
do {
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -18,8 +18,6 @@
|
||||
#include <cli/cli.h>
|
||||
#include <loader/loader.h>
|
||||
#include <protobuf_version.h>
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
|
||||
LIST_DEF(MsgList, PB_Main, M_POD_OPLIST)
|
||||
@@ -38,7 +36,7 @@ typedef struct {
|
||||
FuriStreamBuffer* output_stream;
|
||||
SemaphoreHandle_t close_session_semaphore;
|
||||
SemaphoreHandle_t terminate_semaphore;
|
||||
uint32_t timeout;
|
||||
TickType_t timeout;
|
||||
} RpcSessionContext;
|
||||
|
||||
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;
|
||||
size_t bytes_received = 0;
|
||||
|
||||
uint32_t now = furi_get_tick();
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
int32_t time_left = session_context->timeout - now;
|
||||
time_left = MAX(time_left, 0);
|
||||
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(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 = {
|
||||
.callback = test_rpc_pb_stream_read,
|
||||
.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);
|
||||
}
|
||||
|
||||
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)) {
|
||||
mu_fail("decoded more than expected");
|
||||
}
|
||||
|
||||
@@ -65,8 +65,8 @@ const UnitTest unit_tests[] = {
|
||||
void minunit_print_progress() {
|
||||
static const char progress[] = {'\\', '|', '/', '-'};
|
||||
static uint8_t progress_counter = 0;
|
||||
static uint32_t last_tick = 0;
|
||||
uint32_t current_tick = furi_get_tick();
|
||||
static TickType_t last_tick = 0;
|
||||
TickType_t current_tick = xTaskGetTickCount();
|
||||
if(current_tick - last_tick > 20) {
|
||||
last_tick = current_tick;
|
||||
printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]);
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="example_plugins_app",
|
||||
stack_size=2 * 1024,
|
||||
fap_category="Examples",
|
||||
sources=["*.c", "!plugin*.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
@@ -22,7 +21,6 @@ App(
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="example_plugin1_ep",
|
||||
requires=["example_plugins", "example_plugins_multi"],
|
||||
sources=["plugin1.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
@@ -30,5 +28,4 @@ App(
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="example_plugin2_ep",
|
||||
requires=["example_plugins_multi"],
|
||||
sources=["plugin2.c"],
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@ App(
|
||||
entry_point="example_advanced_plugins_app",
|
||||
stack_size=2 * 1024,
|
||||
fap_category="Examples",
|
||||
sources=["*.c*", "!plugin*.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
|
||||
@@ -95,17 +95,18 @@ void archive_free(ArchiveApp* archive) {
|
||||
}
|
||||
|
||||
void archive_show_loading_popup(ArchiveApp* context, bool show) {
|
||||
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
|
||||
ViewStack* view_stack = context->view_stack;
|
||||
Loading* loading = context->loading;
|
||||
|
||||
if(show) {
|
||||
// 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));
|
||||
} else {
|
||||
view_stack_remove_view(view_stack, loading_get_view(loading));
|
||||
// Restore default timer priority
|
||||
furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal);
|
||||
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
stack_size=2 * 1024,
|
||||
icon="A_BadUsb_14",
|
||||
order=70,
|
||||
resources="resources",
|
||||
fap_libs=["assets"],
|
||||
fap_icon="icon.png",
|
||||
fap_category="USB",
|
||||
|
||||
@@ -6,7 +6,6 @@ App(
|
||||
icon="A_Clock_14",
|
||||
stack_size=2 * 1024,
|
||||
order=81,
|
||||
resources="resources",
|
||||
fap_icon="icon.png",
|
||||
fap_category="Tools",
|
||||
)
|
||||
|
||||
@@ -17,6 +17,5 @@ App(
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
targets=["f7"],
|
||||
entry_point="ibutton_on_system_start",
|
||||
sources=["ibutton_cli.c"],
|
||||
order=60,
|
||||
)
|
||||
|
||||
@@ -39,23 +39,21 @@ static void ibutton_make_app_folder(iButton* ibutton) {
|
||||
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);
|
||||
iButton* ibutton = context;
|
||||
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
view_dispatcher_send_custom_event(
|
||||
ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose);
|
||||
rpc_system_app_set_callback(ibutton->rpc, NULL, NULL);
|
||||
ibutton->rpc = NULL;
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit);
|
||||
} else if(event->type == RpcAppEventTypeLoadFile) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
|
||||
furi_string_set(ibutton->file_path, event->data.string);
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoadFile);
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad);
|
||||
} else {
|
||||
rpc_system_app_confirm(ibutton->rpc, false);
|
||||
rpc_system_app_confirm(ibutton->rpc, event, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
enum iButtonCustomEvent {
|
||||
// Reserve first 100 events for button types and indexes, starting from 0
|
||||
iButtonCustomEventReserved = 100,
|
||||
|
||||
@@ -10,12 +10,8 @@ typedef enum {
|
||||
iButtonCustomEventByteEditResult,
|
||||
iButtonCustomEventWorkerEmulated,
|
||||
iButtonCustomEventWorkerRead,
|
||||
iButtonCustomEventWorkerWriteOK,
|
||||
iButtonCustomEventWorkerWriteSameKey,
|
||||
iButtonCustomEventWorkerWriteNoDetect,
|
||||
iButtonCustomEventWorkerWriteCannotWrite,
|
||||
|
||||
iButtonCustomEventRpcLoadFile,
|
||||
iButtonCustomEventRpcLoad,
|
||||
iButtonCustomEventRpcExit,
|
||||
iButtonCustomEventRpcSessionClose,
|
||||
} iButtonCustomEvent;
|
||||
};
|
||||
|
||||
@@ -23,8 +23,12 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
|
||||
if(event.event == iButtonCustomEventRpcLoadFile) {
|
||||
if(event.event == iButtonCustomEventRpcLoad) {
|
||||
bool result = false;
|
||||
const char* file_path = rpc_system_app_get_data(ibutton->rpc);
|
||||
|
||||
if(file_path && (furi_string_empty(ibutton->file_path))) {
|
||||
furi_string_set(ibutton->file_path, file_path);
|
||||
|
||||
if(ibutton_load_key(ibutton)) {
|
||||
popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop);
|
||||
@@ -35,11 +39,12 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
rpc_system_app_confirm(ibutton->rpc, result);
|
||||
rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result);
|
||||
|
||||
} 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);
|
||||
view_dispatcher_stop(ibutton->view_dispatcher);
|
||||
|
||||
|
||||
@@ -5,26 +5,9 @@ typedef enum {
|
||||
iButtonSceneWriteStateBlinkYellow,
|
||||
} 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) {
|
||||
iButton* ibutton = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
ibutton->view_dispatcher, ibutton_scene_write_to_custom_event(result));
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
|
||||
}
|
||||
|
||||
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) {
|
||||
consumed = true;
|
||||
if((event.event == iButtonCustomEventWorkerWriteOK) ||
|
||||
(event.event == iButtonCustomEventWorkerWriteSameKey)) {
|
||||
if((event.event == iButtonWorkerWriteOK) || (event.event == iButtonWorkerWriteSameKey)) {
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneWriteSuccess);
|
||||
} else if(event.event == iButtonCustomEventWorkerWriteNoDetect) {
|
||||
} else if(event.event == iButtonWorkerWriteNoDetect) {
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink);
|
||||
} else if(event.event == iButtonCustomEventWorkerWriteCannotWrite) {
|
||||
} else if(event.event == iButtonWorkerWriteCannotWrite) {
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink);
|
||||
}
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
||||
@@ -7,8 +7,6 @@ App(
|
||||
icon="A_Infrared_14",
|
||||
stack_size=3 * 1024,
|
||||
order=40,
|
||||
sources=["*.c", "!infrared_cli.c"],
|
||||
resources="resources",
|
||||
fap_libs=["assets"],
|
||||
fap_icon="icon.png",
|
||||
fap_category="Infrared",
|
||||
@@ -19,10 +17,5 @@ App(
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
targets=["f7"],
|
||||
entry_point="infrared_on_system_start",
|
||||
sources=[
|
||||
"infrared_cli.c",
|
||||
"infrared_brute_force.c",
|
||||
"infrared_signal.c",
|
||||
],
|
||||
order=20,
|
||||
)
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#include "infrared_app_i.h"
|
||||
#include "infrared_i.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "InfraredApp"
|
||||
|
||||
#define INFRARED_TX_MIN_INTERVAL_MS 50U
|
||||
|
||||
static const NotificationSequence*
|
||||
infrared_notification_sequences[InfraredNotificationMessageCount] = {
|
||||
static const NotificationSequence* infrared_notification_sequences[] = {
|
||||
&sequence_success,
|
||||
&sequence_set_only_green_255,
|
||||
&sequence_reset_green,
|
||||
@@ -20,66 +16,54 @@ static const NotificationSequence*
|
||||
&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)) {
|
||||
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) {
|
||||
furi_assert(context);
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
return scene_manager_handle_custom_event(infrared->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool infrared_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
return scene_manager_handle_back_event(infrared->scene_manager);
|
||||
}
|
||||
|
||||
static void infrared_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
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);
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
furi_assert(infrared->rpc_ctx);
|
||||
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose);
|
||||
rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL);
|
||||
infrared->rpc_ctx = NULL;
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcExit);
|
||||
} else if(event->type == RpcAppEventTypeLoadFile) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
|
||||
furi_string_set(infrared->file_path, event->data.string);
|
||||
} else if(event == RpcAppEventLoadFile) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoadFile);
|
||||
} else if(event->type == RpcAppEventTypeButtonPress) {
|
||||
furi_assert(
|
||||
event->data.type == RpcAppSystemEventDataTypeString ||
|
||||
event->data.type == RpcAppSystemEventDataTypeInt32);
|
||||
if(event->data.type == RpcAppSystemEventDataTypeString) {
|
||||
furi_string_set(infrared->button_name, event->data.string);
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad);
|
||||
} else if(event == RpcAppEventButtonPress) {
|
||||
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) {
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress);
|
||||
} else if(event == RpcAppEventButtonRelease) {
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease);
|
||||
} 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);
|
||||
}
|
||||
|
||||
static InfraredApp* infrared_alloc() {
|
||||
InfraredApp* infrared = malloc(sizeof(InfraredApp));
|
||||
static Infrared* infrared_alloc() {
|
||||
Infrared* infrared = malloc(sizeof(Infrared));
|
||||
|
||||
infrared->file_path = furi_string_alloc();
|
||||
infrared->button_name = furi_string_alloc();
|
||||
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
app_state->is_learning_new_remote = false;
|
||||
@@ -156,7 +139,7 @@ static InfraredApp* infrared_alloc() {
|
||||
|
||||
infrared->worker = infrared_worker_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->submenu = submenu_alloc();
|
||||
@@ -167,16 +150,16 @@ static InfraredApp* infrared_alloc() {
|
||||
view_dispatcher_add_view(
|
||||
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();
|
||||
view_dispatcher_add_view(
|
||||
infrared->view_dispatcher,
|
||||
InfraredViewVariableItemList,
|
||||
variable_item_list_get_view(infrared->variable_item_list));
|
||||
|
||||
infrared->dialog_ex = dialog_ex_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex));
|
||||
|
||||
infrared->button_menu = button_menu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu));
|
||||
@@ -207,7 +190,7 @@ static InfraredApp* infrared_alloc() {
|
||||
return infrared;
|
||||
}
|
||||
|
||||
static void infrared_free(InfraredApp* infrared) {
|
||||
static void infrared_free(Infrared* infrared) {
|
||||
furi_assert(infrared);
|
||||
ViewDispatcher* view_dispatcher = infrared->view_dispatcher;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
@@ -224,12 +207,12 @@ static void infrared_free(InfraredApp* infrared) {
|
||||
view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput);
|
||||
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);
|
||||
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);
|
||||
button_menu_free(infrared->button_menu);
|
||||
|
||||
@@ -255,7 +238,7 @@ static void infrared_free(InfraredApp* infrared) {
|
||||
scene_manager_free(infrared->scene_manager);
|
||||
|
||||
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_worker_free(infrared->worker);
|
||||
|
||||
@@ -269,67 +252,75 @@ static void infrared_free(InfraredApp* infrared) {
|
||||
infrared->gui = NULL;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool infrared_add_remote_with_button(
|
||||
const InfraredApp* infrared,
|
||||
Infrared* infrared,
|
||||
const char* button_name,
|
||||
const InfraredSignal* signal) {
|
||||
InfraredSignal* signal) {
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
FuriString* new_name = furi_string_alloc_set(INFRARED_DEFAULT_REMOTE_NAME);
|
||||
FuriString* new_path = furi_string_alloc_set(INFRARED_APP_FOLDER);
|
||||
FuriString *new_name, *new_path;
|
||||
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));
|
||||
furi_string_cat_printf(
|
||||
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 {
|
||||
if(!infrared_remote_create(remote, furi_string_get_cstr(new_path))) break;
|
||||
if(!infrared_remote_append_signal(remote, signal, button_name)) break;
|
||||
success = true;
|
||||
} while(false);
|
||||
furi_string_free(new_name);
|
||||
furi_string_free(new_path);
|
||||
return infrared_remote_add_button(remote, button_name, signal);
|
||||
}
|
||||
|
||||
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_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) {
|
||||
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) {
|
||||
void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) {
|
||||
if(infrared->app_state.is_transmitting) {
|
||||
return;
|
||||
}
|
||||
@@ -340,12 +331,12 @@ void infrared_tx_start(InfraredApp* infrared) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(infrared_signal_is_raw(infrared->current_signal)) {
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(infrared->current_signal);
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_worker_set_raw_signal(
|
||||
infrared->worker, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
|
||||
} 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);
|
||||
}
|
||||
|
||||
@@ -359,20 +350,20 @@ void infrared_tx_start(InfraredApp* infrared) {
|
||||
infrared->app_state.is_transmitting = true;
|
||||
}
|
||||
|
||||
void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) {
|
||||
furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote));
|
||||
void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) {
|
||||
furi_assert(button_index < infrared_remote_get_button_count(infrared->remote));
|
||||
|
||||
if(infrared_remote_load_signal(infrared->remote, infrared->current_signal, button_index)) {
|
||||
infrared_tx_start(infrared);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to load\n\"%s\"",
|
||||
infrared_remote_get_signal_name(infrared->remote, button_index));
|
||||
}
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(infrared->remote, button_index);
|
||||
InfraredSignal* signal = infrared_remote_button_get_signal(button);
|
||||
|
||||
infrared_tx_start_signal(infrared, signal);
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@@ -386,65 +377,53 @@ void infrared_tx_stop(InfraredApp* infrared) {
|
||||
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_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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void infrared_play_notification_message(
|
||||
const InfraredApp* infrared,
|
||||
InfraredNotificationMessage message) {
|
||||
furi_assert(message < InfraredNotificationMessageCount);
|
||||
void infrared_play_notification_message(Infrared* infrared, uint32_t message) {
|
||||
furi_assert(message < sizeof(infrared_notification_sequences) / sizeof(NotificationSequence*));
|
||||
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;
|
||||
Loading* loading = infrared->loading;
|
||||
|
||||
if(show) {
|
||||
// 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));
|
||||
} else {
|
||||
view_stack_remove_view(view_stack, loading_get_view(loading));
|
||||
// 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) {
|
||||
furi_assert(context);
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
|
||||
if(infrared_worker_signal_is_decoded(received_signal)) {
|
||||
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 {
|
||||
const uint32_t* timings;
|
||||
size_t timings_size;
|
||||
infrared_worker_get_raw_signal(received_signal, &timings, &timings_size);
|
||||
infrared_signal_set_raw_signal(
|
||||
infrared->current_signal,
|
||||
infrared->received_signal,
|
||||
timings,
|
||||
timings_size,
|
||||
INFRARED_COMMON_CARRIER_FREQUENCY,
|
||||
@@ -457,20 +436,20 @@ void infrared_signal_received_callback(void* context, InfraredWorkerSignal* rece
|
||||
|
||||
void infrared_text_input_callback(void* context) {
|
||||
furi_assert(context);
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone);
|
||||
}
|
||||
|
||||
void infrared_popup_closed_callback(void* context) {
|
||||
furi_assert(context);
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypePopupClosed);
|
||||
}
|
||||
|
||||
int32_t infrared_app(void* p) {
|
||||
InfraredApp* infrared = infrared_alloc();
|
||||
Infrared* infrared = infrared_alloc();
|
||||
|
||||
infrared_make_app_folder(infrared);
|
||||
|
||||
@@ -486,15 +465,13 @@ int32_t infrared_app(void* p) {
|
||||
rpc_system_app_send_started(infrared->rpc_ctx);
|
||||
is_rpc_mode = true;
|
||||
} else {
|
||||
const char* file_path = (const char*)p;
|
||||
is_remote_loaded = infrared_remote_load(infrared->remote, file_path);
|
||||
|
||||
furi_string_set(infrared->file_path, (const char*)p);
|
||||
is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path);
|
||||
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;
|
||||
}
|
||||
|
||||
furi_string_set(infrared->file_path, file_path);
|
||||
}
|
||||
}
|
||||
|
||||
3
applications/main/infrared/infrared.h
Normal file
3
applications/main/infrared/infrared.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct Infrared Infrared;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -111,7 +111,7 @@ bool infrared_brute_force_start(
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -128,10 +128,8 @@ void infrared_brute_force_stop(InfraredBruteForce* brute_force) {
|
||||
|
||||
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) {
|
||||
furi_assert(brute_force->is_started);
|
||||
const bool success = infrared_signal_search_by_name_and_read(
|
||||
brute_force->current_signal,
|
||||
brute_force->ff,
|
||||
furi_string_get_cstr(brute_force->current_record_name));
|
||||
const bool success = infrared_signal_search_and_read(
|
||||
brute_force->current_signal, brute_force->ff, brute_force->current_record_name);
|
||||
if(success) {
|
||||
infrared_signal_transmit(brute_force->current_signal);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief InfraredBruteForce opaque type declaration.
|
||||
*/
|
||||
typedef struct InfraredBruteForce InfraredBruteForce;
|
||||
|
||||
/**
|
||||
* @brief Create a new InfraredBruteForce instance.
|
||||
*
|
||||
* @returns pointer to the created instance.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
uint32_t* record_count);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
bool infrared_brute_force_is_started(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);
|
||||
|
||||
/**
|
||||
* @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_reset(InfraredBruteForce* brute_force);
|
||||
void infrared_brute_force_add_record(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
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);
|
||||
|
||||
@@ -202,7 +202,7 @@ static bool
|
||||
}
|
||||
|
||||
static bool infrared_cli_decode_raw_signal(
|
||||
const InfraredRawSignal* raw_signal,
|
||||
InfraredRawSignal* raw_signal,
|
||||
InfraredDecoderHandler* decoder,
|
||||
FlipperFormat* output_file,
|
||||
const char* signal_name) {
|
||||
@@ -274,7 +274,7 @@ static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* o
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
|
||||
InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
|
||||
printf(
|
||||
"Raw signal: %s, %zu samples\r\n",
|
||||
furi_string_get_cstr(tmp),
|
||||
|
||||
@@ -15,10 +15,9 @@ enum InfraredCustomEventType {
|
||||
InfraredCustomEventTypeButtonSelected,
|
||||
InfraredCustomEventTypeBackPressed,
|
||||
|
||||
InfraredCustomEventTypeRpcLoadFile,
|
||||
InfraredCustomEventTypeRpcLoad,
|
||||
InfraredCustomEventTypeRpcExit,
|
||||
InfraredCustomEventTypeRpcButtonPressName,
|
||||
InfraredCustomEventTypeRpcButtonPressIndex,
|
||||
InfraredCustomEventTypeRpcButtonPress,
|
||||
InfraredCustomEventTypeRpcButtonRelease,
|
||||
InfraredCustomEventTypeRpcSessionClose,
|
||||
};
|
||||
|
||||
149
applications/main/infrared/infrared_i.h
Normal file
149
applications/main/infrared/infrared_i.h
Normal 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);
|
||||
@@ -1,427 +1,197 @@
|
||||
#include "infrared_remote.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#include <toolbox/m_cstr_dup.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <storage/storage.h>
|
||||
#include <core/common_defines.h>
|
||||
|
||||
#define TAG "InfraredRemote"
|
||||
|
||||
#define INFRARED_FILE_HEADER "IR signals file"
|
||||
#define INFRARED_FILE_VERSION (1)
|
||||
|
||||
ARRAY_DEF(StringArray, const char*, M_CSTR_DUP_OPLIST); //-V575
|
||||
ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST);
|
||||
|
||||
struct InfraredRemote {
|
||||
StringArray_t signal_names;
|
||||
InfraredButtonArray_t buttons;
|
||||
FuriString* name;
|
||||
FuriString* path;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
InfraredRemote* remote;
|
||||
FlipperFormat* ff_in;
|
||||
FlipperFormat* ff_out;
|
||||
FuriString* signal_name;
|
||||
InfraredSignal* signal;
|
||||
size_t signal_index;
|
||||
} InfraredBatch;
|
||||
|
||||
typedef struct {
|
||||
size_t signal_index;
|
||||
const char* signal_name;
|
||||
const InfraredSignal* signal;
|
||||
} InfraredBatchTarget;
|
||||
|
||||
typedef bool (
|
||||
*InfraredBatchCallback)(const InfraredBatch* batch, const InfraredBatchTarget* target);
|
||||
static void infrared_remote_clear_buttons(InfraredRemote* remote) {
|
||||
InfraredButtonArray_it_t it;
|
||||
for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
|
||||
InfraredButtonArray_next(it)) {
|
||||
infrared_remote_button_free(*InfraredButtonArray_cref(it));
|
||||
}
|
||||
InfraredButtonArray_reset(remote->buttons);
|
||||
}
|
||||
|
||||
InfraredRemote* infrared_remote_alloc() {
|
||||
InfraredRemote* remote = malloc(sizeof(InfraredRemote));
|
||||
StringArray_init(remote->signal_names);
|
||||
InfraredButtonArray_init(remote->buttons);
|
||||
remote->name = furi_string_alloc();
|
||||
remote->path = furi_string_alloc();
|
||||
return 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->name);
|
||||
free(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->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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
size_t infrared_remote_get_signal_count(const InfraredRemote* remote) {
|
||||
return StringArray_size(remote->signal_names);
|
||||
size_t infrared_remote_get_button_count(InfraredRemote* remote) {
|
||||
return InfraredButtonArray_size(remote->buttons);
|
||||
}
|
||||
|
||||
const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index) {
|
||||
furi_assert(index < infrared_remote_get_signal_count(remote));
|
||||
return *StringArray_cget(remote->signal_names, index);
|
||||
InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) {
|
||||
furi_assert(index < InfraredButtonArray_size(remote->buttons));
|
||||
return *InfraredButtonArray_get(remote->buttons, index);
|
||||
}
|
||||
|
||||
bool infrared_remote_load_signal(
|
||||
const InfraredRemote* remote,
|
||||
InfraredSignal* signal,
|
||||
size_t index) {
|
||||
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) {
|
||||
bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) {
|
||||
for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
|
||||
InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
|
||||
if(!strcmp(infrared_remote_button_get_name(button), name)) {
|
||||
*index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool infrared_remote_append_signal(
|
||||
InfraredRemote* remote,
|
||||
const InfraredSignal* signal,
|
||||
const char* name) {
|
||||
bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) {
|
||||
InfraredRemoteButton* button = infrared_remote_button_alloc();
|
||||
infrared_remote_button_set_name(button, 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);
|
||||
FlipperFormat* ff = flipper_format_file_alloc(storage);
|
||||
|
||||
bool success = false;
|
||||
const char* path = furi_string_get_cstr(remote->path);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_append(ff, path)) break;
|
||||
if(!infrared_signal_save(signal, ff, name)) break;
|
||||
FURI_LOG_I(TAG, "store file: \'%s\'", path);
|
||||
|
||||
StringArray_push_back(remote->signal_names, name);
|
||||
success = true;
|
||||
} while(false);
|
||||
bool success = flipper_format_file_open_always(ff, path) &&
|
||||
flipper_format_write_header_cstr(ff, "IR signals file", 1);
|
||||
if(success) {
|
||||
InfraredButtonArray_it_t it;
|
||||
for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
|
||||
InfraredButtonArray_next(it)) {
|
||||
InfraredRemoteButton* button = *InfraredButtonArray_cref(it);
|
||||
success = infrared_signal_save(
|
||||
infrared_remote_button_get_signal(button),
|
||||
ff,
|
||||
infrared_remote_button_get_name(button));
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_free(ff);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool infrared_remote_batch_start(
|
||||
InfraredRemote* remote,
|
||||
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;
|
||||
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);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_remote_load(InfraredRemote* remote, const char* path) {
|
||||
FURI_LOG_I(TAG, "Loading file: '%s'", path);
|
||||
|
||||
bool infrared_remote_load(InfraredRemote* remote, FuriString* path) {
|
||||
Storage* storage = furi_record_open(RECORD_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;
|
||||
|
||||
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;
|
||||
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))
|
||||
break;
|
||||
path_extract_filename(path, buf, true);
|
||||
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);
|
||||
StringArray_reset(remote->signal_names);
|
||||
|
||||
while(infrared_signal_read_name(ff, tmp)) {
|
||||
StringArray_push_back(remote->signal_names, furi_string_get_cstr(tmp));
|
||||
for(bool can_read = true; can_read;) {
|
||||
InfraredRemoteButton* button = infrared_remote_button_alloc();
|
||||
can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf);
|
||||
if(can_read) {
|
||||
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;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(tmp);
|
||||
furi_string_free(buf);
|
||||
flipper_format_free(ff);
|
||||
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;
|
||||
}
|
||||
|
||||
bool infrared_remote_remove(InfraredRemote* remote) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
const FS_Error status = storage_common_remove(storage, infrared_remote_get_path(remote));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
const bool success = (status == FSE_OK || status == FSE_NOT_EXIST);
|
||||
|
||||
if(success) {
|
||||
FS_Error status = storage_common_remove(storage, furi_string_get_cstr(remote->path));
|
||||
infrared_remote_reset(remote);
|
||||
}
|
||||
|
||||
return success;
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return (status == FSE_OK || status == FSE_NOT_EXIST);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
#include "infrared_signal.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "infrared_remote_button.h"
|
||||
|
||||
/**
|
||||
* @brief InfraredRemote opaque type declaration.
|
||||
*/
|
||||
typedef struct InfraredRemote InfraredRemote;
|
||||
|
||||
/**
|
||||
* @brief Create a new InfraredRemote instance.
|
||||
*
|
||||
* @returns pointer to the created instance.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Get an InfraredRemote instance's name.
|
||||
*
|
||||
* 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_name(InfraredRemote* remote, const char* name);
|
||||
const char* infrared_remote_get_name(InfraredRemote* remote);
|
||||
|
||||
/**
|
||||
* @brief Get an InfraredRemote instance's file path.
|
||||
*
|
||||
* 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);
|
||||
void infrared_remote_set_path(InfraredRemote* remote, const char* path);
|
||||
const char* infrared_remote_get_path(InfraredRemote* remote);
|
||||
|
||||
/**
|
||||
* @brief Get the number of signals listed in an InfraredRemote instance.
|
||||
*
|
||||
* @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);
|
||||
size_t infrared_remote_get_button_count(InfraredRemote* remote);
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Get the name of a signal listed in an InfraredRemote instance.
|
||||
*
|
||||
* @param[in] remote pointer to the instance to be queried.
|
||||
* @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_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal);
|
||||
bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index);
|
||||
bool infrared_remote_delete_button(InfraredRemote* remote, size_t index);
|
||||
void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest);
|
||||
|
||||
/**
|
||||
* @brief Get the index of a signal listed in an InfraredRemote instance by its name.
|
||||
*
|
||||
* @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_store(InfraredRemote* remote);
|
||||
bool infrared_remote_load(InfraredRemote* remote, FuriString* path);
|
||||
bool infrared_remote_remove(InfraredRemote* remote);
|
||||
|
||||
37
applications/main/infrared/infrared_remote_button.c
Normal file
37
applications/main/infrared/infrared_remote_button.c
Normal 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;
|
||||
}
|
||||
14
applications/main/infrared/infrared_remote_button.h
Normal file
14
applications/main/infrared/infrared_remote_button.h
Normal 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);
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
#define TAG "InfraredSignal"
|
||||
|
||||
#define INFRARED_SIGNAL_NAME_KEY "name"
|
||||
|
||||
struct InfraredSignal {
|
||||
bool is_raw;
|
||||
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)) {
|
||||
FURI_LOG_E(TAG, "Unknown protocol");
|
||||
return false;
|
||||
@@ -59,7 +57,7 @@ static bool infrared_signal_is_message_valid(const InfraredMessage* message) {
|
||||
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)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
@@ -85,8 +83,7 @@ static bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
infrared_signal_save_message(const InfraredMessage* message, FlipperFormat* ff) {
|
||||
static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) {
|
||||
const char* protocol_name = infrared_get_protocol_name(message->protocol);
|
||||
return flipper_format_write_string_cstr(ff, "type", "parsed") &&
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
return flipper_format_write_string_cstr(ff, "type", "raw") &&
|
||||
flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) &&
|
||||
@@ -183,11 +180,11 @@ void infrared_signal_free(InfraredSignal* signal) {
|
||||
free(signal);
|
||||
}
|
||||
|
||||
bool infrared_signal_is_raw(const InfraredSignal* signal) {
|
||||
bool infrared_signal_is_raw(InfraredSignal* signal) {
|
||||
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) :
|
||||
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));
|
||||
}
|
||||
|
||||
const InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal) {
|
||||
InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
|
||||
furi_assert(signal->is_raw);
|
||||
return &signal->payload.raw;
|
||||
}
|
||||
@@ -248,14 +245,14 @@ void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage*
|
||||
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);
|
||||
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, "") ||
|
||||
!flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_NAME_KEY, name)) {
|
||||
!flipper_format_write_string_cstr(ff, "name", name)) {
|
||||
return false;
|
||||
} else if(signal->is_raw) {
|
||||
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) {
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
bool success = false;
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool infrared_signal_search_by_index_and_read(
|
||||
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) {
|
||||
void infrared_signal_transmit(InfraredSignal* signal) {
|
||||
if(signal->is_raw) {
|
||||
const InfraredRawSignal* raw_signal = &signal->payload.raw;
|
||||
InfraredRawSignal* raw_signal = &signal->payload.raw;
|
||||
infrared_send_raw_ext(
|
||||
raw_signal->timings,
|
||||
raw_signal->timings_size,
|
||||
@@ -327,7 +309,7 @@ void infrared_signal_transmit(const InfraredSignal* signal) {
|
||||
raw_signal->frequency,
|
||||
raw_signal->duty_cycle);
|
||||
} else {
|
||||
const InfraredMessage* message = &signal->payload.message;
|
||||
InfraredMessage* message = &signal->payload.message;
|
||||
infrared_send(message, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <infrared/encoder_decoder/infrared.h>
|
||||
#include <stddef.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;
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
size_t timings_size; /**< Number of elements in the timings array. */
|
||||
uint32_t* timings; /**< Pointer to an array of timings describing the signal. */
|
||||
uint32_t frequency; /**< Carrier frequency of the signal. */
|
||||
float duty_cycle; /**< Duty cycle of the signal. */
|
||||
size_t timings_size;
|
||||
uint32_t* timings;
|
||||
uint32_t frequency;
|
||||
float duty_cycle;
|
||||
} InfraredRawSignal;
|
||||
|
||||
/**
|
||||
* @brief Create a new InfraredSignal instance.
|
||||
*
|
||||
* @returns pointer to the instance created.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Test whether an InfraredSignal instance holds a raw 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);
|
||||
bool infrared_signal_is_raw(InfraredSignal* signal);
|
||||
bool infrared_signal_is_valid(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);
|
||||
|
||||
/**
|
||||
* @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(
|
||||
InfraredSignal* signal,
|
||||
const uint32_t* timings,
|
||||
size_t timings_size,
|
||||
uint32_t frequency,
|
||||
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);
|
||||
InfraredMessage* infrared_signal_get_message(InfraredSignal* signal);
|
||||
|
||||
/**
|
||||
* @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_save(InfraredSignal* signal, FlipperFormat* ff, const char* name);
|
||||
bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name);
|
||||
|
||||
/**
|
||||
* @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(
|
||||
bool infrared_signal_search_and_read(
|
||||
InfraredSignal* signal,
|
||||
FlipperFormat* ff,
|
||||
const char* name);
|
||||
const FuriString* name);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
void infrared_signal_transmit(InfraredSignal* signal);
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
#include "../../infrared_app_i.h"
|
||||
#include "../../infrared_i.h"
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
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);
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
|
||||
}
|
||||
|
||||
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);
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
|
||||
}
|
||||
|
||||
static void
|
||||
infrared_scene_universal_common_show_popup(InfraredApp* infrared, uint32_t record_count) {
|
||||
static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint32_t record_count) {
|
||||
ViewStack* view_stack = infrared->view_stack;
|
||||
InfraredProgressView* progress = infrared->progress;
|
||||
infrared_progress_view_set_progress_total(progress, record_count);
|
||||
@@ -25,7 +24,7 @@ static void
|
||||
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;
|
||||
InfraredProgressView* progress = infrared->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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel));
|
||||
infrared_brute_force_reset(infrared->brute_force);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void infrared_scene_ask_back_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void infrared_scene_ask_retry_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_debug_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
InfraredWorker* worker = infrared->worker;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeSignalReceived) {
|
||||
InfraredDebugView* debug_view = infrared->debug_view;
|
||||
InfraredSignal* signal = infrared->current_signal;
|
||||
InfraredSignal* signal = infrared->received_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);
|
||||
|
||||
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");
|
||||
|
||||
} else {
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_debug_view_set_text(
|
||||
debug_view,
|
||||
"%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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
InfraredWorker* worker = infrared->worker;
|
||||
infrared_worker_rx_stop(worker);
|
||||
infrared_worker_rx_enable_blink_on_receiving(worker, false);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
#include <furi_hal_infrared.h>
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = variable_item_get_context(item);
|
||||
Infrared* infrared = variable_item_get_context(item);
|
||||
value_index_ir = variable_item_get_current_value_index(item);
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_debug_settings_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
variable_item_list_reset(infrared->variable_item_list);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexAddButton,
|
||||
@@ -10,12 +10,12 @@ typedef enum {
|
||||
} SubmenuIndex;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
view_dispatcher_send_custom_event(infrared->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_button_select_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
@@ -16,16 +16,16 @@ void infrared_scene_edit_button_select_on_enter(void* context) {
|
||||
"Delete Button:";
|
||||
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) {
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(remote, i);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
infrared_remote_get_signal_name(remote, i),
|
||||
infrared_remote_button_get_name(button),
|
||||
i,
|
||||
infrared_scene_edit_button_select_submenu_callback,
|
||||
context);
|
||||
}
|
||||
|
||||
if(button_count && app_state->current_button_index != InfraredButtonIndexNone) {
|
||||
submenu_set_selected_item(submenu, app_state->current_button_index);
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
InfraredAppState* app_state = &infrared->app_state;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
@@ -48,7 +48,7 @@ bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent
|
||||
} else if(edit_mode == InfraredEditModeDelete) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditDelete);
|
||||
} else {
|
||||
furi_crash();
|
||||
furi_assert(0);
|
||||
}
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
|
||||
@@ -1,49 +1,42 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
static void
|
||||
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);
|
||||
}
|
||||
|
||||
void infrared_scene_edit_delete_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
const InfraredEditTarget edit_target = infrared->app_state.edit_target;
|
||||
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);
|
||||
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;
|
||||
furi_check(current_button_index != InfraredButtonIndexNone);
|
||||
|
||||
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);
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%s\nRAW\n%zu samples",
|
||||
infrared_remote_get_signal_name(remote, current_button_index),
|
||||
"%s\nRAW\n%ld samples",
|
||||
infrared_remote_button_get_name(current_button),
|
||||
raw->timings_size);
|
||||
|
||||
} else {
|
||||
const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal);
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%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),
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4),
|
||||
message->address,
|
||||
@@ -56,11 +49,11 @@ void infrared_scene_edit_delete_on_enter(void* context) {
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
"%s\n with %zu buttons",
|
||||
"%s\n with %lu buttons",
|
||||
infrared_remote_get_name(remote),
|
||||
infrared_remote_get_signal_count(remote));
|
||||
infrared_remote_get_button_count(remote));
|
||||
} else {
|
||||
furi_crash();
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
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_context(dialog_ex, context);
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal);
|
||||
view_stack_add_view(infrared->view_stack, dialog_ex_get_view(infrared->dialog_ex));
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
@@ -93,24 +83,18 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
if(edit_target == InfraredEditTargetButton) {
|
||||
furi_assert(app_state->current_button_index != InfraredButtonIndexNone);
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
success = infrared_remote_delete_signal(remote, app_state->current_button_index);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
success = infrared_remote_delete_button(remote, app_state->current_button_index);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_remote_remove(remote);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else {
|
||||
furi_crash();
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to\ndelete %s",
|
||||
edit_target == InfraredEditTargetButton ? "button" : "file");
|
||||
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
|
||||
scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
view_stack_remove_view(infrared->view_stack, dialog_ex_get_view(infrared->dialog_ex));
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_edit_delete_done_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
furi_crash();
|
||||
furi_assert(0);
|
||||
}
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
|
||||
@@ -1,69 +1,44 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
static void infrared_scene_edit_move_button_callback(
|
||||
uint32_t index_old,
|
||||
uint32_t index_new,
|
||||
void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
furi_assert(infrared);
|
||||
static void infrared_scene_move_button(uint32_t index_old, uint32_t index_new, void* context) {
|
||||
InfraredRemote* remote = context;
|
||||
furi_assert(remote);
|
||||
infrared_remote_move_button(remote, index_old, index_new);
|
||||
}
|
||||
|
||||
infrared->app_state.prev_button_index = index_old;
|
||||
infrared->app_state.current_button_index = index_new;
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
infrared->view_dispatcher, InfraredCustomEventTypeButtonSelected);
|
||||
static const char* infrared_scene_get_btn_name(uint32_t index, void* context) {
|
||||
InfraredRemote* remote = context;
|
||||
furi_assert(remote);
|
||||
InfraredRemoteButton* button = infrared_remote_get_button(remote, index);
|
||||
return (infrared_remote_button_get_name(button));
|
||||
}
|
||||
|
||||
void infrared_scene_edit_move_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
|
||||
for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) {
|
||||
infrared_move_view_add_item(
|
||||
infrared->move_view, infrared_remote_get_signal_name(remote, i));
|
||||
}
|
||||
infrared_move_view_set_callback(infrared->move_view, infrared_scene_move_button);
|
||||
|
||||
infrared_move_view_set_callback(
|
||||
infrared->move_view, infrared_scene_edit_move_button_callback, infrared);
|
||||
uint32_t btn_count = infrared_remote_get_button_count(remote);
|
||||
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_stack_add_view(infrared->view_stack, infrared_move_view_get_view(infrared->move_view));
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeButtonSelected) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
UNUSED(event);
|
||||
UNUSED(infrared);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_scene_edit_move_on_exit(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
view_stack_remove_view(infrared->view_stack, infrared_move_view_get_view(infrared->move_view));
|
||||
infrared_move_view_reset(infrared->move_view);
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
infrared_remote_store(remote);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
TextInput* text_input = infrared->text_input;
|
||||
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");
|
||||
|
||||
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;
|
||||
strncpy(
|
||||
infrared->text_store[0],
|
||||
infrared_remote_get_signal_name(remote, current_button_index),
|
||||
infrared_remote_button_get_name(current_button),
|
||||
enter_name_length);
|
||||
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
@@ -42,7 +44,7 @@ void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
|
||||
furi_string_free(folder_path);
|
||||
} else {
|
||||
furi_crash();
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
text_input_set_result_callback(
|
||||
@@ -53,14 +55,11 @@ void infrared_scene_edit_rename_on_enter(void* context) {
|
||||
enter_name_length,
|
||||
false);
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal);
|
||||
view_stack_add_view(infrared->view_stack, text_input_get_view(infrared->text_input));
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
|
||||
}
|
||||
|
||||
bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
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) {
|
||||
const int32_t current_button_index = app_state->current_button_index;
|
||||
furi_assert(current_button_index != InfraredButtonIndexNone);
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
success = infrared_remote_rename_signal(
|
||||
remote, current_button_index, infrared->text_store[0]);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
success = infrared_remote_rename_button(
|
||||
remote, infrared->text_store[0], current_button_index);
|
||||
app_state->current_button_index = InfraredButtonIndexNone;
|
||||
} else if(edit_target == InfraredEditTargetRemote) {
|
||||
success = infrared_rename_current_remote(infrared, infrared->text_store[0]);
|
||||
} else {
|
||||
furi_crash();
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone);
|
||||
} 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, 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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
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);
|
||||
text_input_set_validator(text_input, NULL, NULL);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_edit_rename_done_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_error_databases_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
popup_reset(infrared->popup);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOff);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void infrared_scene_learn_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
|
||||
infrared_worker_rx_stop(infrared->worker);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_learn_done_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
infrared->app_state.is_learning_new_remote = false;
|
||||
popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void infrared_scene_learn_enter_name_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
TextInput* text_input = infrared->text_input;
|
||||
InfraredSignal* signal = infrared->current_signal;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
|
||||
if(infrared_signal_is_raw(signal)) {
|
||||
const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_text_store_set(infrared, 0, "RAW_%zu", raw->timings_size);
|
||||
InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal);
|
||||
infrared_text_store_set(infrared, 0, "RAW_%d", raw->timings_size);
|
||||
} else {
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
infrared_text_store_set(
|
||||
infrared,
|
||||
0,
|
||||
@@ -28,32 +28,31 @@ void infrared_scene_learn_enter_name_on_enter(void* context) {
|
||||
infrared->text_store[0],
|
||||
INFRARED_MAX_BUTTON_NAME_LENGTH,
|
||||
true);
|
||||
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput);
|
||||
}
|
||||
|
||||
bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) {
|
||||
InfraredApp* infrared = context;
|
||||
InfraredSignal* signal = infrared->current_signal;
|
||||
Infrared* infrared = context;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeTextEditDone) {
|
||||
const char* signal_name = infrared->text_store[0];
|
||||
const bool success =
|
||||
infrared->app_state.is_learning_new_remote ?
|
||||
infrared_add_remote_with_button(infrared, signal_name, signal) :
|
||||
infrared_remote_append_signal(infrared->remote, signal, signal_name);
|
||||
bool success = false;
|
||||
if(infrared->app_state.is_learning_new_remote) {
|
||||
success =
|
||||
infrared_add_remote_with_button(infrared, infrared->text_store[0], signal);
|
||||
} else {
|
||||
success =
|
||||
infrared_remote_add_button(infrared->remote, infrared->text_store[0], signal);
|
||||
}
|
||||
|
||||
if(success) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearnDone);
|
||||
dolphin_deed(DolphinDeedIrSave);
|
||||
} else {
|
||||
infrared_show_error_message(
|
||||
infrared,
|
||||
"Failed to\n%s",
|
||||
infrared->app_state.is_learning_new_remote ? "create file" : "add signal");
|
||||
dialog_message_show_storage_error(infrared->dialogs, "Failed to save file");
|
||||
const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart};
|
||||
scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
UNUSED(infrared);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
static void
|
||||
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);
|
||||
}
|
||||
|
||||
void infrared_scene_learn_success_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
DialogEx* dialog_ex = infrared->dialog_ex;
|
||||
InfraredSignal* signal = infrared->current_signal;
|
||||
InfraredSignal* signal = infrared->received_signal;
|
||||
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
} else {
|
||||
const InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
InfraredMessage* message = infrared_signal_get_message(signal);
|
||||
uint8_t addr_digits =
|
||||
ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4);
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
|
||||
bool consumed = false;
|
||||
@@ -84,7 +84,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExPressCenter) {
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
|
||||
infrared_tx_start(infrared);
|
||||
infrared_tx_start_received(infrared);
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExReleaseCenter) {
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
dialog_ex_reset(infrared->dialog_ex);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
typedef enum {
|
||||
ButtonIndexLearn = -2,
|
||||
ButtonIndexPlus = -2,
|
||||
ButtonIndexEdit = -1,
|
||||
ButtonIndexNA = 0,
|
||||
} ButtonIndex;
|
||||
|
||||
static void
|
||||
infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
|
||||
uint16_t custom_type;
|
||||
if(type == InputTypePress) {
|
||||
@@ -26,15 +26,17 @@ static void
|
||||
}
|
||||
|
||||
void infrared_scene_remote_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
InfraredRemote* remote = infrared->remote;
|
||||
ButtonMenu* button_menu = infrared->button_menu;
|
||||
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,
|
||||
infrared_remote_get_signal_name(remote, i),
|
||||
infrared_remote_button_get_name(button),
|
||||
i,
|
||||
infrared_scene_remote_button_menu_callback,
|
||||
ButtonMenuItemTypeCommon,
|
||||
@@ -44,7 +46,7 @@ void infrared_scene_remote_on_enter(void* context) {
|
||||
button_menu_add_item(
|
||||
button_menu,
|
||||
"+",
|
||||
ButtonIndexLearn,
|
||||
ButtonIndexPlus,
|
||||
infrared_scene_remote_button_menu_callback,
|
||||
ButtonMenuItemTypeControl,
|
||||
context);
|
||||
@@ -66,7 +68,7 @@ void infrared_scene_remote_on_enter(void* context) {
|
||||
}
|
||||
|
||||
bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
const bool is_transmitter_idle = !infrared->app_state.is_transmitting;
|
||||
bool consumed = false;
|
||||
@@ -95,7 +97,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
|
||||
if(is_transmitter_idle) {
|
||||
scene_manager_set_scene_state(
|
||||
scene_manager, InfraredSceneRemote, (unsigned)button_index);
|
||||
if(button_index == ButtonIndexLearn) {
|
||||
if(button_index == ButtonIndexPlus) {
|
||||
infrared->app_state.is_learning_new_remote = false;
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneLearn);
|
||||
consumed = true;
|
||||
@@ -114,6 +116,6 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) {
|
||||
}
|
||||
|
||||
void infrared_scene_remote_on_exit(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
button_menu_reset(infrared->button_menu);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
void infrared_scene_remote_list_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
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;
|
||||
dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px);
|
||||
browser_options.base_path = INFRARED_APP_FOLDER;
|
||||
|
||||
while(dialog_file_browser_show(
|
||||
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options)) {
|
||||
const char* file_path = furi_string_get_cstr(infrared->file_path);
|
||||
bool success = dialog_file_browser_show(
|
||||
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
if(remote_loaded) {
|
||||
if(success) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
#include <gui/canvas.h>
|
||||
|
||||
#define TAG "InfraredApp"
|
||||
|
||||
typedef enum {
|
||||
InfraredRpcStateIdle,
|
||||
InfraredRpcStateLoaded,
|
||||
@@ -10,7 +8,7 @@ typedef enum {
|
||||
} InfraredRpcState;
|
||||
|
||||
void infrared_scene_rpc_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
@@ -40,11 +38,12 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypePopupClosed) {
|
||||
view_dispatcher_stop(infrared->view_dispatcher);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcLoadFile) {
|
||||
} else if(event.event == InfraredCustomEventTypeRpcLoad) {
|
||||
bool result = false;
|
||||
if(state == InfraredRpcStateIdle) {
|
||||
result = infrared_remote_load(
|
||||
infrared->remote, furi_string_get_cstr(infrared->file_path));
|
||||
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
|
||||
if(arg && (state == InfraredRpcStateIdle)) {
|
||||
furi_string_set(infrared->file_path, arg);
|
||||
result = infrared_remote_load(infrared->remote, infrared->file_path);
|
||||
if(result) {
|
||||
scene_manager_set_scene_state(
|
||||
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
|
||||
@@ -56,35 +55,20 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
popup_set_text(
|
||||
infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop);
|
||||
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, result);
|
||||
} else if(
|
||||
event.event == InfraredCustomEventTypeRpcButtonPressName ||
|
||||
event.event == InfraredCustomEventTypeRpcButtonPressIndex) {
|
||||
rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
} else if(event.event == InfraredCustomEventTypeRpcButtonPress) {
|
||||
bool result = false;
|
||||
if(state == InfraredRpcStateLoaded) {
|
||||
if(event.event == InfraredCustomEventTypeRpcButtonPressName) {
|
||||
const char* button_name = furi_string_get_cstr(infrared->button_name);
|
||||
size_t index;
|
||||
const bool index_found =
|
||||
infrared_remote_get_signal_index(infrared->remote, button_name, &index);
|
||||
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);
|
||||
const char* arg = rpc_system_app_get_data(infrared->rpc_ctx);
|
||||
if(arg && (state == InfraredRpcStateLoaded)) {
|
||||
size_t button_index = 0;
|
||||
if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) {
|
||||
infrared_tx_start_button_index(infrared, button_index);
|
||||
result = true;
|
||||
scene_manager_set_scene_state(
|
||||
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) {
|
||||
bool result = false;
|
||||
if(state == InfraredRpcStateSending) {
|
||||
@@ -93,11 +77,11 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_set_scene_state(
|
||||
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) {
|
||||
scene_manager_stop(infrared->scene_manager);
|
||||
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) {
|
||||
scene_manager_stop(infrared->scene_manager);
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) ==
|
||||
InfraredRpcStateSending) {
|
||||
infrared_tx_stop(infrared);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexUniversalRemotes,
|
||||
@@ -10,12 +10,12 @@ enum SubmenuIndex {
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void infrared_scene_start_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexUniversalTV,
|
||||
@@ -9,12 +9,12 @@ typedef enum {
|
||||
} SubmenuIndex;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void infrared_scene_universal_on_enter(void* context) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
Submenu* submenu = infrared->submenu;
|
||||
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
SceneManager* scene_manager = infrared->scene_manager;
|
||||
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) {
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
submenu_reset(infrared->submenu);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_audio_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_fan_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_projector_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "../infrared_app_i.h"
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_tv_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
InfraredApp* infrared = context;
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#include "infrared_move_view.h"
|
||||
|
||||
#include <m-array.h>
|
||||
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include <toolbox/m_cstr_dup.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LIST_ITEMS 4U
|
||||
#define LIST_LINE_H 13U
|
||||
@@ -14,41 +13,42 @@
|
||||
|
||||
struct InfraredMoveView {
|
||||
View* view;
|
||||
InfraredMoveCallback callback;
|
||||
void* callback_context;
|
||||
InfraredMoveCallback move_cb;
|
||||
void* cb_context;
|
||||
};
|
||||
|
||||
ARRAY_DEF(InfraredMoveViewItemArray, const char*, M_CSTR_DUP_OPLIST); //-V575
|
||||
|
||||
typedef struct {
|
||||
InfraredMoveViewItemArray_t labels;
|
||||
const char** btn_names;
|
||||
uint32_t btn_number;
|
||||
int32_t list_offset;
|
||||
int32_t current_idx;
|
||||
int32_t start_idx;
|
||||
int32_t item_idx;
|
||||
bool is_moving;
|
||||
|
||||
InfraredMoveGetItemCallback get_item_cb;
|
||||
} InfraredMoveViewModel;
|
||||
|
||||
static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
InfraredMoveViewModel* model = _model;
|
||||
|
||||
UNUSED(model);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move");
|
||||
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const bool show_scrollbar = btn_number > LIST_ITEMS;
|
||||
bool show_scrollbar = model->btn_number > LIST_ITEMS;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
for(uint32_t i = 0; i < MIN(btn_number, LIST_ITEMS); i++) {
|
||||
int32_t idx = CLAMP((uint32_t)(i + model->list_offset), btn_number, 0U);
|
||||
uint8_t x_offset = (model->is_moving && model->current_idx == idx) ? MOVE_X_OFFSET : 0;
|
||||
for(uint32_t i = 0; i < MIN(model->btn_number, LIST_ITEMS); i++) {
|
||||
int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->btn_number, 0u);
|
||||
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 box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1);
|
||||
|
||||
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_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_str_aligned(
|
||||
canvas,
|
||||
x_offset + 3,
|
||||
y_offset + 3,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
*InfraredMoveViewItemArray_cget(model->labels, idx));
|
||||
canvas, x_offset + 3, y_offset + 3, AlignLeft, AlignTop, model->btn_names[idx]);
|
||||
}
|
||||
|
||||
if(show_scrollbar) {
|
||||
@@ -74,22 +69,22 @@ static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_width(canvas),
|
||||
HEADER_H,
|
||||
canvas_height(canvas) - HEADER_H,
|
||||
model->current_idx,
|
||||
btn_number);
|
||||
model->item_idx,
|
||||
model->btn_number);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_list_offset(InfraredMoveViewModel* model) {
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const int32_t bounds = btn_number > (LIST_ITEMS - 1) ? 2 : btn_number;
|
||||
int32_t bounds = model->btn_number > (LIST_ITEMS - 1) ? 2 : model->btn_number;
|
||||
|
||||
if((btn_number > (LIST_ITEMS - 1)) && (model->current_idx >= ((int32_t)btn_number - 1))) {
|
||||
model->list_offset = model->current_idx - (LIST_ITEMS - 1);
|
||||
} else if(model->list_offset < model->current_idx - bounds) {
|
||||
model->list_offset =
|
||||
CLAMP(model->current_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)btn_number - bounds, 0);
|
||||
} else if(model->list_offset > model->current_idx - bounds) {
|
||||
model->list_offset = CLAMP(model->current_idx - 1, (int32_t)btn_number - bounds, 0);
|
||||
if((model->btn_number > (LIST_ITEMS - 1)) &&
|
||||
(model->item_idx >= ((int32_t)model->btn_number - 1))) {
|
||||
model->list_offset = model->item_idx - (LIST_ITEMS - 1);
|
||||
} else if(model->list_offset < model->item_idx - bounds) {
|
||||
model->list_offset = CLAMP(
|
||||
model->item_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)model->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)) &&
|
||||
((event->key == InputKeyUp) || (event->key == InputKeyDown))) {
|
||||
bool is_moving = false;
|
||||
uint32_t index_old = 0;
|
||||
uint32_t index_new = 0;
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const int32_t item_idx_prev = model->current_idx;
|
||||
|
||||
is_moving = model->is_moving;
|
||||
index_old = model->item_idx;
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->current_idx <= 0) {
|
||||
model->current_idx = btn_number;
|
||||
if(model->item_idx <= 0) {
|
||||
model->item_idx = model->btn_number;
|
||||
}
|
||||
model->current_idx--;
|
||||
|
||||
model->item_idx--;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->current_idx++;
|
||||
if(model->current_idx >= (int32_t)(btn_number)) {
|
||||
model->current_idx = 0;
|
||||
model->item_idx++;
|
||||
if(model->item_idx >= (int32_t)(model->btn_number)) {
|
||||
model->item_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(model->is_moving) {
|
||||
InfraredMoveViewItemArray_swap_at(
|
||||
model->labels, item_idx_prev, model->current_idx);
|
||||
}
|
||||
|
||||
index_new = model->item_idx;
|
||||
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;
|
||||
}
|
||||
|
||||
} else if((event->key == InputKeyOk) && (event->type == InputTypeShort)) {
|
||||
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);
|
||||
},
|
||||
{ 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
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void infrared_move_view_set_callback(
|
||||
InfraredMoveView* move_view,
|
||||
InfraredMoveCallback callback,
|
||||
void* context) {
|
||||
furi_assert(move_view);
|
||||
move_view->callback = callback;
|
||||
move_view->callback_context = context;
|
||||
}
|
||||
static void infrared_move_view_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
InfraredMoveView* move_view = 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(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
InfraredMoveViewItemArray_reset(model->labels);
|
||||
model->list_offset = 0;
|
||||
model->start_idx = 0;
|
||||
model->current_idx = 0;
|
||||
model->is_moving = false;
|
||||
if(model->btn_names) {
|
||||
free(model->btn_names);
|
||||
model->btn_names = NULL;
|
||||
}
|
||||
model->btn_number = 0;
|
||||
model->get_item_cb = NULL;
|
||||
},
|
||||
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* move_view = malloc(sizeof(InfraredMoveView));
|
||||
|
||||
move_view->view = view_alloc();
|
||||
view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel));
|
||||
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_exit_callback(move_view->view, infrared_move_view_on_exit);
|
||||
view_set_context(move_view->view, move_view);
|
||||
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ InfraredMoveViewItemArray_init(model->labels); },
|
||||
true);
|
||||
|
||||
return 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);
|
||||
free(move_view);
|
||||
}
|
||||
|
||||
@@ -6,17 +6,20 @@ typedef struct InfraredMoveView InfraredMoveView;
|
||||
|
||||
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);
|
||||
|
||||
void infrared_move_view_free(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,
|
||||
InfraredMoveCallback callback,
|
||||
uint32_t item_count,
|
||||
InfraredMoveGetItemCallback load_cb,
|
||||
void* context);
|
||||
|
||||
void infrared_move_view_add_item(InfraredMoveView* move_view, const char* label);
|
||||
|
||||
void infrared_move_view_reset(InfraredMoveView* move_view);
|
||||
void infrared_move_view_list_update(InfraredMoveView* move_view);
|
||||
@@ -17,6 +17,5 @@ App(
|
||||
targets=["f7"],
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="lfrfid_on_system_start",
|
||||
sources=["lfrfid_cli.c"],
|
||||
order=50,
|
||||
)
|
||||
|
||||
@@ -13,23 +13,21 @@ static bool lfrfid_debug_back_event_callback(void* context) {
|
||||
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);
|
||||
LfRfid* app = (LfRfid*)context;
|
||||
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
if(rpc_event == RpcAppEventSessionClose) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose);
|
||||
// Detach RPC
|
||||
rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL);
|
||||
app->rpc_ctx = NULL;
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
} else if(rpc_event == RpcAppEventAppExit) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit);
|
||||
} else if(event->type == RpcAppEventTypeLoadFile) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeString);
|
||||
furi_string_set(app->file_path, event->data.string);
|
||||
} else if(rpc_event == RpcAppEventLoadFile) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile);
|
||||
} else {
|
||||
rpc_system_app_confirm(app->rpc_ctx, false);
|
||||
rpc_system_app_confirm(app->rpc_ctx, rpc_event, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,15 +24,17 @@ bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
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);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
} else if(event.event == LfRfidEventRpcSessionClose) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
} else if(event.event == LfRfidEventRpcLoadFile) {
|
||||
const char* arg = rpc_system_app_get_data(app->rpc_ctx);
|
||||
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)) {
|
||||
lfrfid_worker_start_thread(app->lfworker);
|
||||
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;
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(app->rpc_ctx, result);
|
||||
rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
||||
@@ -7,78 +7,15 @@ App(
|
||||
icon="A_NFC_14",
|
||||
stack_size=5 * 1024,
|
||||
order=30,
|
||||
resources="resources",
|
||||
sources=[
|
||||
"*.c",
|
||||
"!plugins",
|
||||
"!nfc_cli.c",
|
||||
],
|
||||
fap_libs=["assets"],
|
||||
fap_icon="icon.png",
|
||||
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(
|
||||
appid="nfc_start",
|
||||
targets=["f7"],
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="nfc_on_system_start",
|
||||
sources=["nfc_cli.c"],
|
||||
order=30,
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,33 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
enum NfcCustomEvent {
|
||||
// Reserve first 100 events for button types and indexes, starting from 0
|
||||
NfcCustomEventReserved = 100,
|
||||
|
||||
// Mf classic dict attack events
|
||||
NfcCustomEventDictAttackComplete,
|
||||
NfcCustomEventDictAttackSkip,
|
||||
NfcCustomEventDictAttackDataUpdate,
|
||||
|
||||
NfcCustomEventCardDetected,
|
||||
NfcCustomEventCardLost,
|
||||
|
||||
NfcCustomEventViewExit,
|
||||
NfcCustomEventWorkerExit,
|
||||
NfcCustomEventWorkerUpdate,
|
||||
NfcCustomEventWrongCard,
|
||||
NfcCustomEventTimerExpired,
|
||||
NfcCustomEventByteInputDone,
|
||||
NfcCustomEventTextInputDone,
|
||||
NfcCustomEventDictAttackDone,
|
||||
|
||||
NfcCustomEventRpcLoadFile,
|
||||
NfcCustomEventRpcExit,
|
||||
NfcCustomEventDictAttackSkip,
|
||||
NfcCustomEventRpcLoad,
|
||||
NfcCustomEventRpcSessionClose,
|
||||
|
||||
NfcCustomEventPollerSuccess,
|
||||
NfcCustomEventPollerIncomplete,
|
||||
NfcCustomEventPollerFailure,
|
||||
|
||||
NfcCustomEventListenerUpdate,
|
||||
} NfcCustomEvent;
|
||||
NfcCustomEventUpdateLog,
|
||||
NfcCustomEventSaveShadow,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_protocol_support_base.h"
|
||||
|
||||
extern const NfcProtocolSupportBase nfc_protocol_support_felica;
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_protocol_support_base.h"
|
||||
|
||||
extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_protocol_support_base.h"
|
||||
|
||||
extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b;
|
||||
@@ -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
Reference in New Issue
Block a user