diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 54bdd590..bc2f7887 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -27,6 +27,12 @@ static const uint32_t nfc_test_file_version = 1; #define NFC_TEST_DATA_MAX_LEN 18 #define NFC_TETS_TIMINGS_MAX_LEN 1350 +// Maximum allowed time for buffer preparation to fit 500us nt message timeout +#define NFC_TEST_4_BYTE_BUILD_BUFFER_TIM_MAX (150) +#define NFC_TEST_16_BYTE_BUILD_BUFFER_TIM_MAX (640) +#define NFC_TEST_4_BYTE_BUILD_SIGNAL_TIM_MAX (110) +#define NFC_TEST_16_BYTE_BUILD_SIGNAL_TIM_MAX (440) + typedef struct { Storage* storage; NfcaSignal* signal; @@ -89,13 +95,13 @@ static bool nfc_test_read_signal_from_file(const char* file_name) { static bool nfc_test_digital_signal_test_encode( const char* file_name, - uint32_t encode_max_time, + uint32_t build_signal_max_time_us, + uint32_t build_buffer_max_time_us, uint32_t timing_tolerance, uint32_t timings_sum_tolerance) { furi_assert(nfc_test); bool success = false; - uint32_t time = 0; uint32_t dut_timings_sum = 0; uint32_t ref_timings_sum = 0; uint8_t parity[10] = {}; @@ -109,17 +115,37 @@ static bool nfc_test_digital_signal_test_encode( // Encode signal FURI_CRITICAL_ENTER(); - time = DWT->CYCCNT; + uint32_t time_start = DWT->CYCCNT; + nfca_signal_encode( nfc_test->signal, nfc_test->test_data, nfc_test->test_data_len * 8, parity); + + uint32_t time_signal = + (DWT->CYCCNT - time_start) / furi_hal_cortex_instructions_per_microsecond(); + + time_start = DWT->CYCCNT; + digital_signal_prepare_arr(nfc_test->signal->tx_signal); - time = (DWT->CYCCNT - time) / furi_hal_cortex_instructions_per_microsecond(); + + uint32_t time_buffer = + (DWT->CYCCNT - time_start) / furi_hal_cortex_instructions_per_microsecond(); FURI_CRITICAL_EXIT(); // Check timings - if(time > encode_max_time) { + if(time_signal > build_signal_max_time_us) { FURI_LOG_E( - TAG, "Encoding time: %ld us while accepted value: %ld us", time, encode_max_time); + TAG, + "Build signal time: %ld us while accepted value: %ld us", + time_signal, + build_signal_max_time_us); + break; + } + if(time_buffer > build_buffer_max_time_us) { + FURI_LOG_E( + TAG, + "Build buffer time: %ld us while accepted value: %ld us", + time_buffer, + build_buffer_max_time_us); break; } @@ -156,7 +182,16 @@ static bool nfc_test_digital_signal_test_encode( break; } - FURI_LOG_I(TAG, "Encoding time: %ld us. Acceptable time: %ld us", time, encode_max_time); + FURI_LOG_I( + TAG, + "Build signal time: %ld us. Acceptable time: %ld us", + time_signal, + build_signal_max_time_us); + FURI_LOG_I( + TAG, + "Build buffer time: %ld us. Acceptable time: %ld us", + time_buffer, + build_buffer_max_time_us); FURI_LOG_I( TAG, "Timings sum difference: %ld [1/64MHZ]. Acceptable difference: %ld [1/64MHz]", @@ -171,11 +206,19 @@ static bool nfc_test_digital_signal_test_encode( MU_TEST(nfc_digital_signal_test) { mu_assert( nfc_test_digital_signal_test_encode( - NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, 500, 1, 37), + NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, + NFC_TEST_4_BYTE_BUILD_SIGNAL_TIM_MAX, + NFC_TEST_4_BYTE_BUILD_BUFFER_TIM_MAX, + 1, + 37), "NFC short digital signal test failed\r\n"); mu_assert( nfc_test_digital_signal_test_encode( - NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, 2000, 1, 37), + NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, + NFC_TEST_16_BYTE_BUILD_SIGNAL_TIM_MAX, + NFC_TEST_16_BYTE_BUILD_BUFFER_TIM_MAX, + 1, + 37), "NFC long digital signal test failed\r\n"); } diff --git a/applications/external/nfc_magic/lib/magic/classic_gen1.c b/applications/external/nfc_magic/lib/magic/classic_gen1.c index ebd2b080..8d87d631 100644 --- a/applications/external/nfc_magic/lib/magic/classic_gen1.c +++ b/applications/external/nfc_magic/lib/magic/classic_gen1.c @@ -5,7 +5,6 @@ #define TAG "Magic" #define MAGIC_CMD_WUPA (0x40) -#define MAGIC_CMD_WIPE (0x41) #define MAGIC_CMD_ACCESS (0x43) #define MAGIC_MIFARE_READ_CMD (0x30) @@ -144,32 +143,3 @@ bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data) { return write_success; } - -bool magic_gen1_wipe() { - bool wipe_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_CMD_WIPE; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(2000)); - - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - wipe_success = true; - } while(false); - - return wipe_success; -} \ No newline at end of file diff --git a/applications/external/nfc_magic/lib/magic/classic_gen1.h b/applications/external/nfc_magic/lib/magic/classic_gen1.h index 6d4ff6dc..98de1230 100644 --- a/applications/external/nfc_magic/lib/magic/classic_gen1.h +++ b/applications/external/nfc_magic/lib/magic/classic_gen1.h @@ -9,5 +9,3 @@ bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data); bool magic_gen1_data_access_cmd(); bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data); - -bool magic_gen1_wipe(); \ No newline at end of file diff --git a/applications/external/nfc_magic/lib/magic/magic.c b/applications/external/nfc_magic/lib/magic/magic.c deleted file mode 100644 index ebd2b080..00000000 --- a/applications/external/nfc_magic/lib/magic/magic.c +++ /dev/null @@ -1,175 +0,0 @@ -#include "classic_gen1.h" - -#include - -#define TAG "Magic" - -#define MAGIC_CMD_WUPA (0x40) -#define MAGIC_CMD_WIPE (0x41) -#define MAGIC_CMD_ACCESS (0x43) - -#define MAGIC_MIFARE_READ_CMD (0x30) -#define MAGIC_MIFARE_WRITE_CMD (0xA0) - -#define MAGIC_ACK (0x0A) - -#define MAGIC_BUFFER_SIZE (32) - -bool magic_gen1_wupa() { - bool magic_activated = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - // Start communication - tx_data[0] = MAGIC_CMD_WUPA; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 7, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - magic_activated = true; - } while(false); - - return magic_activated; -} - -bool magic_gen1_data_access_cmd() { - bool write_cmd_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_CMD_ACCESS; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - write_cmd_success = true; - } while(false); - - return write_cmd_success; -} - -bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data) { - furi_assert(data); - - bool read_success = false; - - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_MIFARE_READ_CMD; - tx_data[1] = block_num; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 2 * 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON, - furi_hal_nfc_ll_ms2fc(20)); - - if(ret != FuriHalNfcReturnOk) break; - if(rx_len != 16 * 8) break; - memcpy(data->value, rx_data, sizeof(data->value)); - read_success = true; - } while(false); - - return read_success; -} - -bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data) { - furi_assert(data); - - bool write_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_MIFARE_WRITE_CMD; - tx_data[1] = block_num; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 2 * 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - memcpy(tx_data, data->value, sizeof(data->value)); - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 16 * 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - write_success = true; - } while(false); - - return write_success; -} - -bool magic_gen1_wipe() { - bool wipe_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_CMD_WIPE; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(2000)); - - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - wipe_success = true; - } while(false); - - return wipe_success; -} \ No newline at end of file diff --git a/applications/external/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c index dc22b5d3..9ee7fd3e 100644 --- a/applications/external/nfc_magic/nfc_magic_worker.c +++ b/applications/external/nfc_magic/nfc_magic_worker.c @@ -92,51 +92,49 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) { do { - if(furi_hal_nfc_detect(&nfc_data, 200)) { - if(nfc_data.cuid != magic_dev->cuid) break; - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; - } - furi_hal_nfc_sleep(); - - magic_activate(); - if(magic_dev->type == MagicTypeClassicGen1) { - if(dev_protocol != NfcDeviceProtocolMifareClassic) break; - MfClassicData* mfc_data = &dev_data->mf_classic_data; - - if(mfc_data->type != MfClassicType1k) break; + if(magic_dev->type == MagicTypeClassicGen1) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_deactivate(); + magic_activate(); if(!magic_gen1_wupa()) { - FURI_LOG_E(TAG, "Not Magic card"); + FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)"); nfc_magic_worker->callback( NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); done = true; break; } + magic_deactivate(); + } + magic_activate(); + if(magic_gen1_wupa()) { if(!magic_gen1_data_access_cmd()) { - FURI_LOG_E(TAG, "Not Magic card"); + FURI_LOG_E( + TAG, "No card response to data access command (not a magic card)"); nfc_magic_worker->callback( NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); done = true; break; } + + MfClassicData* mfc_data = &dev_data->mf_classic_data; for(size_t i = 0; i < 64; i++) { FURI_LOG_D(TAG, "Writing block %d", i); if(!magic_gen1_write_blk(i, &mfc_data->block[i])) { FURI_LOG_E(TAG, "Failed to write %d block", i); + done = true; nfc_magic_worker->callback( NfcMagicWorkerEventFail, nfc_magic_worker->context); - done = true; break; } } + done = true; nfc_magic_worker->callback( NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - done = true; break; - } else if(magic_dev->type == MagicTypeGen4) { + } + } else if(magic_dev->type == MagicTypeGen4) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { uint8_t gen4_config[28]; uint32_t password = magic_dev->password; @@ -196,6 +194,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { gen4_config[25] = dev_data->nfc_data.atqa[1]; gen4_config[26] = dev_data->nfc_data.sak; + furi_hal_nfc_sleep(); furi_hal_nfc_activate_nfca(200, &cuid); if(!magic_gen4_set_cfg(password, gen4_config, sizeof(gen4_config), false)) { nfc_magic_worker->callback( @@ -394,6 +393,11 @@ void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) { MfClassicBlock block; memset(&block, 0, sizeof(MfClassicBlock)); + MfClassicBlock empty_block; + memset(&empty_block, 0, sizeof(MfClassicBlock)); + MfClassicBlock trailer_block; + memset(&trailer_block, 0xff, sizeof(MfClassicBlock)); + block.value[0] = 0x01; block.value[1] = 0x02; block.value[2] = 0x03; @@ -402,6 +406,10 @@ void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) { block.value[5] = 0x08; block.value[6] = 0x04; + trailer_block.value[7] = 0x07; + trailer_block.value[8] = 0x80; + trailer_block.value[9] = 0x69; + while(nfc_magic_worker->state == NfcMagicWorkerStateWipe) { do { magic_deactivate(); @@ -415,10 +423,26 @@ void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) { card_found_notified = true; } - if(!magic_gen1_wipe()) break; if(!magic_gen1_data_access_cmd()) break; if(!magic_gen1_write_blk(0, &block)) break; + for(size_t i = 1; i < 64; i++) { + FURI_LOG_D(TAG, "Wiping block %d", i); + bool success = false; + if((i | 0x03) == i) { + success = magic_gen1_write_blk(i, &trailer_block); + } else { + success = magic_gen1_write_blk(i, &empty_block); + } + + if(!success) { + FURI_LOG_E(TAG, "Failed to write %d block", i); + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + break; + } + } + card_wiped = true; nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); } else if(magic_dev->type == MagicTypeGen4) { diff --git a/applications/main/subghz/helpers/subghz_error_type.h b/applications/main/subghz/helpers/subghz_error_type.h index e481aa4b..0f86d6ea 100644 --- a/applications/main/subghz/helpers/subghz_error_type.h +++ b/applications/main/subghz/helpers/subghz_error_type.h @@ -10,4 +10,5 @@ typedef enum { 1, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */ SubGhzErrorTypeOnlyRX = 2, /** Transmission on this frequency is blocked by regional settings */ + SubGhzErrorTypeParserOthers = 3, /** Error in protocol parameters description */ } SubGhzErrorType; diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index aa6f132d..d4bf3e80 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -40,15 +40,26 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { bool result = false; if((state == SubGhzRpcStateLoaded)) { - result = subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx)); - state = SubGhzRpcStateTx; - if(result) subghz_blink_start(subghz); - } - if(!result) { - rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeOnlyRX); - rpc_system_app_set_error_text( - subghz->rpc_ctx, - "Transmission on this frequency is restricted in your region"); + switch( + subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) { + case SubGhzTxRxStartTxStateErrorOnlyRx: + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeOnlyRX); + rpc_system_app_set_error_text( + subghz->rpc_ctx, + "Transmission on this frequency is restricted in your region"); + break; + case SubGhzTxRxStartTxStateErrorParserOthers: + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeParserOthers); + rpc_system_app_set_error_text( + subghz->rpc_ctx, "Error in protocol parameters description"); + break; + + default: //if(SubGhzTxRxStartTxStateOk) + result = true; + subghz_blink_start(subghz); + state = SubGhzRpcStateTx; + break; + } } rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { @@ -56,9 +67,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(state == SubGhzRpcStateTx) { subghz_txrx_stop(subghz->txrx); subghz_blink_stop(subghz); - state = SubGhzRpcStateIdle; result = true; } + state = SubGhzRpcStateIdle; rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); } else if(event.event == SubGhzCustomEventSceneRpcLoad) { bool result = false; @@ -95,7 +106,7 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { void subghz_scene_rpc_on_exit(void* context) { SubGhz* subghz = context; SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); - if(state != SubGhzRpcStateIdle) { + if(state == SubGhzRpcStateTx) { subghz_txrx_stop(subghz->txrx); subghz_blink_stop(subghz); } diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index ac19d65b..60845ac9 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -176,16 +176,19 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { furi_hal_power_suppress_charge_enter(); - furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter); + if(furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter)) { + while(!(furi_hal_subghz_is_async_tx_complete() || cli_cmd_interrupt_received(cli))) { + printf("."); + fflush(stdout); + furi_delay_ms(333); + } + furi_hal_subghz_stop_async_tx(); - while(!(furi_hal_subghz_is_async_tx_complete() || cli_cmd_interrupt_received(cli))) { - printf("."); - fflush(stdout); - furi_delay_ms(333); + } else { + printf("Transmission on this frequency is restricted in your region\r\n"); } - furi_hal_subghz_stop_async_tx(); - furi_hal_subghz_sleep(); + furi_hal_subghz_sleep(); furi_hal_power_suppress_charge_exit(); flipper_format_free(flipper_format); diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png new file mode 100644 index 00000000..8b8dc80b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png new file mode 100644 index 00000000..956de617 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png new file mode 100644 index 00000000..93fdaa07 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png new file mode 100644 index 00000000..a4e19482 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png new file mode 100644 index 00000000..9cbcbd07 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png new file mode 100644 index 00000000..a745cdb0 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png new file mode 100644 index 00000000..768f471e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png new file mode 100644 index 00000000..2f50fb8c Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png new file mode 100644 index 00000000..fc7b7669 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png new file mode 100644 index 00000000..013e2008 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png new file mode 100644 index 00000000..795120e7 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png new file mode 100644 index 00000000..52061a5a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png new file mode 100644 index 00000000..10afed39 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png new file mode 100644 index 00000000..52f87f3a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png new file mode 100644 index 00000000..9696e0b5 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png new file mode 100644 index 00000000..d23ee492 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png new file mode 100644 index 00000000..9d368900 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png new file mode 100644 index 00000000..daf0788a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png new file mode 100644 index 00000000..b4033365 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png new file mode 100644 index 00000000..9d499f23 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png new file mode 100644 index 00000000..0291dfb5 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png new file mode 100644 index 00000000..54a889d8 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png new file mode 100644 index 00000000..ba79b3b8 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png new file mode 100644 index 00000000..bedf366c Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png new file mode 100644 index 00000000..0731760d Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png new file mode 100644 index 00000000..898efdc4 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png new file mode 100644 index 00000000..39f5db8a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png new file mode 100644 index 00000000..bee4cff0 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png new file mode 100644 index 00000000..969b9119 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png new file mode 100644 index 00000000..a72cf182 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png new file mode 100644 index 00000000..9a13e7c6 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png new file mode 100644 index 00000000..da3ee77f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png new file mode 100644 index 00000000..93da7f4f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png new file mode 100644 index 00000000..7510931b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png new file mode 100644 index 00000000..f99454b1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png new file mode 100644 index 00000000..a17a1404 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png new file mode 100644 index 00000000..f763540c Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png new file mode 100644 index 00000000..97a829e9 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png new file mode 100644 index 00000000..7eadf751 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png new file mode 100644 index 00000000..5241195d Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png new file mode 100644 index 00000000..2a3ea8e2 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png new file mode 100644 index 00000000..f4b263b1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png new file mode 100644 index 00000000..1563ff39 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png new file mode 100644 index 00000000..394bb53d Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png new file mode 100644 index 00000000..3f84ad47 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png new file mode 100644 index 00000000..8ea650ad Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png new file mode 100644 index 00000000..2c8cf3f5 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png new file mode 100644 index 00000000..caf6a90e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/meta.txt b/assets/dolphin/external/L1_Kaiju_128x64/meta.txt new file mode 100644 index 00000000..597f4b69 --- /dev/null +++ b/assets/dolphin/external/L1_Kaiju_128x64/meta.txt @@ -0,0 +1,50 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 16 +Active frames: 60 +Frames order: 0 1 2 1 0 3 4 5 6 3 7 1 8 1 7 9 0 10 11 12 13 14 15 16 14 12 17 18 19 20 21 22 22 23 22 24 25 26 27 26 25 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 19 18 14 45 14 45 14 45 14 45 14 16 14 14 11 46 47 1 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 83 +Y: 42 +Text: Sup +AlignH: Left +AlignV: Top +StartFrame: 38 +EndFrame: 40 + +Slot: 0 +X: 66 +Y: 35 +Text: What just +AlignH: Left +AlignV: Center +StartFrame: 62 +EndFrame: 65 + +Slot: 0 +X: 66 +Y: 35 +Text: happened? +AlignH: Left +AlignV: Center +StartFrame: 66 +EndFrame: 68 + +Slot: 0 +X: 87 +Y: 38 +Text: Idk +AlignH: Left +AlignV: Top +StartFrame: 70 +EndFrame: 70 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 42991523..92215471 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -90,6 +90,13 @@ Min butthurt: 0 Max butthurt: 5 Min level: 1 Max level: 3 +Weight: 3 + +Name: L1_Kaiju_128x64 +Min butthurt: 0 +Max butthurt: 10 +Min level: 1 +Max level: 3 Weight: 4 Name: L2_Wake_up_128x64 diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 7f0dcebd..3c075e0d 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.1,, +Version,+,28.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -80,6 +80,7 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_vibro.h,, +Header,+,lib/digital_signal/digital_signal.h,, Header,+,lib/flipper_application/api_hashtable/api_hashtable.h,, Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, Header,+,lib/flipper_application/flipper_application.h,, @@ -617,6 +618,24 @@ Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, u Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" Function,-,difftime,double,"time_t, time_t" +Function,-,digital_sequence_add,void,"DigitalSequence*, uint8_t" +Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" +Function,-,digital_sequence_clear,void,DigitalSequence* +Function,-,digital_sequence_free,void,DigitalSequence* +Function,-,digital_sequence_send,_Bool,DigitalSequence* +Function,-,digital_sequence_set_sendtime,void,"DigitalSequence*, uint32_t" +Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" +Function,-,digital_sequence_timebase_correction,void,"DigitalSequence*, float" +Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" +Function,-,digital_signal_add_pulse,void,"DigitalSignal*, uint32_t, _Bool" +Function,-,digital_signal_alloc,DigitalSignal*,uint32_t +Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*" +Function,-,digital_signal_free,void,DigitalSignal* +Function,-,digital_signal_get_edge,uint32_t,"DigitalSignal*, uint32_t" +Function,-,digital_signal_get_edges_cnt,uint32_t,DigitalSignal* +Function,-,digital_signal_get_start_level,_Bool,DigitalSignal* +Function,-,digital_signal_prepare_arr,void,DigitalSignal* +Function,-,digital_signal_send,void,"DigitalSignal*, const GpioPin*" Function,-,diprintf,int,"int, const char*, ..." Function,+,dir_walk_alloc,DirWalk*,Storage* Function,+,dir_walk_close,void,DirWalk* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 1259f0fc..a9ce0c26 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.1,, +Version,+,28.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -86,6 +86,7 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_vibro.h,, +Header,+,lib/digital_signal/digital_signal.h,, Header,+,lib/flipper_application/api_hashtable/api_hashtable.h,, Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, Header,+,lib/flipper_application/flipper_application.h,, diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index c5036445..e3dc7832 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -28,6 +28,7 @@ "flipperformat", "toolbox", "nfc", + "digital_signal", "pulse_reader", "microtar", "usb_stm32", diff --git a/lib/ReadMe.md b/lib/ReadMe.md index 93236b26..138bef2b 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -27,6 +27,7 @@ - `nfc` - NFC library, used by NFC application - `one_wire` - OneWire library, used by iButton application - `print` - Tiny printf implementation +- `digital_signal` - Digital Signal library used by NFC for software implemented protocols - `pulse_reader` - Pulse Reader library used by NFC for software implemented protocols - `qrcode` - QR-Code library - `stm32wb_cmsis` - STM32WB series CMSIS headers, extends CMSIS Core diff --git a/lib/SConscript b/lib/SConscript index 8727746d..495ba4bf 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -15,7 +15,6 @@ env.Append( Dir("u8g2"), Dir("update_util"), Dir("print"), - Dir("pulse_reader"), ], ) @@ -95,6 +94,7 @@ libs = env.BuildModules( "mbedtls", "subghz", "nfc", + "digital_signal", "pulse_reader", "appframe", "misc", diff --git a/lib/digital_signal/SConscript b/lib/digital_signal/SConscript new file mode 100644 index 00000000..2ddf7a58 --- /dev/null +++ b/lib/digital_signal/SConscript @@ -0,0 +1,20 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/digital_signal", + ], + SDK_HEADERS=[ + File("digital_signal.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="digital_signal") +libenv.ApplyLibFlags() +libenv.Append(CCFLAGS=["-O3", "-funroll-loops", "-Ofast"]) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index ab25458b..94dbeeab 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -212,8 +212,7 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { internals->reload_reg_entries = 0; for(size_t pos = 0; pos < signal->edge_cnt; pos++) { - uint32_t edge_scaled = (internals->factor * signal->edge_timings[pos]) / (1024 * 1024); - uint32_t pulse_duration = edge_scaled + internals->reload_reg_remainder; + uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder; if(pulse_duration < 10 || pulse_duration > 10000000) { FURI_LOG_D( TAG, diff --git a/lib/misc.scons b/lib/misc.scons index 1ff6e2fb..10aa3f9d 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -4,7 +4,6 @@ Import("env") env.Append( CPPPATH=[ - "#/lib/digital_signal", "#/lib/fnv1a_hash", "#/lib/heatshrink", "#/lib/micro-ecc", @@ -26,7 +25,6 @@ libenv.ApplyLibFlags() sources = [] libs_recurse = [ - "digital_signal", "micro-ecc", "u8g2", "update_util", diff --git a/scripts/flipper/assets/dolphin.py b/scripts/flipper/assets/dolphin.py index ebe9fd88..b4a53a62 100644 --- a/scripts/flipper/assets/dolphin.py +++ b/scripts/flipper/assets/dolphin.py @@ -49,11 +49,11 @@ class DolphinBubbleAnimation: def load(self, animation_directory: str): if not os.path.isdir(animation_directory): - raise Exception(f"Animation folder doesn't exists: { animation_directory }") + raise Exception(f"Animation folder doesn't exist: { animation_directory }") meta_filename = os.path.join(animation_directory, "meta.txt") if not os.path.isfile(meta_filename): - raise Exception(f"Animation meta file doesn't exists: { meta_filename }") + raise Exception(f"Animation meta file doesn't exist: { meta_filename }") self.logger.info(f"Loading meta from {meta_filename}") file = FlipperFormatFile()