diff --git a/CHANGELOG.md b/CHANGELOG.md index 73489b07c..0c840f37f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,30 +1,57 @@ -### Note: -- This is a smaller release due to the bugfixes it contains, make sure to also read the changelog for [mntm-006](https://github.com/Next-Flip/Momentum-Firmware/releases/tag/mntm-006) - ### Added: - Apps: - - Games: Laser Tag (by @RocketGod-git & @jamisonderek) -- NFC: Added new Saflok parser (#196 #201 by @zinongli & @xtruan & @zacharyweiss & @evilmog & @Arkwin) -- OFW: Desktop: New Procrastination dolphin animation (by @Astrrra) + - NFC: Cyborg Detector (by @RocketGod-git) +- Sub-GHz: + - UL: Add Marantec24 (static 24 bit) with add manually (by @xMasterX) + - UL: Add GangQi protocol (static 34 bit) with button parsing and add manually (by @xMasterX & @Skorpionm) + - UL: Add Hollarm protocol (static 42 bit) with button parsing and add manually (by @xMasterX & @Skorpionm) + - UL: Add Hay21 protocol (dynamic 21 bit) with button parsing (by @xMasterX) +- BadKB: + - OFW: Add linux/gnome badusb demo files (by @thomasnemer) + - Add older qFlipper install demos for windows and macos (by @DXVVAY & @grugnoymeme) +- OFW: Dolphin: Happy mode in Desktop settings (by @portasynthinca3) +- OFW: GUI: Add up and down button drawing functions to GUI elements (by @DerSkythe) +- OFW: RPC: Support 5V on GPIO control for ext. modules (by @gsurkov) +- OFW: Toolbox: Proper integer parsing library `strint` (by @portasynthinca3) ### Updated: - Apps: - - Picopass: CVE-2024-41566, When keys are unknown emulate with a dummy MAC and ignore reader MACs (by @nvx) - - Seader: Card parsing and saving UI and logic improvements (by @bettse) - - Authenticator: Confirm token export on Flipper (by @akopachov) - - NFC Playlist: Allow delay up to 12s (by @xtruan) - - BLE Spam: Fix delay help section (by @Willy-JL) - - WAV Player: Fix unresponsiveness (by @Willy-JL) -- Sub-GHz: - - Show Weather Station icon for WS signals (by @Willy-JL) - - Update Oregon 2 and 3 weather info display to new design (by @Willy-JL) -- API: Publishing T5577 page 1 block count macro (by @zinongli) + - WAV Player: Better fix for unresponsiveness, handle thread exit signal (by @CookiePLMonster) + - Laster Tag: External Infrared board support (by @RocketGod-git), RFID support for ammo reload (by @jamisonderek) + - ESP Flasher: Update blackmagic bin with WiFi Logs (by @DrZlo13) + - Picopass: File loading improvements and fixes (by @bettse) + - Quac!: Setting for external IR board support (by @daniilty), option to import all IR signals from file, code improvements (by @rdefeo) + - UL: Sub-GHz Bruteforcer: Add new protocols for existing dump option (by @xMasterX) + - UL: NRF24 Apps: Use string library compatible with OFW SDK (by @xMasterX) + - OFW: SPI Mem Manager: Fixed UI rendering bug related to line breaks (by @portasynthinca3) +- CLI: Print plugin name on load fail (by @Willy-JL) +- OFW: NFC: Rename 'Detect Reader' to 'Extract MF Keys' (by @bettse) +- Infrared: + - OFW: IR button operation fails now shows more informative messages (by @RebornedBrain) + - OFW: Add Airwell AW-HKD012-N91 to univeral AC remote (by @valeraOlexienko) + - OFW: Add TCL 75S451 to TV universal remote (by @christhetech131) +- OFW: GUI: Change dialog_ex text ownership model (by @skotopes) +- OFW: CCID: App changes and improvements (by @kidbomb) +- OFW: API: Exposed `view_dispatcher_get_event_loop` (by @CookiePLMonster) +- Furi: + - UL: Extra checks for OTG power enable/disable (by @xMasterX) + - OFW: Replace all calls to strncpy with strlcpy, use strdup more, expose strlcat (by @CookiePLMonster) + - OFW: Threading, Timers improvements (by @CookiePLMonster) + - OFW: FuriTimer uses an event instead of a volatile bool to wait for deletion (by @CookiePLMonster) ### Fixed: -- Sub-GHz: Fix Acurite 986 temperature value conversion (by @Willy-JL) -- Desktop: - - Fix disabling keybinds (by @Willy-JL) - - Sanity check PIN length for good measure (by @Willy-JL) - - Fix PIN locked with no PIN set edge case (by @Willy-JL) -- Settings: Fix duplicates in Power Settings when opening submenus (by @Willy-JL) -- RGB Backlight: Fix config migration (by @Willy-JL) +- RFID: + - OFW: Fix detection of GProx II cards and false detection of other cards (by @Astrrra) + - OFW: Fix Guard GProxII False Positive and 36-bit Parsing (by @zinongli) + - OFW: GProxII Fix Writing and Rendering Conflict (by @zinongli) +- Desktop: Fallback Poweroff prompt when power settings is unavailable (by @Willy-JL) +- Storage: Fallback SD format prompt when storage settings is unavailable (by @Willy-JL) +- About: Fix BLE stack version string (by @Willy-JL) +- OFW: Loader: Warn about missing SD card for main apps (by @Willy-JL) +- NFC: + - OFW: Fix crash on Ultralight unlock (by @Astrrra) + - OFW: FeliCa anti-collision fix (by @RebornedBrain) +- OFW: RPC: Broken file interaction fixes (by @RebornedBrain) +- OFW: GUI: Fix dialog_ex NULL ptr crash (by @Willy-JL) +- OFW: Furi: Clean up of LFS traces (by @hedger) +- OFW: Debug: Use proper hook for handle_exit in flipperapps (by @skotopes) diff --git a/SConstruct b/SConstruct index f404bd02e..722713a28 100644 --- a/SConstruct +++ b/SConstruct @@ -234,7 +234,7 @@ firmware_debug = distenv.PhonyTarget( ) distenv.Depends(firmware_debug, firmware_flash) -distenv.PhonyTarget( +firmware_blackmagic = distenv.PhonyTarget( "blackmagic", "${GDBPYCOM}", source=firmware_env["FW_ELF"], @@ -242,6 +242,7 @@ distenv.PhonyTarget( GDBREMOTE="${BLACKMAGIC_ADDR}", FBT_FAP_DEBUG_ELF_ROOT=firmware_env["FBT_FAP_DEBUG_ELF_ROOT"], ) +distenv.Depends(firmware_blackmagic, firmware_flash) # Debug alien elf debug_other_opts = [ diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c index abb8ad3dd..4158c1a60 100644 --- a/applications/debug/ccid_test/ccid_test_app.c +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -105,7 +105,7 @@ void ccid_test_app_free(CcidTestApp* app) { furi_record_close(RECORD_GUI); app->gui = NULL; - free(app->iso7816_handler); + iso7816_handler_free(app->iso7816_handler); // Free rest free(app); @@ -121,8 +121,7 @@ int32_t ccid_test_app(void* p) { furi_hal_usb_unlock(); furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true); - furi_hal_usb_ccid_set_callbacks( - (CcidCallbacks*)&app->iso7816_handler->ccid_callbacks, app->iso7816_handler); + iso7816_handler_set_usb_ccid_callbacks(); furi_hal_usb_ccid_insert_smartcard(); //handle button events @@ -142,7 +141,7 @@ int32_t ccid_test_app(void* p) { } //tear down USB - furi_hal_usb_ccid_set_callbacks(NULL, NULL); + iso7816_handler_reset_usb_ccid_callbacks(); furi_hal_usb_set_config(usb_mode_prev, NULL); //teardown view diff --git a/applications/debug/ccid_test/iso7816/iso7816_handler.c b/applications/debug/ccid_test/iso7816/iso7816_handler.c index 97214d1b2..8f0f758b2 100644 --- a/applications/debug/ccid_test/iso7816/iso7816_handler.c +++ b/applications/debug/ccid_test/iso7816/iso7816_handler.c @@ -6,11 +6,17 @@ #include #include +#include "iso7816_handler.h" + #include "iso7816_t0_apdu.h" #include "iso7816_atr.h" -#include "iso7816_handler.h" #include "iso7816_response.h" +static Iso7816Handler* iso7816_handler; +static CcidCallbacks* ccid_callbacks; +static uint8_t* command_apdu_buffer; +static uint8_t* response_apdu_buffer; + void iso7816_icc_power_on_callback(uint8_t* atr_data, uint32_t* atr_data_len, void* context) { furi_check(context); @@ -40,12 +46,11 @@ void iso7816_xfr_datablock_callback( Iso7816Handler* handler = (Iso7816Handler*)context; - ISO7816_Response_APDU* response_apdu = (ISO7816_Response_APDU*)&handler->response_apdu_buffer; - - ISO7816_Command_APDU* command_apdu = (ISO7816_Command_APDU*)&handler->command_apdu_buffer; + ISO7816_Response_APDU* response_apdu = (ISO7816_Response_APDU*)response_apdu_buffer; + ISO7816_Command_APDU* command_apdu = (ISO7816_Command_APDU*)command_apdu_buffer; uint8_t result = iso7816_read_command_apdu( - command_apdu, pc_to_reader_datablock, pc_to_reader_datablock_len); + command_apdu, pc_to_reader_datablock, pc_to_reader_datablock_len, CCID_SHORT_APDU_SIZE); if(result == ISO7816_READ_COMMAND_APDU_OK) { handler->iso7816_process_command(command_apdu, response_apdu); @@ -61,8 +66,31 @@ void iso7816_xfr_datablock_callback( } Iso7816Handler* iso7816_handler_alloc() { - Iso7816Handler* handler = malloc(sizeof(Iso7816Handler)); - handler->ccid_callbacks.icc_power_on_callback = iso7816_icc_power_on_callback; - handler->ccid_callbacks.xfr_datablock_callback = iso7816_xfr_datablock_callback; - return handler; + iso7816_handler = malloc(sizeof(Iso7816Handler)); + + command_apdu_buffer = malloc(sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE); + response_apdu_buffer = malloc(sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE); + + ccid_callbacks = malloc(sizeof(CcidCallbacks)); + ccid_callbacks->icc_power_on_callback = iso7816_icc_power_on_callback; + ccid_callbacks->xfr_datablock_callback = iso7816_xfr_datablock_callback; + + return iso7816_handler; +} + +void iso7816_handler_set_usb_ccid_callbacks() { + furi_hal_usb_ccid_set_callbacks(ccid_callbacks, iso7816_handler); +} + +void iso7816_handler_reset_usb_ccid_callbacks() { + furi_hal_usb_ccid_set_callbacks(NULL, NULL); +} + +void iso7816_handler_free(Iso7816Handler* handler) { + free(ccid_callbacks); + + free(command_apdu_buffer); + free(response_apdu_buffer); + + free(handler); } diff --git a/applications/debug/ccid_test/iso7816/iso7816_handler.h b/applications/debug/ccid_test/iso7816/iso7816_handler.h index d67118ce6..4f9703e46 100644 --- a/applications/debug/ccid_test/iso7816/iso7816_handler.h +++ b/applications/debug/ccid_test/iso7816/iso7816_handler.h @@ -5,14 +5,14 @@ #include "iso7816_t0_apdu.h" typedef struct { - CcidCallbacks ccid_callbacks; void (*iso7816_answer_to_reset)(Iso7816Atr* atr); void (*iso7816_process_command)( const ISO7816_Command_APDU* command, ISO7816_Response_APDU* response); - - uint8_t command_apdu_buffer[sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE]; - uint8_t response_apdu_buffer[sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE]; } Iso7816Handler; Iso7816Handler* iso7816_handler_alloc(); + +void iso7816_handler_free(Iso7816Handler* handler); +void iso7816_handler_set_usb_ccid_callbacks(); +void iso7816_handler_reset_usb_ccid_callbacks(); diff --git a/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c index 216f2582f..023daebe2 100644 --- a/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c +++ b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c @@ -1,49 +1,48 @@ /* Implements rudimentary iso7816-3 support for APDU (T=0) */ #include #include -#include -#include #include "iso7816_t0_apdu.h" -//reads dataBuffer with dataLen size, translate it into a ISO7816_Command_APDU type +//reads pc_to_reader_datablock_len with pc_to_reader_datablock_len size, translate it into a ISO7816_Command_APDU type //extra data will be pointed to commandDataBuffer uint8_t iso7816_read_command_apdu( ISO7816_Command_APDU* command, - const uint8_t* dataBuffer, - uint32_t dataLen) { - command->CLA = dataBuffer[0]; - command->INS = dataBuffer[1]; - command->P1 = dataBuffer[2]; - command->P2 = dataBuffer[3]; + const uint8_t* pc_to_reader_datablock, + uint32_t pc_to_reader_datablock_len, + uint32_t max_apdu_size) { + command->CLA = pc_to_reader_datablock[0]; + command->INS = pc_to_reader_datablock[1]; + command->P1 = pc_to_reader_datablock[2]; + command->P2 = pc_to_reader_datablock[3]; - if(dataLen == 4) { + if(pc_to_reader_datablock_len == 4) { command->Lc = 0; command->Le = 0; command->LePresent = false; return ISO7816_READ_COMMAND_APDU_OK; - } else if(dataLen == 5) { + } else if(pc_to_reader_datablock_len == 5) { //short le command->Lc = 0; - command->Le = dataBuffer[4]; + command->Le = pc_to_reader_datablock[4]; command->LePresent = true; return ISO7816_READ_COMMAND_APDU_OK; - } else if(dataLen > 5 && dataBuffer[4] != 0x00) { + } else if(pc_to_reader_datablock_len > 5 && pc_to_reader_datablock[4] != 0x00) { //short lc - command->Lc = dataBuffer[4]; - if(command->Lc > 0 && command->Lc < CCID_SHORT_APDU_SIZE) { //-V560 - memcpy(command->Data, &dataBuffer[5], command->Lc); + command->Lc = pc_to_reader_datablock[4]; + if(command->Lc > 0 && command->Lc < max_apdu_size) { //-V560 + memcpy(command->Data, &pc_to_reader_datablock[5], command->Lc); //does it have a short le too? - if(dataLen == (uint32_t)(command->Lc + 5)) { + if(pc_to_reader_datablock_len == (uint32_t)(command->Lc + 5)) { command->Le = 0; command->LePresent = false; return ISO7816_READ_COMMAND_APDU_OK; - } else if(dataLen == (uint32_t)(command->Lc + 6)) { - command->Le = dataBuffer[dataLen - 1]; + } else if(pc_to_reader_datablock_len == (uint32_t)(command->Lc + 6)) { + command->Le = pc_to_reader_datablock[pc_to_reader_datablock_len - 1]; command->LePresent = true; return ISO7816_READ_COMMAND_APDU_OK; diff --git a/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h index a21dfbafc..bbd2a1b23 100644 --- a/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h +++ b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h @@ -34,7 +34,8 @@ typedef struct { uint8_t iso7816_read_command_apdu( ISO7816_Command_APDU* command, const uint8_t* pc_to_reader_datablock, - uint32_t pc_to_reader_datablock_len); + uint32_t pc_to_reader_datablock_len, + uint32_t max_apdu_size); void iso7816_write_response_apdu( const ISO7816_Response_APDU* response, uint8_t* reader_to_pc_datablock, diff --git a/applications/debug/expansion_test/expansion_test.c b/applications/debug/expansion_test/expansion_test.c index e3bcf4e9c..665874f47 100644 --- a/applications/debug/expansion_test/expansion_test.c +++ b/applications/debug/expansion_test/expansion_test.c @@ -6,18 +6,27 @@ * 13 -> 16 (USART TX to LPUART RX) * 14 -> 15 (USART RX to LPUART TX) * + * Optional: Connect an LED with an appropriate series resistor + * between pins 1 and 8. It will always be on if the device is + * connected to USB power, so unplug it before running the app. + * * What this application does: * * - Enables module support and emulates the module on a single device * (hence the above connection), * - Connects to the expansion module service, sets baud rate, + * - Enables OTG (5V) on GPIO via plain expansion protocol, + * - Waits 5 cycles of idle loop (1 second), * - Starts the RPC session, + * - Disables OTG (5V) on GPIO via RPC messages, + * - Waits 5 cycles of idle loop (1 second), * - Creates a directory at `/ext/ExpansionTest` and writes a file * named `test.txt` under it, * - Plays an audiovisual alert (sound and blinking display), - * - Waits 10 cycles of idle loop, + * - Enables OTG (5V) on GPIO via RPC messages, + * - Waits 5 cycles of idle loop (1 second), * - Stops the RPC session, - * - Waits another 10 cycles of idle loop, + * - Disables OTG (5V) on GPIO via plain expansion protocol, * - Exits (plays a sound if any of the above steps failed). */ #include @@ -302,6 +311,22 @@ static bool expansion_test_app_handshake(ExpansionTestApp* instance) { return success; } +static bool expansion_test_app_enable_otg(ExpansionTestApp* instance, bool enable) { + bool success = false; + + do { + const ExpansionFrameControlCommand command = enable ? + ExpansionFrameControlCommandEnableOtg : + ExpansionFrameControlCommandDisableOtg; + if(!expansion_test_app_send_control_request(instance, command)) break; + if(!expansion_test_app_receive_frame(instance, &instance->frame)) break; + if(!expansion_test_app_is_success_response(&instance->frame)) break; + success = true; + } while(false); + + return success; +} + static bool expansion_test_app_start_rpc(ExpansionTestApp* instance) { bool success = false; @@ -396,6 +421,27 @@ static bool expansion_test_app_rpc_alert(ExpansionTestApp* instance) { return success; } +static bool expansion_test_app_rpc_enable_otg(ExpansionTestApp* instance, bool enable) { + bool success = false; + + instance->msg.command_id++; + instance->msg.command_status = PB_CommandStatus_OK; + instance->msg.which_content = PB_Main_gpio_set_otg_mode_tag; + instance->msg.content.gpio_set_otg_mode.mode = enable ? PB_Gpio_GpioOtgMode_ON : + PB_Gpio_GpioOtgMode_OFF; + instance->msg.has_next = false; + + do { + if(!expansion_test_app_send_rpc_request(instance, &instance->msg)) break; + if(!expansion_test_app_receive_rpc_request(instance, &instance->msg)) break; + if(instance->msg.which_content != PB_Main_empty_tag) break; + if(instance->msg.command_status != PB_CommandStatus_OK) break; + success = true; + } while(false); + + return success; +} + static bool expansion_test_app_idle(ExpansionTestApp* instance, uint32_t num_cycles) { uint32_t num_cycles_done; for(num_cycles_done = 0; num_cycles_done < num_cycles; ++num_cycles_done) { @@ -434,13 +480,18 @@ int32_t expansion_test_app(void* p) { if(!expansion_test_app_send_presence(instance)) break; if(!expansion_test_app_wait_ready(instance)) break; if(!expansion_test_app_handshake(instance)) break; + if(!expansion_test_app_enable_otg(instance, true)) break; + if(!expansion_test_app_idle(instance, 5)) break; if(!expansion_test_app_start_rpc(instance)) break; + if(!expansion_test_app_rpc_enable_otg(instance, false)) break; + if(!expansion_test_app_idle(instance, 5)) break; if(!expansion_test_app_rpc_mkdir(instance)) break; if(!expansion_test_app_rpc_write(instance)) break; if(!expansion_test_app_rpc_alert(instance)) break; - if(!expansion_test_app_idle(instance, 10)) break; + if(!expansion_test_app_rpc_enable_otg(instance, true)) break; + if(!expansion_test_app_idle(instance, 5)) break; if(!expansion_test_app_stop_rpc(instance)) break; - if(!expansion_test_app_idle(instance, 10)) break; + if(!expansion_test_app_enable_otg(instance, false)) break; success = true; } while(false); diff --git a/applications/debug/rpc_debug_app/rpc_debug_app.c b/applications/debug/rpc_debug_app/rpc_debug_app.c index 1536b8918..1affeae29 100644 --- a/applications/debug/rpc_debug_app/rpc_debug_app.c +++ b/applications/debug/rpc_debug_app/rpc_debug_app.c @@ -24,7 +24,7 @@ static void rpc_debug_app_tick_event_callback(void* context) { 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, "", buf_size); + strlcpy(buf, "", buf_size); return; } diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c index 367ca7a4f..09d58c2c9 100644 --- a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c @@ -1,5 +1,7 @@ #include "../rpc_debug_app.h" +#include + static bool rpc_debug_app_scene_input_error_code_validator_callback( const char* text, FuriString* error, @@ -24,7 +26,7 @@ static void rpc_debug_app_scene_input_error_code_result_callback(void* context) void rpc_debug_app_scene_input_error_code_on_enter(void* context) { RpcDebugApp* app = context; - strncpy(app->text_store, "666", TEXT_STORE_SIZE); + strlcpy(app->text_store, "666", TEXT_STORE_SIZE); text_input_set_header_text(app->text_input, "Enter error code"); text_input_set_validator( app->text_input, rpc_debug_app_scene_input_error_code_validator_callback, NULL); @@ -44,9 +46,8 @@ bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEv if(event.type == SceneManagerEventTypeCustom) { if(event.event == RpcDebugAppCustomEventInputErrorCode) { - char* end; - int error_code = strtol(app->text_store, &end, 10); - if(!*end) { + uint32_t error_code; + if(strint_to_uint32(app->text_store, NULL, &error_code, 10) == StrintParseNoError) { rpc_system_app_set_error_code(app->rpc, error_code); } scene_manager_previous_scene(app->scene_manager); diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c index b07f8f4af..cca293b66 100644 --- a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c @@ -7,7 +7,7 @@ static void rpc_debug_app_scene_input_error_text_result_callback(void* context) void rpc_debug_app_scene_input_error_text_on_enter(void* context) { RpcDebugApp* app = context; - strncpy(app->text_store, "I'm a scary error message!", TEXT_STORE_SIZE); + strlcpy(app->text_store, "I'm a scary error message!", TEXT_STORE_SIZE); text_input_set_header_text(app->text_input, "Enter error text"); text_input_set_result_callback( app->text_input, diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c index 10d5e36f6..686a6f95d 100644 --- a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c @@ -2,7 +2,7 @@ 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); + strlcpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE); text_box_set_text(app->text_box, app->text_store); text_box_set_font(app->text_box, TextBoxFontHex); diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index bf38ba4c2..1d5a473ca 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -321,7 +323,7 @@ int32_t uart_echo_app(void* p) { uint32_t baudrate = DEFAULT_BAUD_RATE; if(p) { const char* baudrate_str = p; - if(sscanf(baudrate_str, "%lu", &baudrate) != 1) { + if(strint_to_uint32(baudrate_str, NULL, &baudrate, 10) != StrintParseNoError) { FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str); baudrate = DEFAULT_BAUD_RATE; } diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam index f5f84ead7..c87305847 100644 --- a/applications/debug/unit_tests/application.fam +++ b/applications/debug/unit_tests/application.fam @@ -220,3 +220,11 @@ App( entry_point="get_api", requires=["unit_tests"], ) + +App( + appid="test_strint", + sources=["tests/common/*.c", "tests/strint/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/gangqi_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/gangqi_raw.sub new file mode 100644 index 000000000..a91f55f83 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/gangqi_raw.sub @@ -0,0 +1,12 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok270Async +Protocol: RAW +RAW_Data: 1572 -3304 199 -134 67 -132 133 -168 429 -100 1609 -66 587 -51388 727 -578 377 -1382 1081 -538 393 -1340 1111 -526 1143 -482 441 -1312 1151 -490 1169 -482 1169 -484 1145 -520 417 -1310 511 -2206 1137 -506 1167 -482 445 -1280 1149 -510 447 -1284 449 -1276 1171 -484 461 -1272 1189 -484 443 -1284 1165 -474 469 -1262 1159 -490 479 -1242 1195 -500 421 -1310 1135 -492 1175 -488 439 -1308 1141 -500 1151 -488 1167 -490 447 -1266 1171 -512 433 -1302 1133 -502 1153 -484 471 -1264 1169 -476 1151 -480 1167 -504 1131 -516 445 -1284 549 -2170 1163 -490 1177 -488 437 -1308 1139 -500 455 -1276 429 -1308 1131 -512 443 -1308 1139 -516 433 -1274 1175 -498 455 -1276 1163 -460 483 -1272 1163 -462 487 -1276 1147 -486 1169 -486 439 -1306 1137 -510 1149 -486 1171 -490 451 -1274 1173 -478 459 -1278 1147 -490 1173 -490 435 -1274 1177 -506 1149 -484 1167 -478 1173 -486 451 -1274 545 -2206 1145 -472 1167 -488 445 -1296 1163 -500 457 -1280 429 -1304 1161 -482 443 -1304 1139 -480 465 -1270 1163 -504 459 -1272 1163 -496 457 -1272 1163 -500 453 -1274 1163 -496 1143 -490 441 -1306 1143 -502 1145 -484 1165 -490 453 -1300 1139 -484 463 -1300 1143 -502 1145 -488 473 -1268 1139 -508 1145 -488 1167 -478 1171 -464 471 -1300 533 -2184 1147 -486 1163 -524 417 -1312 1117 -516 447 -1282 441 -1304 1129 -520 449 -1306 1137 -514 433 -1276 1147 -528 417 -1316 1117 -512 447 -1298 1131 -514 445 -1300 1149 -478 1179 -476 461 -1276 1145 -526 1141 -486 1163 -486 449 -1276 1139 -508 461 -1278 1147 -492 1175 -484 473 -1274 1141 -506 1145 -480 1169 -486 1145 -520 449 -1276 547 -2200 1147 -486 1145 -490 453 -1308 1149 -486 439 -1302 447 -1282 1139 -500 445 -1316 1145 -482 471 -1270 1139 -508 457 -1306 1133 -502 457 -1278 1147 -488 447 -1312 1133 -490 1171 -482 439 -1306 1137 -502 1147 -482 1167 -522 419 -1312 1137 -482 463 -1276 1175 -498 1141 -488 473 -1274 1139 -502 1147 -490 1171 -490 1141 -504 453 -1284 545 -2170 1187 -454 1185 -488 453 -1274 1137 -486 465 -1308 417 -1314 1141 -502 447 -1284 1177 -486 439 -1308 1143 -500 453 -1276 1151 -486 447 -1280 1163 -486 445 -1298 1175 -464 1177 -486 473 -1268 1141 -506 1149 -484 1173 -490 447 -1274 1173 -478 459 -1278 1183 -484 1137 -516 433 -1272 1177 -466 1175 -488 1167 -492 1143 -490 453 -1272 545 -2198 1141 -524 1147 -494 443 -1290 1149 -490 473 -1272 447 -1286 1141 -490 455 -1308 1137 -482 465 -1272 1165 -506 455 -1276 1147 -490 449 -1280 1161 -488 447 -1298 1149 -490 1173 -488 439 -1308 1139 -500 1145 -524 1141 -480 449 -1276 1171 -480 463 -1276 1177 -462 1171 -490 439 -1308 1141 -502 1145 -518 1139 -502 1161 -482 +RAW_Data: 441 -1294 535 -2190 1161 -492 1143 -478 483 -1278 1135 -482 465 -1276 481 -1286 1141 -488 457 -1308 1139 -480 463 -1276 1149 -514 447 -1284 1141 -488 469 -1268 1153 -522 449 -1276 1173 -478 1153 -490 477 -1272 1141 -504 1147 -484 1163 -478 449 -1306 1139 -512 431 -1300 1173 -478 1153 -488 443 -1304 1139 -504 1151 -486 1171 -480 1139 -488 453 -1308 511 -2234 1149 -464 1183 -484 427 -1306 1165 -488 447 -1274 445 -1298 1143 -488 451 -1306 1151 -488 439 -1306 1173 -476 459 -1278 1149 -490 449 -1316 1119 -516 445 -1286 1163 -480 1139 -508 459 -1306 1135 -504 1145 -486 1171 -488 449 -1276 1139 -508 461 -1278 1145 -490 1175 -486 437 -1308 1143 -498 1155 -486 1169 -488 1145 -520 417 -1306 511 -2204 1143 -506 1165 -480 447 -1282 1143 -506 467 -1298 431 -1306 1133 -502 455 -1298 1135 -502 423 -1312 1147 -486 449 -1280 1163 -490 445 -1302 1145 -526 417 -1312 1153 -484 1137 -516 435 -1308 1143 -498 1145 -486 1165 -488 447 -1272 1171 -514 431 -1310 1143 -498 1143 -486 439 -1304 1141 -504 1149 -484 1171 -488 1143 -504 447 -1280 547 -2208 1147 -492 1133 -514 447 -1284 1141 -522 417 -1306 447 -1280 1141 -520 431 -1304 1161 -488 451 -1304 1139 -478 461 -1278 1143 -526 419 -1308 1135 -492 449 -1316 1151 -482 1139 -514 439 -1306 1139 -506 1147 -518 1137 -488 449 -1276 1171 -476 463 -1276 1145 -492 1175 -484 437 -1304 1139 -508 1151 -486 1169 -476 1177 -484 449 -1278 547 -2204 1145 -484 1145 -488 451 -1304 1155 -480 475 -1274 445 -1298 1147 -492 441 -1302 1133 -520 447 -1274 1143 -508 459 -1276 1151 -518 447 -1278 1141 -488 449 -1308 1153 -484 1167 -486 449 -1308 1105 -542 1123 -518 1137 -508 447 -1282 1143 -514 435 -1276 1175 -498 1147 -482 437 -1308 1141 -504 1149 -484 1167 -488 1143 -522 449 -243660 97 -66 297 -538 65 -100 131 -464 97 -100 265 -132 6257 -68 359583 -65666 99 -510 957 -22306 101 -1456 1111 -482 491 -1272 1021 -764 65 -1412 1083 -558 355 -1366 1075 -622 161 -64 263 -478 1195 -464 1145 -520 425 -1306 475 -1266 1139 -536 1091 -548 407 -1304 1139 -500 1151 -482 1167 -476 445 -1314 447 -1280 549 -2200 1133 -524 1111 -518 439 -1308 1143 -496 449 -1264 441 -1314 1145 -506 449 -1276 1177 -488 439 -1308 1137 -504 455 -1276 1145 -490 449 -1276 1161 -492 449 -1312 1131 -524 1143 -484 471 -1276 1143 -506 1155 -486 1163 -478 1171 -464 445 -1318 453 -1278 1149 -478 1167 -480 463 -1302 1165 -476 1147 -490 1173 -490 451 -1270 445 -1288 547 -2172 1173 -482 1147 -492 447 -1294 1163 -506 459 -1278 457 -1278 1161 -480 447 -1302 1137 -484 463 -1272 1163 -504 455 -1276 1147 -524 453 -1278 1151 -484 445 -1294 1165 -498 1149 -486 +RAW_Data: 469 -1268 1143 -506 1149 -488 1165 -488 1181 -464 447 -1284 449 -1312 1153 -486 1137 -518 435 -1272 1175 -498 1141 -488 1169 -492 449 -1274 445 -1294 535 -2204 1147 -490 1169 -492 451 -1272 1171 -480 457 -1278 453 -1278 1169 -490 451 -1308 1139 -514 429 -1304 1135 -504 453 -1276 1149 -490 447 -1298 1149 -488 445 -1296 1165 -496 1149 -488 479 -1276 1139 -504 1149 -490 1167 -492 1143 -478 481 -1272 445 -1284 1163 -486 1175 -488 437 -1302 1141 -504 1151 -486 1167 -478 445 -1306 445 -1286 539 -2178 1171 -486 1163 -490 451 -1276 1173 -478 459 -1276 455 -1278 1165 -470 481 -1284 1149 -490 471 -1270 1141 -504 455 -1272 1167 -502 455 -1276 1149 -490 447 -1284 1155 -514 1135 -484 465 -1274 1161 -506 1147 -484 1163 -520 1145 -492 453 -1298 417 -1316 1147 -478 1165 -478 461 -1276 1147 -514 1135 -514 1127 -522 449 -1276 445 -1294 547 -2202 1153 -478 1137 -508 457 -1270 1167 -496 453 -1274 441 -1312 1141 -488 455 -1308 1137 -482 465 -1274 1177 -498 419 -1312 1151 -482 443 -1294 1165 -462 485 -1276 1175 -496 1145 -488 473 -1272 1141 -504 1145 -488 1171 -492 1145 -490 453 -1272 445 -1296 1175 -480 1161 -502 451 -1264 1163 -502 1145 -486 1165 -480 449 -1272 477 -1286 551 -2174 1163 -480 1169 -482 463 -1276 1177 -462 477 -1264 439 -1316 1139 -502 439 -1306 1161 -492 449 -1272 1173 -480 461 -1276 1145 -492 449 -1310 1131 -492 445 -1300 1175 -460 1179 -486 471 -1270 1139 -506 1157 -484 1169 -490 1147 -490 455 -1302 447 -1286 1163 -482 1139 -484 463 -1274 1175 -498 1175 -456 1173 -490 455 -1274 445 -1284 551 -2168 1189 -484 1151 -484 449 -1284 1153 -480 451 -1314 451 -1276 1171 -480 463 -1272 1179 -464 485 -1278 1149 -482 447 -1282 1161 -486 447 -1278 1197 -464 485 -1276 1145 -490 1171 -488 435 -1276 1177 -464 1179 -488 1167 -482 1139 -504 447 -1280 483 -1276 1149 -488 1175 -488 435 -1272 1173 -502 1145 -490 1171 -492 447 -1266 441 -1294 547 -2206 1149 -488 1175 -488 435 -1274 1177 -464 483 -1278 427 -1306 1165 -456 483 -1274 1173 -476 461 -1302 1141 -502 453 -1280 1145 -488 449 -1282 1161 -478 445 -1320 1145 -492 1165 -472 439 -1326 1161 -460 1175 -490 1163 -490 1143 -492 451 -1304 409 -1294 1175 -484 1153 -488 447 -1282 1191 -480 1137 -484 1189 -490 449 -1272 445 -1268 555 -2204 1131 -512 1165 -476 453 -1264 1197 -478 459 -1278 455 -1276 1167 -490 451 -1272 1169 -480 461 -1276 1175 -460 483 -1278 1149 -484 481 -1274 1149 -486 447 -1310 1163 -460 1171 -488 441 -1308 1139 -500 1147 -488 1167 -490 1143 -490 487 -1272 445 -1286 1165 -478 1173 -478 457 -1278 1151 -488 1175 -486 1161 -482 445 -1268 473 -1288 539 -2194 1139 -506 1151 -486 +RAW_Data: 471 -1270 1139 -506 453 -1262 469 -1286 1143 -504 453 -1280 1175 -488 473 -1270 1139 -504 455 -1272 1163 -464 485 -1272 1163 -494 453 -1272 1165 -462 1175 -490 479 -1272 1143 -504 1147 -490 1169 -492 1141 -490 451 -1268 479 -1282 1147 -490 1143 -520 429 -1308 1161 -482 1139 -504 1143 -488 471 -1274 467 -1264 539 -2198 1141 -474 1183 -492 443 -1298 1153 -488 477 -1270 445 -1286 1143 -506 451 -1282 1173 -488 435 -1274 1175 -464 485 -1278 1179 -462 449 -1308 1133 -490 449 -1314 1149 -484 1169 -488 433 -1276 1177 -496 1145 -488 1165 -474 1185 -488 451 -1276 449 -1286 1151 -478 1167 -504 457 -1278 1149 -486 1173 -486 1129 -524 445 -1274 445 -250178 99 -496 131 -406 65 -568 265 -132 361 -132 163 -326 163 -328 229 -164 65 -360 163 -98 99 -328 899 -100 333 -66 396157 -74330 1085 -550 419 -1336 1107 -482 477 -1302 423 -1270 1149 -12632 63 -128 63 -100 97 -98 97 -66 131 -662 335 -1344 1119 -520 1147 -478 461 -1302 1107 -568 195 -96 63 -5076 689 -484 491 -1238 1187 -476 1155 -518 1143 -488 453 -1270 447 -1282 451 -1310 1143 -476 463 -1278 523 -2210 1147 -506 1141 -508 429 -1308 1127 -520 447 -1272 445 -1286 1145 -524 419 -1306 1171 -484 439 -1306 1139 -504 425 -1310 1151 -488 451 -1282 1151 -508 449 -1296 1133 -508 1137 -506 453 -1300 1137 -504 1151 -488 441 -1308 1139 -502 1145 -490 475 -1270 1139 -506 1147 -522 1109 -522 449 -1278 443 -1316 453 -1278 1137 -482 465 -1270 571 -2160 1171 -490 1143 -524 421 -1308 1139 -516 435 -1306 417 -1314 1139 -506 451 -1276 1175 -488 435 -1272 1175 -502 453 -1276 1149 -492 449 -1308 1133 -524 415 -1296 1163 -500 1145 -488 443 -1308 1139 -502 1145 -490 475 -1272 1177 -476 1151 -486 471 -1270 1171 -478 1149 -488 1171 -492 447 -1270 441 -1288 485 -1274 1137 -514 465 -1274 545 -2166 1167 -490 1149 -488 457 -1276 1169 -480 463 -1274 451 -1282 1171 -476 481 -1280 1171 -486 469 -1266 1169 -476 457 -1276 1147 -488 447 -1278 1187 -480 445 -1280 1195 -460 1173 -486 471 -1266 1165 -480 1149 -490 479 -1272 1139 -506 1145 -494 475 -1272 1137 -506 1177 -460 1177 -482 451 -1274 477 -1254 489 -1244 1167 -486 459 -1264 567 -2190 1143 -494 1183 -492 459 -1244 1171 -484 463 -1274 451 -1274 1173 -494 457 -1272 1167 -484 459 -1274 1173 -464 481 -1270 1175 -466 485 -1270 1159 -468 483 -1270 1163 -468 1177 -488 471 -1268 1169 -478 1145 -486 481 -1268 1169 -484 1183 -454 475 -1268 1163 -484 1153 -492 1171 -486 449 -1272 477 -1252 491 -1272 1171 -454 497 -1238 581 -2164 1169 -482 1175 -456 461 -1272 1169 -484 461 -1268 467 -1292 1181 -456 461 -1272 1203 -452 461 -1272 1193 -442 487 -1276 +RAW_Data: 1149 -488 475 -1262 1175 -462 481 -1276 1173 -464 1175 -492 475 -1238 1173 -470 1179 -492 473 -1240 1175 -468 1177 -488 471 -1270 1175 -480 1151 -486 1167 -494 451 -1266 477 -1254 487 -1268 1169 -454 461 -1274 567 -2162 1203 -438 1205 -436 499 -1244 1175 -474 485 -1274 445 -1284 1163 -478 477 -1252 1183 -464 465 -1278 1197 -460 483 -1272 1169 -476 455 -1274 1175 -458 481 -1274 1149 -486 1169 -488 469 -1268 1165 -482 1185 -452 479 -1270 1169 -482 1147 -486 477 -1266 1163 -484 1151 -486 1169 -494 455 -1274 479 -1260 451 -1280 1171 -492 473 -1236 577 -2164 1171 -494 1147 -494 455 -1270 1173 -478 491 -1238 479 -1258 1175 -494 455 -1272 1169 -482 459 -1274 1175 -462 481 -1274 1159 -462 481 -1292 1161 -480 459 -1272 1175 -462 1175 -492 471 -1238 1177 -468 1181 -492 437 -1298 1139 -506 1175 -460 483 -1272 1169 -478 1149 -486 1165 -468 467 -1278 463 -1270 451 -1306 1141 -492 459 -1274 573 -2164 1179 -492 1147 -460 467 -1278 1187 -450 479 -1272 477 -1254 1185 -468 451 -1278 1205 -454 479 -1266 1165 -482 461 -1272 1179 -462 477 -1262 1161 -470 485 -1272 1159 -468 1179 -486 473 -1268 1163 -484 1185 -454 477 -1266 1163 -482 1185 -460 483 -1272 1141 -470 1181 -488 1163 -494 457 -1274 477 -1254 489 -1242 1167 -484 461 -1272 569 -2166 1169 -494 1147 -492 455 -1268 1169 -486 465 -1274 449 -1278 1171 -468 467 -1276 1191 -468 457 -1286 1171 -456 467 -1272 1207 -476 457 -1270 1161 -466 487 -1270 1161 -468 1175 -484 475 -1268 1165 -480 1151 -490 477 -1240 1207 -478 1153 -490 479 -1272 1141 -470 1177 -486 1167 -492 451 -1274 443 -1288 451 -1276 1167 -514 435 -1272 547 -2198 1169 -490 1145 -490 455 -1274 1169 -480 461 -1270 471 -1272 1177 -488 459 -1278 1169 -486 467 -1270 1141 -504 457 -1272 1163 -498 453 -1274 1161 -464 481 -1274 1179 -464 1173 -488 477 -1272 1139 -504 1145 -490 477 -1272 1141 -504 1145 -482 475 -1268 1165 -480 1191 -492 1143 -490 451 -1270 479 -1250 487 -1278 1137 -484 461 -1268 547 -2200 1165 -490 1177 -488 453 -1274 1139 -518 433 -1302 447 -1288 1139 -476 483 -1270 1173 -486 433 -1300 1141 -506 455 -1276 1177 -496 453 -1278 1147 -486 445 -1298 1161 -498 1141 -490 475 -1274 1139 -502 1145 -488 483 -1272 1141 -508 1153 -486 471 -1268 1141 -510 1151 -490 1167 -492 447 -1268 445 -1296 449 -1314 1145 -490 439 -255780 65 -2012 197 -98 531 -98 133 -168 299 -100 101 -166 197 -134 133 -598 301 -68 2607 -66 429 -98 329 -64 2195 -98 2207 -66 232093 -614 1031 -610 1045 -592 1053 -608 335 -1378 1049 -628 1023 -608 1029 -634 405 -2394 165 -688 983 -670 133 -1580 63 -128 63 -100 99 -98 99 -100 99 -728 +RAW_Data: 95 -16850 1035 -592 361 -1392 1069 -548 1105 -548 381 -1352 1113 -514 433 -1310 1113 -532 1115 -516 1163 -506 411 -1306 1143 -516 1157 -482 1139 -502 437 -1310 1163 -476 447 -1312 1143 -482 461 -1274 553 -2170 1153 -524 1143 -478 449 -1308 1143 -512 433 -1310 417 -1306 1141 -520 421 -1312 1137 -516 431 -1276 1177 -464 485 -1272 1163 -462 481 -1264 1163 -506 455 -1272 1163 -502 1145 -488 477 -1274 1137 -504 455 -1264 1175 -496 1145 -488 1165 -492 455 -1272 1169 -480 1153 -490 1175 -490 451 -1274 1175 -474 459 -1276 1145 -492 447 -1294 539 -2196 1171 -490 1147 -490 457 -1272 1173 -480 457 -1276 455 -1274 1169 -490 453 -1310 1137 -516 433 -1304 1131 -504 453 -1262 1161 -506 453 -1260 1169 -502 455 -1274 1163 -496 1145 -488 475 -1270 1169 -480 459 -1272 1165 -500 1145 -488 1169 -492 449 -1272 1173 -476 1153 -488 1167 -492 449 -1272 1169 -480 465 -1272 1163 -504 453 -1276 521 -2204 1149 -488 1171 -490 453 -1274 1171 -478 463 -1276 451 -1282 1167 -478 449 -1308 1139 -484 465 -1270 1167 -504 455 -1280 1149 -486 483 -1278 1147 -486 447 -1282 1161 -490 1171 -490 437 -1274 1173 -500 455 -1272 1161 -464 1173 -490 1173 -490 455 -1274 1169 -480 1155 -488 1169 -472 449 -1282 1175 -486 471 -1272 1141 -504 453 -1274 549 -2198 1163 -490 1143 -478 485 -1276 1137 -482 463 -1276 451 -1284 1169 -470 471 -1310 1163 -490 451 -1272 1141 -508 457 -1278 1147 -488 447 -1278 1161 -490 449 -1312 1149 -484 1173 -488 475 -1272 1139 -502 455 -1272 1163 -496 1143 -490 1165 -492 449 -1272 1171 -478 1183 -492 1143 -478 451 -1274 1171 -484 461 -1276 1177 -462 483 -1278 525 -2176 1175 -490 1165 -482 483 -1278 1135 -484 463 -1276 451 -1276 1175 -490 453 -1274 1171 -482 461 -1274 1177 -462 483 -1278 1143 -492 485 -1244 1161 -494 445 -1294 1163 -502 1147 -490 475 -1272 1139 -504 457 -1274 1163 -502 1143 -488 1171 -490 451 -1274 1171 -478 1153 -490 1169 -492 449 -1270 1173 -476 457 -1276 1177 -464 483 -1274 535 -2190 1169 -492 1139 -490 455 -1274 1171 -478 461 -1272 439 -1314 1143 -522 421 -1312 1143 -482 463 -1274 1175 -462 449 -1312 1153 -484 447 -1278 1173 -484 441 -1310 1143 -520 1143 -494 445 -1288 1151 -484 439 -1304 1139 -504 1147 -486 1173 -486 449 -1274 1175 -512 1127 -512 1135 -504 445 -1278 1141 -514 437 -1312 1145 -496 419 -1312 521 -2212 1141 -506 1151 -486 443 -1302 1141 -506 457 -1276 457 -1272 1167 -488 451 -1310 1139 -478 463 -1274 1181 -498 419 -1314 1153 -484 447 -1284 1163 -478 443 -1314 1141 -492 1163 -476 441 -1296 1165 -518 419 -1312 1149 -486 1139 -516 1131 -520 447 -1272 1141 -508 1151 -518 1139 -478 445 -1310 1143 -516 435 -1304 +RAW_Data: 1135 -502 423 -1312 523 -2206 1147 -486 1171 -488 447 -1276 1141 -508 461 -1310 419 -1310 1139 -488 453 -1310 1141 -512 431 -1276 1141 -528 419 -1314 1149 -484 445 -1298 1165 -500 421 -1310 1131 -528 1143 -484 439 -1310 1139 -504 421 -1312 1149 -486 1139 -518 1127 -520 447 -1306 1139 -514 1121 -512 1135 -500 447 -1280 1173 -484 435 -1304 1143 -504 455 -1276 537 -2228 1135 -508 1139 -474 473 -1274 1163 -478 447 -1304 445 -1282 1143 -520 449 -1278 1153 -520 445 -1274 1141 -508 459 -1278 1151 -488 449 -1284 1153 -482 451 -1304 1141 -506 1161 -484 447 -1296 1163 -500 451 -1266 1175 -460 1175 -488 1165 -492 451 -1270 1169 -480 1151 -490 1171 -490 451 -1308 1135 -480 461 -1296 1133 -508 457 -1278 521 -2208 1151 -488 1163 -492 451 -1272 1171 -480 463 -1276 455 -1276 1169 -490 451 -1302 1137 -482 465 -1270 1163 -502 453 -1274 1147 -492 481 -1274 1145 -490 447 -1314 1153 -484 1139 -484 465 -1274 1179 -496 419 -1310 1149 -486 1171 -482 1163 -488 453 -1278 1173 -478 1155 -488 1171 -488 449 -1276 1171 -476 459 -1276 1147 -492 449 -1312 523 -2204 1149 -488 1171 -478 449 -1306 1137 -512 431 -1274 453 -1314 1135 -488 453 -1310 1141 -516 433 -1308 1141 -498 451 -1262 1165 -502 455 -1272 1163 -496 451 -1274 1163 -498 1147 -488 477 -1268 1141 -508 457 -1276 1149 -488 1171 -486 1133 -524 449 -1274 1143 -504 1149 -516 1137 -504 439 -1306 1127 -490 477 -1270 1173 -476 457 -1278 523 -2206 1151 -488 1167 -490 449 -1306 1141 -482 461 -1294 439 -1272 1177 -462 469 -1302 1161 -482 445 -1268 1169 -482 463 -1306 1163 -476 457 -1272 1167 -496 453 -1276 1147 -488 1171 -490 439 -1306 1143 -500 453 -1278 1151 -490 1177 -488 1163 -492 449 -1270 1141 -508 1151 -488 1169 -492 447 -1268 1171 -480 459 -1278 1177 -466 483 -255224 163 -470 197 -726 65 -198 129 -494 363 -168 365 -100 369 -98 763 -68 265 -68 633 -98 165 -66 495 -66 199 -134 97 -300 1051 -100 297 -132 369 -134 299 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/marantec24_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/marantec24_raw.sub new file mode 100644 index 000000000..4a8c34b21 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/marantec24_raw.sub @@ -0,0 +1,14 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 868350000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 1224 -1300 1801 -3200 1615 -2374 821 -3198 1603 -2410 785 -3196 815 -3188 1605 -2398 1605 -2398 1607 -2400 1605 -2398 1605 -2396 1601 -2400 1605 -2398 809 -3184 1603 -2396 815 -3194 811 -3190 811 -3194 1603 -2398 1603 -2398 1601 -2392 811 -3194 1605 -2394 1607 -16170 131 -196 217 -493440 135 -1296 1807 -3210 1591 -2396 811 -3198 1591 -2394 807 -3218 807 -3188 1607 -2400 1605 -2394 1599 -2402 1601 -2392 1609 -2400 1605 -2392 1603 -2392 815 -3190 1609 -2402 809 -3188 813 -3192 813 -3190 1605 -2400 1603 -2396 1605 -2396 813 -3194 1601 -2402 1601 -16170 133 -200 219 -382462 101 -1314 1813 -3194 1581 -2414 801 -3214 1605 -2398 807 -3190 811 -3190 1601 -2394 1603 -2398 1607 -2398 1605 -2396 1605 -2394 1601 -2394 1609 -2396 815 -3194 1603 -2400 809 -3194 811 -3190 811 -3190 1603 -2396 1603 -2396 1607 -2398 811 -3192 1607 -2396 1601 -16176 131 -198 217 -348436 303 -1106 1817 -3194 1609 -2386 803 -3214 1597 -2392 811 -3186 815 -3192 1611 -2396 1601 -2394 1609 -2404 1605 -2396 1605 -2396 1607 -2392 1601 -2396 813 -3192 1603 -2394 813 -3190 815 -3188 811 -3194 1601 -2396 1607 -2394 1601 -2396 815 -3186 1611 -2396 1609 -16168 131 -198 217 -349492 347 -1080 1799 -3192 1617 -2388 801 -3218 1601 -2396 809 -3190 809 -3192 1605 -2394 1599 -2394 1605 -2398 1607 -2398 1599 -2398 1607 -2396 1601 -2396 811 -3192 1605 -2400 811 -3184 813 -3192 811 -3192 1607 -2398 1601 -2398 1607 -2400 811 -3190 1603 -2394 1607 -16176 131 -204 213 -447994 135 -1330 977 -1036 65 -230 181 -514880 135 -66 67 -1140 1815 -3192 1615 -2388 803 -3214 1603 -2396 813 -3190 811 -3190 1609 -2398 1601 -2392 1603 -2396 1607 -2398 1607 -2398 1607 -2398 1605 -2394 813 -3186 1611 -2404 809 -3186 811 -3192 813 -3188 1605 -2394 1607 -2400 1607 -2398 807 -3190 1605 -2398 1603 -16172 131 -200 217 -395118 99 -1304 1841 -3160 1621 -2388 805 -3186 1631 -2392 809 -3182 811 -3188 1605 -2402 1601 -2398 1601 -2402 1607 -2398 1605 -2398 1601 -2392 1605 -2404 809 -3188 1599 -2396 817 -3192 811 -3192 811 -3190 1599 -2392 1603 -2398 1607 -2404 809 -3192 1603 -2402 1605 -16164 133 -198 187 -382202 137 -1302 1803 -3192 1601 -2404 803 -3184 1633 -2392 807 -3190 811 -3188 1601 -2400 1607 -2402 1601 -2392 1601 -2396 1607 -2400 1603 -2398 1605 -2398 811 -3184 1601 -2400 813 -3188 809 -3190 811 -3192 1605 -2400 1605 -2396 1599 -2402 809 -3192 1607 -2400 1607 -16174 99 -232 181 -361858 65 -1368 1785 -3228 1567 -2426 777 -3222 1601 -2402 785 -3198 815 -3186 1609 -2402 1601 -2396 1605 -2402 1603 -2396 1605 -2398 1601 -2400 1603 -2398 813 -3194 1605 -2400 809 -3186 813 -3190 813 -3188 1607 -2396 1605 -2400 1605 -2394 809 -3182 1607 -2400 1607 -16210 65 -236 177 -379100 1819 -3192 1611 -2384 799 -3220 1601 -2396 811 -3186 809 -3188 1605 -2394 1607 -2398 +RAW_Data: 1599 -2394 1603 -2396 1609 -2402 1603 -2396 1607 -2398 809 -3196 1603 -2394 809 -3188 813 -3194 809 -3188 1605 -2394 1599 -2396 1609 -2404 811 -3184 1605 -2400 1601 -16162 133 -234 191 -393094 97 -1316 1021 -1010 67 -246 173 -677840 131 -64 59 -1178 1773 -3210 1593 -2430 773 -3222 1601 -2396 811 -3186 813 -3190 1603 -2398 1603 -2396 1607 -2398 1605 -2400 1601 -2396 1605 -2396 1601 -2396 815 -3190 1607 -2402 811 -3190 811 -3186 811 -3192 1607 -2396 1603 -2396 1601 -2400 813 -3188 1607 -2404 1607 -15160 827 -3204 1577 -2404 813 -3200 1591 -2390 807 -3220 807 -3186 1599 -2400 1605 -2398 1603 -2400 1603 -2396 1605 -2398 1605 -2398 1599 -2394 809 -3190 1611 -2394 809 -3186 815 -3192 809 -3192 1603 -2398 1601 -2396 1599 -2396 815 -3188 1609 -2400 1605 -15178 801 -3222 1575 -2392 805 -3190 1615 -2396 809 -3190 819 -3198 1601 -2402 1577 -2402 1609 -2400 1605 -2398 1605 -2396 1605 -2398 1601 -2400 809 -3184 1605 -2396 809 -3190 811 -3192 813 -3190 1601 -2404 1605 -2394 1599 -2396 811 -3190 1605 -2400 1601 -15174 831 -3166 1615 -2396 783 -3200 1611 -2398 813 -3192 811 -3188 1601 -2396 1603 -2398 1607 -2396 1605 -2396 1603 -2398 1601 -2394 1601 -2398 811 -3192 1601 -2394 813 -3192 809 -3194 807 -3190 1605 -2394 1599 -2394 1601 -2394 813 -3194 1601 -2398 1603 -15178 793 -3228 1577 -2404 809 -3186 1611 -2396 815 -3192 811 -3188 1605 -2400 1577 -2404 1603 -2396 1607 -2400 1601 -2398 1603 -2394 1605 -2396 811 -3192 1605 -2394 811 -3190 811 -3188 807 -3186 1601 -2396 1605 -2398 1601 -2398 813 -3186 1601 -2394 1605 -15178 797 -3204 1581 -2400 813 -3188 1603 -2400 813 -3192 811 -3192 1607 -2398 1605 -2398 1607 -2394 1601 -2400 1605 -2398 1601 -2396 1601 -2394 809 -3190 1605 -2400 811 -3186 813 -3192 811 -3184 1601 -2396 1605 -2396 1605 -2400 809 -3184 1599 -2400 1605 -15146 827 -3188 1613 -2402 785 -3200 1605 -2396 815 -3192 811 -3190 1605 -2396 1605 -2396 1607 -2398 1601 -2398 1601 -2392 1599 -2398 1603 -2398 811 -3186 1605 -2396 811 -3190 811 -3192 809 -3190 1597 -2394 1599 -2398 1605 -2398 813 -3186 1599 -2396 1605 -15176 793 -3226 1577 -2398 817 -3198 1605 -2370 817 -3192 813 -3192 1609 -2396 1601 -2396 1605 -2396 1603 -2394 1605 -2394 1607 -2398 1607 -2394 809 -3188 1605 -2400 811 -3194 809 -3188 811 -3192 1605 -2400 1603 -2388 1605 -2394 811 -3186 1605 -2396 1605 -15180 797 -3202 1605 -2398 781 -3196 1605 -2396 815 -3184 817 -3196 1603 -2398 1605 -2398 1605 -2400 1603 -2394 1601 -2396 1607 -2398 1605 -2396 811 -3188 1605 -2396 809 -3186 807 -3194 811 -3194 1603 -2396 1597 -2396 1605 -2396 811 -3192 1603 -2396 1601 -15146 831 -3190 1603 -2404 777 -3224 1605 -2398 785 -3196 813 -3192 1607 -2396 1601 -2398 1607 -2394 1605 -2398 1603 -2394 1601 -2400 1599 -2396 807 -3192 1603 -2396 809 -3186 +RAW_Data: 813 -3190 809 -3190 1603 -2400 1603 -2398 1599 -2390 813 -3192 1603 -2394 1603 -15146 823 -3204 1603 -2398 787 -3204 1595 -2388 805 -3216 807 -3192 1605 -2400 1601 -2398 1597 -2394 1605 -2396 1603 -2398 1605 -2392 1601 -2396 809 -3192 1605 -2398 807 -3190 809 -3186 811 -3194 1603 -2394 1605 -2394 1599 -2398 813 -3192 1609 -2394 1603 -15174 795 -3206 1605 -2398 783 -3196 1607 -2400 809 -3188 813 -3188 1609 -2402 1601 -2394 1609 -2396 1603 -2396 1603 -2400 1599 -2388 1599 -2400 811 -3192 1607 -2394 811 -3186 811 -3190 809 -3194 1603 -2400 1599 -2394 1601 -2392 813 -3192 1603 -2396 1605 -15162 799 -3218 1581 -2404 807 -3202 1603 -2396 787 -3196 819 -3192 1611 -2394 1603 -2396 1607 -2402 1601 -2392 1599 -2392 1601 -2400 1607 -2398 811 -3192 1603 -2396 811 -3190 813 -3188 813 -3186 1603 -2394 1601 -2392 1605 -2400 809 -3188 1609 -2394 1601 -15156 829 -3188 1601 -2414 797 -3182 1629 -2394 807 -3192 811 -3188 1603 -2398 1599 -2392 1603 -2398 1603 -2396 1603 -2396 1607 -2400 1607 -2392 809 -3186 1603 -2400 813 -3194 811 -3192 809 -3188 1599 -2400 1603 -2404 1609 -2394 809 -3194 1599 -2394 1607 -15176 829 -3158 1623 -2404 773 -3224 1605 -2386 799 -3210 811 -3190 1607 -2392 1605 -2398 1607 -2400 1601 -2388 1605 -2398 1603 -2402 1599 -2392 809 -3188 1605 -2400 811 -3186 813 -3190 809 -3190 1603 -2396 1599 -2404 1603 -2400 809 -3188 1605 -2396 1607 -15146 829 -3216 1561 -2414 817 -3196 1599 -2404 797 -3180 809 -3212 1607 -2400 1601 -2394 1603 -2400 1603 -2396 1599 -2400 1605 -2396 1605 -2394 813 -3186 1607 -2394 811 -3186 809 -3190 813 -3192 1607 -2398 1603 -2398 1601 -2396 813 -3192 1603 -2392 1605 -15144 827 -3188 1613 -2404 809 -3192 1615 -2370 811 -3188 811 -3188 1611 -2398 1603 -2400 1601 -2394 1601 -2402 1605 -2396 1601 -2394 1609 -2398 811 -3186 1603 -2392 813 -3196 811 -3182 809 -3188 1603 -2402 1605 -2400 1599 -2394 809 -3194 1607 -2398 1601 -15148 831 -3186 1587 -2424 811 -3192 1575 -2408 803 -3188 805 -3222 1599 -2392 1601 -2400 1609 -2398 1601 -2392 1597 -2394 1601 -2400 1601 -2400 807 -3190 1601 -2400 813 -3190 809 -3190 809 -3188 1607 -2398 1601 -2396 1605 -2398 809 -3184 1603 -2400 1605 -15176 795 -3200 1613 -2400 783 -3198 1603 -2398 813 -3192 809 -3186 1607 -2398 1605 -2398 1605 -2398 1599 -2394 1605 -2398 1603 -2392 1599 -2396 809 -3194 1609 -2390 811 -3194 813 -3188 811 -3188 1599 -2394 1603 -2398 1601 -2394 815 -3192 1605 -2402 1601 -15150 831 -3186 1605 -2412 799 -3206 1601 -2392 809 -3186 813 -3192 1603 -2402 1601 -2392 1605 -2398 1607 -2396 1601 -2392 1609 -2402 1599 -2396 811 -3186 1601 -2398 815 -3196 809 -3188 809 -3188 1607 -2394 1605 -2394 1603 -2394 811 -3194 1603 -2398 1607 -15174 797 -3218 1563 -2424 785 -3194 1633 -2396 795 -3178 805 -3214 1607 -2394 1603 -2400 +RAW_Data: 1603 -2392 1603 -2398 1605 -2400 1597 -2392 1603 -2396 811 -3198 1603 -2390 809 -3192 811 -3188 813 -3192 1599 -2394 1607 -2398 1607 -2394 811 -3192 1601 -2394 1605 -15152 861 -3154 1603 -2410 799 -3186 1601 -2422 809 -3190 809 -3186 1601 -2392 1603 -2394 1611 -2398 1605 -2400 1601 -2392 1609 -2396 1603 -2398 809 -3186 1605 -2400 811 -3188 811 -3188 807 -3192 1605 -2402 1597 -2392 1603 -2396 813 -3194 1601 -2398 1605 -15174 795 -3200 1597 -2428 785 -3196 1613 -2398 785 -3192 815 -3192 1611 -2400 1603 -2394 1603 -2400 1601 -2398 1607 -2394 1603 -2398 1603 -2396 811 -3190 1605 -2394 811 -3194 811 -3182 811 -3192 1607 -2392 1599 -2394 1601 -2398 811 -3192 1607 -2392 1603 -15156 827 -3190 1607 -2408 797 -3210 1599 -2398 777 -3218 811 -3192 1601 -2400 1599 -2398 1601 -2396 1597 -2396 1601 -2392 1607 -2392 1605 -2400 813 -3190 1601 -2396 811 -3194 809 -3190 813 -3190 1603 -2394 1599 -2390 1603 -2400 811 -3190 1607 -2398 1605 -15150 825 -3192 1603 -2404 793 -3178 1605 -2422 777 -3222 775 -3218 1603 -2398 1605 -2398 1597 -2392 1603 -2398 1605 -2400 1601 -2394 1601 -2392 811 -3192 1609 -2398 813 -3190 809 -3182 809 -3184 1607 -2398 1603 -2396 1607 -2398 813 -3190 1607 -2396 1601 -15172 797 -3230 1563 -2424 805 -3190 1599 -2404 795 -3182 807 -3214 1601 -2402 1603 -2396 1601 -2396 1603 -2398 1605 -2398 1599 -2392 1601 -2402 813 -3192 1601 -2396 809 -3188 811 -3188 809 -3192 1605 -2400 1607 -2398 1599 -2394 809 -3190 1607 -2400 1605 -15180 827 -3190 1577 -2394 819 -3196 1613 -2366 815 -3196 813 -3190 1607 -2396 1607 -2402 1599 -2398 1605 -2400 1603 -2398 1601 -2392 1605 -2400 811 -3186 1603 -2400 813 -3190 809 -3196 807 -3182 1601 -2396 1609 -2398 1601 -2398 813 -3192 1601 -2394 1607 -15174 793 -3202 1599 -2424 787 -3196 1607 -2404 785 -3192 815 -3188 1609 -2404 1603 -2398 1607 -2396 1601 -2400 1603 -2394 1601 -2394 1603 -2400 811 -3190 1605 -2400 811 -3188 809 -3190 813 -3194 1597 -2398 1607 -2394 1603 -2394 809 -3190 1603 -2392 1607 -15180 795 -3230 1581 -2392 807 -3218 1593 -2408 777 -3220 781 -3204 1597 -2424 1605 -2394 1595 -2392 1605 -2394 1599 -2396 1605 -2402 1603 -2394 809 -3188 1601 -2396 813 -3194 811 -3188 813 -3190 1605 -2398 1607 -2392 1601 -2398 811 -3192 1599 -2400 1605 -15178 797 -3218 1595 -2400 803 -3198 1605 -2370 825 -3182 803 -3214 1603 -2396 1601 -2398 1601 -2394 1599 -2398 1603 -2396 1607 -2394 1605 -2394 809 -3190 1607 -2398 809 -3194 811 -3188 809 -3194 1605 -2394 1599 -2396 1605 -2396 809 -3196 1603 -2402 1599 -15160 829 -3192 1579 -2400 817 -3198 1605 -2398 783 -3194 813 -3190 1609 -2400 1607 -2400 1599 -2394 1601 -2394 1609 -2398 1601 -2396 1605 -2394 809 -3186 1609 -2400 811 -3186 809 -3196 811 -3190 1605 -2396 1601 -2398 1607 -2396 809 -3186 1605 -2398 1607 -15170 +RAW_Data: 827 -3192 1595 -2376 813 -3192 1601 -2414 797 -3186 807 -3220 1601 -2398 1603 -2394 1599 -2396 1607 -2400 1605 -2394 1597 -2398 1603 -2398 813 -3192 1603 -2394 809 -3188 809 -3188 811 -3192 1607 -2396 1601 -2398 1605 -2398 811 -3188 1603 -2400 1603 -15174 829 -3162 1627 -2374 803 -3224 1571 -2412 801 -3186 807 -3222 1605 -2392 1605 -2392 1599 -2394 1605 -2404 1601 -2396 1607 -2396 1601 -2394 809 -3188 1601 -2398 811 -3190 815 -3194 811 -3184 1603 -2400 1605 -2400 1605 -2394 809 -3188 1609 -2394 1605 -15154 829 -3188 1603 -2412 801 -3210 1603 -2398 807 -3182 809 -3196 1605 -2400 1605 -2396 1599 -2402 1607 -2390 1599 -2396 1599 -2400 1607 -2396 811 -3192 1605 -2396 813 -3196 809 -3190 811 -3192 1609 -2394 1597 -2396 1607 -2398 809 -3186 1605 -2394 1605 -15178 795 -3216 1577 -2408 819 -3190 1607 -2394 817 -3194 779 -3194 1607 -2404 1605 -2398 1607 -2394 1601 -2398 1605 -2400 1603 -2402 1607 -2394 811 -3192 1601 -2396 809 -3186 813 -3194 811 -3188 1599 -2394 1601 -2402 1601 -2398 811 -3186 1603 -2396 1611 -15176 799 -3224 1567 -2424 785 -3202 1599 -2396 809 -3204 783 -3198 1605 -2398 1607 -2402 1605 -2398 1607 -2400 1599 -2398 1605 -2392 1603 -2400 811 -3186 1601 -2400 811 -3194 809 -3184 811 -3186 1603 -2396 1603 -2398 1607 -2398 811 -3188 1605 -2396 1605 -15174 829 -3166 1605 -2402 809 -3190 1615 -2368 815 -3194 815 -3192 1605 -2396 1603 -2400 1603 -2400 1605 -2392 1601 -2400 1607 -2400 1601 -2402 811 -3194 1599 -2392 809 -3188 813 -3184 813 -3192 1605 -2400 1605 -2400 1607 -2390 809 -3188 1607 -2398 1603 -15174 829 -3158 1617 -2400 787 -3192 1607 -2404 811 -3184 815 -3190 1607 -2402 1601 -2396 1605 -2400 1601 -2396 1603 -2396 1607 -2400 1603 -2394 809 -3188 1609 -2398 809 -3190 811 -3188 809 -3188 1607 -2396 1599 -2396 1607 -2400 809 -3188 1607 -2398 1603 -15172 797 -3228 1575 -2424 771 -3202 1615 -2386 801 -3198 807 -3218 1587 -2390 1603 -2394 1603 -2424 1603 -2396 1603 -2398 1599 -2394 1605 -2396 809 -3192 1605 -2400 807 -3184 809 -3190 813 -3190 1603 -2390 1601 -2394 1601 -2396 813 -3196 1607 -2402 1599 -15152 829 -3194 1605 -2408 793 -3182 1597 -2426 807 -3184 811 -3192 1607 -2392 1603 -2396 1605 -2400 1601 -2394 1603 -2394 1605 -2400 1605 -2394 809 -3190 1605 -2396 811 -3192 809 -3186 811 -3190 1601 -2394 1605 -2400 1607 -2398 809 -3186 1601 -2394 1603 -15174 829 -3160 1635 -2382 811 -3194 1609 -2400 779 -3220 781 -3222 1575 -2406 1603 -2398 1607 -2402 1601 -2394 1603 -2396 1601 -2402 1605 -2398 805 -3190 1605 -2394 813 -3194 809 -3188 811 -3192 1603 -2396 1599 -2394 1607 -2394 811 -3194 1603 -2394 1601 -15174 799 -3216 1599 -2402 785 -3198 1615 -2376 819 -3194 811 -3190 1599 -2394 1623 -2392 1599 -2396 1611 -2382 1599 -2390 1633 -2392 1599 -2396 811 -3194 1599 -2394 811 -3188 +RAW_Data: 811 -3190 809 -3192 1603 -2398 1607 -2392 1599 -2398 811 -3190 1603 -2402 1601 -15172 795 -3194 1603 -2404 807 -3186 1599 -2394 821 -3196 785 -3196 1609 -2396 1609 -2398 1603 -2396 1605 -2400 1599 -2394 1605 -2396 1605 -2398 809 -3190 1601 -2396 811 -3190 809 -3194 809 -3188 1601 -2398 1603 -2398 1607 -2392 807 -3190 1603 -2402 1601 -15178 803 -3198 1579 -2410 807 -3188 1625 -2406 793 -3204 779 -3192 1609 -2408 1597 -2392 1595 -2424 1605 -2394 1601 -2394 1601 -2392 1601 -2396 809 -3194 1607 -2398 811 -3194 811 -3186 809 -3182 1607 -2398 1605 -2392 1605 -2392 809 -3190 1611 -2400 1605 -15154 799 -3228 1567 -2434 795 -3180 1627 -2392 809 -3190 809 -3186 1603 -2396 1599 -2394 1603 -2398 1603 -2398 1603 -2392 1607 -2400 1601 -2390 809 -3186 1601 -2404 811 -3188 811 -3190 813 -3186 1605 -2398 1605 -2392 1599 -2400 813 -3190 1599 -2396 1603 -15184 799 -3194 1607 -2410 795 -3176 1603 -2428 807 -3188 811 -3188 1603 -2394 1607 -2398 1599 -2396 1603 -2400 1605 -2394 1601 -2394 1607 -2400 809 -3186 1607 -2400 809 -3184 811 -3188 809 -3196 1605 -2396 1603 -2394 1607 -2392 809 -3188 1607 -2396 1609 -15176 795 -3226 1567 -2406 819 -3198 1579 -2406 809 -3190 821 -3198 1575 -2400 1603 -2398 1611 -2400 1603 -2398 1607 -2396 1601 -2394 1607 -2402 809 -3190 1603 -2396 811 -3186 809 -3192 811 -3192 1607 -2394 1603 -2398 1601 -2396 809 -3196 1605 -2398 1603 -15148 827 -3196 1597 -2426 775 -3200 1613 -2398 783 -3198 813 -3184 1613 -2400 1607 -2400 1605 -2398 1603 -2400 1601 -2394 1603 -2398 1605 -2398 809 -3192 1603 -2396 811 -3190 811 -3190 809 -3194 1605 -2394 1601 -2396 1601 -2400 811 -3192 1607 -2396 1603 -15162 797 -3244 1573 -2398 817 -3196 1603 -2404 795 -3210 775 -3220 1603 -2396 1603 -2396 1607 -2394 1607 -2368 1633 -2392 1601 -2396 1607 -2396 811 -3190 1605 -2400 807 -3186 811 -3194 809 -3186 1607 -2396 1603 -2392 1599 -2394 811 -3186 1611 -2396 1601 -15178 763 -3288 1501 -2496 741 -3254 1543 -2442 777 -3194 815 -3206 1585 -2390 1603 -2426 1601 -2392 1599 -2400 1605 -2396 1599 -2398 1603 -2398 809 -3190 1601 -2396 811 -3188 811 -3188 811 -3194 1607 -2396 1599 -2396 1605 -2398 813 -3190 1605 -2400 1603 -16206 65 -230 181 -579692 165 -1118 1797 -3196 1591 -2420 773 -3224 1603 -2394 809 -3188 809 -3188 1609 -2402 1603 -2398 1599 -2400 1605 -2400 1605 -2396 1605 -2396 1603 -2394 813 -3194 1607 -2398 809 -3188 809 -3192 809 -3196 1603 -2394 1599 -2396 1605 -2402 811 -3190 1605 -2400 1605 -15178 793 -3222 1577 -2402 809 -3192 1601 -2394 807 -3202 819 -3196 1609 -2396 1603 -2396 1601 -2392 1613 -2400 1579 -2398 1613 -2396 1603 -2396 813 -3192 1607 -2394 813 -3198 809 -3188 809 -3188 1599 -2398 1607 -2400 1607 -2398 807 -3186 1603 -2400 1607 -15178 823 -3168 1615 -2398 809 -3192 1615 -2398 783 -3228 +RAW_Data: 779 -3200 1607 -2394 1605 -2402 1607 -2394 1601 -2396 1603 -2400 1607 -2398 1603 -2396 809 -3182 1607 -2402 813 -3194 811 -3188 811 -3186 1603 -2402 1605 -2398 1607 -2396 809 -3188 1603 -2398 1609 -15162 829 -3204 1575 -2428 783 -3202 1597 -2422 775 -3214 809 -3192 1605 -2400 1603 -2402 1601 -2392 1603 -2394 1605 -2392 1611 -2398 1603 -2398 811 -3182 1609 -2400 813 -3196 811 -3186 811 -3186 1609 -2400 1605 -2394 1601 -2402 807 -3190 1607 -2398 1607 -15178 829 -3182 1595 -2424 773 -3224 1589 -2384 805 -3222 809 -3188 1603 -2398 1605 -2400 1599 -2396 1601 -2392 1601 -2400 1609 -2396 1605 -2398 813 -3190 1603 -2396 813 -3186 811 -3194 811 -3196 1603 -2394 1603 -2400 1603 -2396 809 -3186 1609 -2400 1605 -15160 825 -3198 1605 -2402 785 -3208 1599 -2426 775 -3220 807 -3186 1601 -2396 1609 -2398 1607 -2392 1599 -2402 1605 -2398 1599 -2398 1603 -2402 807 -3190 1603 -2398 813 -3186 813 -3196 809 -3184 1601 -2394 1603 -2428 1571 -2400 815 -3220 1575 -2402 1609 -15178 793 -3234 1575 -2404 819 -3188 1599 -2398 807 -3204 819 -3192 1581 -2400 1607 -2396 1607 -2402 1601 -2392 1605 -2398 1605 -2400 1605 -2398 811 -3190 1609 -2396 811 -3186 811 -3194 811 -3186 1603 -2396 1609 -2396 1601 -2400 811 -3192 1603 -2394 1603 -15188 831 -3184 1609 -2382 803 -3190 1629 -2396 809 -3192 809 -3188 1607 -2396 1607 -2398 1607 -2392 1599 -2396 1607 -2398 1605 -2398 1605 -2394 811 -3188 1607 -2400 813 -3192 811 -3190 813 -3186 1603 -2400 1603 -2402 1599 -2394 811 -3190 1609 -2396 1609 -15164 823 -3196 1599 -2396 789 -3210 1595 -2424 809 -3188 811 -3186 1603 -2398 1605 -2390 1601 -2394 1609 -2400 1605 -2396 1607 -2394 1603 -2398 813 -3194 1599 -2394 815 -3194 809 -3192 809 -3190 1607 -2398 1605 -2396 1603 -2394 809 -3188 1605 -2402 1601 -15162 827 -3196 1579 -2426 805 -3206 1605 -2366 811 -3208 799 -3212 1603 -2400 1603 -2394 1601 -2398 1605 -2398 1599 -2394 1601 -2394 1609 -2394 813 -3192 1607 -2398 813 -3186 807 -3188 813 -3188 1601 -2396 1609 -2396 1603 -2392 811 -3194 1601 -2398 1611 -15174 831 -3186 1595 -2406 785 -3200 1625 -2378 813 -3190 815 -3202 1593 -2424 1603 -2400 1603 -2394 1597 -2400 1603 -2400 1605 -2396 1597 -2400 811 -3192 1601 -2400 811 -3190 811 -3190 811 -3192 1607 -2398 1603 -2392 1607 -2394 811 -3194 1603 -2396 1601 -15154 831 -3190 1605 -2404 797 -3180 1635 -2392 807 -3190 809 -3188 1605 -2400 1603 -2394 1605 -2396 1605 -2398 1601 -2394 1605 -2400 1605 -2396 811 -3192 1603 -2394 809 -3186 815 -3192 805 -3192 1601 -2396 1601 -2366 1629 -2388 807 -3192 1603 -2396 1599 -15118 825 -3198 1605 -2400 783 -3198 1589 -2392 809 -3180 809 -3186 1629 -2400 1573 -2390 1631 -2362 1633 -2360 1631 -2366 1599 -2394 1603 -2392 805 -3214 1601 -2396 809 -3184 811 -3190 807 -3160 1631 -2394 1601 -2364 1601 -2420 +RAW_Data: 807 -3192 1603 -2360 1633 -15120 827 -3200 1575 -2400 809 -3196 1605 -2394 807 -3186 809 -3160 1633 -2392 1599 -2366 1599 -2394 1631 -2358 1635 -2366 1601 -2396 1629 -2390 777 -3190 1629 -2400 775 -3218 775 -3216 809 -3188 1603 -2362 1631 -2390 1601 -2396 811 -3190 1597 -2400 1601 -15128 809 -3220 1577 -2398 809 -3196 1603 -2364 811 -3194 811 -3192 1605 -2400 1605 -2360 1601 -2396 1601 -2392 1603 -2390 1631 -2394 1571 -2394 807 -3218 1603 -2396 775 -3214 807 -3186 807 -3162 1603 -2394 1601 -2390 1603 -2396 805 -3222 1603 -2364 1635 -15142 791 -3228 1571 -2400 811 -3188 1601 -2394 809 -3194 809 -3186 1605 -2398 1601 -2390 1603 -2362 1633 -2362 1601 -2394 1635 -2362 1631 -2390 807 -3190 1605 -2394 773 -3220 775 -3186 807 -3192 1599 -2424 1597 -2366 1603 -2396 807 -3216 1601 -2398 1601 -15140 813 -3196 1571 -2398 813 -3190 1601 -2396 811 -3186 811 -3192 1603 -2402 1601 -2392 1601 -2392 1609 -2394 1603 -2394 1607 -2394 1611 -2396 811 -3190 1605 -2400 811 -3190 811 -3190 813 -3190 1601 -2400 1603 -2394 1607 -2398 809 -3188 1607 -2398 1605 -15176 793 -3226 1579 -2400 811 -3186 1613 -2400 785 -3194 815 -3194 1611 -2396 1607 -2396 1601 -2400 1607 -2396 1605 -2398 1603 -2394 1599 -2392 811 -3190 1605 -2396 815 -3192 815 -3190 813 -3188 1601 -2398 1603 -2398 1603 -2398 811 -3194 1605 -2398 1601 -15156 859 -3148 1639 -2378 803 -3214 1569 -2430 807 -3186 809 -3194 1601 -2396 1605 -2398 1607 -2400 1605 -2396 1605 -2396 1607 -2392 1599 -2394 811 -3184 1609 -2400 815 -3194 809 -3188 809 -3196 1605 -2394 1607 -2394 1599 -2396 813 -3194 1603 -2398 1603 -15170 797 -3212 1595 -2402 819 -3184 1613 -2390 831 -3174 819 -3196 1597 -2402 1589 -2388 1601 -2396 1633 -2398 1601 -2392 1599 -2400 1607 -2396 811 -3186 1609 -2402 809 -3188 813 -3192 809 -3188 1607 -2396 1601 -2398 1601 -2398 811 -3192 1599 -2396 1607 -15174 831 -3186 1579 -2402 801 -3202 1615 -2388 827 -3178 803 -3188 1617 -2392 1601 -2392 1635 -2396 1599 -2392 1603 -2398 1605 -2400 1601 -2392 813 -3194 1601 -2400 813 -3190 811 -3190 809 -3188 1603 -2396 1601 -2398 1605 -2400 809 -3184 1601 -2396 1607 -15178 831 -3166 1611 -2398 815 -3198 1577 -2398 811 -3194 815 -3192 1607 -2400 1605 -2400 1599 -2398 1603 -2396 1601 -2398 1605 -2400 1609 -2392 811 -3192 1605 -2398 811 -3188 811 -3192 813 -3190 1607 -2394 1605 -2398 1603 -2400 809 -3190 1601 -2396 1609 -15174 795 -3230 1567 -2432 781 -3204 1597 -2394 807 -3222 807 -3182 1605 -2398 1603 -2392 1603 -2396 1607 -2396 1603 -2404 1605 -2400 1599 -2396 811 -3186 1611 -2398 813 -3188 811 -3192 811 -3188 1605 -2404 1603 -2400 1605 -2398 807 -3190 1605 -2396 1605 -15164 827 -3198 1605 -2398 783 -3206 1595 -2424 807 -3192 807 -3184 1605 -2396 1605 -2400 1607 -2398 1599 -2394 1607 -2396 1605 -2400 1599 -2394 +RAW_Data: 809 -3184 1609 -2400 809 -3194 813 -3188 807 -3188 1603 -2400 1605 -2400 1599 -2398 809 -3190 1605 -2400 1603 -15170 803 -3208 1595 -2402 809 -3196 1589 -2388 807 -3216 809 -3182 1603 -2394 1607 -2398 1603 -2396 1607 -2396 1603 -2400 1605 -2396 1599 -2394 811 -3188 1605 -2396 817 -3196 811 -3182 811 -3192 1607 -2394 1603 -2394 1607 -2398 811 -3190 1603 -2398 1601 -15186 833 -3156 1605 -2412 799 -3218 1595 -2392 809 -3186 811 -3186 1609 -2404 1607 -2394 1605 -2396 1601 -2400 1607 -2396 1601 -2392 1607 -2400 809 -3192 1603 -2396 815 -3194 809 -3188 809 -3192 1603 -2400 1599 -2394 1605 -2400 811 -3188 1599 -2400 1605 -16142 167 -202 diff --git a/applications/debug/unit_tests/tests/strint/strint_test.c b/applications/debug/unit_tests/tests/strint/strint_test.c new file mode 100644 index 000000000..d8fd9113d --- /dev/null +++ b/applications/debug/unit_tests/tests/strint/strint_test.c @@ -0,0 +1,142 @@ +#include +#include + +#include "../test.h" // IWYU pragma: keep + +#include + +MU_TEST(strint_test_basic) { + uint32_t result = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123456", NULL, &result, 10)); + mu_assert_int_eq(123456, result); +} + +MU_TEST(strint_test_junk) { + uint32_t result = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_uint32(" 123456 ", NULL, &result, 10)); + mu_assert_int_eq(123456, result); + mu_assert_int_eq( + StrintParseNoError, strint_to_uint32(" \r\n\r\n 123456 ", NULL, &result, 10)); + mu_assert_int_eq(123456, result); +} + +MU_TEST(strint_test_tail) { + uint32_t result = 0; + char* tail; + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123456tail", &tail, &result, 10)); + mu_assert_int_eq(123456, result); + mu_assert_string_eq("tail", tail); + mu_assert_int_eq( + StrintParseNoError, strint_to_uint32(" \r\n 123456tail", &tail, &result, 10)); + mu_assert_int_eq(123456, result); + mu_assert_string_eq("tail", tail); +} + +MU_TEST(strint_test_errors) { + uint32_t result = 123; + mu_assert_int_eq(StrintParseAbsentError, strint_to_uint32("", NULL, &result, 10)); + mu_assert_int_eq(123, result); + mu_assert_int_eq(StrintParseAbsentError, strint_to_uint32(" asd\r\n", NULL, &result, 10)); + mu_assert_int_eq(123, result); + mu_assert_int_eq(StrintParseSignError, strint_to_uint32("+++123456", NULL, &result, 10)); + mu_assert_int_eq(123, result); + mu_assert_int_eq(StrintParseSignError, strint_to_uint32("-1", NULL, &result, 10)); + mu_assert_int_eq(123, result); + mu_assert_int_eq( + StrintParseOverflowError, + strint_to_uint32("0xAAAAAAAAAAAAAAAADEADBEEF!!!!!!", NULL, &result, 0)); + mu_assert_int_eq(123, result); + mu_assert_int_eq(StrintParseOverflowError, strint_to_uint32("4294967296", NULL, &result, 0)); + mu_assert_int_eq(123, result); + + int32_t result_i32 = 123; + mu_assert_int_eq( + StrintParseOverflowError, strint_to_int32("-2147483649", NULL, &result_i32, 0)); + mu_assert_int_eq(123, result_i32); +} + +MU_TEST(strint_test_bases) { + uint32_t result = 0; + + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0x123", NULL, &result, 0)); + mu_assert_int_eq(0x123, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0X123", NULL, &result, 0)); + mu_assert_int_eq(0x123, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0xDEADBEEF", NULL, &result, 0)); + mu_assert_int_eq(0xDEADBEEF, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0xDEADBEEF", NULL, &result, 16)); + mu_assert_int_eq(0xDEADBEEF, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123", NULL, &result, 16)); + mu_assert_int_eq(0x123, result); + + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123", NULL, &result, 0)); + mu_assert_int_eq(123, result); + + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0123", NULL, &result, 0)); + mu_assert_int_eq(0123, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0123", NULL, &result, 8)); + mu_assert_int_eq(0123, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("123", NULL, &result, 8)); + mu_assert_int_eq(0123, result); + + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0b101", NULL, &result, 0)); + mu_assert_int_eq(0b101, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0b101", NULL, &result, 2)); + mu_assert_int_eq(0b101, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("0B101", NULL, &result, 0)); + mu_assert_int_eq(0b101, result); + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("101", NULL, &result, 2)); + mu_assert_int_eq(0b101, result); +} + +MU_TEST_SUITE(strint_test_limits) { + uint64_t result_u64 = 0; + mu_assert_int_eq( + StrintParseNoError, strint_to_uint64("18446744073709551615", NULL, &result_u64, 0)); + // `mu_assert_int_eq' does not support longs :( + mu_assert(UINT64_MAX == result_u64, "result does not equal UINT64_MAX"); + + int64_t result_i64 = 0; + mu_assert_int_eq( + StrintParseNoError, strint_to_int64("9223372036854775807", NULL, &result_i64, 0)); + mu_assert(INT64_MAX == result_i64, "result does not equal INT64_MAX"); + mu_assert_int_eq( + StrintParseNoError, strint_to_int64("-9223372036854775808", NULL, &result_i64, 0)); + mu_assert(INT64_MIN == result_i64, "result does not equal INT64_MIN"); + + uint32_t result_u32 = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_uint32("4294967295", NULL, &result_u32, 0)); + mu_assert_int_eq(UINT32_MAX, result_u32); + + int32_t result_i32 = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_int32("2147483647", NULL, &result_i32, 0)); + mu_assert_int_eq(INT32_MAX, result_i32); + mu_assert_int_eq(StrintParseNoError, strint_to_int32("-2147483648", NULL, &result_i32, 0)); + mu_assert_int_eq(INT32_MIN, result_i32); + + uint16_t result_u16 = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_uint16("65535", NULL, &result_u16, 0)); + mu_assert_int_eq(UINT16_MAX, result_u16); + + int16_t result_i16 = 0; + mu_assert_int_eq(StrintParseNoError, strint_to_int16("32767", NULL, &result_i16, 0)); + mu_assert_int_eq(INT16_MAX, result_i16); + mu_assert_int_eq(StrintParseNoError, strint_to_int16("-32768", NULL, &result_i16, 0)); + mu_assert_int_eq(INT16_MIN, result_i16); +} + +MU_TEST_SUITE(test_strint_suite) { + MU_RUN_TEST(strint_test_basic); + MU_RUN_TEST(strint_test_junk); + MU_RUN_TEST(strint_test_tail); + MU_RUN_TEST(strint_test_errors); + MU_RUN_TEST(strint_test_bases); + MU_RUN_TEST(strint_test_limits); +} + +int run_minunit_test_strint(void) { + MU_RUN_SUITE(test_strint_suite); + return MU_EXIT_CODE; +} + +TEST_API_DEFINE(run_minunit_test_strint) diff --git a/applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c b/applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c index 2afdaf5c1..d10d97c24 100644 --- a/applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c +++ b/applications/examples/example_number_input/scenes/example_number_input_scene_show_number.c @@ -13,8 +13,7 @@ static void example_number_input_scene_update_view(void* context) { dialog_ex_set_header(dialog_ex, "The number is", 64, 0, AlignCenter, AlignTop); - static char buffer[12]; //needs static for extended lifetime - + char buffer[12] = {}; snprintf(buffer, sizeof(buffer), "%ld", app->current_number); dialog_ex_set_text(dialog_ex, buffer, 64, 29, AlignCenter, AlignCenter); diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c index 895f05ce7..e5af819e9 100644 --- a/applications/examples/example_thermo/example_thermo.c +++ b/applications/examples/example_thermo/example_thermo.c @@ -257,7 +257,7 @@ static void example_thermo_draw_callback(Canvas* canvas, void* ctx) { snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units); } else { /* Or show a message that no data is available */ - strncpy(text_store, "-- No data --", TEXT_STORE_SIZE); + strlcpy(text_store, "-- No data --", TEXT_STORE_SIZE); } canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store); diff --git a/applications/external b/applications/external index 5864b3f10..2f67c3c91 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit 5864b3f1010951a9246381caa5769b4c96053f7b +Subproject commit 2f67c3c91826ff3147d57276a64ce4bdf00a3597 diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 96f6b4744..607f48a04 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "ble_hid.h" #include @@ -99,7 +100,7 @@ uint16_t ducky_get_keycode(BadKbScript* bad_kb, const char* param, bool accept_c bool ducky_get_number(const char* param, uint32_t* val) { uint32_t value = 0; - if(sscanf(param, "%lu", &value) == 1) { + if(strint_to_uint32(param, NULL, &value, 10) == StrintParseNoError) { *val = value; return true; } diff --git a/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_gnome.txt b/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_gnome.txt new file mode 100644 index 000000000..ec2ec108e --- /dev/null +++ b/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_gnome.txt @@ -0,0 +1,51 @@ +ID 1234:abcd Generic:USB Keyboard +REM Declare ourselves as a generic usb keyboard + +REM This will install qFlipper on Linux/Gnome, using the latest AppImage package + +REM Open a terminal +ALT F2 +DELAY 1000 +STRINGLN gnome-terminal --maximize +DELAY 1000 + +REM Ensure we have a folder to run executables from +STRINGLN mkdir -p $HOME/.local/bin + +REM Download the latest AppImage +STRINGLN curl -fsSL "https://update.flipperzero.one/qFlipper/release/linux-amd64/AppImage" -o "$HOME/.local/bin/qFlipper" +DELAY 1000 + +REM Make it executable +STRINGLN chmod +x $HOME/.local/bin/qFlipper + +REM Extract the appimage in /tmp to install icon and .desktop file +STRINGLN cd /tmp +STRINGLN $HOME/.local/bin/qFlipper --appimage-extract > /dev/null +STRINGLN sed "s@Exec=qFlipper@Exec=$HOME/.local/bin/qFlipper@" squashfs-root/usr/share/applications/qFlipper.desktop > $HOME/.local/share/applications/qFlipper.desktop +STRINGLN mkdir -p $HOME/.local/share/icons/hicolor/512x512/apps +STRINGLN cp squashfs-root/usr/share/icons/hicolor/512x512/apps/qFlipper.png $HOME/.local/share/icons/hicolor/512x512/apps/qFlipper.png +STRINGLN rm -rf squashfs-root +STRINGLN cd + +REM Depending on the Linux distribution and display manager +REM there might be several ways to update desktop entries +REM try all +STRINGLN xdg-desktop-menu forceupdate || true +STRINGLN update-desktop-database ~/.local/share/applications || true + +STRINGLN echo " +ENTER +REPEAT 60 +STRINGLN ========================================================================================== +STRINGLN qFlipper has been installed to $HOME/.local/bin/ +STRINGLN It should appear in your Applications menu. +STRINGLN If it does not, you might want to log out and log in again. +ENTER +STRINGLN If you prefer to run qFlipper from your terminal, either use the absolute path +STRINGLN or make sure $HOME/.local/bin/ is included in your PATH environment variable. +ENTER +STRINGLN Additional configurations might be required by your Linux distribution such as +STRINGLN group membership, udev rules or else. +STRINGLN ========================================================================================== +STRINGLN " diff --git a/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_macOS.txt b/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_macOS.txt new file mode 100644 index 000000000..252954ecb --- /dev/null +++ b/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_macOS.txt @@ -0,0 +1,16 @@ +ID 05ac:021e Apple:Keyboard +REM Keep these 3 lines IF (and only if) it's the first time you are performing a badKB attack against a specific macOS target. +REM In fact, it helps Flipper Zero bypass the macOS keyboard setup assistant. Otherwise the attack will not start. +REM Author: 47LeCoste +REM Version 1.0 (Flipper Ducky) +REM Target: macOS +DELAY 3000 +F4 +DELAY 2500 +STRING Terminal +DELAY 2500 +ENTER +DELAY 1500 +STRING (cd /tmp && curl -L -o qFlipper.dmg https://update.flipperzero.one/qFlipper/release/macos-amd64/dmg && hdiutil attach qFlipper.dmg && app_volume=$(ls /Volumes | grep -i "qFlipper") && (test -e /Applications/qFlipper.app && rm -rf /Applications/qFlipper.app ); cp -R "/Volumes/$app_volume/qFlipper.app" /Applications/ && hdiutil detach "/Volumes/$app_volume" && rm qFlipper.dmg && open /Applications/qFlipper.app) +DELAY 1000 +ENTER diff --git a/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_windows.txt b/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_windows.txt new file mode 100644 index 000000000..942823215 --- /dev/null +++ b/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_windows.txt @@ -0,0 +1,42 @@ +REM Written by @dexv +DELAY 2000 +GUI r +DELAY 500 +STRING powershell +ENTER +DELAY 1000 +STRING $url = "https://update.flipperzero.one/qFlipper/release/windows-amd64/portable" +ENTER +STRING $output = "$env:USERPROFILE\Documents\qFlipper.zip" +ENTER +STRING $destination = "$env:USERPROFILE\Documents\qFlipper" +ENTER +STRING $shortcutPath = "$env:USERPROFILE\Desktop\qFlipper.lnk" +ENTER +STRING $scriptPath = "$env:USERPROFILE\Documents\qFlipperInstall.ps1" +ENTER +STRING $driverPath = "$destination\STM32 Driver" +ENTER +STRING $installBat = "$driverPath\install.bat" +ENTER +STRING (New-Object System.Net.WebClient).DownloadFile($url, $output) +ENTER +STRING Expand-Archive -Path $output -DestinationPath $destination -Force +ENTER +STRING Set-Location -Path $destination +ENTER +STRING Start-Process -FilePath ".\qFlipper.exe" +ENTER +STRING Start-Process -Wait -FilePath "cmd.exe" -ArgumentList "/c $installBat" +ENTER +STRING $shell = New-Object -ComObject WScript.Shell +ENTER +STRING $shortcut = $shell.CreateShortcut($shortcutPath) +ENTER +STRING $shortcut.TargetPath = "$destination\qFlipper.exe" +ENTER +STRING $shortcut.Save() +ENTER +DELAY 500 +STRING "powershell -ExecutionPolicy Bypass -File $scriptPath" +ENTER diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_gnome.txt b/applications/main/bad_kb/resources/badusb/Demos/demo_gnome.txt new file mode 100644 index 000000000..7d91de412 --- /dev/null +++ b/applications/main/bad_kb/resources/badusb/Demos/demo_gnome.txt @@ -0,0 +1,87 @@ +ID 1234:abcd Generic:USB Keyboard +REM Declare ourselves as a generic usb keyboard +REM You can override this to use something else +REM Check the `lsusb` command to know your own devices IDs + +REM This is BadUSB demo script for Linux/Gnome + +REM Open terminal window +DELAY 1000 +ALT F2 +DELAY 500 +STRING gnome-terminal --maximize +DELAY 500 +ENTER +DELAY 750 + +REM Clear the screen in case some banner was displayed +STRING clear +ENTER + +REM Bigger shell script example +STRING cat > /dev/null << EOF +ENTER + +STRING Hello World! +ENTER + +DEFAULT_DELAY 50 + +STRING = +REPEAT 59 +ENTER +ENTER + +STRING _.-------.._ -, +ENTER +HOME +STRING .-"'''"--..,,_/ /'-, -, \ +ENTER +HOME +STRING .:" /:/ /'\ \ ,_..., '. | | +ENTER +HOME +STRING / ,----/:/ /'\ _\~'_-"' _; +ENTER +HOME +STRING ' / /'"""'\ \ \.~'_-' ,-"'/ +ENTER +HOME +STRING | | | 0 | | .-' ,/' / +ENTER +HOME +STRING | ,..\ \ ,.-"' ,/' / +ENTER +HOME +STRING ; : '/'""\' ,/--==,/-----, +ENTER +HOME +STRING | '-...| -.___-Z:_______J...---; +ENTER +HOME +STRING : ' _-' +ENTER +HOME +STRING _L_ _ ___ ___ ___ ___ ____--"' +ENTER +HOME +STRING | __|| | |_ _|| _ \| _ \| __|| _ \ +ENTER +HOME +STRING | _| | |__ | | | _/| _/| _| | / +ENTER +HOME +STRING |_| |____||___||_| |_| |___||_|_\ +ENTER +HOME +ENTER + +STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format +ENTER +STRING More information about script syntax can be found here: +ENTER +STRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md +ENTER + +STRING EOF +ENTER diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_macos.txt b/applications/main/bad_kb/resources/badusb/Demos/demo_macos.txt index 1fd4544b3..82543b28f 100644 --- a/applications/main/bad_kb/resources/badusb/Demos/demo_macos.txt +++ b/applications/main/bad_kb/resources/badusb/Demos/demo_macos.txt @@ -2,7 +2,7 @@ ID 1234:5678 Apple:Keyboard REM You can change these values to VID/PID of original Apple keyboard REM to bypass Keyboard Setup Assistant -REM This is BadKB demo script for macOS +REM This is BadUSB demo script for macOS REM Open terminal window DELAY 1000 @@ -75,7 +75,7 @@ ENTER HOME ENTER -STRING Flipper Zero BadKB feature is compatible with USB Rubber Ducky script format +STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format ENTER STRING More information about script syntax can be found here: ENTER diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_windows.txt b/applications/main/bad_kb/resources/badusb/Demos/demo_windows.txt index f3b1309d1..2ed33b3c0 100644 --- a/applications/main/bad_kb/resources/badusb/Demos/demo_windows.txt +++ b/applications/main/bad_kb/resources/badusb/Demos/demo_windows.txt @@ -1,4 +1,4 @@ -REM This is BadKB demo script for windows +REM This is BadUSB demo script for windows REM Open windows notepad DELAY 1000 @@ -76,7 +76,7 @@ ENTER HOME ENTER -STRING Flipper Zero BadKB feature is compatible with USB Rubber Ducky script format +STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format ENTER STRING More information about script syntax can be found here: ENTER diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index f28b58b23..a3ec8559f 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -94,9 +94,9 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == GpioStartEventOtgOn) { - furi_hal_power_enable_otg(); + if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg(); } else if(event.event == GpioStartEventOtgOff) { - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); } else if(event.event == GpioStartEventManualControl) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest); scene_manager_next_scene(app->scene_manager, GpioSceneTest); diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 6619041c5..1da170d3e 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -185,7 +185,7 @@ bool ibutton_load_key(iButton* ibutton, bool show_error) { FuriString* tmp = furi_string_alloc(); path_extract_filename(ibutton->file_path, tmp, true); - strncpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE); + strlcpy(ibutton->key_name, furi_string_get_cstr(tmp), IBUTTON_KEY_NAME_SIZE); furi_string_free(tmp); } else if(show_error) { @@ -245,7 +245,7 @@ bool ibutton_delete_key(iButton* ibutton) { } void ibutton_reset_key(iButton* ibutton) { - memset(ibutton->key_name, 0, IBUTTON_KEY_NAME_SIZE + 1); + ibutton->key_name[0] = '\0'; furi_string_reset(ibutton->file_path); ibutton_key_reset(ibutton->key); } diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h index aef4fb41c..df76597e1 100644 --- a/applications/main/ibutton/ibutton_i.h +++ b/applications/main/ibutton/ibutton_i.h @@ -32,7 +32,7 @@ #define IBUTTON_APP_FILENAME_PREFIX "iBtn" #define IBUTTON_APP_FILENAME_EXTENSION ".ibtn" -#define IBUTTON_KEY_NAME_SIZE 29 +#define IBUTTON_KEY_NAME_SIZE 30 typedef enum { iButtonWriteModeInvalid, @@ -56,7 +56,7 @@ struct iButton { iButtonWriteMode write_mode; FuriString* file_path; - char key_name[IBUTTON_KEY_NAME_SIZE + 1]; + char key_name[IBUTTON_KEY_NAME_SIZE]; Submenu* submenu; ByteInput* byte_input; diff --git a/applications/main/ibutton/scenes/ibutton_scene_rpc.c b/applications/main/ibutton/scenes/ibutton_scene_rpc.c index f4f193a47..87f51f2d8 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_rpc.c +++ b/applications/main/ibutton/scenes/ibutton_scene_rpc.c @@ -1,22 +1,26 @@ #include "../ibutton_i.h" void ibutton_scene_rpc_on_enter(void* context) { - iButton* ibutton = context; + UNUSED(context); +} + +static void ibutton_rpc_start_emulation(iButton* ibutton) { Popup* popup = ibutton->popup; popup_set_header(popup, "iButton", 82, 28, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); - + popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop); popup_set_icon(popup, 2, 14, &I_iButtonKey_49x44); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); + ibutton_worker_emulate_start(ibutton->worker, ibutton->key); + + ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); notification_message(ibutton->notifications, &sequence_display_backlight_on); } bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { iButton* ibutton = context; - Popup* popup = ibutton->popup; bool consumed = false; @@ -27,17 +31,13 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { bool result = false; if(ibutton_load_key(ibutton, false)) { - popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - - ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); - ibutton_worker_emulate_start(ibutton->worker, ibutton->key); - + ibutton_rpc_start_emulation(ibutton); result = true; + } else { + rpc_system_app_set_error_code(ibutton->rpc, RpcAppSystemErrorCodeParseFile); + rpc_system_app_set_error_text(ibutton->rpc, "Cannot load key file"); } - rpc_system_app_confirm(ibutton->rpc, result); - } else if(event.event == iButtonCustomEventRpcExit) { rpc_system_app_confirm(ibutton->rpc, true); scene_manager_stop(ibutton->scene_manager); diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index 70a691f55..674f0cc67 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -288,7 +288,7 @@ static void infrared_free(InfraredApp* infrared) { free(infrared); } -bool infrared_add_remote_with_button( +InfraredErrorCode infrared_add_remote_with_button( const InfraredApp* infrared, const char* button_name, const InfraredSignal* signal) { @@ -301,21 +301,23 @@ bool infrared_add_remote_with_button( furi_string_cat_printf( new_path, "/%s%s", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; 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; + error = infrared_remote_create(remote, furi_string_get_cstr(new_path)); + if(INFRARED_ERROR_PRESENT(error)) break; + + error = infrared_remote_append_signal(remote, signal, button_name); } while(false); furi_string_free(new_name); furi_string_free(new_path); - return success; + return error; } -bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name) { +InfraredErrorCode + infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name) { InfraredRemote* remote = infrared->remote; const char* old_path = infrared_remote_get_path(remote); @@ -335,12 +337,13 @@ bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new 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)); + const InfraredErrorCode error = + 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; + return error; } void infrared_tx_start(InfraredApp* infrared) { @@ -373,17 +376,16 @@ void infrared_tx_start(InfraredApp* infrared) { infrared->app_state.is_transmitting = true; } -void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) { +InfraredErrorCode infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index) { furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote)); - if(infrared_remote_load_signal(infrared->remote, infrared->current_signal, button_index)) { + InfraredErrorCode error = + infrared_remote_load_signal(infrared->remote, infrared->current_signal, button_index); + + if(!INFRARED_ERROR_PRESENT(error)) { infrared_tx_start(infrared); - } else { - infrared_show_error_message( - infrared, - "Failed to load\n\"%s\"", - infrared_remote_get_signal_name(infrared->remote, button_index)); } + return error; } void infrared_tx_stop(InfraredApp* infrared) { @@ -406,7 +408,7 @@ void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback call furi_thread_start(infrared->task_thread); } -bool infrared_blocking_task_finalize(InfraredApp* infrared) { +InfraredErrorCode infrared_blocking_task_finalize(InfraredApp* infrared) { furi_thread_join(infrared->task_thread); return furi_thread_get_return_code(infrared->task_thread); } @@ -458,9 +460,9 @@ void infrared_set_tx_pin(InfraredApp* infrared, FuriHalInfraredTxPin tx_pin) { void infrared_enable_otg(InfraredApp* infrared, bool enable) { if(enable) { - furi_hal_power_enable_otg(); + if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg(); } else { - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); } infrared->app_state.is_otg_enabled = enable; } @@ -556,10 +558,18 @@ int32_t infrared_app(void* p) { is_rpc_mode = true; } else { const char* file_path = (const char*)p; - is_remote_loaded = infrared_remote_load(infrared->remote, file_path); + InfraredErrorCode error = infrared_remote_load(infrared->remote, file_path); - if(!is_remote_loaded) { - infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path); + if(!INFRARED_ERROR_PRESENT(error)) { + is_remote_loaded = true; + } else { + is_remote_loaded = false; + bool wrong_file_type = INFRARED_ERROR_CHECK(error, InfraredErrorCodeWrongFileType); + const char* format = wrong_file_type ? + "Library file\n\"%s\" can't be openned as a remote" : + "Failed to load\n\"%s\""; + + infrared_show_error_message(infrared, format, file_path); return -1; } diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 75d4e230d..692cc9671 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -43,8 +43,8 @@ #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_MAX_BUTTON_NAME_LENGTH 23 +#define INFRARED_MAX_REMOTE_NAME_LENGTH 23 #define INFRARED_APP_FOLDER EXT_PATH("infrared") #define INFRARED_APP_EXTENSION ".ir" @@ -174,9 +174,9 @@ typedef enum { * @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. + * @return InfraredErrorCodeNone if the remote was successfully created, otherwise error code. */ -bool infrared_add_remote_with_button( +InfraredErrorCode infrared_add_remote_with_button( const InfraredApp* infrared, const char* name, const InfraredSignal* signal); @@ -186,9 +186,10 @@ bool infrared_add_remote_with_button( * * @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. + * @return InfraredErrorCodeNone if the remote was successfully renamed, otherwise error code. */ -bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name); +InfraredErrorCode + infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name); /** * @brief Begin transmission of the currently loaded signal. @@ -206,9 +207,9 @@ void infrared_tx_start(InfraredApp* infrared); * * @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. + * @returns InfraredErrorCodeNone if the signal could be loaded, otherwise error code. */ -void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index); +InfraredErrorCode infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index); /** * @brief Stop transmission of the currently loaded signal. @@ -236,9 +237,9 @@ void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback call * (e.g. to display the results), the caller code MUST set it explicitly. * * @param[in,out] infrared pointer to the application instance. - * @return true if the blocking task finished successfully, false otherwise. + * @return InfraredErrorCodeNone if the blocking task finished successfully, otherwise error code. */ -bool infrared_blocking_task_finalize(InfraredApp* infrared); +InfraredErrorCode infrared_blocking_task_finalize(InfraredApp* infrared); /** * @brief Set the internal text store with formatted text. diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c index b28918489..8c7422d5e 100644 --- a/applications/main/infrared/infrared_brute_force.c +++ b/applications/main/infrared/infrared_brute_force.c @@ -50,10 +50,10 @@ void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const brute_force->db_filename = db_filename; } -bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { +InfraredErrorCode infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { furi_assert(!brute_force->is_started); furi_assert(brute_force->db_filename); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); @@ -61,12 +61,15 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { InfraredSignal* signal = infrared_signal_alloc(); do { - if(!flipper_format_buffered_file_open_existing(ff, brute_force->db_filename)) break; + if(!flipper_format_buffered_file_open_existing(ff, brute_force->db_filename)) { + error = InfraredErrorCodeFileOperationFailed; + break; + } bool signals_valid = false; - while(infrared_signal_read_name(ff, signal_name)) { - signals_valid = infrared_signal_read_body(signal, ff) && - infrared_signal_is_valid(signal); + while(infrared_signal_read_name(ff, signal_name) == InfraredErrorCodeNone) { + error = infrared_signal_read_body(signal, ff); + signals_valid = (!INFRARED_ERROR_PRESENT(error)) && infrared_signal_is_valid(signal); if(!signals_valid) break; InfraredBruteForceRecord* record = @@ -75,9 +78,7 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { ++(record->count); } } - if(!signals_valid) break; - success = true; } while(false); infrared_signal_free(signal); @@ -85,7 +86,7 @@ bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return error; } bool infrared_brute_force_start( @@ -139,10 +140,12 @@ 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)); + brute_force->current_signal, + brute_force->ff, + furi_string_get_cstr(brute_force->current_record_name)) == + InfraredErrorCodeNone; if(success) { infrared_signal_transmit(brute_force->current_signal); } diff --git a/applications/main/infrared/infrared_brute_force.h b/applications/main/infrared/infrared_brute_force.h index 8eda08a63..879642257 100644 --- a/applications/main/infrared/infrared_brute_force.h +++ b/applications/main/infrared/infrared_brute_force.h @@ -10,6 +10,7 @@ #include #include +#include "infrared_error_code.h" /** * @brief InfraredBruteForce opaque type declaration. @@ -45,9 +46,9 @@ void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const * 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. + * @returns InfraredErrorCodeNone on success, otherwise error code. */ -bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); +InfraredErrorCode infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); /** * @brief Start transmitting signals from a category stored in an InfraredBruteForce's instance dictionary. diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 2763d2345..cdd5b9a11 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "infrared_signal.h" @@ -176,25 +177,28 @@ static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { return false; } - uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); - uint32_t frequency = atoi(frequency_str); - float duty_cycle = (float)atoi(duty_cycle_str) / 100; + uint32_t frequency; + uint32_t duty_cycle_u32; + if(strint_to_uint32(frequency_str, NULL, &frequency, 10) != StrintParseNoError || + strint_to_uint32(duty_cycle_str, NULL, &duty_cycle_u32, 10) != StrintParseNoError) + return false; + float duty_cycle = duty_cycle_u32 / 100.0f; str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE; + uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); size_t timings_size = 0; while(1) { while(*str == ' ') { ++str; } - char timing_str[INFRARED_CLI_BUF_SIZE]; - if(sscanf(str, "%9s", timing_str) != 1) { + uint32_t timing; + char* next_token; + if(strint_to_uint32(str, &next_token, &timing, 10) != StrintParseNoError) { break; } - - str += strlen(timing_str); - uint32_t timing = atoi(timing_str); + str = next_token; if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) { break; @@ -228,9 +232,15 @@ static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args) { static bool infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) { - bool ret = infrared_signal_save(signal, file, name); - if(!ret) { - printf("Failed to save signal: \"%s\"\r\n", name); + bool ret = true; + InfraredErrorCode error = infrared_signal_save(signal, file, name); + if(INFRARED_ERROR_PRESENT(error)) { + printf( + "Failed to save signal: \"%s\" code: 0x%X index: 0x%02X\r\n", + name, + INFRARED_ERROR_GET_CODE(error), + INFRARED_ERROR_GET_INDEX(error)); + ret = false; } return ret; } @@ -292,7 +302,7 @@ static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* o FuriString* tmp; tmp = furi_string_alloc(); - while(infrared_signal_read(signal, input_file, tmp)) { + while(infrared_signal_read(signal, input_file, tmp) == InfraredErrorCodeNone) { ret = false; if(!infrared_signal_is_valid(signal)) { printf("Invalid signal\r\n"); @@ -460,7 +470,7 @@ static void printf("Missing signal name.\r\n"); break; } - if(!infrared_brute_force_calculate_messages(brute_force)) { + if(infrared_brute_force_calculate_messages(brute_force) != InfraredErrorCodeNone) { printf("Invalid remote name.\r\n"); break; } diff --git a/applications/main/infrared/infrared_error_code.h b/applications/main/infrared/infrared_error_code.h new file mode 100644 index 000000000..841721b17 --- /dev/null +++ b/applications/main/infrared/infrared_error_code.h @@ -0,0 +1,45 @@ +#pragma once + +typedef enum { + InfraredErrorCodeNone = 0, + InfraredErrorCodeFileOperationFailed = 0x800000, + InfraredErrorCodeWrongFileType = 0x80000100, + InfraredErrorCodeWrongFileVersion = 0x80000200, + + //Common signal errors + InfraredErrorCodeSignalTypeUnknown = 0x80000300, + InfraredErrorCodeSignalNameNotFound = 0x80000400, + InfraredErrorCodeSignalUnableToReadType = 0x80000500, + InfraredErrorCodeSignalUnableToWriteType = 0x80000600, + + //Raw signal errors + InfraredErrorCodeSignalRawUnableToReadFrequency = 0x80000700, + InfraredErrorCodeSignalRawUnableToReadDutyCycle = 0x80000800, + InfraredErrorCodeSignalRawUnableToReadTimingsSize = 0x80000900, + InfraredErrorCodeSignalRawUnableToReadTooLongData = 0x80000A00, + InfraredErrorCodeSignalRawUnableToReadData = 0x80000B00, + + InfraredErrorCodeSignalRawUnableToWriteFrequency = 0x80000C00, + InfraredErrorCodeSignalRawUnableToWriteDutyCycle = 0x80000D00, + InfraredErrorCodeSignalRawUnableToWriteData = 0x80000E00, + + //Message signal errors + InfraredErrorCodeSignalMessageUnableToReadProtocol = 0x80000F00, + InfraredErrorCodeSignalMessageUnableToReadAddress = 0x80001000, + InfraredErrorCodeSignalMessageUnableToReadCommand = 0x80001100, + InfraredErrorCodeSignalMessageIsInvalid = 0x80001200, + + InfraredErrorCodeSignalMessageUnableToWriteProtocol = 0x80001300, + InfraredErrorCodeSignalMessageUnableToWriteAddress = 0x80001400, + InfraredErrorCodeSignalMessageUnableToWriteCommand = 0x80001500, +} InfraredErrorCode; + +#define INFRARED_ERROR_CODE_MASK (0xFFFFFF00) +#define INFRARED_ERROR_INDEX_MASK (0x000000FF) + +#define INFRARED_ERROR_GET_CODE(error) ((error) & INFRARED_ERROR_CODE_MASK) +#define INFRARED_ERROR_GET_INDEX(error) ((error) & INFRARED_ERROR_INDEX_MASK) +#define INFRARED_ERROR_SET_INDEX(code, index) ((code) |= ((index) & INFRARED_ERROR_INDEX_MASK)) + +#define INFRARED_ERROR_PRESENT(error) (INFRARED_ERROR_GET_CODE(error) != InfraredErrorCodeNone) +#define INFRARED_ERROR_CHECK(error, test_code) (INFRARED_ERROR_GET_CODE(error) == (test_code)) diff --git a/applications/main/infrared/infrared_remote.c b/applications/main/infrared/infrared_remote.c index 11bbf197b..9da08cbd4 100644 --- a/applications/main/infrared/infrared_remote.c +++ b/applications/main/infrared/infrared_remote.c @@ -8,8 +8,9 @@ #define TAG "InfraredRemote" -#define INFRARED_FILE_HEADER "IR signals file" -#define INFRARED_FILE_VERSION (1) +#define INFRARED_FILE_HEADER "IR signals file" +#define INFRARED_LIBRARY_HEADER "IR library file" +#define INFRARED_FILE_VERSION (1) ARRAY_DEF(StringArray, const char*, M_CSTR_DUP_OPLIST); //-V575 @@ -34,7 +35,7 @@ typedef struct { const InfraredSignal* signal; } InfraredBatchTarget; -typedef bool ( +typedef InfraredErrorCode ( *InfraredBatchCallback)(const InfraredBatch* batch, const InfraredBatchTarget* target); InfraredRemote* infrared_remote_alloc(void) { @@ -80,7 +81,7 @@ const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t return *StringArray_cget(remote->signal_names, index); } -bool infrared_remote_load_signal( +InfraredErrorCode infrared_remote_load_signal( const InfraredRemote* remote, InfraredSignal* signal, size_t index) { @@ -89,25 +90,27 @@ bool infrared_remote_load_signal( Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { const char* path = furi_string_get_cstr(remote->path); - if(!flipper_format_buffered_file_open_existing(ff, path)) break; + if(!flipper_format_buffered_file_open_existing(ff, path)) { + error = InfraredErrorCodeFileOperationFailed; + break; + } - if(!infrared_signal_search_by_index_and_read(signal, ff, index)) { + error = infrared_signal_search_by_index_and_read(signal, ff, index); + if(INFRARED_ERROR_PRESENT(error)) { 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; + return error; } bool infrared_remote_get_signal_index( @@ -128,31 +131,35 @@ bool infrared_remote_get_signal_index( return false; } -bool infrared_remote_append_signal( +InfraredErrorCode infrared_remote_append_signal( InfraredRemote* remote, const InfraredSignal* signal, const char* name) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_file_alloc(storage); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; 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; + if(!flipper_format_file_open_append(ff, path)) { + error = InfraredErrorCodeFileOperationFailed; + break; + } + + error = infrared_signal_save(signal, ff, name); + if(INFRARED_ERROR_PRESENT(error)) break; StringArray_push_back(remote->signal_names, name); - success = true; } while(false); flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return error; } -static bool infrared_remote_batch_start( +static InfraredErrorCode infrared_remote_batch_start( InfraredRemote* remote, InfraredBatchCallback batch_callback, const InfraredBatchTarget* target) { @@ -179,33 +186,59 @@ static bool infrared_remote_batch_start( status = storage_common_stat(storage, path_out, NULL); } while(status == FSE_OK || status == FSE_EXIST); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; + StringArray_t buf_names; + StringArray_init_set(buf_names, remote->signal_names); 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)) + if(!flipper_format_buffered_file_open_existing(batch_context.ff_in, path_in) || + !flipper_format_buffered_file_open_always(batch_context.ff_out, path_out) || + !flipper_format_write_header_cstr( + batch_context.ff_out, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION)) { + error = InfraredErrorCodeFileOperationFailed; 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)) + error = infrared_signal_read( + batch_context.signal, batch_context.ff_in, batch_context.signal_name); + if(INFRARED_ERROR_PRESENT(error)) { + INFRARED_ERROR_SET_INDEX(error, batch_context.signal_index); break; - if(!batch_callback(&batch_context, target)) break; + } + + error = batch_callback(&batch_context, target); + if(INFRARED_ERROR_PRESENT(error)) { + INFRARED_ERROR_SET_INDEX(error, batch_context.signal_index); + break; + } + } + if(INFRARED_ERROR_PRESENT(error)) break; + + if(!flipper_format_buffered_file_close(batch_context.ff_out) || + !flipper_format_buffered_file_close(batch_context.ff_in)) { + error = InfraredErrorCodeFileOperationFailed; + 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); + error = (status == FSE_OK || status == FSE_EXIST) ? InfraredErrorCodeNone : + InfraredErrorCodeFileOperationFailed; } while(false); + if(INFRARED_ERROR_PRESENT(error)) { + //Remove all temp data and rollback signal names + flipper_format_buffered_file_close(batch_context.ff_out); + flipper_format_buffered_file_close(batch_context.ff_in); + status = storage_common_stat(storage, path_out, NULL); + if(status == FSE_OK || status == FSE_EXIST) storage_common_remove(storage, path_out); + + StringArray_reset(remote->signal_names); + StringArray_set(remote->signal_names, buf_names); + } + + StringArray_clear(buf_names); infrared_signal_free(batch_context.signal); furi_string_free(batch_context.signal_name); flipper_format_free(batch_context.ff_out); @@ -214,15 +247,18 @@ static bool infrared_remote_batch_start( furi_record_close(RECORD_STORAGE); - return success; + return error; } -static bool infrared_remote_insert_signal_callback( +static InfraredErrorCode 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; + InfraredErrorCode error = + infrared_signal_save(target->signal, batch->ff_out, target->signal_name); + if(INFRARED_ERROR_PRESENT(error)) return error; + StringArray_push_at( batch->remote->signal_names, target->signal_index, target->signal_name); } @@ -232,7 +268,7 @@ static bool infrared_remote_insert_signal_callback( batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name)); } -bool infrared_remote_insert_signal( +InfraredErrorCode infrared_remote_insert_signal( InfraredRemote* remote, const InfraredSignal* signal, const char* name, @@ -251,7 +287,7 @@ bool infrared_remote_insert_signal( remote, infrared_remote_insert_signal_callback, &insert_target); } -static bool infrared_remote_rename_signal_callback( +static InfraredErrorCode infrared_remote_rename_signal_callback( const InfraredBatch* batch, const InfraredBatchTarget* target) { const char* signal_name; @@ -268,7 +304,8 @@ static bool infrared_remote_rename_signal_callback( 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) { +InfraredErrorCode + 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 = { @@ -281,7 +318,7 @@ bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const c remote, infrared_remote_rename_signal_callback, &rename_target); } -static bool infrared_remote_delete_signal_callback( +static InfraredErrorCode infrared_remote_delete_signal_callback( const InfraredBatch* batch, const InfraredBatchTarget* target) { if(batch->signal_index == target->signal_index) { @@ -294,10 +331,10 @@ static bool infrared_remote_delete_signal_callback( batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name)); } - return true; + return InfraredErrorCodeNone; } -bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index) { +InfraredErrorCode infrared_remote_delete_signal(InfraredRemote* remote, size_t index) { furi_assert(index < infrared_remote_get_signal_count(remote)); const InfraredBatchTarget delete_target = { @@ -310,33 +347,35 @@ bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index) { remote, infrared_remote_delete_signal_callback, &delete_target); } -bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index) { +InfraredErrorCode + 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; + InfraredErrorCode error = InfraredErrorCodeNone; + if(index == new_index) return error; 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; + error = infrared_remote_load_signal(remote, signal, index); + if(INFRARED_ERROR_PRESENT(error)) break; - success = true; + error = infrared_remote_delete_signal(remote, index); + if(INFRARED_ERROR_PRESENT(error)) break; + + error = infrared_remote_insert_signal(remote, signal, signal_name, new_index); } while(false); free(signal_name); infrared_signal_free(signal); - return success; + return error; } -bool infrared_remote_create(InfraredRemote* remote, const char* path) { +InfraredErrorCode infrared_remote_create(InfraredRemote* remote, const char* path) { FURI_LOG_I(TAG, "Creating new file: '%s'", path); infrared_remote_reset(remote); @@ -358,45 +397,64 @@ bool infrared_remote_create(InfraredRemote* remote, const char* path) { flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed; } -bool infrared_remote_load(InfraredRemote* remote, const char* path) { +InfraredErrorCode infrared_remote_load(InfraredRemote* remote, const char* path) { FURI_LOG_I(TAG, "Loading file: '%s'", path); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); FuriString* tmp = furi_string_alloc(); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!flipper_format_buffered_file_open_existing(ff, path)) break; + if(!flipper_format_buffered_file_open_existing(ff, path)) { + error = InfraredErrorCodeFileOperationFailed; + break; + } uint32_t version; - if(!flipper_format_read_header(ff, tmp, &version)) break; - - if(!furi_string_equal(tmp, INFRARED_FILE_HEADER) || (version != INFRARED_FILE_VERSION)) + if(!flipper_format_read_header(ff, tmp, &version)) { + error = InfraredErrorCodeFileOperationFailed; break; + } + + if(furi_string_equal(tmp, INFRARED_LIBRARY_HEADER)) { + FURI_LOG_E(TAG, "Library file can't be loaded in this context"); + error = InfraredErrorCodeWrongFileType; + break; + } + + if(!furi_string_equal(tmp, INFRARED_FILE_HEADER)) { + error = InfraredErrorCodeWrongFileType; + FURI_LOG_E(TAG, "Filetype unknown"); + break; + } + + if(version != INFRARED_FILE_VERSION) { + error = InfraredErrorCodeWrongFileVersion; + FURI_LOG_E(TAG, "Wrong file version"); + break; + } infrared_remote_set_path(remote, path); StringArray_reset(remote->signal_names); - while(infrared_signal_read_name(ff, tmp)) { + while(infrared_signal_read_name(ff, tmp) == InfraredErrorCodeNone) { StringArray_push_back(remote->signal_names, furi_string_get_cstr(tmp)); } - - success = true; } while(false); furi_string_free(tmp); flipper_format_free(ff); furi_record_close(RECORD_STORAGE); - return success; + return error; } -bool infrared_remote_rename(InfraredRemote* remote, const char* new_path) { +InfraredErrorCode 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); @@ -409,10 +467,10 @@ bool infrared_remote_rename(InfraredRemote* remote, const char* new_path) { infrared_remote_set_path(remote, new_path); } - return success; + return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed; } -bool infrared_remote_remove(InfraredRemote* remote) { +InfraredErrorCode 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); @@ -423,5 +481,5 @@ bool infrared_remote_remove(InfraredRemote* remote) { infrared_remote_reset(remote); } - return success; + return success ? InfraredErrorCodeNone : InfraredErrorCodeFileOperationFailed; } diff --git a/applications/main/infrared/infrared_remote.h b/applications/main/infrared/infrared_remote.h index 4604a25ef..14416cd65 100644 --- a/applications/main/infrared/infrared_remote.h +++ b/applications/main/infrared/infrared_remote.h @@ -105,12 +105,10 @@ bool infrared_remote_get_signal_index( * @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. + * @return InfraredErrorCodeNone if the signal was successfully loaded, otherwise error code. */ -bool infrared_remote_load_signal( - const InfraredRemote* remote, - InfraredSignal* signal, - size_t index); +InfraredErrorCode + infrared_remote_load_signal(const InfraredRemote* remote, InfraredSignal* signal, size_t index); /** * @brief Append a signal to the file associated with an InfraredRemote instance. @@ -121,9 +119,9 @@ bool infrared_remote_load_signal( * @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. + * @returns InfraredErrorCodeNone if the signal was successfully appended, otherwise error code. */ -bool infrared_remote_append_signal( +InfraredErrorCode infrared_remote_append_signal( InfraredRemote* remote, const InfraredSignal* signal, const char* name); @@ -141,9 +139,10 @@ bool infrared_remote_append_signal( * @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. + * @returns InfraredErrorCodeNone if the signal was successfully inserted, otherwise error + * code describing what error happened ORed with index pointing which signal caused an error. */ -bool infrared_remote_insert_signal( +InfraredErrorCode infrared_remote_insert_signal( InfraredRemote* remote, const InfraredSignal* signal, const char* name, @@ -157,9 +156,10 @@ bool infrared_remote_insert_signal( * @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. + * @returns InfraredErrorCodeNone if the signal was successfully renamed, otherwise error code. */ -bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name); +InfraredErrorCode + 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. @@ -169,17 +169,21 @@ bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const c * @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. + * @returns InfraredErrorCodeNone if the signal was moved successfully, otherwise error + * code describing what error happened ORed with index pointing which signal caused an error. */ -bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index); +InfraredErrorCode + 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. + * @returns InfraredErrorCodeNone if the signal was successfully deleted, otherwise error + * code describing what error happened ORed with index pointing which signal caused an error. */ -bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index); +InfraredErrorCode infrared_remote_delete_signal(InfraredRemote* remote, size_t index); /** * @brief Create a new file and associate it with an InfraredRemote instance. @@ -188,9 +192,9 @@ bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index); * * @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. + * @returns InfraredErrorCodeNone if the file was successfully created, otherwise error code. */ -bool infrared_remote_create(InfraredRemote* remote, const char* path); +InfraredErrorCode infrared_remote_create(InfraredRemote* remote, const char* path); /** * @brief Associate an InfraredRemote instance with a file and load the signal names from it. @@ -200,9 +204,9 @@ bool infrared_remote_create(InfraredRemote* remote, const char* path); * * @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. + * @returns InfraredErrorCodeNone if the file was successfully loaded, otherwise error code. */ -bool infrared_remote_load(InfraredRemote* remote, const char* path); +InfraredErrorCode infrared_remote_load(InfraredRemote* remote, const char* path); /** * @brief Rename the file associated with an InfraredRemote instance. @@ -211,9 +215,9 @@ bool infrared_remote_load(InfraredRemote* remote, const char* path); * * @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. + * @returns InfraredErrorCodeNone if the file was successfully renamed, otherwise error code. */ -bool infrared_remote_rename(InfraredRemote* remote, const char* new_path); +InfraredErrorCode infrared_remote_rename(InfraredRemote* remote, const char* new_path); /** * @brief Remove the file associated with an InfraredRemote instance. @@ -224,6 +228,6 @@ bool infrared_remote_rename(InfraredRemote* remote, const char* new_path); * 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. + * @returns InfraredErrorCodeNone if the file was successfully removed, otherwise error code. */ -bool infrared_remote_remove(InfraredRemote* remote); +InfraredErrorCode infrared_remote_remove(InfraredRemote* remote); diff --git a/applications/main/infrared/infrared_signal.c b/applications/main/infrared/infrared_signal.c index dd40754d7..532f7beba 100644 --- a/applications/main/infrared/infrared_signal.c +++ b/applications/main/infrared/infrared_signal.c @@ -101,104 +101,177 @@ static bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) { return true; } -static inline bool +static inline InfraredErrorCode infrared_signal_save_message(const InfraredMessage* message, FlipperFormat* ff) { const char* protocol_name = infrared_get_protocol_name(message->protocol); - return flipper_format_write_string_cstr( - ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_PARSED) && - flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_PROTOCOL_KEY, protocol_name) && - flipper_format_write_hex( - ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message->address, 4) && - flipper_format_write_hex( - ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message->command, 4); + InfraredErrorCode error = InfraredErrorCodeNone; + do { + if(!flipper_format_write_string_cstr( + ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_PARSED)) { + error = InfraredErrorCodeSignalUnableToWriteType; + break; + } + + if(!flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_PROTOCOL_KEY, protocol_name)) { + error = InfraredErrorCodeSignalMessageUnableToWriteProtocol; + break; + } + + if(!flipper_format_write_hex( + ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message->address, 4)) { + error = InfraredErrorCodeSignalMessageUnableToWriteAddress; + break; + } + + if(!flipper_format_write_hex( + ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message->command, 4)) { + error = InfraredErrorCodeSignalMessageUnableToWriteCommand; + break; + } + + } while(false); + + return error; } -static inline bool infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) { +static inline InfraredErrorCode + infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) { furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); - return flipper_format_write_string_cstr( - ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_RAW) && - flipper_format_write_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &raw->frequency, 1) && - flipper_format_write_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &raw->duty_cycle, 1) && - flipper_format_write_uint32( - ff, INFRARED_SIGNAL_DATA_KEY, raw->timings, raw->timings_size); + + InfraredErrorCode error = InfraredErrorCodeNone; + do { + if(!flipper_format_write_string_cstr( + ff, INFRARED_SIGNAL_TYPE_KEY, INFRARED_SIGNAL_TYPE_RAW)) { + error = InfraredErrorCodeSignalUnableToWriteType; + break; + } + + if(!flipper_format_write_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &raw->frequency, 1)) { + error = InfraredErrorCodeSignalRawUnableToWriteFrequency; + break; + } + + if(!flipper_format_write_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &raw->duty_cycle, 1)) { + error = InfraredErrorCodeSignalRawUnableToWriteDutyCycle; + break; + } + + if(!flipper_format_write_uint32( + ff, INFRARED_SIGNAL_DATA_KEY, raw->timings, raw->timings_size)) { + error = InfraredErrorCodeSignalRawUnableToWriteData; + break; + } + } while(false); + return error; } -static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { +static inline InfraredErrorCode + infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { FuriString* buf; buf = furi_string_alloc(); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!flipper_format_read_string(ff, INFRARED_SIGNAL_PROTOCOL_KEY, buf)) break; + if(!flipper_format_read_string(ff, INFRARED_SIGNAL_PROTOCOL_KEY, buf)) { + error = InfraredErrorCodeSignalMessageUnableToReadProtocol; + break; + } InfraredMessage message; message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf)); - if(!flipper_format_read_hex(ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message.address, 4)) + if(!flipper_format_read_hex( + ff, INFRARED_SIGNAL_ADDRESS_KEY, (uint8_t*)&message.address, 4)) { + error = InfraredErrorCodeSignalMessageUnableToReadAddress; break; - if(!flipper_format_read_hex(ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message.command, 4)) + } + if(!flipper_format_read_hex( + ff, INFRARED_SIGNAL_COMMAND_KEY, (uint8_t*)&message.command, 4)) { + error = InfraredErrorCodeSignalMessageUnableToReadCommand; break; - if(!infrared_signal_is_message_valid(&message)) break; + } + + if(!infrared_signal_is_message_valid(&message)) { + error = InfraredErrorCodeSignalMessageIsInvalid; + break; + } infrared_signal_set_message(signal, &message); - success = true; } while(false); furi_string_free(buf); - return success; + return error; } -static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { - bool success = false; +static inline InfraredErrorCode + infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { + InfraredErrorCode error = InfraredErrorCodeNone; do { uint32_t frequency; - if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &frequency, 1)) break; + if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_FREQUENCY_KEY, &frequency, 1)) { + error = InfraredErrorCodeSignalRawUnableToReadFrequency; + break; + } float duty_cycle; - if(!flipper_format_read_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &duty_cycle, 1)) break; + if(!flipper_format_read_float(ff, INFRARED_SIGNAL_DUTY_CYCLE_KEY, &duty_cycle, 1)) { + error = InfraredErrorCodeSignalRawUnableToReadDutyCycle; + break; + } uint32_t timings_size; - if(!flipper_format_get_value_count(ff, INFRARED_SIGNAL_DATA_KEY, &timings_size)) break; + if(!flipper_format_get_value_count(ff, INFRARED_SIGNAL_DATA_KEY, &timings_size)) { + error = InfraredErrorCodeSignalRawUnableToReadTimingsSize; + break; + } - if(timings_size > MAX_TIMINGS_AMOUNT) break; + if(timings_size > MAX_TIMINGS_AMOUNT) { + error = InfraredErrorCodeSignalRawUnableToReadTooLongData; + break; + } uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); if(!flipper_format_read_uint32(ff, INFRARED_SIGNAL_DATA_KEY, timings, timings_size)) { + error = InfraredErrorCodeSignalRawUnableToReadData; free(timings); break; } + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); free(timings); - success = true; + error = InfraredErrorCodeNone; } while(false); - return success; + return error; } -bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { +InfraredErrorCode infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { FuriString* tmp = furi_string_alloc(); - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!flipper_format_read_string(ff, INFRARED_SIGNAL_TYPE_KEY, tmp)) break; - - if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_RAW)) { - if(!infrared_signal_read_raw(signal, ff)) break; - } else if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_PARSED)) { - if(!infrared_signal_read_message(signal, ff)) break; - } else { - FURI_LOG_E(TAG, "Unknown signal type: %s", furi_string_get_cstr(tmp)); + if(!flipper_format_read_string(ff, INFRARED_SIGNAL_TYPE_KEY, tmp)) { + error = InfraredErrorCodeSignalUnableToReadType; break; } - success = true; + if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_RAW)) { + error = infrared_signal_read_raw(signal, ff); + } else if(furi_string_equal(tmp, INFRARED_SIGNAL_TYPE_PARSED)) { + error = infrared_signal_read_message(signal, ff); + } else { + FURI_LOG_E(TAG, "Unknown signal type: %s", furi_string_get_cstr(tmp)); + error = InfraredErrorCodeSignalTypeUnknown; + break; + } } while(false); furi_string_free(tmp); - return success; + + return error; } InfraredSignal* infrared_signal_alloc(void) { @@ -285,68 +358,88 @@ const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal) return &signal->payload.message; } -bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name) { +InfraredErrorCode + infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name) { + InfraredErrorCode error = InfraredErrorCodeNone; + if(!flipper_format_write_comment_cstr(ff, "") || !flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_NAME_KEY, name)) { - return false; + error = InfraredErrorCodeFileOperationFailed; } else if(signal->is_raw) { - return infrared_signal_save_raw(&signal->payload.raw, ff); + error = infrared_signal_save_raw(&signal->payload.raw, ff); } else { - return infrared_signal_save_message(&signal->payload.message, ff); + error = infrared_signal_save_message(&signal->payload.message, ff); } + + return error; } -bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { - bool success = false; +InfraredErrorCode + infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { + InfraredErrorCode error = InfraredErrorCodeNone; do { - if(!infrared_signal_read_name(ff, name)) break; - if(!infrared_signal_read_body(signal, ff)) break; + error = infrared_signal_read_name(ff, name); + if(INFRARED_ERROR_PRESENT(error)) break; - success = true; //-V779 + error = infrared_signal_read_body(signal, ff); } while(false); - return success; + return error; } -bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name) { - return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name); +InfraredErrorCode infrared_signal_read_name(FlipperFormat* ff, FuriString* name) { + return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name) ? + InfraredErrorCodeNone : + InfraredErrorCodeSignalNameNotFound; } -bool infrared_signal_search_by_name_and_read( +InfraredErrorCode infrared_signal_search_by_name_and_read( InfraredSignal* signal, FlipperFormat* ff, const char* name) { - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; FuriString* tmp = furi_string_alloc(); - while(infrared_signal_read_name(ff, tmp)) { + do { + error = infrared_signal_read_name(ff, tmp); + if(INFRARED_ERROR_PRESENT(error)) break; + if(furi_string_equal(tmp, name)) { - success = infrared_signal_read_body(signal, ff); + error = infrared_signal_read_body(signal, ff); break; } - } + } while(true); furi_string_free(tmp); - return success; + return error; } -bool infrared_signal_search_by_index_and_read( +InfraredErrorCode infrared_signal_search_by_index_and_read( InfraredSignal* signal, FlipperFormat* ff, size_t index) { - bool success = false; + InfraredErrorCode error = InfraredErrorCodeNone; FuriString* tmp = furi_string_alloc(); - for(uint32_t i = 0; infrared_signal_read_name(ff, tmp); ++i) { + for(uint32_t i = 0;; ++i) { + error = infrared_signal_read_name(ff, tmp); + if(INFRARED_ERROR_PRESENT(error)) { + INFRARED_ERROR_SET_INDEX(error, i); + break; + } + if(i == index) { - success = infrared_signal_read_body(signal, ff); + error = infrared_signal_read_body(signal, ff); + if(INFRARED_ERROR_PRESENT(error)) { + INFRARED_ERROR_SET_INDEX(error, i); + } break; } } furi_string_free(tmp); - return success; + return error; } void infrared_signal_transmit(const InfraredSignal* signal) { diff --git a/applications/main/infrared/infrared_signal.h b/applications/main/infrared/infrared_signal.h index dcfde6e72..3fa7768b3 100644 --- a/applications/main/infrared/infrared_signal.h +++ b/applications/main/infrared/infrared_signal.h @@ -8,6 +8,7 @@ */ #pragma once +#include "infrared_error_code.h" #include #include @@ -136,9 +137,10 @@ const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal) * @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). + * @returns InfraredErrorCodeNone if a signal was successfully read, otherwise error code */ -bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); +InfraredErrorCode + infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); /** * @brief Read a signal name from a FlipperFormat file. @@ -147,9 +149,9 @@ bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* * * @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). + * @returns InfraredErrorCodeNone if a signal name was successfully read, otherwise error code */ -bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name); +InfraredErrorCode infrared_signal_read_name(FlipperFormat* ff, FuriString* name); /** * @brief Read a signal from a FlipperFormat file. @@ -158,9 +160,9 @@ bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name); * * @param[in,out] ff pointer to the FlipperFormat file instance to read from. * @param[out] body pointer to the InfraredSignal instance to hold the signal body. Must be properly allocated. - * @returns true if a signal body was successfully read, false otherwise (e.g. syntax error). + * @returns InfraredErrorCodeNone if a signal body was successfully read, otherwise error code. */ -bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff); +InfraredErrorCode infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff); /** * @brief Read a signal with a particular name from a FlipperFormat file into an InfraredSignal instance. @@ -171,9 +173,9 @@ bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff); * @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). + * @returns InfraredErrorCodeNone if a signal was found and successfully read, otherwise error code. */ -bool infrared_signal_search_by_name_and_read( +InfraredErrorCode infrared_signal_search_by_name_and_read( InfraredSignal* signal, FlipperFormat* ff, const char* name); @@ -187,9 +189,9 @@ bool infrared_signal_search_by_name_and_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). + * @returns InfraredErrorCodeNone if a signal was found and successfully read, otherwise error code. */ -bool infrared_signal_search_by_index_and_read( +InfraredErrorCode infrared_signal_search_by_index_and_read( InfraredSignal* signal, FlipperFormat* ff, size_t index); @@ -203,8 +205,10 @@ bool infrared_signal_search_by_index_and_read( * @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. + * @returns InfraredErrorCodeNone if a signal was successfully saved, otherwise error code */ -bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name); +InfraredErrorCode + infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name); /** * @brief Transmit a signal contained in an InfraredSignal instance. diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir index 1041fb3c5..2dfcc4f56 100644 --- a/applications/main/infrared/resources/infrared/assets/ac.ir +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -1091,3 +1091,41 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 4348 4439 520 1646 520 1646 520 1646 519 1646 520 561 520 561 520 1646 519 561 520 561 520 562 519 562 519 561 520 1646 520 1647 518 563 518 1646 519 562 519 561 520 561 520 562 519 562 519 561 520 1648 517 1647 519 1646 519 1647 519 1646 520 1646 520 1645 520 1647 519 561 520 561 520 562 519 562 519 562 519 562 519 561 520 562 519 561 520 1646 520 562 519 1647 518 1646 520 562 519 560 521 561 520 561 520 561 520 562 519 562 519 560 521 562 519 562 519 560 521 1646 520 1646 520 561 520 562 519 561 520 562 519 561 520 561 520 561 520 561 520 561 520 1647 518 1646 520 562 519 562 519 561 520 1646 520 561 520 5409 4348 4440 519 1645 521 1646 519 1645 521 1645 521 561 520 561 520 1644 522 561 520 561 520 561 520 560 521 562 519 1646 520 1646 520 562 519 1644 522 561 520 561 520 561 520 561 520 561 520 561 520 1646 520 1645 520 1646 520 1645 521 1646 520 1646 520 1644 522 1645 521 560 521 560 521 561 520 561 520 560 521 560 521 561 520 561 520 561 520 1645 521 562 519 1645 521 1645 520 561 520 562 519 561 520 561 520 561 520 560 521 560 521 560 521 560 521 561 520 560 521 1646 520 1646 520 561 520 560 521 559 522 560 521 561 520 561 520 560 521 560 521 560 521 1646 520 1645 520 561 520 560 521 560 521 1645 521 561 520 +# +# Model: Airwell AW-HKD012-N91 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4387 4398 547 1609 547 530 547 1610 547 1611 545 530 547 530 547 1608 547 530 548 530 547 1611 545 532 546 532 547 1609 547 1610 547 531 547 1608 548 530 547 530 548 530 547 1610 546 1609 547 1610 547 1609 547 1609 547 1611 545 1609 548 1610 546 530 547 530 548 529 549 531 547 531 546 531 547 1608 548 1610 547 1608 548 533 545 1608 548 532 546 532 546 1611 545 532 547 532 545 530 548 1608 547 530 549 1608 547 1609 548 5203 4386 4398 547 1609 546 530 547 1609 546 1607 548 531 547 531 547 1609 547 530 548 531 547 1609 547 531 547 531 547 1608 547 1613 544 531 546 1609 547 531 547 531 547 532 546 1609 547 1609 546 1609 547 1609 547 1608 547 1608 548 1608 548 1609 547 530 547 530 547 530 547 532 546 530 547 530 548 1610 546 1608 547 1609 547 530 547 1609 547 530 547 530 548 1609 546 530 548 530 547 532 546 1610 546 531 546 1608 548 1608 548 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4388 4398 547 1608 548 531 546 1610 546 1609 547 530 547 529 548 1608 548 532 547 530 548 1612 544 529 549 530 548 1608 547 1609 547 531 546 1608 548 1607 549 529 549 1608 549 1609 548 1608 548 1608 548 1611 545 1608 548 530 548 1609 547 531 547 530 548 530 548 531 547 529 549 530 548 530 547 531 547 530 548 530 547 529 549 530 548 532 547 530 548 1609 547 1610 547 1608 548 1609 547 1608 548 1608 548 1608 548 1608 548 5203 4388 4396 549 1609 547 529 549 1610 546 1608 548 529 549 530 547 1609 547 530 548 529 549 1608 548 531 547 532 546 1609 547 1609 547 530 548 1609 548 1609 548 529 548 1608 548 1609 548 1609 547 1609 547 1608 548 1609 547 532 546 1608 548 531 548 531 548 530 548 530 548 531 547 530 548 531 548 531 547 530 548 530 548 530 548 531 547 529 549 529 549 1609 548 1608 548 1609 547 1608 548 1608 548 1608 548 1607 549 1607 549 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4384 4400 572 1585 571 505 572 1583 573 1584 572 508 570 503 575 1584 572 505 572 506 572 1583 573 504 573 506 571 1586 570 1585 572 532 546 1586 570 1585 571 506 571 1585 571 1583 573 1586 570 1583 573 1584 572 1589 569 505 572 1585 571 506 571 506 573 506 572 505 573 532 545 504 574 509 570 1611 545 506 572 1582 574 506 572 507 571 507 571 507 570 1584 572 507 571 1587 569 506 572 1584 572 1585 571 1583 573 1612 544 5179 4386 4400 570 1584 572 507 571 1583 572 1585 571 506 572 506 572 1584 572 505 572 504 574 1584 572 507 571 504 574 1583 573 1585 572 507 571 1584 572 1610 545 508 571 1587 569 1583 573 1583 573 1585 571 1585 572 1585 572 505 572 1584 572 505 573 507 572 506 571 504 574 505 573 505 574 508 571 1585 571 507 571 1585 571 506 571 506 572 504 574 505 572 1586 570 507 571 1586 570 505 573 1584 572 1585 571 1587 569 1584 573 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4386 4398 575 1582 574 503 575 1583 573 1582 574 505 573 504 574 1582 574 506 572 508 570 1583 573 504 574 505 573 1583 573 1584 573 505 573 1582 575 1583 574 504 574 1582 574 1583 573 1583 573 1583 573 1585 571 1586 572 504 573 1584 572 504 573 505 573 505 573 505 573 504 573 506 571 1583 574 505 573 1583 573 1583 573 1584 572 1583 573 505 572 505 573 504 574 1583 574 505 573 505 573 504 574 505 572 1584 572 1584 573 5178 4387 4400 571 1583 573 504 574 1584 572 1584 572 507 572 504 574 1582 574 505 572 505 573 1583 573 504 574 504 574 1582 574 1584 573 503 574 1583 573 1582 574 505 573 1583 573 1582 575 1583 573 1610 546 1584 572 1583 573 505 573 1610 546 506 572 505 573 504 574 504 574 505 573 505 573 1584 573 505 573 1582 574 1584 572 1583 573 1583 573 504 574 503 575 504 574 1585 571 507 571 504 573 506 572 505 572 1584 572 1585 571 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4388 4399 547 1608 548 530 548 1610 547 1610 547 529 549 529 548 1608 548 530 548 530 548 1607 549 533 545 531 548 1608 548 1610 546 531 547 1609 547 1608 548 529 549 1609 547 1609 547 1609 547 1609 547 1609 547 1608 548 529 548 1638 519 530 548 530 548 530 548 529 550 528 549 530 548 530 548 1608 548 530 548 1609 548 1610 547 1609 547 531 546 529 549 1608 548 530 548 1609 548 530 548 529 548 530 548 1609 548 1609 548 5205 4387 4398 547 1609 548 531 546 1609 547 1609 547 530 548 531 546 1609 547 531 548 530 573 1583 573 507 571 506 572 1583 573 1582 574 504 574 1581 575 1582 574 506 572 1583 574 1583 573 1583 573 1585 571 1584 572 1585 570 507 571 1582 574 505 574 532 545 505 573 505 572 506 571 505 573 505 573 1584 572 506 572 1583 573 1584 572 1583 573 505 572 504 573 1583 573 505 573 1586 571 506 572 505 573 507 572 1583 573 1584 572 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4388 4399 572 1583 573 532 546 1585 571 1583 574 503 575 505 573 1584 572 504 574 505 573 1584 573 506 572 504 573 1584 573 1584 572 505 574 1611 545 506 573 1583 573 1585 571 1586 545 1609 547 531 547 1611 545 1608 548 1607 549 530 548 529 548 531 548 532 546 1610 546 533 545 530 547 1609 547 1610 547 1609 547 533 545 529 548 530 548 530 547 531 546 530 547 530 548 533 544 1608 548 1608 548 1610 546 1606 550 1609 547 5203 4388 4397 548 1609 547 531 547 1608 548 1608 548 530 548 530 548 1608 548 531 547 531 547 1610 546 531 547 530 548 1609 547 1611 546 532 547 1609 547 531 547 1608 548 1610 546 1609 547 1608 548 530 547 1609 547 1608 548 1609 547 531 546 530 548 530 547 530 547 1608 548 532 547 534 545 1608 548 1608 548 1609 547 530 548 531 547 531 547 532 546 531 546 531 547 532 546 530 548 1608 547 1608 548 1610 546 1608 548 1608 548 \ No newline at end of file diff --git a/applications/main/infrared/resources/infrared/assets/tv.ir b/applications/main/infrared/resources/infrared/assets/tv.ir index b23f3798c..0ebbcce75 100755 --- a/applications/main/infrared/resources/infrared/assets/tv.ir +++ b/applications/main/infrared/resources/infrared/assets/tv.ir @@ -38,31 +38,31 @@ type: parsed protocol: SIRC address: 01 00 00 00 command: 14 00 00 00 -# +# name: Power type: parsed protocol: NEC address: 40 00 00 00 command: 0B 00 00 00 -# +# name: Ch_next type: parsed protocol: NEC address: 40 00 00 00 command: 11 00 00 00 -# +# name: Ch_prev type: parsed protocol: NEC address: 40 00 00 00 command: 10 00 00 00 -# +# name: Vol_up type: parsed protocol: NEC address: 40 00 00 00 command: 13 00 00 00 -# +# name: Mute type: parsed protocol: NEC @@ -80,7 +80,7 @@ type: parsed protocol: Kaseikyo address: 80 02 20 00 command: 00 02 00 00 -# +# name: Vol_dn type: parsed protocol: Kaseikyo @@ -92,13 +92,13 @@ type: parsed protocol: Kaseikyo address: 80 02 20 00 command: 20 03 00 00 -# +# name: Ch_next type: parsed protocol: Kaseikyo address: 80 02 20 00 command: 40 03 00 00 -# +# name: Ch_prev type: parsed protocol: Kaseikyo @@ -110,43 +110,43 @@ type: parsed protocol: NEC address: 04 00 00 00 command: 40 00 00 00 -# +# name: Mute type: parsed protocol: NEC address: 04 00 00 00 command: 12 00 00 00 -# +# name: Power type: parsed protocol: NECext address: 00 7F 00 00 command: 15 EA 00 00 -# +# name: Mute type: parsed protocol: NECext address: 00 7F 00 00 command: 16 E9 00 00 -# +# name: Vol_up type: parsed protocol: NECext address: 00 7F 00 00 command: 1B E4 00 00 -# +# name: Vol_dn type: parsed protocol: NECext address: 00 7F 00 00 command: 1A E5 00 00 -# +# name: Ch_next type: parsed protocol: NECext address: 00 7F 00 00 command: 19 E6 00 00 -# +# name: Ch_prev type: parsed protocol: NECext @@ -447,7 +447,7 @@ type: parsed protocol: Samsung32 address: 0E 00 00 00 command: 15 00 00 00 -# +# name: Vol_dn type: parsed protocol: NEC @@ -1143,17 +1143,17 @@ type: parsed protocol: RC6 address: 00 00 00 00 command: 0D 00 00 00 -# +# # Samsung Standby -# +# name: Power type: parsed protocol: Samsung32 address: 07 00 00 00 command: E0 00 00 00 -# +# # Samsung Power Off -# +# name: Power type: parsed protocol: Samsung32 @@ -1789,31 +1789,31 @@ type: parsed protocol: NEC address: 00 00 00 00 command: 33 00 00 00 -# +# name: Power type: parsed protocol: NEC address: 28 00 00 00 command: 0B 00 00 00 -# +# name: Ch_next type: parsed protocol: NEC address: 28 00 00 00 command: 0C 00 00 00 -# +# name: Ch_prev type: parsed protocol: NEC address: 28 00 00 00 command: 0D 00 00 00 -# +# name: Vol_up type: parsed protocol: NEC address: 28 00 00 00 command: 0E 00 00 00 -# +# name: Vol_dn type: parsed protocol: NEC @@ -1825,79 +1825,79 @@ type: parsed protocol: NEC address: 28 00 00 00 command: 10 00 00 00 -# +# name: Ch_next type: parsed protocol: RC5 address: 01 00 00 00 command: 14 00 00 00 -# +# name: Ch_next type: parsed protocol: SIRC20 address: 5A 0E 00 00 command: 10 00 00 00 -# +# name: Ch_prev type: parsed protocol: SIRC20 address: 5A 0E 00 00 command: 11 00 00 00 -# +# name: Vol_up type: parsed protocol: RC5 address: 00 00 00 00 command: 15 00 00 00 -# +# name: Vol_dn type: parsed protocol: RC5 address: 00 00 00 00 command: 14 00 00 00 -# +# name: Ch_next type: parsed protocol: RC5 address: 00 00 00 00 command: 18 00 00 00 -# +# name: Ch_prev type: parsed protocol: RC5 address: 00 00 00 00 command: 17 00 00 00 -# +# name: Power type: parsed protocol: NEC address: 80 00 00 00 command: 12 00 00 00 -# +# name: Vol_up type: parsed protocol: NEC address: 80 00 00 00 command: 1A 00 00 00 -# +# name: Vol_dn type: parsed protocol: NEC address: 80 00 00 00 command: 1E 00 00 00 -# +# name: Mute type: parsed protocol: NEC address: 80 00 00 00 command: 10 00 00 00 -# +# name: Ch_next type: parsed protocol: SIRC20 address: 10 01 00 00 command: 34 00 00 00 -# +# name: Ch_prev type: parsed protocol: SIRC20 @@ -1905,37 +1905,37 @@ address: 10 01 00 00 command: 33 00 00 00 # # Sharp TV -# +# name: Power type: parsed protocol: NECext address: 00 BD 00 00 command: 01 FE 00 00 -# +# name: Vol_up type: parsed protocol: NECext address: 00 BD 00 00 command: 0C F3 00 00 -# +# name: Vol_dn type: parsed protocol: NECext address: 00 BD 00 00 command: 10 EF 00 00 -# +# name: Ch_next type: parsed protocol: NECext address: 00 BD 00 00 command: 18 E7 00 00 -# +# name: Ch_prev type: parsed protocol: NECext address: 00 BD 00 00 command: 1C E3 00 00 -# +# name: Mute type: parsed protocol: NECext @@ -1947,105 +1947,105 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 278 1811 277 788 246 794 250 764 280 786 248 792 252 1813 275 1815 273 791 253 1812 276 789 255 785 249 791 253 1812 276 789 255 45322 280 1809 279 786 248 766 278 788 246 794 250 1815 273 792 252 788 246 1819 280 785 249 1817 271 1819 280 1810 278 787 247 1818 281 43217 274 1818 270 794 250 764 280 786 248 792 252 788 256 1809 279 1811 277 788 246 1819 280 785 249 766 278 762 272 1819 280 785 248 -# +# name: Mute type: raw frequency: 38000 duty_cycle: 0.330000 data: 278 1812 276 762 282 758 276 765 279 761 273 1818 281 1809 279 1811 277 762 282 1809 279 760 274 766 278 762 282 1809 279 760 274 44279 276 1813 275 763 281 759 275 766 278 762 272 768 276 764 280 760 274 1817 271 768 276 1815 273 1817 271 1819 280 759 275 1816 272 44276 279 1812 276 763 281 758 276 765 279 761 273 1818 281 1810 278 1811 277 762 272 1819 279 760 274 766 278 762 282 1809 279 760 274 -# +# name: Vol_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 272 1817 271 794 250 790 254 786 248 792 252 762 272 794 250 1815 273 792 252 1813 275 790 254 785 249 766 278 1813 275 789 255 46372 273 1817 271 794 250 763 281 785 248 792 252 1813 275 1814 274 791 253 1812 276 789 255 1810 278 1812 276 1813 275 790 254 1811 277 42170 277 1814 274 791 253 787 247 793 251 763 281 759 275 791 253 1812 276 789 255 1810 278 787 247 793 251 789 255 1810 278 787 247 -# +# name: Vol_dn type: raw frequency: 38000 duty_cycle: 0.330000 data: 275 1814 274 791 253 787 247 793 251 789 255 1810 278 787 247 1818 281 785 249 1816 272 793 251 789 255 785 249 1816 272 766 278 45325 274 1815 273 792 252 762 272 794 250 790 254 786 247 1818 270 794 250 1815 273 792 252 1813 275 1815 273 1816 272 793 251 1814 274 43224 277 1814 274 764 280 786 248 792 252 788 246 1820 279 786 247 1817 271 768 276 1815 273 792 252 761 273 794 250 1815 273 791 253 -# +# name: Ch_next type: raw frequency: 38000 duty_cycle: 0.330000 data: 272 1817 271 794 250 790 254 786 248 792 252 1813 275 790 254 759 275 792 252 1813 275 789 255 785 248 792 252 1813 275 789 255 46372 273 1817 271 793 251 763 281 759 275 791 253 787 247 1818 281 1810 278 1812 276 789 255 1809 279 1811 277 1813 275 790 254 1811 277 42169 277 1815 273 792 252 787 247 794 250 789 255 1810 278 787 247 794 250 789 255 1810 278 761 273 793 251 789 255 1810 278 787 247 -# +# name: Ch_prev type: raw frequency: 38000 duty_cycle: 0.330000 data: 273 1816 272 767 277 789 255 785 249 791 253 787 246 1818 281 785 248 765 279 1812 276 789 255 759 275 791 253 1812 276 789 255 46372 281 1808 280 785 249 791 253 787 247 793 251 1814 274 791 253 1812 276 1814 274 791 253 1812 276 1814 274 1815 273 792 252 1813 275 42172 272 1819 280 785 249 765 279 761 273 768 276 764 280 1811 277 788 246 768 276 1815 273 792 252 788 246 794 250 1815 273 791 253 -# +# # Brandt TV -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 9346 4516 657 502 659 502 688 473 687 475 634 527 635 527 634 1662 633 528 662 1634 662 1658 635 1660 608 1687 609 1687 633 1662 634 527 634 1662 634 528 633 1663 632 529 632 530 631 1665 630 531 630 531 631 531 630 1666 630 531 630 1666 630 1666 630 532 630 1666 630 1666 630 1666 630 -# +# name: Vol_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 9348 4542 631 529 632 503 659 502 688 474 634 527 634 527 634 1661 635 528 661 1636 660 1659 634 1637 632 1687 608 1687 609 1687 609 552 632 1664 633 528 633 1663 633 529 632 1664 631 1665 631 531 630 531 631 531 631 1665 631 531 631 1666 630 531 631 531 630 1666 630 1666 630 1666 631 -# +# name: Vol_dn type: raw frequency: 38000 duty_cycle: 0.330000 data: 9376 4512 660 475 684 475 635 527 634 528 633 527 634 527 634 1662 662 525 636 1658 637 1658 637 1658 636 1659 636 1660 635 1660 635 526 635 1661 634 527 634 1662 633 1663 632 1664 631 1665 630 531 631 531 630 531 630 1666 630 531 630 531 631 531 630 531 631 1666 630 1666 630 1667 630 -# +# name: Ch_next type: raw frequency: 38000 duty_cycle: 0.330000 data: 9315 4513 657 501 660 501 659 500 661 502 688 499 661 474 634 1660 634 527 634 1660 634 1686 608 1687 607 1686 608 1686 608 1686 608 552 608 1686 608 1687 607 1687 632 530 631 1664 630 1665 630 531 630 531 630 531 630 531 630 531 630 1665 630 531 630 531 630 1665 630 1665 630 1665 630 -# +# name: Ch_prev type: raw frequency: 38000 duty_cycle: 0.330000 data: 9345 4540 631 503 658 529 661 475 683 476 635 526 635 527 634 1661 634 527 662 1658 636 1659 634 1660 608 1686 609 1686 634 1661 634 527 634 1661 634 1662 633 1662 633 1663 632 1664 630 1665 630 531 630 531 630 531 630 531 631 531 630 531 630 531 630 532 629 1666 630 1666 629 1666 629 -# +# name: Mute type: raw frequency: 38000 duty_cycle: 0.330000 data: 9348 4516 657 501 660 502 659 502 659 502 688 474 634 527 634 1662 634 528 633 1687 634 1660 608 1664 631 1687 608 1687 609 1686 609 552 609 1687 608 553 632 529 633 529 632 530 631 1665 630 531 630 531 631 531 630 1666 630 1666 630 1666 630 1666 630 531 631 1666 630 1666 630 1666 630 -# +# name: Power type: parsed protocol: Samsung32 address: 3E 00 00 00 command: 0C 00 00 00 -# +# name: Vol_up type: parsed protocol: Samsung32 address: 3E 00 00 00 command: 14 00 00 00 -# +# name: Vol_dn type: parsed protocol: Samsung32 address: 3E 00 00 00 command: 15 00 00 00 -# +# name: Ch_next type: parsed protocol: Samsung32 address: 3E 00 00 00 command: 12 00 00 00 -# +# name: Ch_prev type: parsed protocol: Samsung32 address: 3E 00 00 00 command: 13 00 00 00 -# +# name: Mute type: parsed protocol: Samsung32 @@ -2053,37 +2053,37 @@ address: 3E 00 00 00 command: 0D 00 00 00 # # TCL LED49D2930 / Thomson Remote RC3000E02 -# +# name: Power type: parsed protocol: RCA address: 0F 00 00 00 command: 54 00 00 00 -# +# name: Mute type: parsed protocol: RCA address: 0F 00 00 00 command: FC 00 00 00 -# +# name: Ch_next type: parsed protocol: RCA address: 0F 00 00 00 command: B4 00 00 00 -# +# name: Ch_prev type: parsed protocol: RCA address: 0F 00 00 00 command: 34 00 00 00 -# +# name: Vol_up type: parsed protocol: RCA address: 0F 00 00 00 command: F4 00 00 00 -# +# name: Vol_dn type: parsed protocol: RCA @@ -2095,251 +2095,251 @@ type: parsed protocol: NEC address: 01 00 00 00 command: 18 00 00 00 -# +# name: Ch_prev type: parsed protocol: NEC address: 08 00 00 00 command: DD 00 00 00 -# +# name: Power type: parsed protocol: NECext address: 04 F4 00 00 command: 08 F7 00 00 -# +# name: Vol_up type: parsed protocol: NECext address: 04 F4 00 00 command: 02 FD 00 00 -# +# name: Vol_dn type: parsed protocol: NECext address: 04 F4 00 00 command: 03 FC 00 00 -# +# name: Mute type: parsed protocol: NECext address: 04 F4 00 00 command: 09 F6 00 00 -# +# name: Ch_prev type: parsed protocol: Samsung32 address: 07 00 00 00 command: 13 00 00 00 -# +# name: Vol_up type: parsed protocol: SIRC address: 10 00 00 00 command: 12 00 00 00 -# +# name: Vol_dn type: parsed protocol: SIRC address: 10 00 00 00 command: 13 00 00 00 -# +# name: Mute type: parsed protocol: SIRC address: 10 00 00 00 command: 14 00 00 00 -# +# name: Ch_next type: parsed protocol: SIRC address: 0D 00 00 00 command: 10 00 00 00 -# +# name: Ch_prev type: parsed protocol: SIRC address: 0D 00 00 00 command: 11 00 00 00 -# +# name: Mute type: parsed protocol: NEC address: 01 00 00 00 command: 11 00 00 00 -# +# name: Ch_prev type: parsed protocol: RC5 address: 01 00 00 00 command: 22 00 00 00 -# +# name: Power type: parsed protocol: NEC address: 40 00 00 00 command: 12 00 00 00 -# +# name: Vol_dn type: parsed protocol: NEC address: 01 00 00 00 command: 10 00 00 00 -# +# name: Power type: parsed protocol: NECext address: 00 7F 00 00 command: 1E E1 00 00 -# +# name: Mute type: parsed protocol: NECext address: 00 7F 00 00 command: 5C A3 00 00 -# +# name: Vol_up type: parsed protocol: NEC address: 20 00 00 00 command: 02 00 00 00 -# +# name: Vol_dn type: parsed protocol: NEC address: 20 00 00 00 command: 09 00 00 00 -# +# name: Ch_next type: parsed protocol: NEC address: 20 00 00 00 command: 03 00 00 00 -# +# name: Ch_prev type: parsed protocol: NEC address: 20 00 00 00 command: 41 00 00 00 -# +# name: Vol_up type: parsed protocol: NECext address: 02 7D 00 00 command: 0F F0 00 00 -# +# name: Vol_dn type: parsed protocol: NECext address: 02 7D 00 00 command: 5A A5 00 00 -# +# name: Ch_next type: parsed protocol: NECext address: 02 7D 00 00 command: 0C F3 00 00 -# +# name: Ch_prev type: parsed protocol: NECext address: 02 7D 00 00 command: 19 E6 00 00 -# +# name: Mute type: parsed protocol: NEC address: 40 00 00 00 command: 10 00 00 00 -# +# name: Ch_next type: parsed protocol: Samsung32 address: 07 00 00 00 command: 10 00 00 00 -# +# name: Ch_prev type: parsed protocol: Samsung32 address: 07 00 00 00 command: 12 00 00 00 -# +# name: Power type: parsed protocol: NEC address: A0 00 00 00 command: 1C 00 00 00 -# +# name: Mute type: parsed protocol: NEC address: A0 00 00 00 command: 5F 00 00 00 -# +# # Xiaomi TV -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1126 497 708 498 706 1433 708 1433 707 497 707 1458 707 497 707 1434 681 523 681 1459 681 1460 680 9808 1100 525 677 527 677 1464 676 1465 675 529 675 1466 674 530 675 1466 675 530 674 1466 674 1466 675 9814 1095 529 675 529 675 1466 675 1466 674 530 674 1466 675 530 674 1466 674 530 674 1466 675 1466 674 9814 1095 530 674 530 674 1466 674 1466 675 530 674 1467 674 530 674 1467 674 530 674 1467 674 1467 674 9814 1095 530 674 530 674 1467 674 1467 673 530 674 1467 673 531 674 1467 673 530 675 1467 673 1467 674 9814 1095 530 674 530 674 1467 674 1467 673 531 673 1467 674 531 673 1467 673 531 673 1467 673 1467 674 -# +# name: Vol_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 1126 496 707 1122 705 499 704 811 704 1124 704 502 701 502 702 1461 678 1150 677 527 677 527 676 12307 1096 529 675 1154 674 530 674 842 674 1155 673 530 674 530 674 1466 674 1154 674 531 673 530 674 12309 1094 530 674 1155 673 531 673 843 673 1155 673 531 673 530 674 1467 673 1155 673 530 674 531 673 12309 1094 530 674 1155 673 531 673 843 673 1155 673 531 673 531 673 1467 673 1155 673 531 673 531 673 12309 1094 531 673 1155 673 531 673 843 673 1155 673 531 673 531 673 1467 673 1155 673 531 673 531 673 12310 1093 531 673 1156 672 532 672 844 672 1156 672 532 672 532 672 1468 672 1157 671 532 672 532 672 -# +# name: Vol_dn type: raw frequency: 38000 duty_cycle: 0.330000 data: 1096 528 676 1152 676 529 675 842 673 1155 674 530 674 530 674 1466 674 1467 673 531 674 843 673 11688 1094 530 674 1156 673 532 673 844 672 1156 673 531 673 531 674 1467 673 1468 673 531 673 843 675 11686 1095 530 674 1155 671 534 673 843 673 1156 672 532 673 531 674 1468 672 1468 673 530 674 843 673 11689 1092 532 672 1157 673 531 673 844 673 1155 673 531 674 530 673 1468 673 1467 674 531 673 844 671 11690 1093 532 673 1155 673 533 672 844 672 1156 672 531 673 532 672 1468 673 1468 673 531 673 844 671 11688 1095 531 672 1182 647 532 672 845 672 1156 672 532 671 534 672 1469 670 1469 671 534 672 845 671 -# +# name: Ch_next type: raw frequency: 38000 duty_cycle: 0.330000 data: 1097 505 699 1154 673 531 673 842 675 1156 672 529 675 532 672 842 674 843 674 1156 672 1466 674 11688 1094 531 673 1157 671 531 673 844 671 1156 648 557 672 533 674 842 673 844 672 1156 673 1467 674 11687 1094 531 674 1157 670 534 671 843 674 1156 673 530 672 534 673 844 672 845 671 1158 671 1467 670 11690 1094 531 671 1158 671 531 673 844 674 1155 673 530 673 532 673 842 672 845 646 1184 699 1440 673 11687 1094 532 671 1157 672 531 673 844 672 1155 674 531 673 531 672 845 672 844 672 1157 670 1470 645 11715 1093 531 674 1155 673 531 673 870 647 1156 673 531 674 557 647 843 672 845 672 1156 673 1467 673 -# +# name: Ch_prev type: raw frequency: 38000 duty_cycle: 0.330000 data: 1119 528 675 1154 647 558 674 841 675 1153 675 531 673 532 673 843 673 1155 675 1153 674 531 673 12312 1120 505 672 1156 673 532 673 844 673 1155 673 532 671 534 672 842 675 1155 670 1158 673 530 674 12312 1093 532 671 1156 675 530 672 845 672 1156 673 533 671 533 670 846 672 1156 674 1154 674 529 675 12312 1093 557 646 1182 646 532 674 843 673 1181 646 534 672 532 674 842 673 1158 670 1156 674 530 673 12315 1091 530 674 1155 673 530 673 844 673 1157 644 559 673 532 672 844 670 1157 672 1158 672 532 672 12313 1093 533 671 1157 672 532 673 845 671 1157 670 558 647 532 646 871 672 1157 671 1181 646 534 671 -# +# # Daewoo TV -# +# name: Power type: parsed protocol: NEC address: 80 00 00 00 command: 82 00 00 00 -# +# name: Vol_up type: parsed protocol: NEC address: 80 00 00 00 command: 9F 00 00 00 -# +# name: Vol_dn type: parsed protocol: NEC address: 80 00 00 00 command: 8B 00 00 00 -# +# name: Ch_next type: parsed protocol: NEC address: 80 00 00 00 command: 9B 00 00 00 -# +# name: Ch_prev type: parsed protocol: NEC address: 80 00 00 00 command: 8F 00 00 00 -# +# name: Mute type: parsed protocol: NEC @@ -2369,25 +2369,25 @@ type: parsed protocol: NECext address: 00 7F 00 00 command: 06 F9 00 00 -# +# name: Power type: parsed protocol: NECext address: 69 69 00 00 command: 01 FE 00 00 -# +# name: Vol_up type: parsed protocol: NECext address: 69 69 00 00 command: 0A F5 00 00 -# +# name: Vol_dn type: parsed protocol: NECext address: 69 69 00 00 command: 0B F4 00 00 -# +# name: Mute type: parsed protocol: NECext @@ -2465,1099 +2465,1099 @@ type: parsed protocol: NEC address: 38 00 00 00 command: 04 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 634 2571 505 519 479 519 479 518 480 518 480 518 480 518 480 517 481 547 481 517 481 20040 590 2555 501 1007 999 997 510 548 480 486 512 486 512 486 542 485 513 516 482 116758 593 2552 504 1004 992 1004 514 514 514 483 515 513 485 483 545 482 516 482 516 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 525 1955 449 1999 476 4545 446 4544 478 2032 443 2006 469 2011 444 4577 445 4545 447 4574 448 2002 473 4547 444 34913 447 2032 443 2007 478 4542 449 4541 471 2039 446 2004 471 2008 447 4574 448 4543 448 4572 450 2030 445 4545 446 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 389 1737 280 796 253 744 295 754 274 775 274 776 273 1827 271 1828 270 805 254 1820 278 796 253 797 252 745 294 1806 302 773 245 48942 305 1821 277 798 251 746 303 747 271 778 271 1829 279 796 253 796 253 1821 277 798 251 1823 275 1824 274 1825 273 802 247 1827 271 42824 381 1745 272 804 245 752 297 753 275 773 276 774 275 1825 273 1826 272 803 246 1828 270 805 254 795 244 753 296 1804 294 781 247 48939 379 1746 271 804 245 779 270 753 275 774 275 1825 273 802 247 802 247 1827 271 804 245 1829 279 1820 278 1821 277 798 251 1823 275 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 562 1721 561 594 557 597 564 617 513 615 536 618 543 1715 566 1716 566 1692 559 594 567 588 563 618 543 611 540 615 536 618 543 1715 556 623 538 617 534 621 530 624 516 638 513 642 509 1722 560 620 541 1717 565 1692 559 1724 568 1715 556 1701 560 1723 559 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8436 4189 538 1563 566 1559 539 510 559 543 516 507 542 560 509 540 509 567 512 1586 512 1562 567 1559 539 536 533 1566 542 507 562 513 536 540 509 22102 647 1478 559 1568 540 508 541 535 534 515 534 568 511 538 511 539 540 1585 513 1560 559 1567 541 534 535 1564 534 515 534 568 511 538 511 22125 644 1482 565 1561 537 511 538 564 515 508 541 535 534 541 508 516 563 1588 510 1563 556 1570 538 510 559 1567 541 534 515 535 534 541 508 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 448 2031 444 2005 480 4540 452 4539 473 2037 438 2011 474 2006 449 4571 451 4539 453 4568 444 2036 449 4541 451 34906 527 1953 451 1998 477 4543 449 4542 480 2030 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 4545 446 -# +# name: Power type: parsed protocol: NEC42 address: 7B 00 00 00 command: 00 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8041 3979 513 536 482 515 513 1559 509 515 514 1560 508 515 514 509 509 514 484 4000 533 1566 512 537 481 1566 512 537 481 1566 512 537 481 516 513 537 481 24150 8044 3977 505 518 510 539 479 1567 511 512 506 1568 510 539 479 543 485 538 480 3977 536 1564 514 534 484 1563 515 508 510 1563 515 508 510 540 489 534 484 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 383 2027 295 2112 271 2138 266 890 271 887 294 926 235 2114 300 887 274 915 236 2145 269 887 274 884 297 923 238 920 241 917 264 895 266 26573 384 2026 296 2111 273 2136 268 889 272 886 295 924 237 2113 301 886 275 914 237 2144 270 886 275 914 267 921 240 919 242 916 265 924 237 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 177 8474 175 5510 174 8476 173 8477 177 8504 171 5515 175 8476 178 8472 177 8473 176 5541 174 8476 173 45583 171 8481 178 5507 177 8473 176 8474 175 8506 173 5512 172 8478 176 8475 174 8476 178 5538 177 8474 175 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8044 3976 506 517 511 1563 505 517 511 538 480 517 511 538 460 563 455 568 460 3993 530 545 483 1564 514 1559 509 1564 514 509 509 540 488 535 483 513 505 24150 8043 3978 514 509 509 1564 514 509 509 540 478 519 509 540 458 565 464 559 459 3994 529 546 482 1565 513 1560 508 1565 513 510 508 541 487 536 482 541 477 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 558 2942 450 10021 482 2989 484 10015 447 3024 449 10021 472 3100 485 2986 477 2994 500 2999 475 2996 477 2994 479 2992 482 3018 476 2995 479 6464 484 36270 477 3023 450 10020 473 2999 485 10014 448 3022 452 10019 474 3098 478 2994 480 2991 503 2996 477 2994 480 2992 482 2990 484 3015 479 2992 481 6462 485 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 587 2407 476 1062 965 547 482 547 451 577 452 546 452 22108 590 2404 479 1060 967 1028 510 519 509 548 480 487 511 120791 645 2411 472 1066 961 1065 483 515 514 514 504 493 515 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 172 7439 171 7441 169 7443 177 7434 176 7462 178 4887 176 4916 177 4914 169 7469 171 4920 174 4918 175 55174 176 7436 174 7437 173 7439 171 7440 175 7463 172 4894 174 4917 171 4921 172 7465 175 4916 178 4914 169 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 589 2556 500 524 474 554 454 544 454 543 455 543 455 543 455 543 445 583 446 552 446 20046 584 2561 505 1033 485 514 963 518 480 1032 516 512 476 522 506 491 507 522 476 116758 586 2560 506 1033 484 513 964 548 450 1031 507 522 476 521 507 490 508 521 477 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 586 2407 476 1063 964 578 450 548 450 547 481 517 481 577 451 546 452 546 472 556 452 545 453 575 453 514 484 544 484 543 455 14954 510 2483 481 1058 480 548 959 552 446 1067 481 516 512 546 482 515 992 1003 535 493 515 543 486 513 475 522 506 552 446 111671 589 2405 478 1061 477 551 967 514 484 1059 479 549 479 548 480 517 990 1036 512 516 482 546 483 515 503 525 483 544 454 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8444 4180 537 1564 565 1560 538 1561 557 1568 540 1559 539 536 533 542 517 559 510 1563 535 1564 565 1561 537 512 567 1558 540 535 534 1566 542 1557 562 23122 564 1562 557 1569 539 1560 538 1587 542 1558 540 534 535 515 534 541 538 1587 511 1563 566 1560 538 536 533 1567 541 534 515 1585 533 1566 542 23166 561 1565 564 1561 537 1563 535 1590 539 1561 537 538 541 534 515 535 534 1564 534 1566 563 1563 535 540 539 1560 538 511 538 1588 541 1559 539 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 527 1923 481 1998 477 4543 449 4542 470 2040 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 2034 441 34899 524 1956 448 2001 474 4546 446 4545 477 2033 442 2007 478 2002 443 4578 444 4546 445 4575 447 2003 472 2037 448 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 533 1356 437 3474 427 3483 429 3455 436 1454 430 1459 405 28168 510 1379 434 3477 434 3476 425 3459 432 1457 427 1462 402 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 179 7433 177 4915 178 7434 175 7436 174 7464 176 7435 175 4916 177 4915 173 4918 170 4922 171 4920 173 55174 175 7437 173 4919 174 7437 173 7439 171 7467 173 7438 172 4920 173 4919 174 4917 176 4915 178 4914 169 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 169 6731 176 6748 169 6730 177 6748 169 6755 172 4427 177 4447 178 6721 175 6749 178 4446 168 4456 169 54704 176 6723 174 6750 177 6723 173 6750 177 6747 170 4429 175 4449 176 6723 174 6751 176 4448 177 4447 178 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3506 3494 876 830 840 2576 847 2568 844 862 819 2570 842 864 816 863 818 2570 842 836 844 2572 840 866 815 865 815 2573 839 867 813 866 814 2573 850 857 813 2575 847 2568 844 834 847 2569 843 835 845 2571 872 2571 842 32654 3512 3488 872 834 847 2570 842 2573 839 867 814 2574 849 858 822 857 813 2575 848 859 821 2566 846 832 848 860 821 2567 845 833 848 860 820 2568 844 834 847 2569 843 2572 840 838 843 2574 849 829 841 2575 868 2575 837 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 560 2939 453 10018 475 2996 477 10022 450 3021 452 10018 475 3097 478 6464 483 6460 477 6466 502 6469 479 2993 480 2990 484 36274 564 2936 456 10015 478 2993 481 10020 534 2936 456 10014 479 3093 482 6461 476 6466 482 6461 476 6495 473 2999 485 2986 477 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 10726 41047 10727 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1617 4604 1559 1537 1560 1537 1560 4661 1533 33422 1613 4607 1566 1530 1556 1540 1536 4685 1539 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 174 4972 177 4910 173 4944 170 6988 174 6984 177 6951 175 6983 174 14269 177 4969 175 4912 176 4941 178 6979 172 6986 175 6953 178 6980 171 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 174 7067 176 10544 1055 719 1053 739 1054 738 953 822 1052 2566 169 3435 171 3431 175 3446 170 3432 174 3446 170 3432 174 3428 178 3442 174 3429 177 3425 171 39320 2323 4900 178 10543 1056 719 1053 739 1054 738 953 821 1053 2566 169 3435 171 3431 175 3445 171 3432 174 3446 170 3432 174 3428 178 3442 174 3428 178 3425 171 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3506 3492 868 839 841 2575 848 858 822 2566 846 832 848 859 821 858 812 2576 847 860 820 2567 845 833 847 860 821 2568 844 862 818 2570 842 864 817 2571 841 2574 849 2567 845 861 820 2568 845 834 847 2570 873 2570 842 34395 3503 3496 874 833 847 2568 845 834 847 2570 842 864 816 835 846 862 819 2569 843 835 846 2571 841 865 816 864 816 2571 841 837 844 2573 850 857 813 2575 848 2567 845 2570 842 864 816 2572 840 838 842 2574 869 2574 838 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 170 8479 170 5516 178 8472 177 8474 175 8506 173 5513 171 8479 175 8476 178 8472 177 5540 175 8475 174 45584 177 8473 176 5509 175 8476 173 8477 176 8504 170 5516 178 8472 177 8474 175 8476 173 5543 172 8479 170 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 178 4969 170 6958 173 4944 175 6983 173 6956 174 6984 177 6980 171 16308 180 4966 173 6955 176 4941 172 6985 176 6982 169 6960 176 6982 174 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 585 2409 474 550 478 550 448 1033 994 1063 485 512 506 79916 585 2410 473 551 477 550 448 1034 993 1033 515 543 475 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1192 1012 6649 26844 1192 1013 6648 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3134 6105 6263 82963 3134 6105 6263 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 588 1511 567 1533 565 589 531 597 513 589 541 587 533 1565 533 569 541 1533 565 562 568 1531 537 1537 561 593 537 591 539 1507 561 1539 569 22152 586 1513 565 1535 563 590 540 562 538 564 566 588 542 1557 531 597 513 1534 564 590 540 1533 535 1539 559 568 562 592 538 1509 569 1531 567 -# +# name: Power type: parsed protocol: RC5 address: 00 00 00 00 command: 20 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 689 1461 566 1534 564 589 531 597 513 1534 564 564 566 1533 535 620 510 1537 561 592 538 1535 543 1531 567 587 533 595 535 1511 567 1533 565 22161 588 1512 566 1534 564 564 556 598 512 1535 563 565 565 1534 534 594 536 1538 560 593 537 1536 542 1532 566 587 543 585 535 1511 567 1534 564 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 588 2558 498 526 482 515 483 546 452 545 453 545 453 545 453 545 453 544 474 554 444 20047 583 2562 504 519 479 519 479 1003 515 483 1024 548 450 1001 995 1001 506 116771 593 2552 504 520 478 551 447 1004 513 514 993 549 449 1002 994 1002 516 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 587 2559 507 517 481 517 481 547 451 516 482 546 452 546 452 546 452 545 473 525 483 20038 592 2553 503 521 477 552 446 521 477 1004 514 515 992 1034 483 514 484 513 485 116769 593 2552 504 520 478 550 448 520 478 1003 515 514 993 1032 486 513 475 522 476 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 4558 4576 558 596 565 97881 4554 4579 565 589 562 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1039 7202 958 4713 981 4713 961 7255 956 4739 955 16077 1038 7204 956 4714 980 4715 959 7256 954 4741 954 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 506 2638 510 516 482 516 482 546 452 546 452 545 453 545 453 545 453 575 443 554 444 20048 592 2554 502 1036 481 517 481 517 511 516 482 516 961 1035 513 515 483 514 484 116757 594 2552 504 1034 484 514 484 514 504 524 484 513 964 1032 506 522 476 492 506 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 170 7441 169 4924 174 7437 193 7444 171 7441 174 4918 170 7441 174 4917 171 7440 195 7443 172 7439 171 55187 174 7437 173 4919 175 7437 173 7438 172 7466 175 4891 172 7439 171 4921 172 7439 171 7441 174 7464 171 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 179 4967 172 4915 178 6980 171 4945 169 4948 176 4912 176 4941 178 16307 176 4969 175 4912 176 6982 174 4942 171 4945 169 4919 174 4943 176 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1042 793 898 883 869 858 924 884 878 877 875 879 873 855 928 1717 901 854 1794 878 894 887 875 1716 902 89114 985 798 903 878 874 880 902 852 900 855 897 857 905 876 896 1722 906 849 1789 883 899 855 897 1721 897 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 170 8480 169 8481 178 8472 177 8474 175 8476 173 5513 176 8474 175 8476 173 8507 178 5509 175 8505 174 45558 175 8476 173 8476 173 8477 172 8478 171 8480 169 5517 177 8473 176 8474 175 8506 174 5512 172 8509 171 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 628 2577 499 528 480 545 453 544 454 544 454 544 454 544 454 543 455 543 475 553 445 20047 593 2553 503 521 477 551 447 1004 514 515 992 519 479 1003 515 513 485 543 455 116768 585 2561 505 519 479 519 479 1002 516 513 994 548 450 1001 506 522 476 521 477 -# +# name: Power type: parsed protocol: NECext address: 80 63 00 00 command: 0F 15 00 00 -# +# name: Power type: parsed protocol: NECext address: 80 64 00 00 command: 49 08 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 298 1828 270 804 245 1829 279 1820 278 797 252 771 278 1822 276 1824 274 800 249 1825 273 802 247 776 252 771 278 1822 276 799 250 48931 388 1737 280 796 253 1821 277 1822 276 798 251 1823 275 800 249 774 275 1825 273 776 273 1801 297 1828 270 1829 279 796 253 1821 277 42813 301 1825 273 801 248 1826 272 1827 271 804 245 805 244 1830 278 1821 277 798 251 1823 275 799 250 774 244 779 280 1820 278 796 253 48926 382 1744 354 696 271 1828 270 1829 279 796 253 1821 277 797 252 746 303 1823 275 774 275 1799 299 1826 272 1827 271 804 245 1829 279 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 177 5508 176 5539 176 8475 174 5542 173 8478 171 5545 170 8481 168 8482 177 8473 176 5541 174 8476 173 45573 169 5517 177 5538 177 8473 176 5541 174 8476 173 5544 171 8479 170 8481 178 8472 177 5540 175 8475 174 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 175 8474 175 8475 174 8477 172 8478 171 8480 169 8481 178 5538 177 5510 174 5542 173 5543 172 5544 171 45575 177 8472 177 8474 175 8476 173 8477 172 8478 171 8481 178 5507 177 5539 176 5540 175 5542 173 5543 172 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8050 3971 511 1562 516 1558 510 539 490 1557 511 1563 515 533 485 538 490 533 485 3983 530 1569 509 1564 514 1559 509 1565 513 1560 508 515 513 536 482 541 488 24152 8042 3979 514 1560 508 1565 513 510 508 1565 513 1560 508 515 513 536 482 541 488 3980 533 1567 511 1562 516 1557 511 1563 515 1558 510 539 489 534 484 539 490 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 175 8475 174 5512 177 8473 176 8475 174 8506 179 5508 176 8474 175 8475 174 8477 172 5544 171 8480 169 45587 176 8475 174 5511 173 8477 177 8473 176 8504 170 5516 178 8472 177 8473 176 8475 174 5542 173 8478 171 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 176 7436 174 7438 172 7439 171 7441 174 7463 172 7440 170 4921 172 4919 174 4917 176 4916 177 4914 174 55176 175 7437 173 7439 191 7446 174 7438 177 7434 176 7435 175 4917 176 4915 179 4914 174 4917 171 4946 178 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 176 7435 175 4917 177 7435 175 7436 189 7449 176 7435 175 7437 173 4918 175 7436 174 4918 175 4916 178 55171 175 7436 174 4918 170 7441 174 7438 177 7460 170 7441 174 7438 177 4914 174 7437 178 4914 169 4922 171 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8049 3973 509 1564 514 509 509 1564 514 535 483 1564 514 535 483 540 489 535 483 3980 533 1566 512 537 481 1566 512 511 507 1566 512 537 481 542 486 511 507 24149 8045 3976 516 1558 510 512 516 1557 511 512 516 1558 510 512 516 507 511 512 506 3984 539 1560 508 515 513 1560 508 541 488 1560 508 541 488 536 482 514 504 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 366 202 867 527 170 130516 343 227 863 529 168 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 176 5992 176 1372 176 1320 177 1345 172 1349 179 1370 178 1317 175 4444 176 1346 171 1351 177 1345 172 1349 179 1344 174 5963 175 65574 172 5995 178 1344 174 1348 175 1347 175 1347 170 1378 170 1325 172 4447 178 1344 173 1349 169 1353 175 1347 170 1351 177 5961 177 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 497 1478 488 511 467 1482 494 531 447 1477 499 501 466 533 445 530 468 507 471 529 438 12857 488 1485 491 509 469 1481 495 529 448 1476 490 510 468 532 445 529 469 480 498 502 465 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 982 6313 961 2662 903 2718 929 2719 907 6363 900 2722 904 6365 909 6361 903 6368 906 2742 905 46364 989 6307 906 2716 911 2712 925 2723 903 6367 907 2715 901 6369 905 6365 909 6361 903 2745 902 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 599 257 975 317 177 130649 308 228 974 319 175 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 176 4969 170 6958 173 6985 177 6981 171 6958 178 6980 177 6981 170 16308 180 4966 173 6955 176 6982 169 6988 174 6956 175 6983 173 6984 172 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 178 5990 173 1349 174 1348 175 1348 174 4444 176 1346 171 1351 177 4416 178 1344 173 1348 169 1353 175 1347 170 1351 177 9048 177 65573 173 5995 173 1348 175 1348 174 1347 176 4444 171 1351 177 1345 173 4421 173 1348 169 1352 176 1347 170 1351 177 1345 172 9053 177 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 174 4972 177 4910 173 6985 177 4940 174 6984 178 6951 175 6983 174 14269 177 4968 176 4912 176 6981 175 4941 173 6985 177 6953 178 6979 172 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1318 225 177 130305 1609 231 171 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 585 2410 473 1065 962 581 447 519 479 580 448 549 449 579 449 518 480 548 480 517 481 517 481 577 451 516 482 576 452 545 453 14955 510 2483 481 1058 480 549 969 543 445 1037 511 547 481 546 482 516 482 545 484 545 483 514 484 544 963 1063 485 543 445 111612 626 2427 557 954 513 512 995 547 451 1031 507 521 508 551 477 520 478 550 478 549 479 488 510 548 969 1057 481 517 481 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 203 272 1215 302 171 130229 1423 275 178 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 176 7436 174 7438 192 7446 174 7437 178 7434 176 7435 175 4917 176 4915 178 4913 175 4917 197 7440 175 55130 286 7377 177 7435 175 7437 173 7438 172 7466 174 7411 178 4913 170 4921 172 4919 174 4918 175 7436 174 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 175 7437 173 4918 170 7441 174 7437 178 7460 170 7441 179 7433 177 4915 178 4913 170 4922 171 4920 173 55124 291 7372 172 4921 172 7439 171 7441 174 7463 172 7440 170 7442 178 4913 170 4922 171 4920 173 4918 175 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 172 8477 172 8478 171 8480 169 5516 179 8502 178 5509 175 8475 174 8476 173 8479 170 5545 170 8480 169 45588 176 8473 176 8474 175 8476 173 5513 177 8504 171 5515 174 8476 178 8472 177 8473 176 5541 174 8476 173 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 177 7436 174 4918 175 7436 174 4918 175 7462 178 4887 176 4916 177 4914 174 4917 171 4921 172 4919 175 55184 175 7435 175 4918 175 7436 174 4918 175 7462 178 4914 174 4917 171 4920 173 4919 174 4917 176 4915 178 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 508 2484 480 1060 967 544 485 544 454 574 454 543 455 573 445 553 445 522 506 552 446 552 446 551 477 551 447 581 447 520 478 550 478 15908 626 2427 476 1062 476 522 995 547 451 1061 477 520 508 550 478 519 479 519 999 1058 959 1037 990 582 446 1035 483 111666 590 2404 479 1059 479 549 968 543 455 1027 511 548 480 517 511 486 512 546 961 1065 962 1034 993 579 449 1032 486 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 175 8475 174 8475 174 8477 172 8478 171 8480 169 5517 178 8473 175 8475 179 8501 173 5513 176 8505 169 45562 179 8472 177 8473 176 8474 175 8476 173 8478 171 5515 174 8476 178 8473 176 8505 174 5512 172 8508 171 120769 178 93377 175 7437 173 4919 174 7437 173 7439 171 7467 173 7439 171 7441 174 4918 170 4921 172 7439 171 7467 173 55167 171 7440 170 4922 171 7440 195 7443 172 7439 171 7441 174 7438 177 4915 173 4918 195 7442 173 7439 170 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 295 1805 273 776 242 1808 270 754 244 1806 272 777 241 758 270 754 264 760 268 756 272 14149 297 1802 266 784 244 1805 263 762 246 1803 265 785 244 780 248 751 267 758 271 753 265 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 535 1723 569 585 566 615 536 619 542 586 565 616 535 1722 539 615 536 1721 561 620 541 587 564 617 534 621 540 588 563 618 543 1714 537 617 534 621 540 615 536 618 543 612 539 615 536 1722 560 594 567 1717 534 1723 569 1714 557 1700 643 1639 643 1641 559 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 502 2521 504 521 997 545 453 575 453 545 453 575 454 22097 591 2433 511 513 994 1062 476 552 476 522 476 552 476 120839 628 2455 509 515 992 1064 484 513 505 493 515 543 475 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 591 2404 479 1060 967 1029 509 519 509 549 479 518 480 79926 585 2408 556 956 989 1033 515 544 484 543 475 522 476 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 586 2560 506 518 480 548 450 548 450 517 481 517 481 517 481 547 451 546 482 516 482 20040 590 2556 500 1038 480 518 969 543 455 543 475 1006 511 517 481 517 511 486 481 116778 584 2561 505 1033 485 513 964 548 450 548 480 1001 506 522 476 522 506 521 446 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 917 206 175 186 170 21561 170 2280 175 2274 502 1929 174 2276 169 5178 170 2261 173 3724 498 1932 171 2279 176 2273 172 3709 172 2277 178 3720 171 17223 177 7619 174 2275 170 2279 176 2256 168 2280 175 5172 176 2256 168 3729 173 2276 179 2253 171 2278 177 3703 178 2271 174 3724 177 17251 170 7627 177 2272 173 2276 169 2263 171 2277 178 5169 169 2262 172 3726 175 2256 168 2280 175 2274 171 3710 171 2278 177 3720 171 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 565 233 653 313 170 130328 752 235 233 107 229 398 177 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 586 2439 505 549 968 1027 511 517 511 517 481 547 481 21583 584 2439 505 520 997 1059 479 549 479 518 480 548 480 120894 593 2432 501 522 995 1061 477 521 507 520 478 550 478 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 558 8032 474 8115 503 8116 482 8108 480 8141 477 5239 476 8114 504 8115 483 8107 481 5236 509 8111 477 45290 554 8036 481 8108 510 8110 478 8112 476 8145 473 5243 482 8107 511 8109 479 8111 477 5240 505 8115 473 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 173 7438 172 4920 173 7438 172 4920 173 7465 175 4890 173 7439 171 4920 173 4919 174 4917 176 4915 178 55179 170 7441 169 4924 174 7437 178 4913 175 7463 172 4893 170 7441 174 4918 170 4922 171 4920 173 4918 175 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 225 745 222 774 193 778 200 797 175 769 193 777 201 771 196 774 224 747 220 776 202 769 198 248 220 252 196 250 218 253 225 746 221 250 198 248 220 252 226 246 222 223 225 248 220 252 216 229 219 253 225 247 221 277 176 243 220 279 189 230 228 244 224 248 220 252 196 250 218 253 215 257 201 770 197 799 189 257 201 271 197 37716 222 749 218 778 200 771 196 801 172 772 200 771 196 774 193 777 221 750 217 780 197 773 194 251 217 255 193 279 199 273 195 749 218 254 194 252 226 245 223 275 178 242 221 277 201 245 193 253 225 247 221 250 218 254 194 252 226 272 196 223 225 248 220 251 217 255 193 253 225 247 221 251 197 773 194 803 174 297 176 244 219 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3503 2655 197 642 876 2568 844 834 846 833 848 860 821 831 839 2576 847 2569 843 2572 841 2574 849 858 823 857 813 866 815 865 815 2572 841 2575 848 2568 844 2570 842 836 844 863 818 834 847 861 820 2568 845 2571 872 2571 842 32651 3505 3495 875 2567 845 861 820 832 849 859 811 840 840 2576 847 2568 844 2571 842 2574 849 830 840 867 814 838 842 865 815 2572 840 2575 848 2568 845 2571 841 865 815 864 817 834 846 861 819 2569 843 2572 871 2572 840 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 347 677 219 252 196 276 202 742 225 798 174 770 192 778 200 771 196 775 223 748 219 777 200 770 197 249 219 253 195 251 227 271 197 747 220 252 216 229 219 253 225 273 195 277 176 244 219 253 215 230 228 244 224 248 220 252 196 250 218 280 198 247 201 245 223 249 219 253 195 251 227 245 223 248 200 797 175 795 198 248 200 246 222 37666 344 678 228 245 223 222 226 771 196 800 198 747 220 750 217 753 224 773 194 776 202 769 198 799 173 246 227 245 223 249 199 247 221 749 218 280 198 247 201 245 223 249 219 253 195 251 227 244 224 248 200 272 196 250 218 254 194 252 226 245 223 249 199 274 194 251 227 245 193 253 225 273 195 251 217 753 225 746 221 251 197 275 193 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 174 7437 173 7440 174 7437 178 7433 177 7461 169 7442 178 4914 174 4917 171 4921 172 4919 174 7463 177 55163 177 7435 174 7437 173 7438 172 7440 175 7463 172 7413 176 4915 178 4913 175 4917 171 4920 174 7438 172 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8042 3979 513 510 508 541 487 1559 509 515 513 1560 508 515 513 510 508 541 477 3981 532 1568 510 1563 515 1558 510 1564 514 1559 509 514 514 535 483 540 488 24151 8042 3979 513 536 482 541 487 1560 508 541 488 1560 508 541 487 536 482 541 477 3980 533 1566 512 1561 507 1566 512 1562 516 1557 511 538 491 533 485 538 490 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8988 4504 640 487 562 592 538 590 510 592 538 590 540 588 532 596 514 614 516 1689 562 1668 563 1695 536 1695 566 1664 567 1690 612 1618 643 1430 801 1613 648 481 558 570 540 589 541 587 533 595 535 592 508 594 536 592 538 1667 564 1693 558 1672 569 1688 563 1668 644 1586 563 1694 567 40630 8994 2265 557 96833 8987 2273 538 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 173 7439 171 4922 172 7439 171 4921 172 7466 174 4917 176 4915 173 4918 170 7441 174 7438 192 7445 175 55181 174 7437 173 4918 175 7437 173 4919 175 7463 177 4888 175 4917 176 4915 178 7460 170 7440 175 7437 178 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1636 4610 1563 1533 1584 7760 1561 28769 1641 4606 1557 1539 1588 7757 1564 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 590 2404 479 1059 968 575 454 544 454 574 454 543 455 543 475 522 476 552 476 552 446 551 447 581 448 520 478 581 447 519 479 549 479 15967 587 2407 476 1062 476 522 995 547 451 1030 508 520 509 550 478 519 479 519 998 1028 999 1057 481 547 960 1066 482 111641 587 2407 476 1063 485 513 994 547 451 1031 507 551 477 551 477 520 478 550 968 1059 968 1027 511 548 959 1036 512 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 588 2406 477 1061 966 577 451 516 482 545 483 545 453 575 443 524 484 514 504 554 454 543 455 543 475 553 445 583 445 521 477 582 446 15969 585 2409 474 1065 483 514 993 549 449 1033 515 543 475 553 475 522 476 552 965 1030 997 576 452 1029 998 1028 479 111668 587 2407 475 1063 485 543 964 517 481 1031 507 552 476 551 477 520 478 550 968 1028 999 574 454 1027 990 1036 481 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 174 7439 196 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 7442 178 7433 177 4915 173 4918 170 55186 174 7438 172 7440 170 7441 174 7438 177 7461 169 7416 173 7464 176 7435 175 7437 173 4918 175 4917 176 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 173 7438 172 7441 174 7438 177 7434 176 7462 168 7443 177 7435 175 4917 176 4915 173 7438 177 4941 173 55166 174 7438 197 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 4922 171 4893 195 7443 172 4920 173 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 175 7436 179 4913 175 4917 171 4920 173 4945 169 4896 177 7435 175 7436 174 7464 176 4916 177 4914 169 55180 170 7441 169 4924 169 4922 171 4920 173 4919 174 4917 176 7435 175 7463 177 7434 176 4916 177 4914 174 -# +# name: Power type: parsed protocol: RC5 address: 02 00 00 00 command: 0C 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 176 5991 177 1372 176 1320 177 1344 173 1349 174 1374 169 1327 170 4449 176 1346 171 1351 177 1345 172 1349 168 1353 175 5963 175 65573 173 5995 178 1344 174 1348 175 1348 174 1347 170 1378 170 1325 172 4447 178 1344 174 1349 169 1353 175 1347 170 1352 176 5961 177 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 178 7761 176 11308 546 957 540 1958 538 970 537 1955 541 962 545 1953 543 964 543 962 545 957 540 970 548 960 547 1945 541 1950 546 965 542 1953 543 962 545 1945 540 970 537 1958 538 7881 172 7744 172 11318 536 971 536 1956 540 963 534 1964 542 966 541 1951 535 968 539 971 536 971 536 969 538 964 533 1965 541 1954 542 963 534 1956 540 971 536 1959 537 968 539 1951 535 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 278 1845 274 808 271 806 273 812 278 805 275 805 274 1840 279 1844 275 809 281 1836 272 806 274 812 278 805 274 1842 277 802 277 44956 279 1842 277 804 275 802 277 808 271 811 279 1838 281 798 271 814 276 1844 275 806 273 1841 278 1845 274 1846 273 808 271 1843 276 44959 275 1845 274 807 272 805 275 811 279 804 275 805 274 1839 280 1844 275 808 271 1845 274 805 274 811 279 804 275 1841 278 801 278 44955 280 1841 278 802 277 801 278 807 272 810 280 1837 271 807 272 813 277 1843 276 805 274 1839 280 1843 276 1845 274 807 272 1842 277 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 509 1717 504 629 512 631 510 627 504 633 508 633 508 1713 508 1719 512 1713 508 625 505 637 504 633 508 629 512 629 512 623 508 1719 512 626 505 628 513 631 510 627 514 623 507 632 509 1713 508 632 509 1716 505 1715 506 1724 507 1716 505 1719 512 1715 506 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 506 493 505 4059 505 5051 501 506 502 4065 499 511 497 4063 501 5058 504 504 504 4060 504 5052 500 507 501 4066 498 5056 506 5042 500 515 503 119614 504 505 503 4065 499 5051 501 511 497 4069 505 499 499 4072 502 5050 502 506 502 4066 498 5053 499 512 506 4060 504 5044 498 5061 501 508 500 -# +# name: Power type: parsed protocol: NECext address: 87 22 00 00 command: E0 1F 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8313 4161 515 1574 514 1571 507 569 510 562 507 563 506 562 507 568 511 562 507 1580 508 1577 511 1582 506 567 513 1575 513 554 505 571 509 564 505 22604 513 1573 505 1589 509 563 506 564 505 562 507 569 511 562 507 563 506 1579 509 1584 514 1576 512 558 511 1574 514 562 507 565 515 556 513 22593 514 1581 507 1583 505 564 505 563 506 570 510 563 506 564 505 562 507 1586 512 1578 510 1577 511 557 512 1581 507 566 514 556 513 555 514 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8735 4383 558 573 557 550 560 568 562 544 566 1722 560 540 560 1732 560 544 566 1719 563 1701 560 1723 559 1704 557 574 556 1699 562 574 556 1703 559 1727 565 1698 563 1721 561 546 564 1723 559 541 559 577 564 541 559 571 560 548 562 565 566 1697 565 567 564 1693 558 1733 559 1701 560 39926 8754 2247 565 92341 8758 2243 589 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3298 3336 821 2506 825 881 820 2505 826 2529 823 856 825 2524 817 866 825 2528 813 2513 818 888 813 862 819 887 814 865 826 2522 820 864 827 2526 815 861 820 887 814 2510 821 885 816 863 818 2531 821 2512 819 2559 793 32401 3298 3349 818 2507 824 882 819 2509 822 2527 814 868 823 2530 821 855 826 2531 821 2504 827 879 822 857 824 875 816 867 824 2529 823 854 827 2530 821 853 817 889 822 2506 825 873 818 866 825 2527 814 2513 818 2564 788 -# +# name: Power type: parsed protocol: NEC42 address: 1C 01 00 00 command: 12 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 289 2112 261 2109 295 2101 262 918 294 912 259 915 297 2098 265 916 296 909 262 2107 297 905 266 913 289 918 263 910 292 909 262 918 294 24789 263 2106 298 2098 265 2110 294 913 258 916 296 905 266 2108 296 911 260 914 288 2107 266 914 298 908 263 911 291 910 261 919 293 913 258 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3215 1637 410 430 405 439 406 1242 408 435 410 1242 408 428 407 440 405 435 410 1240 410 1243 407 431 404 440 405 437 408 1238 402 1254 406 434 401 439 406 438 407 431 404 440 405 437 408 428 407 439 406 434 401 439 406 438 407 1241 409 434 401 441 404 432 403 444 401 1249 401 439 406 438 407 1241 409 434 401 441 404 433 402 444 401 1249 401 439 406 1248 402 436 409 434 401 441 404 433 402 444 401 439 406 45471 3239 1614 403 435 400 444 401 1250 400 437 408 1248 402 438 407 433 402 442 403 1245 405 1248 423 420 405 431 404 443 402 1248 402 1248 422 421 404 435 400 443 402 440 405 431 404 443 402 438 407 433 402 442 403 435 400 443 402 1250 400 436 399 447 408 432 403 438 407 1246 404 434 401 443 402 1250 400 436 399 447 408 432 403 437 408 1246 404 434 401 1253 407 434 401 436 399 447 408 432 403 437 408 436 399 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 7928 3948 504 515 503 518 500 1583 505 516 502 1584 504 510 508 516 502 516 502 3952 510 1579 509 507 501 1587 501 519 510 1571 507 518 500 517 501 517 501 23073 7931 3943 509 514 504 515 503 1578 500 524 505 1580 508 510 508 514 504 511 507 3951 511 1576 502 513 505 1586 502 515 503 1582 506 515 503 513 505 516 502 -# +# name: Power type: parsed protocol: NECext address: 18 18 00 00 command: C0 3F 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 492 4975 496 4993 498 498 500 4056 498 504 494 4055 499 4963 497 532 496 4031 492 4996 495 502 496 4060 494 4972 499 4990 491 506 492 4063 491 511 497 4052 492 4970 490 539 500 4027 496 103358 500 4961 500 4995 496 505 493 4056 498 499 499 4057 497 4969 491 533 496 4026 497 4997 494 508 490 4059 495 4967 494 5001 490 511 497 4053 491 505 493 4063 491 4975 496 528 490 4032 491 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3425 3482 848 2607 846 2639 845 2608 846 2639 845 2612 852 2624 850 2613 851 911 851 885 847 919 843 891 851 915 847 890 852 907 845 897 845 917 845 891 851 915 847 887 845 2639 845 2612 852 2625 849 2613 851 2630 844 34282 3455 3478 852 2601 852 2633 851 2605 848 2629 845 2617 847 2633 851 2604 849 917 845 890 852 913 849 889 843 915 847 896 846 916 846 890 852 914 848 885 847 919 843 895 847 2630 844 2617 847 2634 850 2605 848 2636 848 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 500 500 498 4066 498 5058 504 502 506 4061 503 508 500 4060 504 5055 497 5055 497 511 497 4071 503 5047 505 507 501 4065 499 5049 503 512 496 124314 501 508 500 4067 507 5043 499 513 505 4060 504 501 497 4073 501 5052 500 5052 500 512 496 4066 498 5058 504 506 502 4058 506 5053 499 509 499 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 176 7763 174 8817 169 10842 544 1955 541 967 540 1952 544 959 538 972 546 962 545 1947 538 1952 544 967 540 1955 541 964 543 1947 539 972 546 1949 547 7873 170 7746 170 10350 175 11811 543 960 537 1961 535 972 535 970 537 965 542 1956 540 1956 540 965 542 1947 539 973 534 1960 536 969 538 1952 534 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 2570 2682 1189 1208 1186 2665 1186 1217 1187 2668 1183 1215 1179 2671 1190 2695 1186 1187 1187 2692 1179 2671 1190 1213 1181 1193 1191 1207 1187 2663 1188 1215 1179 2676 1185 46941 2563 2685 1186 1191 1183 2698 1184 1188 1186 2691 1180 1197 1187 2694 1187 2666 1185 1210 1184 2674 1187 2695 1186 1185 1189 1206 1188 1189 1185 2696 1186 1186 1187 2689 1182 -# +# name: Power type: parsed protocol: RC5 address: 00 00 00 00 command: 26 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3325 1560 406 444 401 453 402 1226 404 449 406 1226 404 443 402 454 401 449 406 1224 406 1228 402 446 409 444 401 451 404 1223 407 1230 410 440 405 445 410 443 402 446 409 444 401 451 404 442 403 453 402 448 407 443 402 451 404 1224 406 448 407 444 401 445 410 446 409 1221 409 442 403 1231 409 439 406 1227 403 450 405 441 404 452 403 1227 403 448 407 446 409 439 406 447 408 444 401 445 400 456 410 440 405 52348 3320 1553 403 445 400 454 401 1230 400 447 408 1228 402 449 406 444 401 452 403 1225 405 1229 431 421 404 442 403 454 401 1229 401 1229 431 423 402 446 399 455 400 451 404 442 403 453 402 448 407 443 402 451 404 444 401 452 403 1229 401 445 400 457 398 451 404 446 399 1235 405 443 402 1232 408 444 401 1225 405 452 403 447 398 452 403 1231 399 449 406 447 408 443 402 444 401 456 399 450 405 445 400 453 402 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 990 915 980 909 986 924 981 2829 981 934 981 2823 987 923 982 2828 982 933 982 906 989 33895 989 907 988 927 988 900 985 2841 979 915 980 2851 980 909 986 2840 980 914 981 934 981 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 821 5754 848 2490 841 2492 819 2524 817 5726 845 2492 839 5727 844 5757 845 5727 854 2483 848 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 5092 1621 406 2599 406 2598 407 2605 1653 1616 411 2619 1609 1606 1654 1618 409 2623 382 2600 405 -# +# name: Power type: parsed protocol: NEC address: 15 00 00 00 command: 12 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1144 1010 6795 26754 1151 997 6798 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1144 1009 1120 1006 1143 1991 1116 26758 1146 1006 1123 1003 1146 1988 1119 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 170 46238 169 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8906 4165 572 1672 569 1678 563 640 572 637 565 643 569 633 569 643 569 1674 567 639 563 1684 567 637 565 1681 570 1675 566 1673 568 1681 570 636 566 640 572 637 565 639 563 1684 567 640 572 630 572 640 572 634 568 1675 566 1681 570 1670 571 638 564 1681 570 1669 572 1678 563 1680 571 40485 8898 2252 570 85621 8955 2194 567 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 446 1191 449 1194 446 1195 445 1204 446 1200 1316 459 447 1193 447 1202 448 1197 443 1201 449 1191 449 34491 443 1204 446 1197 443 1198 442 1207 443 1202 1314 436 440 1201 449 1199 441 1205 445 1198 442 1199 441 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 4268 4327 522 1593 516 1603 516 1597 522 519 520 520 519 515 514 531 518 520 519 519 520 521 518 519 520 1597 522 1595 514 1597 522 1599 520 1595 514 524 515 1604 515 521 518 523 516 524 515 519 520 525 514 524 515 1599 520 522 517 1596 513 1605 514 1602 517 1595 514 1607 522 1592 516 40481 8748 2187 523 93986 8721 2189 521 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 247 3006 244 153 178 1007 251 117 637 597 381 678 218 156 175 529 382 679 217 157 174 24963 247 3038 252 117 219 994 249 119 217 483 250 117 173 531 278 779 173 167 169 569 383 679 217 156 175 126538 246 3039 251 118 218 995 247 120 195 504 249 118 172 532 277 780 172 168 178 560 382 680 216 157 174 -# +# name: Power type: parsed protocol: NEC address: 10 00 00 00 command: EF 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 4278 4318 521 517 522 520 519 517 522 520 519 521 518 516 513 531 518 520 519 1595 514 1605 514 1599 520 1598 521 1595 513 1598 521 1600 519 1596 513 1602 517 1601 518 1595 514 1604 515 525 514 520 519 526 513 525 514 524 515 527 522 513 516 526 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93985 8722 2189 521 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 2746 8423 2744 19607 2746 19601 2742 8431 2745 8424 2742 19608 2745 8419 2747 19608 2745 19607 2746 8422 2744 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 170 6683 174 4889 175 10382 372 849 821 2498 176 10378 264 2479 842 2492 839 2475 846 2483 838 2481 840 2495 836 2477 844 845 846 37009 172 6681 176 4888 175 10381 373 848 924 2395 844 2490 174 10383 248 2492 172 10386 245 2495 169 -# +# name: Power type: parsed protocol: NEC address: C4 00 00 00 command: 18 00 00 00 -# +# name: Power type: parsed protocol: NEC address: 37 00 00 00 command: 12 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 179 2644 178 8174 170 2646 176 8181 173 2648 174 8177 177 2640 172 5419 174 5413 170 2649 173 2644 178 2646 176 2646 176 5409 174 28831 174 2651 171 8183 171 2648 174 8174 170 2655 177 8177 177 2642 170 5412 171 5420 173 2649 173 2646 176 2640 172 2653 169 5419 174 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 174 2648 174 8178 176 2640 171 8185 169 2653 169 8182 172 2645 177 2647 557 2264 558 5027 174 5410 173 5418 175 5413 170 28837 168 2649 173 8184 170 2652 170 8181 173 2643 169 8188 176 2646 176 2643 168 2648 174 5417 176 5411 172 5413 170 5413 170 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 178 706 266 139 268 137 173 173 198 234 178 126768 175 299 169 86 275 130 267 138 172 230 177 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 554 1922 584 3809 582 3782 578 3821 580 1920 555 1941 544 1922 574 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 476 1470 465 3479 474 3469 474 3477 475 1467 468 1471 474 27473 472 1474 472 3475 467 3478 475 3468 474 1471 475 1468 467 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 301 2188 236 2169 235 2205 230 1009 234 1070 183 1064 179 2198 206 1046 207 1069 173 2207 207 1042 211 1062 201 1043 210 1032 231 1005 237 1039 234 1006 236 -# +# name: Power type: parsed protocol: NEC address: 6D 00 00 00 command: 14 00 00 00 -# +# name: Power type: parsed protocol: NEC address: 20 00 00 00 command: 11 00 00 00 -# +# name: Power type: parsed protocol: NEC address: A0 00 00 00 command: 0B 00 00 00 -# +# name: Power type: parsed protocol: NEC address: FF 00 00 00 command: 3F 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 176 7764 173 11361 544 1951 545 1948 176 8815 171 2319 177 2322 174 2321 175 2318 178 2313 173 18281 170 7746 170 11369 536 1954 542 1957 539 969 538 1954 542 1949 536 1962 534 1960 174 2320 176 2315 170 2328 178 2317 168 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 172 23035 176 2267 178 2285 170 2270 175 2288 177 11602 222 2218 216 2246 173 183 173 935 221 218 174 864 221 1250 171 1310 223 2219 216 2247 218 1244 223 216 176 869 170 1296 217 2239 216 1254 223 216 176 866 219 9127 169 7642 173 2284 171 2273 172 2290 175 2263 171 10143 178 10623 222 1245 222 217 175 1843 220 2226 219 1264 223 1237 174 183 178 952 219 2222 223 216 176 866 219 1249 172 9190 173 7637 178 2266 169 2286 169 2280 175 2284 171 10125 175 10622 224 215 177 868 216 2228 217 2238 171 1299 224 215 177 865 174 183 173 934 222 216 176 1848 215 1247 220 1265 222 762 170 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 7410 1482 382 2742 375 2747 380 2751 1630 1535 400 2724 1657 1529 1629 1538 407 2720 407 2716 401 2722 4125 1549 376 2751 376 2748 379 2744 1626 1541 405 2723 1658 1530 1628 1531 404 2726 401 2726 401 2723 4124 1542 383 2747 380 2747 380 2745 1626 1534 401 2729 1652 1539 1629 1532 403 2719 408 2722 405 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 4277 4319 520 518 521 521 518 518 521 1597 522 1594 515 1597 522 1599 520 1594 515 1600 519 1600 519 1594 515 526 513 527 522 512 517 528 521 517 522 516 513 1605 514 522 517 525 514 525 514 521 518 526 513 525 514 1600 519 523 516 1597 522 1596 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93986 8721 2188 522 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1325 431 445 1199 1317 455 441 1207 443 1202 1294 457 449 1190 440 1209 441 1205 445 1198 1318 454 442 93237 1320 434 442 1201 1325 448 448 1200 440 1205 1291 460 446 1193 447 1202 448 1198 442 1201 1325 447 449 -# +# name: Power type: parsed protocol: NEC address: 00 00 00 00 command: 0B 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 300 1791 297 744 295 743 296 751 298 745 294 747 302 736 293 1837 271 745 294 747 302 1793 295 752 297 1802 296 1801 297 742 297 1806 302 31592 296 1801 297 742 297 749 300 744 295 745 294 745 294 752 297 1829 269 746 293 745 294 1809 300 744 295 1802 296 1799 299 748 301 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 178 2003 177 2899 177 1996 174 2908 168 2910 177 2000 170 2004 176 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8898 4173 564 642 570 1677 564 1677 564 645 567 640 572 631 571 641 571 635 567 639 563 1683 568 1673 568 641 571 636 566 637 565 647 565 641 571 634 568 642 570 1671 570 639 563 1682 569 632 570 643 569 636 566 1677 564 1683 568 636 566 1680 571 636 566 1674 567 1682 569 1674 567 40489 8904 2246 566 85626 8902 2248 564 -# +# name: Power type: parsed protocol: NEC address: AA 00 00 00 command: 80 00 00 00 -# +# name: Power type: parsed protocol: NEC address: 51 00 00 00 command: 08 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3892 3856 525 978 529 977 530 969 528 977 530 974 523 975 532 1924 531 971 526 1924 531 975 532 1916 529 976 531 1921 524 1947 508 1925 530 1920 525 1926 529 1925 530 970 527 1927 528 976 531 1915 530 978 529 1921 1033 9201 3871 3867 524 977 530 975 522 981 526 972 525 983 524 978 529 1921 524 982 525 1923 532 973 524 1928 527 971 526 1931 524 1950 505 1922 523 1931 524 1924 531 1923 532 972 525 1922 523 985 533 1918 527 975 532 1922 1032 -# +# name: Power type: parsed protocol: NEC address: 6E 00 00 00 command: 14 00 00 00 -# +# name: Power type: parsed protocol: RC5 address: 07 00 00 00 command: 0C 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 4758 1543 403 2731 407 2726 402 2739 1601 1514 401 2760 1580 1530 1577 1540 406 2758 400 2708 1602 1535 400 2740 408 2729 409 2726 401 2731 1599 1520 405 2758 1572 1540 1578 1532 403 2737 431 2706 1604 1535 411 2722 405 2734 403 2734 404 2731 1599 1512 403 2764 1576 1539 1578 1533 402 2730 428 2712 1608 1534 402 -# +# name: Power type: parsed protocol: NEC address: AA 00 00 00 command: A7 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 171 313 176 798 337 133 600 232 170 126777 176 65 169 496 176 200 609 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 500 527 491 4033 500 4986 495 510 498 4054 500 499 499 4048 496 4974 497 530 499 4026 497 4989 492 513 495 4057 497 4967 493 4992 499 506 492 4060 494 505 493 4054 500 98563 497 529 500 4025 498 4988 493 512 496 4056 498 501 497 4050 493 4976 495 532 497 4028 495 4991 500 505 493 4059 495 4969 491 4994 497 508 490 4062 492 507 491 4056 498 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 879 901 871 1796 1770 903 869 917 876 916 877 913 880 906 877 918 875 914 879 1788 1768 1784 875 87826 871 921 872 1797 1769 897 875 919 874 915 878 910 873 920 873 914 879 913 870 1800 1776 1767 871 -# +# name: Power type: parsed protocol: Kaseikyo address: 90 02 20 00 command: D0 03 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 4411 4332 558 1660 561 596 565 612 559 597 564 617 565 585 566 620 561 591 560 620 561 595 566 611 560 597 564 1653 558 592 559 627 565 589 562 617 564 1630 560 617 564 592 559 22591 4439 4322 558 1639 561 618 563 590 561 622 560 592 559 624 557 597 564 612 559 600 561 618 563 590 561 622 559 1628 562 621 561 594 567 609 562 597 564 1652 559 595 566 617 564 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3444 1767 413 487 419 1274 417 481 415 1277 414 488 418 1267 414 492 414 1276 415 484 412 1282 419 478 418 1275 416 1276 415 480 416 1280 421 479 417 1272 419 1275 416 1272 419 1274 417 484 412 484 412 493 413 1277 414 485 421 1273 418 479 417 486 420 1271 420 476 420 486 420 479 417 482 414 1280 411 1276 415 488 418 1273 418 478 418 488 418 481 415 1275 416 487 419 478 418 485 411 1280 421 475 421 1275 416 1273 418 69071 3439 1759 441 456 440 1253 438 463 443 1243 438 468 438 1251 440 460 446 1247 444 454 442 1251 440 461 445 1241 440 1256 445 455 441 1248 443 461 445 1242 439 1254 447 1245 446 1240 441 465 441 458 438 462 444 1249 442 456 440 1252 439 463 443 452 444 1252 439 461 445 454 442 461 445 452 444 1249 442 1250 441 454 442 1254 437 463 443 456 440 463 443 1245 446 456 440 462 444 451 445 1251 440 460 436 1253 438 1256 445 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 8705 4317 583 682 581 688 585 678 585 1721 581 1722 580 681 582 690 583 682 581 1721 581 1725 587 1713 579 689 584 683 580 1718 584 1724 588 1714 588 677 586 683 580 683 580 1726 586 680 583 678 585 687 586 679 584 1718 584 1721 581 1719 583 685 588 1716 586 1713 579 1729 583 1719 583 41145 8706 2217 585 94686 8705 2217 584 -# +# name: Power type: parsed protocol: NECext address: 10 2D 00 00 command: 1F E0 00 00 -# +# name: Power type: parsed protocol: RC5 address: 05 00 00 00 command: 0C 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 178 7753 174 2308 178 2284 171 2316 170 2298 177 10228 174 10724 223 1256 221 217 175 1868 169 2293 172 1327 216 1263 178 1316 217 2245 220 218 174 887 218 1261 170 10707 174 7752 175 2296 169 2314 171 2293 172 2307 179 10216 176 6265 174 10729 219 1258 219 1272 215 1268 173 2310 221 1255 222 217 175 877 172 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 532 1692 559 561 529 574 567 559 531 566 564 555 535 1694 557 568 532 1691 560 560 530 573 557 568 532 565 565 555 535 567 563 1688 533 564 567 554 536 567 563 562 538 558 562 558 532 1697 565 561 539 1683 558 1689 532 1697 564 1687 534 1689 562 1684 537 -# +# name: Power type: parsed protocol: NECext address: 12 FF 00 00 command: 0E F1 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 446 1694 455 1706 453 1705 424 628 452 597 452 622 458 1673 456 625 455 594 455 1706 454 590 459 621 459 591 458 616 453 591 458 622 539 22750 459 1675 454 1733 427 1712 427 622 458 588 451 622 458 1681 458 618 451 595 454 1705 455 598 451 625 454 592 457 616 453 598 451 626 535 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3429 3445 875 2555 868 875 867 871 871 2560 873 868 874 2551 872 874 868 871 871 869 873 870 872 865 867 2565 868 873 869 2556 867 2567 866 874 868 2560 873 870 872 2555 868 2564 869 2586 847 2577 846 2589 844 870 872 32983 3470 3430 869 2559 874 868 874 867 875 2550 873 873 869 2559 874 865 867 877 875 862 870 873 869 872 870 2555 868 877 875 2554 869 2559 874 869 873 2554 869 873 869 2562 871 2553 870 2590 843 2586 847 2581 842 876 866 -# +# name: Power type: parsed protocol: NEC address: 83 00 00 00 command: FF 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 4275 4320 519 520 519 523 516 520 519 1599 520 520 519 515 524 521 518 520 519 1595 524 1595 524 1588 520 521 518 1599 520 1591 517 1603 516 1599 520 1595 524 1594 525 1588 521 1597 522 518 521 513 526 519 520 518 521 517 522 520 519 517 522 519 520 1597 522 1589 519 1601 518 1597 522 40475 8724 2186 514 93995 8752 2183 516 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 10380 4892 599 620 592 605 597 620 592 604 598 623 589 2081 598 627 595 2080 599 2101 598 2105 574 2099 590 2088 591 2111 599 591 590 2116 594 599 593 626 596 2082 597 619 593 2086 593 626 596 594 598 627 595 598 594 2106 593 604 598 2100 589 607 595 2107 593 2079 590 2116 594 2082 750 41149 8722 2109 590 94682 8727 2104 596 -# +# name: Power type: parsed protocol: NEC address: 83 00 00 00 command: 08 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 4271 4324 515 1600 519 1600 519 1594 515 1603 516 1601 518 1593 516 1605 514 1601 518 520 519 522 517 520 519 522 517 523 516 518 521 523 516 522 517 1598 521 1597 522 1591 518 1600 519 521 518 516 523 522 517 521 518 520 519 523 516 520 519 522 517 1599 520 1591 518 1603 516 1599 520 40477 8753 2183 516 93993 8724 2185 515 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 7847 3931 470 1448 467 495 472 1443 472 490 467 1451 464 491 466 499 468 491 466 4413 467 1455 470 486 471 1449 466 495 472 1441 464 501 466 492 465 494 463 22093 7851 3934 467 1454 471 490 467 1446 469 496 471 1446 469 489 468 494 473 484 473 4410 470 1449 466 489 468 1455 470 489 468 1449 466 496 471 486 471 490 467 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 3462 1592 490 332 513 1200 489 331 514 1201 489 355 490 1201 489 356 512 1178 489 356 512 1178 512 334 487 1202 488 1202 488 357 512 1178 512 334 486 1203 487 1202 488 1203 487 1204 486 383 461 1228 488 357 488 357 487 357 487 1203 486 1204 486 359 485 1205 485 360 485 361 484 360 485 360 485 361 484 361 484 361 484 1206 484 360 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 1206 484 361 484 71543 3434 1620 486 359 485 1205 485 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1205 485 1206 484 360 485 1206 484 360 485 1206 484 1206 484 1206 484 1206 484 361 484 1206 484 360 485 360 485 361 484 1206 484 1206 484 360 484 1206 484 360 485 361 484 361 484 360 485 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1207 483 361 484 1206 484 361 484 71543 3435 1619 486 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 484 1205 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 1205 485 1206 484 360 485 1205 485 360 485 360 485 360 485 1205 485 1206 484 360 485 1206 484 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1205 485 360 485 1206 484 360 485 71542 3436 1619 486 358 487 1204 486 359 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 1206 484 1206 484 1206 484 1206 484 360 485 1206 484 360 485 360 485 361 484 1206 484 1206 484 361 484 1206 484 361 484 361 484 361 484 361 484 361 484 361 484 360 485 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 360 485 1206 484 361 484 1206 484 361 484 71542 3437 1618 487 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1206 484 360 485 1205 485 360 485 1206 484 1205 485 1206 484 1205 485 360 485 1205 485 360 485 360 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 1205 485 360 485 -# +# name: Mute type: parsed protocol: NEC @@ -3571,3 +3571,17 @@ type: parsed protocol: NEC address: 01 00 00 00 command: 18 00 00 00 +# +# TCL 75S451 +# +name: Ch_next +type: parsed +protocol: NECext +address: EA C7 00 00 +command: 19 E6 00 00 +# +name: Ch_prev +type: parsed +protocol: NECext +address: EA C7 00 00 +command: 33 CC 00 00 \ No newline at end of file diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index 9bdcdc4a8..a52f141c4 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -34,12 +34,12 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { static int32_t infrared_scene_universal_common_task_callback(void* context) { InfraredApp* infrared = context; - const bool success = infrared_brute_force_calculate_messages(infrared->brute_force); + const InfraredErrorCode error = infrared_brute_force_calculate_messages(infrared->brute_force); view_dispatcher_send_custom_event( infrared->view_dispatcher, infrared_custom_event_pack(InfraredCustomEventTypeTaskFinished, 0)); - return success; + return error; } void infrared_scene_universal_common_on_enter(void* context) { @@ -93,9 +93,9 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); } } else if(event_type == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); - if(!task_success) { + if(INFRARED_ERROR_PRESENT(task_error)) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); } else { view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete.c b/applications/main/infrared/scenes/infrared_scene_edit_delete.c index 90a2633d3..63d71318c 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete.c @@ -11,12 +11,12 @@ static int32_t infrared_scene_edit_delete_task_callback(void* context) { InfraredAppState* app_state = &infrared->app_state; const InfraredEditTarget edit_target = app_state->edit_target; - bool success; + InfraredErrorCode error = InfraredErrorCodeNone; if(edit_target == InfraredEditTargetButton) { furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - success = infrared_remote_delete_signal(infrared->remote, app_state->current_button_index); + error = infrared_remote_delete_signal(infrared->remote, app_state->current_button_index); } else if(edit_target == InfraredEditTargetRemote) { - success = infrared_remote_remove(infrared->remote); + error = infrared_remote_remove(infrared->remote); } else { furi_crash(); } @@ -24,7 +24,7 @@ static int32_t infrared_scene_edit_delete_task_callback(void* context) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } void infrared_scene_edit_delete_on_enter(void* context) { @@ -39,11 +39,15 @@ void infrared_scene_edit_delete_on_enter(void* context) { 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)) { + InfraredErrorCode error = + infrared_remote_load_signal(remote, infrared->current_signal, current_button_index); + if(INFRARED_ERROR_PRESENT(error)) { + const char* format = + (INFRARED_ERROR_CHECK(error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) ? + "Failed to delete\n\"%s\" is too long.\nTry to edit file from pc" : + "Failed to load\n\"%s\""; infrared_show_error_message( - infrared, - "Failed to load\n\"%s\"", - infrared_remote_get_signal_name(remote, current_button_index)); + infrared, format, infrared_remote_get_signal_name(remote, current_button_index)); scene_manager_previous_scene(infrared->scene_manager); return; } @@ -107,18 +111,30 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) infrared_blocking_task_start(infrared, infrared_scene_edit_delete_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); InfraredAppState* app_state = &infrared->app_state; - if(task_success) { + if(!INFRARED_ERROR_PRESENT(task_error)) { scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); } else { - const char* edit_target_text = - app_state->edit_target == InfraredEditTargetButton ? "button" : "file"; - infrared_show_error_message(infrared, "Failed to\ndelete %s", edit_target_text); + if(INFRARED_ERROR_CHECK( + task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) { + const uint8_t index = INFRARED_ERROR_GET_INDEX(task_error); + const char* format = + "Failed to delete\n\"%s\" is too long.\nTry to edit file from pc"; + infrared_show_error_message( + infrared, + format, + infrared_remote_get_signal_name(infrared->remote, index)); + } else { + const char* edit_target_text = + app_state->edit_target == InfraredEditTargetButton ? "button" : "file"; + infrared_show_error_message( + infrared, "Failed to\ndelete %s", edit_target_text); + } - const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote}; scene_manager_search_and_switch_to_previous_scene_one_of( scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index fcac1fc15..1c6415c24 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -2,14 +2,14 @@ static int32_t infrared_scene_edit_move_task_callback(void* context) { InfraredApp* infrared = context; - const bool success = infrared_remote_move_signal( + const InfraredErrorCode error = infrared_remote_move_signal( infrared->remote, infrared->app_state.prev_button_index, infrared->app_state.current_button_index); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } static void infrared_scene_edit_move_button_callback( @@ -51,14 +51,26 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { infrared_blocking_task_start(infrared, infrared_scene_edit_move_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); - if(!task_success) { - const char* signal_name = infrared_remote_get_signal_name( - infrared->remote, infrared->app_state.current_button_index); - infrared_show_error_message(infrared, "Failed to move\n\"%s\"", signal_name); - scene_manager_search_and_switch_to_previous_scene( - infrared->scene_manager, InfraredSceneRemoteList); + if(INFRARED_ERROR_PRESENT(task_error)) { + const char* format = "Failed to move\n\"%s\""; + uint8_t signal_index = infrared->app_state.prev_button_index; + + if(INFRARED_ERROR_CHECK( + task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData)) { + signal_index = INFRARED_ERROR_GET_INDEX(task_error); + format = "Failed to move\n\"%s\" is too long.\nTry to edit file from pc"; + } + furi_assert(format); + + const char* signal_name = + infrared_remote_get_signal_name(infrared->remote, signal_index); + infrared_show_error_message(infrared, format, signal_name); + + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote}; + scene_manager_search_and_switch_to_previous_scene_one_of( + infrared->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } else { view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename.c b/applications/main/infrared/scenes/infrared_scene_edit_rename.c index a546ad6e5..ea49080a1 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename.c @@ -8,13 +8,13 @@ static int32_t infrared_scene_edit_rename_task_callback(void* context) { InfraredAppState* app_state = &infrared->app_state; const InfraredEditTarget edit_target = app_state->edit_target; - bool success; + InfraredErrorCode error = InfraredErrorCodeNone; if(edit_target == InfraredEditTargetButton) { furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - success = infrared_remote_rename_signal( + error = infrared_remote_rename_signal( infrared->remote, app_state->current_button_index, infrared->text_store[0]); } else if(edit_target == InfraredEditTargetRemote) { - success = infrared_rename_current_remote(infrared, infrared->text_store[0]); + error = infrared_rename_current_remote(infrared, infrared->text_store[0]); } else { furi_crash(); } @@ -22,7 +22,7 @@ static int32_t infrared_scene_edit_rename_task_callback(void* context) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } void infrared_scene_edit_rename_on_enter(void* context) { @@ -39,7 +39,7 @@ void infrared_scene_edit_rename_on_enter(void* context) { furi_check(current_button_index != InfraredButtonIndexNone); enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH; - strncpy( + strlcpy( infrared->text_store[0], infrared_remote_get_signal_name(remote, current_button_index), enter_name_length); @@ -47,7 +47,7 @@ void infrared_scene_edit_rename_on_enter(void* context) { } else if(edit_target == InfraredEditTargetRemote) { text_input_set_header_text(text_input, "Name the remote"); enter_name_length = INFRARED_MAX_REMOTE_NAME_LENGTH; - strncpy(infrared->text_store[0], infrared_remote_get_name(remote), enter_name_length); + strlcpy(infrared->text_store[0], infrared_remote_get_name(remote), enter_name_length); FuriString* folder_path; folder_path = furi_string_alloc(); @@ -89,17 +89,30 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) infrared_blocking_task_start(infrared, infrared_scene_edit_rename_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); InfraredAppState* app_state = &infrared->app_state; - if(task_success) { + if(!INFRARED_ERROR_PRESENT(task_error)) { scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); } else { - const char* edit_target_text = - app_state->edit_target == InfraredEditTargetButton ? "button" : "file"; - infrared_show_error_message(infrared, "Failed to\nrename %s", edit_target_text); - scene_manager_search_and_switch_to_previous_scene( - scene_manager, InfraredSceneRemoteList); + bool long_signal = INFRARED_ERROR_CHECK( + task_error, InfraredErrorCodeSignalRawUnableToReadTooLongData); + + const char* format = "Failed to rename\n%s"; + const char* target = infrared->app_state.edit_target == InfraredEditTargetButton ? + "button" : + "file"; + if(long_signal) { + format = "Failed to rename\n\"%s\" is too long.\nTry to edit file from pc"; + target = infrared_remote_get_signal_name( + infrared->remote, INFRARED_ERROR_GET_INDEX(task_error)); + } + + infrared_show_error_message(infrared, format, target); + + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneRemote}; + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } app_state->current_button_index = InfraredButtonIndexNone; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c index be46a8691..7ffc9644b 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c @@ -41,12 +41,12 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeTextEditDone) { const char* signal_name = infrared->text_store[0]; - const bool success = + const InfraredErrorCode error = 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); - if(success) { + if(!INFRARED_ERROR_PRESENT(error)) { scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); dolphin_deed(DolphinDeedIrSave); } else { diff --git a/applications/main/infrared/scenes/infrared_scene_remote.c b/applications/main/infrared/scenes/infrared_scene_remote.c index 6c1d1ec4e..6dd7aae8b 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote.c +++ b/applications/main/infrared/scenes/infrared_scene_remote.c @@ -85,7 +85,13 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { if(custom_type == InfraredCustomEventTypeTransmitStarted) { furi_assert(button_index >= 0); - infrared_tx_start_button_index(infrared, button_index); + InfraredErrorCode error = infrared_tx_start_button_index(infrared, button_index); + if(INFRARED_ERROR_PRESENT(error)) { + infrared_show_error_message( + infrared, + "Failed to load\n\"%s\"", + infrared_remote_get_signal_name(infrared->remote, button_index)); + } consumed = true; } else if(custom_type == InfraredCustomEventTypeTransmitStopped) { infrared_tx_stop(infrared); diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 9c880ac2f..93665769c 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -2,11 +2,11 @@ static int32_t infrared_scene_remote_list_task_callback(void* context) { InfraredApp* infrared = context; - const bool success = + const InfraredErrorCode error = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { @@ -38,13 +38,19 @@ bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); - if(task_success) { + if(!INFRARED_ERROR_PRESENT(task_error)) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); } else { + bool wrong_file_type = + INFRARED_ERROR_CHECK(task_error, InfraredErrorCodeWrongFileType); + const char* format = wrong_file_type ? + "Library file\n\"%s\" can't be openned as a remote" : + "Failed to load\n\"%s\""; + infrared_show_error_message( - infrared, "Failed to load\n\"%s\"", furi_string_get_cstr(infrared->file_path)); + infrared, format, furi_string_get_cstr(infrared->file_path)); infrared_scene_remote_list_select_and_load(infrared); } } diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index 4c263a117..8f9dc4338 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -11,27 +11,33 @@ typedef enum { static int32_t infrared_scene_rpc_task_callback(void* context) { InfraredApp* infrared = context; - const bool success = + const InfraredErrorCode error = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return success; + return error; } void infrared_scene_rpc_on_enter(void* context) { InfraredApp* infrared = context; + scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle); +} + +static void infrared_scene_rpc_show(InfraredApp* infrared) { Popup* popup = infrared->popup; popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom); popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); + popup_set_text(popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); - popup_set_context(popup, context); + popup_set_context(popup, infrared); popup_set_callback(popup, infrared_popup_closed_callback); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); - scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle); + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending); notification_message(infrared->notifications, &sequence_display_backlight_on); } @@ -51,25 +57,24 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { } } else if(event.event == InfraredCustomEventTypeTaskFinished) { - const bool task_success = infrared_blocking_task_finalize(infrared); + const InfraredErrorCode task_error = infrared_blocking_task_finalize(infrared); - if(task_success) { + if(!INFRARED_ERROR_PRESENT(task_error)) { const char* remote_name = infrared_remote_get_name(infrared->remote); infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); scene_manager_set_scene_state( infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); - } else { - infrared_text_store_set( - infrared, 0, "failed to load\n%s", furi_string_get_cstr(infrared->file_path)); + FuriString* str = furi_string_alloc(); + furi_string_printf( + str, "Failed to load\n%s", furi_string_get_cstr(infrared->file_path)); + + rpc_system_app_set_error_code(infrared->rpc_ctx, RpcAppSystemErrorCodeParseFile); + rpc_system_app_set_error_text(infrared->rpc_ctx, furi_string_get_cstr(str)); + + furi_string_free(str); } - - popup_set_text( - infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); - - rpc_system_app_confirm(infrared->rpc_ctx, task_success); - + rpc_system_app_confirm(infrared->rpc_ctx, !INFRARED_ERROR_PRESENT(task_error)); } else if( event.event == InfraredCustomEventTypeRpcButtonPressName || event.event == InfraredCustomEventTypeRpcButtonPressIndex) { @@ -88,10 +93,21 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { TAG, "Sending signal with index \"%ld\"", app_state->current_button_index); } if(infrared->app_state.current_button_index != InfraredButtonIndexNone) { - infrared_tx_start_button_index(infrared, app_state->current_button_index); - scene_manager_set_scene_state( - infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending); - result = true; + InfraredErrorCode error = + infrared_tx_start_button_index(infrared, app_state->current_button_index); + if(!INFRARED_ERROR_PRESENT(error)) { + const char* remote_name = infrared_remote_get_name(infrared->remote); + infrared_text_store_set(infrared, 0, "emulating\n%s", remote_name); + + infrared_scene_rpc_show(infrared); + result = true; + } else { + rpc_system_app_set_error_code( + infrared->rpc_ctx, RpcAppSystemErrorCodeInternalParse); + rpc_system_app_set_error_text( + infrared->rpc_ctx, "Cannot load button data"); + result = false; + } } } rpc_system_app_confirm(infrared->rpc_ctx, result); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c index 906218d74..65ffa1ef6 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c @@ -2,23 +2,30 @@ void lfrfid_scene_rpc_on_enter(void* context) { LfRfid* app = context; + app->rpc_state = LfRfidRpcStateIdle; +} + +static void lfrfid_rpc_start_emulation(LfRfid* app) { Popup* popup = app->popup; + lfrfid_text_store_set(app, "emulating\n%s", furi_string_get_cstr(app->file_name)); + popup_set_header(popup, "LF RFID", 89, 42, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); + popup_set_text(popup, app->text_store, 89, 44, AlignCenter, AlignTop); popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); - notification_message(app->notifications, &sequence_display_backlight_on); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); - app->rpc_state = LfRfidRpcStateIdle; + notification_message(app->notifications, &sequence_display_backlight_on); + notification_message(app->notifications, &sequence_blink_start_magenta); + app->rpc_state = LfRfidRpcStateEmulating; } bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) { LfRfid* app = context; - Popup* popup = app->popup; - UNUSED(event); bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -34,16 +41,11 @@ bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) { bool result = false; if(app->rpc_state == LfRfidRpcStateIdle) { 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); - app->rpc_state = LfRfidRpcStateEmulating; - - lfrfid_text_store_set( - app, "emulating\n%s", furi_string_get_cstr(app->file_name)); - popup_set_text(popup, app->text_store, 89, 44, AlignCenter, AlignTop); - - notification_message(app->notifications, &sequence_blink_start_magenta); + lfrfid_rpc_start_emulation(app); result = true; + } else { + rpc_system_app_set_error_code(app->rpc_ctx, RpcAppSystemErrorCodeParseFile); + rpc_system_app_set_error_text(app->rpc_ctx, "Cannot load key file"); } } rpc_system_app_confirm(app->rpc_ctx, result); diff --git a/applications/main/momentum_app/scenes/momentum_app_scene_misc_dolphin.c b/applications/main/momentum_app/scenes/momentum_app_scene_misc_dolphin.c index f6593431f..131205ab4 100644 --- a/applications/main/momentum_app/scenes/momentum_app_scene_misc_dolphin.c +++ b/applications/main/momentum_app/scenes/momentum_app_scene_misc_dolphin.c @@ -114,6 +114,8 @@ void momentum_app_scene_misc_dolphin_on_enter(void* context) { VariableItemList* var_item_list = app->var_item_list; VariableItem* item; uint8_t value_index; + DolphinSettings settings; + dolphin_get_settings(app->dolphin, &settings); uint8_t level = dolphin_get_level(app->dolphin_xp); char level_str[4]; @@ -150,6 +152,13 @@ void momentum_app_scene_misc_dolphin_on_enter(void* context) { app); variable_item_set_current_value_index(item, app->dolphin_angry); variable_item_set_current_value_text(item, angry_str); + variable_item_set_locked( + item, + settings.happy_mode, + "Settings >\n" + "Desktop >\n" + "Happy Mode\n" + "is enabled!"); item = variable_item_list_add( var_item_list, @@ -161,6 +170,13 @@ void momentum_app_scene_misc_dolphin_on_enter(void* context) { momentum_settings.butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_values)); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, butthurt_timer_names[value_index]); + variable_item_set_locked( + item, + settings.happy_mode, + "Settings >\n" + "Desktop >\n" + "Happy Mode\n" + "is enabled!"); variable_item_list_set_enter_callback( var_item_list, momentum_app_scene_misc_dolphin_var_item_list_callback, app); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c index 810242fbc..6ceb4bce0 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c @@ -1,6 +1,6 @@ #include "iso14443_3a_render.h" -void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size) { +void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* data, size_t size) { for(size_t i = 0; i < size; i++) { furi_string_cat_printf(str, " %02X", data[i]); } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h index 34e347aa3..b81515566 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h @@ -11,7 +11,7 @@ void nfc_render_iso14443_3a_info( void nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str); -void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size); +void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* data, size_t size); void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 7a51e3d86..4fece16be 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -117,7 +117,7 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { if(!mf_classic_is_card_read(data)) { submenu_add_item( submenu, - "Detect Reader", + "Extract MF Keys", SubmenuIndexDetectReader, nfc_protocol_support_common_submenu_callback, instance); @@ -155,7 +155,7 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { if(!mf_classic_is_card_read(data)) { submenu_add_item( submenu, - "Detect Reader", + "Extract MF Keys", SubmenuIndexDetectReader, nfc_protocol_support_common_submenu_callback, instance); diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c index c4ad67ff8..ef83d1942 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c @@ -10,22 +10,29 @@ static void nfc_render_mf_ultralight_pages_count(const MfUltralightData* data, F } void nfc_render_mf_ultralight_pwd_pack(const MfUltralightData* data, FuriString* str) { + MfUltralightConfigPages* config; + bool all_pages = mf_ultralight_is_all_data_read(data); - if(all_pages) { + bool has_config = mf_ultralight_get_config_page(data, &config); + + if(!has_config) { + furi_string_cat_printf(str, "\e#Already Unlocked!"); + } else if(all_pages) { furi_string_cat_printf(str, "\e#All Pages Are Unlocked!"); } else { furi_string_cat_printf(str, "\e#Some Pages Are Locked!"); } - MfUltralightConfigPages* config; - mf_ultralight_get_config_page(data, &config); + if(has_config) { + furi_string_cat_printf(str, "\nPassword: "); + nfc_render_iso14443_3a_format_bytes( + str, config->password.data, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE); - furi_string_cat_printf(str, "\nPassword: "); - nfc_render_iso14443_3a_format_bytes( - str, config->password.data, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE); - - furi_string_cat_printf(str, "\nPACK: "); - nfc_render_iso14443_3a_format_bytes(str, config->pack.data, MF_ULTRALIGHT_AUTH_PACK_SIZE); + furi_string_cat_printf(str, "\nPACK: "); + nfc_render_iso14443_3a_format_bytes(str, config->pack.data, MF_ULTRALIGHT_AUTH_PACK_SIZE); + } else { + furi_string_cat_printf(str, "\nThis card does not support\npassword protection!"); + } nfc_render_mf_ultralight_pages_count(data, str); } diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 072e8d7ef..9772be1e7 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -731,6 +731,10 @@ static bool nfc_protocol_support_scene_rpc_on_event(NfcApp* instance, SceneManag if(nfc_load_file(instance, instance->file_path, false)) { nfc_protocol_support_scene_rpc_setup_ui_and_emulate(instance); success = true; + } else { + rpc_system_app_set_error_code( + instance->rpc_ctx, RpcAppSystemErrorCodeParseFile); + rpc_system_app_set_error_text(instance->rpc_ctx, "Cannot load key file"); } } rpc_system_app_confirm(instance->rpc_ctx, success); diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index e61e172f0..e563eacd1 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -496,7 +496,7 @@ int32_t nfc_app(void* p) { nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); furi_string_set(nfc->file_path, args); - if(nfc_load_file(nfc, nfc->file_path, false)) { + if(nfc_load_file(nfc, nfc->file_path, true)) { nfc->fav_timeout = is_favorite; nfc_show_initial_scene_for_device(nfc); } else { diff --git a/applications/main/nfc/plugins/supported_cards/itso.c b/applications/main/nfc/plugins/supported_cards/itso.c index 92dbbd7b6..4e9264de4 100644 --- a/applications/main/nfc/plugins/supported_cards/itso.c +++ b/applications/main/nfc/plugins/supported_cards/itso.c @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -72,7 +73,10 @@ static bool itso_parse(const NfcDevice* device, FuriString* parsed_data) { dateBuff[17] = '\0'; // DateStamp is defined in BS EN 1545 - Days passed since 01/01/1997 - uint32_t dateStamp = (int)strtol(datep, NULL, 16); + uint32_t dateStamp; + if(strint_to_uint32(datep, NULL, &dateStamp, 16) != StrintParseNoError) { + return false; + } uint32_t unixTimestamp = dateStamp * 24 * 60 * 60 + 852076800U; furi_string_set(parsed_data, "\e#ITSO Card\n"); diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 70f726acf..9d6fc66eb 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -29,7 +29,11 @@ void nfc_scene_start_on_enter(void* context) { submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); submenu_add_item( - submenu, "Detect Reader", SubmenuIndexDetectReader, nfc_scene_start_submenu_callback, nfc); + submenu, + "Extract MF Keys", + SubmenuIndexDetectReader, + nfc_scene_start_submenu_callback, + nfc); submenu_add_item(submenu, "Saved", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); submenu_add_item( submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc); diff --git a/applications/main/onewire/onewire_cli.c b/applications/main/onewire/onewire_cli.c index 128b58743..4ee81a462 100644 --- a/applications/main/onewire/onewire_cli.c +++ b/applications/main/onewire/onewire_cli.c @@ -20,7 +20,7 @@ static void onewire_cli_search(Cli* cli) { printf("Search started\r\n"); onewire_host_start(onewire); - furi_hal_power_enable_otg(); + if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg(); while(!done) { if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) { @@ -37,7 +37,7 @@ static void onewire_cli_search(Cli* cli) { furi_delay_ms(100); } - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); onewire_host_free(onewire); } diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 9af1ce47c..86723e5d2 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -125,6 +125,9 @@ typedef enum { SetTypePricenton315, SetTypePricenton433, SetTypeBETT_433, + SetTypeGangQi_433, + SetTypeHollarm_433, + SetTypeMarantec24_868, SetTypeLinear_300_00, // SetTypeNeroSketch, //Deleted in OFW // SetTypeNeroRadio, //Deleted in OFW diff --git a/applications/main/subghz/helpers/subghz_error_type.h b/applications/main/subghz/helpers/subghz_error_type.h deleted file mode 100644 index 0f86d6ea7..000000000 --- a/applications/main/subghz/helpers/subghz_error_type.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -/** SubGhzErrorType */ -typedef enum { - SubGhzErrorTypeNoError = 0, /** There are no errors */ - SubGhzErrorTypeParseFile = - 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/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c index 724da6b87..68172612a 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -382,3 +382,35 @@ bool subghz_txrx_gen_secplus_v1_protocol( } return ret; } + +void subghz_txrx_gen_serial_gangqi(uint64_t* result_key) { + uint64_t randkey; + uint64_t only_required_bytes; + uint16_t sum_of_3bytes; + uint8_t xorbytes; + + do { + randkey = (uint64_t)rand(); + only_required_bytes = (randkey & 0x0FFFF0000) | 0x200000000; + sum_of_3bytes = ((only_required_bytes >> 32) & 0xFF) + + ((only_required_bytes >> 24) & 0xFF) + + ((only_required_bytes >> 16) & 0xFF); + xorbytes = ((only_required_bytes >> 32) & 0xFF) ^ ((only_required_bytes >> 24) & 0xFF) ^ + ((only_required_bytes >> 16) & 0xFF); + } while( + !((((!(sum_of_3bytes & 0x3)) && ((0xB < sum_of_3bytes) && (sum_of_3bytes < 0x141))) && + ((((only_required_bytes >> 32) & 0xFF) == 0x2) || + (((only_required_bytes >> 32) & 0xFF) == 0x3))) && + ((((xorbytes == 0xBA) || (xorbytes == 0xE2)) || + ((xorbytes == 0x3A) || (xorbytes == 0xF2))) || + (xorbytes == 0xB2)))); + + // Serial 01 button 01 + uint64_t new_key = only_required_bytes | (0b01 << 14) | (0xD << 10) | (0b01 << 8); + + uint8_t crc = -0xD7 - ((new_key >> 32) & 0xFF) - ((new_key >> 24) & 0xFF) - + ((new_key >> 16) & 0xFF) - ((new_key >> 8) & 0xFF); + + // Add crc sum to the end + *result_key = (new_key | crc); +} diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h index 0b9cfefab..55932bd39 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h @@ -146,3 +146,10 @@ bool subghz_txrx_gen_secplus_v1_protocol( SubGhzTxRx* instance, const char* name_preset, uint32_t frequency); + +/** + * Generate valid serial number for GangQi protocol + * + * @return uint64_t if success + */ +void subghz_txrx_gen_serial_gangqi(uint64_t* result_key); diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index 4675afaeb..f058821e0 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -10,23 +10,33 @@ typedef enum { void subghz_scene_rpc_on_enter(void* context) { SubGhz* subghz = context; + scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); +} + +static void subghz_format_file_name_tmp(SubGhz* subghz) { + FuriString* file_name; + file_name = furi_string_alloc(); + path_extract_filename(subghz->file_path, file_name, true); + snprintf( + subghz->file_name_tmp, SUBGHZ_MAX_LEN_NAME, "loaded\n%s", furi_string_get_cstr(file_name)); + furi_string_free(file_name); +} + +static void subghz_scene_rpc_emulation_show(SubGhz* subghz) { Popup* popup = subghz->popup; + subghz_format_file_name_tmp(subghz); popup_set_header(popup, "Sub-GHz", 89, 42, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); + popup_set_text(popup, subghz->file_name_tmp, 89, 44, AlignCenter, AlignTop); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); - scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); - notification_message(subghz->notifications, &sequence_display_backlight_on); } bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; - Popup* popup = subghz->popup; bool consumed = false; SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); @@ -45,13 +55,15 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { 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_code( + subghz->rpc_ctx, RpcAppSystemErrorCodeRegionLock); rpc_system_app_set_error_text( subghz->rpc_ctx, "Transmission on this frequency is restricted in your settings"); break; case SubGhzTxRxStartTxStateErrorParserOthers: - rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeParserOthers); + rpc_system_app_set_error_code( + subghz->rpc_ctx, RpcAppSystemErrorCodeInternalParse); rpc_system_app_set_error_text( subghz->rpc_ctx, "Error in protocol parameters description"); break; @@ -79,22 +91,12 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { bool result = false; if(state == SubGhzRpcStateIdle) { if(subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), false)) { + subghz_scene_rpc_emulation_show(subghz); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateLoaded); result = true; - FuriString* file_name = furi_string_alloc(); - path_extract_filename(subghz->file_path, file_name, true); - - snprintf( - subghz->file_name_tmp, - SUBGHZ_MAX_LEN_NAME, - "loaded\n%s", - furi_string_get_cstr(file_name)); - popup_set_text(popup, subghz->file_name_tmp, 89, 44, AlignCenter, AlignTop); - - furi_string_free(file_name); } else { - rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeParseFile); + rpc_system_app_set_error_code(subghz->rpc_ctx, RpcAppSystemErrorCodeParseFile); rpc_system_app_set_error_text(subghz->rpc_ctx, "Cannot parse file"); } } diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index bb36e9494..4e775add1 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -63,7 +63,7 @@ void subghz_scene_save_name_on_enter(void* context) { furi_string_set(subghz->file_path, dir_name); } - strncpy(subghz->file_name_tmp, furi_string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME); + strlcpy(subghz->file_name_tmp, furi_string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME); text_input_set_header_text(text_input, "Name signal"); text_input_set_result_callback( text_input, diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 324cc48f0..43bf05044 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -63,6 +63,9 @@ static const char* submenu_names[SetTypeMAX] = { [SetTypeCAMESpace] = "KL: CAME Space 433MHz", [SetTypePricenton315] = "Princeton 315MHz", [SetTypePricenton433] = "Princeton 433MHz", + [SetTypeGangQi_433] = "GangQi 433MHz", + [SetTypeHollarm_433] = "Hollarm 433MHz", + [SetTypeMarantec24_868] = "Marantec24 868MHz", [SetTypeBETT_433] = "BETT 433MHz", [SetTypeLinear_300_00] = "Linear 300MHz", // [SetTypeNeroSketch] = "Nero Sketch", // Deleted in OFW @@ -111,7 +114,7 @@ typedef struct { union { struct { const char* name; - uint32_t key; + uint64_t key; uint8_t bits; uint16_t te; } data; @@ -179,7 +182,11 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { return true; } - uint32_t key = (uint32_t)rand(); + uint64_t key = (uint64_t)rand(); + + uint64_t gangqi_key; + subghz_txrx_gen_serial_gangqi(&gangqi_key); + GenInfo gen_info = {0}; switch(event.event) { case SetTypePricenton433: @@ -302,6 +309,42 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { .data.bits = 24, .data.te = 0}; break; + case SetTypeGangQi_433: + gen_info = (GenInfo){ + .type = GenData, + .mod = "AM650", + .freq = 433920000, + .data.name = + SUBGHZ_PROTOCOL_GANGQI_NAME, // Add button 0xD arm and crc sum to the end + .data.key = gangqi_key, + .data.bits = 34, + .data.te = 0}; + break; + case SetTypeHollarm_433: + gen_info = (GenInfo){ + .type = GenData, + .mod = "AM650", + .freq = 433920000, + .data.name = SUBGHZ_PROTOCOL_HOLLARM_NAME, // Add button 0x2 and crc sum to the end + .data.key = (key & 0x000FFF0000) | 0xF0B0002200 | + ((((((key & 0x000FFF0000) | 0xF0B0002200) >> 32) & 0xFF) + + ((((key & 0x000FFF0000) | 0xF0B0002200) >> 24) & 0xFF) + + ((((key & 0x000FFF0000) | 0xF0B0002200) >> 16) & 0xFF) + + ((((key & 0x000FFF0000) | 0xF0B0002200) >> 8) & 0xFF)) & + 0xFF), + .data.bits = 42, + .data.te = 0}; + break; + case SetTypeMarantec24_868: + gen_info = (GenInfo){ + .type = GenData, + .mod = "AM650", + .freq = 868350000, + .data.name = SUBGHZ_PROTOCOL_MARANTEC24_NAME, // Add button code 0x8 to the end + .data.key = (key & 0xFFFFF0) | 0x000008, + .data.bits = 24, + .data.te = 0}; + break; case SetTypeFaacSLH_433: gen_info = (GenInfo){ .type = GenFaacSLH, @@ -321,7 +364,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { .faac_slh.serial = ((key & 0x00FFFFF0) | 0xA0000006) >> 4, .faac_slh.btn = 0x06, .faac_slh.cnt = 0x02, - .faac_slh.seed = key, + .faac_slh.seed = (key & 0x0FFFFFFF), .faac_slh.manuf = "FAAC_SLH"}; break; case SetTypeBeninca433: diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index e53f7eb24..9c8f551cb 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -3,18 +3,20 @@ #include #include -#include -#include +#include +#include #include #include #include #include -#include #include #include #include +#include +#include + #include "helpers/subghz_chat.h" #include @@ -76,9 +78,8 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { uint32_t frequency = 433920000; if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) != + StrintParseNoError) { cli_print_usage("subghz tx_carrier", "", furi_string_get_cstr(args)); return; } @@ -120,9 +121,8 @@ void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { uint32_t frequency = 433920000; if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) != + StrintParseNoError) { cli_print_usage("subghz rx_carrier", "", furi_string_get_cstr(args)); return; } @@ -186,23 +186,14 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf( - furi_string_get_cstr(args), - "%lx %lu %lu %lu %lu", - &key, - &frequency, - &te, - &repeat, - &device_ind); - if(ret != 5) { - printf( - "sscanf returned %d, key: %lx, frequency: %lu, te: %lu, repeat: %lu, device: %lu\r\n ", - ret, - key, - frequency, - te, - repeat, - device_ind); + char* args_cstr = (char*)furi_string_get_cstr(args); + StrintParseError parse_err = StrintParseNoError; + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &key, 16); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &te, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &repeat, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); + if(parse_err) { cli_print_usage( "subghz tx", "<3 Byte Key: in hex> ", @@ -318,10 +309,11 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); - if(ret != 2) { - printf( - "sscanf returned %d, frequency: %lu device: %lu\r\n", ret, frequency, device_ind); + char* args_cstr = (char*)furi_string_get_cstr(args); + StrintParseError parse_err = StrintParseNoError; + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); + if(parse_err) { cli_print_usage( "subghz rx", " ", @@ -405,9 +397,8 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { uint32_t frequency = 433920000; if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &frequency, 10) != + StrintParseNoError) { cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); return; } @@ -624,9 +615,11 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) } if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &repeat, &device_ind); - if(ret != 2) { - printf("sscanf returned %d, repeat: %lu device: %lu\r\n", ret, repeat, device_ind); + char* args_cstr = (char*)furi_string_get_cstr(args); + StrintParseError parse_err = StrintParseNoError; + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); + if(parse_err) { cli_print_usage( "subghz tx_from_file:", " ", @@ -939,10 +932,11 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); - if(ret != 2) { - printf("sscanf returned %d, Frequency: %lu\r\n", ret, frequency); - printf("sscanf returned %d, Device: %lu\r\n", ret, device_ind); + char* args_cstr = (char*)furi_string_get_cstr(args); + StrintParseError parse_err = StrintParseNoError; + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); + if(parse_err) { cli_print_usage( "subghz chat", " ", diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 9f0eae6fa..b56fb4b1b 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -1,7 +1,6 @@ #pragma once #include "helpers/subghz_types.h" -#include "helpers/subghz_error_type.h" #include #include "subghz.h" #include "views/receiver.h" diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index 091085167..6837f4e18 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -497,7 +497,10 @@ void cli_plugin_wrapper(const char* name, Cli* cli, FuriString* args, void* cont const CliCallback handler = plugin_manager_get_ep(manager, 0); handler(cli, args, context); } else { - printf("CLI plugin failed (code %" PRIu16 "), update firmware or check logs\r\n", error); + printf( + "CLI plugin '%s' failed (code %" PRIu16 "), update firmware or check logs\r\n", + name, + error); } furi_string_free(path); plugin_manager_free(manager); diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 21535531f..01ce90dbb 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -9,6 +9,7 @@ #include #include #include +#include // Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` #define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" @@ -371,9 +372,9 @@ void cli_command_led(Cli* cli, FuriString* args, void* context) { } furi_string_free(light_name); // Read light value from the rest of the string - char* end_ptr; - uint32_t value = strtoul(furi_string_get_cstr(args), &end_ptr, 0); - if(!(value < 256 && *end_ptr == '\0')) { + uint32_t value; + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &value, 0) != StrintParseNoError || + value >= 256) { cli_print_usage("led", " <0-255>", furi_string_get_cstr(args)); return; } diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index ec3289473..b816b81ff 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -83,8 +83,7 @@ static bool animation_storage_load_single_manifest_info( if(furi_string_cmp_str(read_string, name)) break; flipper_format_set_strict_mode(file, true); - manifest_info->name = malloc(furi_string_size(read_string) + 1); - strcpy((char*)manifest_info->name, furi_string_get_cstr(read_string)); + manifest_info->name = strdup(furi_string_get_cstr(read_string)); if(!flipper_format_read_uint32(file, "Min butthurt", &u32value, 1)) break; manifest_info->min_butthurt = u32value; @@ -137,9 +136,7 @@ void animation_storage_fill_animation_list(StorageAnimationList_t* animation_lis storage_animation->manifest_info.name = NULL; if(!flipper_format_read_string(file, "Name", read_string)) break; - storage_animation->manifest_info.name = malloc(furi_string_size(read_string) + 1); - strcpy( - (char*)storage_animation->manifest_info.name, furi_string_get_cstr(read_string)); + storage_animation->manifest_info.name = strdup(furi_string_get_cstr(read_string)); if(!flipper_format_read_uint32(file, "Min butthurt", &u32value, 1)) break; storage_animation->manifest_info.min_butthurt = u32value; @@ -433,8 +430,7 @@ static bool animation_storage_load_bubbles(BubbleAnimation* animation, FlipperFo furi_string_replace_all(str, "\\n", "\n"); - FURI_CONST_ASSIGN_PTR(bubble->bubble.text, malloc(furi_string_size(str) + 1)); - strcpy((char*)bubble->bubble.text, furi_string_get_cstr(str)); + FURI_CONST_ASSIGN_PTR(bubble->bubble.text, strdup(furi_string_get_cstr(str))); if(!flipper_format_read_string(ff, "AlignH", str)) break; if(!animation_storage_cast_align(str, (Align*)&bubble->bubble.align_h)) break; diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 10d95ddb4..d618f14df 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -440,6 +440,30 @@ void desktop_unlock(Desktop* desktop) { desktop->locked = false; } +int32_t desktop_shutdown(void* context) { + // Attempt to launch the app, and if failed offer to shutdown (simpler UI) + Desktop* desktop = context; + LoaderStatus result = loader_start(desktop->loader, "Power", "off", NULL); + if(result != LoaderStatusOk) { + // Mimic applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Turn Off Device?", 64, 0, AlignCenter, AlignTop); + dialog_message_set_text( + message, " I will be\nwaiting for\n you here...", 78, 14, AlignLeft, AlignTop); + dialog_message_set_icon(message, &I_dolph_cry_49x54, 14, 10); + dialog_message_set_buttons(message, "Cancel", NULL, "Power Off"); + DialogMessageButton res = dialog_message_show(furi_record_open(RECORD_DIALOGS), message); + furi_record_close(RECORD_DIALOGS); + dialog_message_free(message); + if(res == DialogMessageButtonRight) { + Power* power = furi_record_open(RECORD_POWER); + power_off(power); + furi_record_close(RECORD_POWER); + } + } + return 0; +} + void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) { desktop->in_transition = true; diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 91b7060ce..10badcc07 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -93,4 +93,5 @@ struct Desktop { void desktop_lock(Desktop* desktop, bool pin_lock); void desktop_unlock(Desktop* desktop); +int32_t desktop_shutdown(void* context); void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled); diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index a9fc3b7db..c15ca4e10 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "../desktop.h" #include "../desktop_i.h" @@ -83,7 +84,8 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case DesktopLockedEventOpenPowerOff: { if(momentum_settings.lockscreen_poweroff) { - loader_start_detached_with_gui_error(desktop->loader, "Power", "off"); + // Workaround for shutdown when app can't be opened + run_parallel(desktop_shutdown, desktop, 512); } consumed = true; break; diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 13a431d7f..a1f4df3e8 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "../desktop_i.h" #include "../views/desktop_events.h" @@ -119,7 +120,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; case DesktopMainEventOpenPowerOff: { - loader_start_detached_with_gui_error(desktop->loader, "Power", "off"); + // Workaround for shutdown when app can't be opened + run_parallel(desktop_shutdown, desktop, 512); consumed = true; break; } diff --git a/applications/services/desktop/views/desktop_view_pin_input.c b/applications/services/desktop/views/desktop_view_pin_input.c index 71b02ab85..3c18f166c 100644 --- a/applications/services/desktop/views/desktop_view_pin_input.c +++ b/applications/services/desktop/views/desktop_view_pin_input.c @@ -103,7 +103,7 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu furi_assert(canvas); furi_assert(model); - uint8_t draw_pin_size = MAX(4, model->pin.length + 1); + uint8_t draw_pin_size = MAX(MIN_PIN_LENGTH, model->pin.length + 1); if(model->locked_input || (model->pin.length == MAX_PIN_LENGTH)) { draw_pin_size = model->pin.length; } diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index fde0509f9..d9c6b50ae 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -48,6 +48,26 @@ void dolphin_deed(DolphinDeed deed) { furi_record_close(RECORD_DOLPHIN); } +void dolphin_get_settings(Dolphin* dolphin, DolphinSettings* settings) { + furi_check(dolphin); + furi_check(settings); + + DolphinEvent event; + event.type = DolphinEventTypeSettingsGet; + event.settings = settings; + dolphin_event_send_wait(dolphin, &event); +} + +void dolphin_set_settings(Dolphin* dolphin, DolphinSettings* settings) { + furi_check(dolphin); + furi_check(settings); + + DolphinEvent event; + event.type = DolphinEventTypeSettingsSet; + event.settings = settings; + dolphin_event_send_wait(dolphin, &event); +} + DolphinStats dolphin_stats(Dolphin* dolphin) { furi_check(dolphin); @@ -230,7 +250,9 @@ static bool dolphin_process_event(FuriEventLoopObject* object, void* context) { } else if(event.type == DolphinEventTypeStats) { event.stats->icounter = dolphin->state->data.icounter; - event.stats->butthurt = dolphin->state->data.butthurt; + event.stats->butthurt = (dolphin->state->data.flags & DolphinFlagHappyMode) ? + 0 : + dolphin->state->data.butthurt; event.stats->timestamp = dolphin->state->data.timestamp; event.stats->level = dolphin_get_level(dolphin->state->data.icounter); event.stats->level_up_is_pending = @@ -248,6 +270,15 @@ static bool dolphin_process_event(FuriEventLoopObject* object, void* context) { dolphin_state_load(dolphin->state); dolphin_reset_butthurt_timer(dolphin); + } else if(event.type == DolphinEventTypeSettingsGet) { + event.settings->happy_mode = dolphin->state->data.flags & DolphinFlagHappyMode; + + } else if(event.type == DolphinEventTypeSettingsSet) { + dolphin->state->data.flags &= ~DolphinFlagHappyMode; + if(event.settings->happy_mode) dolphin->state->data.flags |= DolphinFlagHappyMode; + dolphin->state->dirty = true; + dolphin_state_save(dolphin->state); + } else { furi_crash(); } diff --git a/applications/services/dolphin/dolphin.h b/applications/services/dolphin/dolphin.h index e66113021..ce2760d65 100644 --- a/applications/services/dolphin/dolphin.h +++ b/applications/services/dolphin/dolphin.h @@ -21,6 +21,10 @@ typedef struct { bool level_up_is_pending; } DolphinStats; +typedef struct { + bool happy_mode; +} DolphinSettings; + typedef enum { DolphinPubsubEventUpdate, } DolphinPubsubEvent; @@ -31,6 +35,10 @@ typedef enum { */ void dolphin_deed(DolphinDeed deed); +void dolphin_get_settings(Dolphin* dolphin, DolphinSettings* settings); + +void dolphin_set_settings(Dolphin* dolphin, DolphinSettings* settings); + /** Retrieve dolphin stats * Thread safe, blocking */ diff --git a/applications/services/dolphin/dolphin_i.h b/applications/services/dolphin/dolphin_i.h index 6a6b3dfd8..8e8d40825 100644 --- a/applications/services/dolphin/dolphin_i.h +++ b/applications/services/dolphin/dolphin_i.h @@ -13,6 +13,8 @@ typedef enum { DolphinEventTypeFlush, DolphinEventTypeLevel, DolphinEventTypeReloadState, + DolphinEventTypeSettingsGet, + DolphinEventTypeSettingsSet, } DolphinEventType; typedef struct { @@ -21,6 +23,7 @@ typedef struct { union { DolphinDeed deed; DolphinStats* stats; + DolphinSettings* settings; }; } DolphinEvent; diff --git a/applications/services/dolphin/helpers/dolphin_state.h b/applications/services/dolphin/helpers/dolphin_state.h index ab27ab082..2b1f83611 100644 --- a/applications/services/dolphin/helpers/dolphin_state.h +++ b/applications/services/dolphin/helpers/dolphin_state.h @@ -6,16 +6,16 @@ #include "dolphin_deed.h" -#ifdef __cplusplus -extern "C" { -#endif - extern const uint32_t DOLPHIN_LEVELS[]; extern const size_t DOLPHIN_LEVEL_COUNT; #define BUTTHURT_MAX 14 #define BUTTHURT_MIN 0 +typedef enum { + DolphinFlagHappyMode = 1, +} DolphinFlags; + typedef struct DolphinState DolphinState; typedef struct { uint8_t icounter_daily_limit[DolphinAppMAX]; @@ -57,7 +57,3 @@ bool dolphin_state_is_levelup(uint32_t icounter); void dolphin_state_increase_level(DolphinState* dolphin_state); uint8_t dolphin_get_level(uint32_t icounter); - -#ifdef __cplusplus -} -#endif diff --git a/applications/services/expansion/expansion_protocol.h b/applications/services/expansion/expansion_protocol.h index 6ed818f82..a8d682330 100644 --- a/applications/services/expansion/expansion_protocol.h +++ b/applications/services/expansion/expansion_protocol.h @@ -64,8 +64,28 @@ typedef enum { * @brief Enumeration of suported control commands. */ typedef enum { - ExpansionFrameControlCommandStartRpc = 0x00, /**< Start an RPC session. */ - ExpansionFrameControlCommandStopRpc = 0x01, /**< Stop an open RPC session. */ + /** @brief Start an RPC session. + * + * Must only be used while the RPC session is NOT active. + */ + ExpansionFrameControlCommandStartRpc = 0x00, + /** @brief Stop an open RPC session. + * + * Must only be used while the RPC session IS active. + */ + ExpansionFrameControlCommandStopRpc = 0x01, + /** @brief Enable OTG (5V) on external GPIO. + * + * Must only be used while the RPC session is NOT active, + * otherwise OTG is to be controlled via RPC messages. + */ + ExpansionFrameControlCommandEnableOtg = 0x02, + /** @brief Disable OTG (5V) on external GPIO. + * + * Must only be used while the RPC session is NOT active, + * otherwise OTG is to be controlled via RPC messages. + */ + ExpansionFrameControlCommandDisableOtg = 0x03, } ExpansionFrameControlCommand; #pragma pack(push, 1) diff --git a/applications/services/expansion/expansion_worker.c b/applications/services/expansion/expansion_worker.c index edc1d09cc..2a64c9f7b 100644 --- a/applications/services/expansion/expansion_worker.c +++ b/applications/services/expansion/expansion_worker.c @@ -246,9 +246,18 @@ static bool expansion_worker_handle_state_connected( do { if(rx_frame->header.type == ExpansionFrameTypeControl) { - if(rx_frame->content.control.command != ExpansionFrameControlCommandStartRpc) break; - instance->state = ExpansionWorkerStateRpcActive; - if(!expansion_worker_rpc_session_open(instance)) break; + const uint8_t command = rx_frame->content.control.command; + if(command == ExpansionFrameControlCommandStartRpc) { + if(!expansion_worker_rpc_session_open(instance)) break; + instance->state = ExpansionWorkerStateRpcActive; + } else if(command == ExpansionFrameControlCommandEnableOtg) { + if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg(); + } else if(command == ExpansionFrameControlCommandDisableOtg) { + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); + } else { + break; + } + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; } else if(rx_frame->header.type == ExpansionFrameTypeHeartbeat) { @@ -280,9 +289,14 @@ static bool expansion_worker_handle_state_rpc_active( if(size_consumed != rx_frame->content.data.size) break; } else if(rx_frame->header.type == ExpansionFrameTypeControl) { - if(rx_frame->content.control.command != ExpansionFrameControlCommandStopRpc) break; - instance->state = ExpansionWorkerStateConnected; - expansion_worker_rpc_session_close(instance); + const uint8_t command = rx_frame->content.control.command; + if(command == ExpansionFrameControlCommandStopRpc) { + instance->state = ExpansionWorkerStateConnected; + expansion_worker_rpc_session_close(instance); + } else { + break; + } + if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; } else if(rx_frame->header.type == ExpansionFrameTypeStatus) { diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 28edf0b24..88c0c4f37 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -211,6 +211,70 @@ void elements_button_right(Canvas* canvas, const char* str) { canvas_invert_color(canvas); } +void elements_button_up(Canvas* canvas, const char* str) { + furi_check(canvas); + + const Icon* icon = &I_ButtonUp_7x4; + + const size_t button_height = 12; + const size_t vertical_offset = 3; + const size_t horizontal_offset = 3; + const size_t string_width = canvas_string_width(canvas, str); + const int32_t icon_h_offset = 3; + const int32_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset; + const int32_t icon_v_offset = icon_get_height(icon) + (int32_t)vertical_offset; + const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; + + const int32_t x = 0; + const int32_t y = 0 + button_height; + + int32_t line_x = x + button_width; + int32_t line_y = y - button_height; + + canvas_draw_box(canvas, x, line_y, button_width, button_height); + canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1); + canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2); + canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3); + + canvas_invert_color(canvas); + canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon); + canvas_draw_str( + canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str); + canvas_invert_color(canvas); +} + +void elements_button_down(Canvas* canvas, const char* str) { + furi_check(canvas); + + const Icon* icon = &I_ButtonDown_7x4; + + const size_t button_height = 12; + const size_t vertical_offset = 3; + const size_t horizontal_offset = 3; + const size_t string_width = canvas_string_width(canvas, str); + const int32_t icon_h_offset = 3; + const int32_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset; + const int32_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1; + const size_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; + + const int32_t x = canvas_width(canvas); + const int32_t y = button_height; + + int32_t line_x = x - button_width; + int32_t line_y = y - button_height; + + canvas_draw_box(canvas, line_x, line_y, button_width, button_height); + canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1); + canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2); + canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3); + + canvas_invert_color(canvas); + canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str); + canvas_draw_icon( + canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon); + canvas_invert_color(canvas); +} + void elements_button_center(Canvas* canvas, const char* str) { furi_check(canvas); diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index d736bf108..565d9c053 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -113,6 +113,28 @@ void elements_button_left(Canvas* canvas, const char* str); */ void elements_button_right(Canvas* canvas, const char* str); +/** + * @brief This function draws a button in the top left corner of the canvas with icon and string. + * + * The design and layout of the button is defined within this function. + * + * @param[in] canvas This is a pointer to the @c Canvas structure where the button will be drawn. + * @param[in] str This is a pointer to the character string that will be drawn within the button. + * + */ +void elements_button_up(Canvas* canvas, const char* str); + +/** + * @brief This function draws a button in the top right corner of the canvas with icon and string. + * + * The design and layout of the button is defined within this function. + * + * @param[in] canvas This is a pointer to the @c Canvas structure where the button will be drawn. + * @param[in] str This is a pointer to the character string that will be drawn within the button. + * + */ +void elements_button_down(Canvas* canvas, const char* str); + /** Draw button in center * * @param canvas Canvas instance diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index 433d631aa..3e23ee6c1 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -33,7 +33,7 @@ typedef struct { static const uint8_t keyboard_origin_x = 7; static const uint8_t keyboard_origin_y = 31; -static const uint8_t keyboard_row_count = 2; +static const int8_t keyboard_row_count = 2; static const uint8_t enter_symbol = '\r'; static const uint8_t backspace_symbol = '\b'; static const uint8_t max_drawable_bytes = 8; @@ -613,11 +613,11 @@ static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { } canvas_set_font(canvas, FontKeyboard); // Draw keyboard - for(uint8_t row = 0; row < keyboard_row_count; row++) { + for(int8_t row = 0; row < keyboard_row_count; row++) { const uint8_t column_count = byte_input_get_row_size(row); const ByteInputKey* keys = byte_input_get_row(row); - for(size_t column = 0; column < column_count; column++) { + for(uint8_t column = 0; column < column_count; column++) { bool selected = model->selected_row == row && model->selected_column == column; const Icon* icon = NULL; if(keys[column].value == enter_symbol) { diff --git a/applications/services/gui/modules/dialog_ex.c b/applications/services/gui/modules/dialog_ex.c index 7171f6892..d27de1242 100644 --- a/applications/services/gui/modules/dialog_ex.c +++ b/applications/services/gui/modules/dialog_ex.c @@ -10,7 +10,7 @@ struct DialogEx { }; typedef struct { - const char* text; + FuriString* text; uint8_t x; uint8_t y; Align horizontal; @@ -28,16 +28,15 @@ typedef struct { TextElement text; IconElement icon; - const char* left_text; - const char* center_text; - const char* right_text; + FuriString* left_text; + FuriString* center_text; + FuriString* right_text; } DialogExModel; static void dialog_ex_view_draw_callback(Canvas* canvas, void* _model) { DialogExModel* model = _model; // Prepare canvas - canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); if(model->icon.icon != NULL) { @@ -46,94 +45,94 @@ static void dialog_ex_view_draw_callback(Canvas* canvas, void* _model) { // Draw header canvas_set_font(canvas, FontPrimary); - if(model->header.text != NULL) { + if(furi_string_size(model->header.text)) { elements_multiline_text_aligned( canvas, model->header.x, model->header.y, model->header.horizontal, model->header.vertical, - model->header.text); + furi_string_get_cstr(model->header.text)); } // Draw text canvas_set_font(canvas, FontSecondary); - if(model->text.text != NULL) { + if(furi_string_size(model->text.text)) { elements_multiline_text_aligned( canvas, model->text.x, model->text.y, model->text.horizontal, model->text.vertical, - model->text.text); + furi_string_get_cstr(model->text.text)); } // Draw buttons - if(model->left_text != NULL) { - elements_button_left(canvas, model->left_text); + if(furi_string_size(model->left_text)) { + elements_button_left(canvas, furi_string_get_cstr(model->left_text)); } - if(model->center_text != NULL) { - elements_button_center(canvas, model->center_text); + if(furi_string_size(model->center_text)) { + elements_button_center(canvas, furi_string_get_cstr(model->center_text)); } - if(model->right_text != NULL) { - elements_button_right(canvas, model->right_text); + if(furi_string_size(model->right_text)) { + elements_button_right(canvas, furi_string_get_cstr(model->right_text)); } } static bool dialog_ex_view_input_callback(InputEvent* event, void* context) { DialogEx* dialog_ex = context; bool consumed = false; - const char* left_text = NULL; - const char* center_text = NULL; - const char* right_text = NULL; + bool left_text_present = false; + bool center_text_present = false; + bool right_text_present = false; with_view_model( dialog_ex->view, DialogExModel * model, { - left_text = model->left_text; - center_text = model->center_text; - right_text = model->right_text; + left_text_present = furi_string_size(model->left_text); + center_text_present = furi_string_size(model->center_text); + right_text_present = furi_string_size(model->right_text); }, - true); + false); if(dialog_ex->callback) { if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft && left_text != NULL) { + if(event->key == InputKeyLeft && left_text_present) { dialog_ex->callback(DialogExResultLeft, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyOk && center_text != NULL) { + } else if(event->key == InputKeyOk && center_text_present) { dialog_ex->callback(DialogExResultCenter, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyRight && right_text != NULL) { + } else if(event->key == InputKeyRight && right_text_present) { dialog_ex->callback(DialogExResultRight, dialog_ex->context); consumed = true; } } if(event->type == InputTypePress && dialog_ex->enable_extended_events) { - if(event->key == InputKeyLeft && left_text != NULL) { + if(event->key == InputKeyLeft && left_text_present) { dialog_ex->callback(DialogExPressLeft, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyOk && center_text != NULL) { + } else if(event->key == InputKeyOk && center_text_present) { dialog_ex->callback(DialogExPressCenter, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyRight && right_text != NULL) { + } else if(event->key == InputKeyRight && right_text_present) { dialog_ex->callback(DialogExPressRight, dialog_ex->context); consumed = true; } } if(event->type == InputTypeRelease && dialog_ex->enable_extended_events) { - if(event->key == InputKeyLeft && left_text != NULL) { + if(event->key == InputKeyLeft && left_text_present) { dialog_ex->callback(DialogExReleaseLeft, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyOk && center_text != NULL) { + } else if(event->key == InputKeyOk && center_text_present) { dialog_ex->callback(DialogExReleaseCenter, dialog_ex->context); consumed = true; - } else if(event->key == InputKeyRight && right_text != NULL) { + } else if(event->key == InputKeyRight && right_text_present) { dialog_ex->callback(DialogExReleaseRight, dialog_ex->context); consumed = true; } @@ -154,13 +153,13 @@ DialogEx* dialog_ex_alloc(void) { dialog_ex->view, DialogExModel * model, { - model->header.text = NULL; + model->header.text = furi_string_alloc(); model->header.x = 0; model->header.y = 0; model->header.horizontal = AlignLeft; model->header.vertical = AlignBottom; - model->text.text = NULL; + model->text.text = furi_string_alloc(); model->text.x = 0; model->text.y = 0; model->text.horizontal = AlignLeft; @@ -170,17 +169,28 @@ DialogEx* dialog_ex_alloc(void) { model->icon.y = 0; model->icon.icon = NULL; - model->left_text = NULL; - model->center_text = NULL; - model->right_text = NULL; + model->left_text = furi_string_alloc(); + model->center_text = furi_string_alloc(); + model->right_text = furi_string_alloc(); }, - true); + false); dialog_ex->enable_extended_events = false; return dialog_ex; } void dialog_ex_free(DialogEx* dialog_ex) { furi_check(dialog_ex); + with_view_model( + dialog_ex->view, + DialogExModel * model, + { + furi_string_free(model->header.text); + furi_string_free(model->text.text); + furi_string_free(model->left_text); + furi_string_free(model->center_text); + furi_string_free(model->right_text); + }, + false); view_free(dialog_ex->view); free(dialog_ex); } @@ -212,7 +222,7 @@ void dialog_ex_set_header( dialog_ex->view, DialogExModel * model, { - model->header.text = text; + furi_string_set(model->header.text, text ? text : ""); model->header.x = x; model->header.y = y; model->header.horizontal = horizontal; @@ -233,7 +243,7 @@ void dialog_ex_set_text( dialog_ex->view, DialogExModel * model, { - model->text.text = text; + furi_string_set(model->text.text, text ? text : ""); model->text.x = x; model->text.y = y; model->text.horizontal = horizontal; @@ -257,34 +267,44 @@ void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* i void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text) { furi_check(dialog_ex); - with_view_model(dialog_ex->view, DialogExModel * model, { model->left_text = text; }, true); + with_view_model( + dialog_ex->view, + DialogExModel * model, + { furi_string_set(model->left_text, text ? text : ""); }, + true); } void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text) { furi_check(dialog_ex); - with_view_model(dialog_ex->view, DialogExModel * model, { model->center_text = text; }, true); + with_view_model( + dialog_ex->view, + DialogExModel * model, + { furi_string_set(model->center_text, text ? text : ""); }, + true); } void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) { furi_check(dialog_ex); - with_view_model(dialog_ex->view, DialogExModel * model, { model->right_text = text; }, true); + with_view_model( + dialog_ex->view, + DialogExModel * model, + { furi_string_set(model->right_text, text ? text : ""); }, + true); } void dialog_ex_reset(DialogEx* dialog_ex) { furi_check(dialog_ex); - TextElement clean_text_el = { - .text = NULL, .x = 0, .y = 0, .horizontal = AlignLeft, .vertical = AlignLeft}; - IconElement clean_icon_el = {.icon = NULL, .x = 0, .y = 0}; with_view_model( dialog_ex->view, DialogExModel * model, { - model->header = clean_text_el; - model->text = clean_text_el; - model->icon = clean_icon_el; - model->left_text = NULL; - model->center_text = NULL; - model->right_text = NULL; + model->icon.icon = NULL; + furi_string_reset(model->header.text); + furi_string_reset(model->text.text); + + furi_string_reset(model->left_text); + furi_string_reset(model->center_text); + furi_string_reset(model->right_text); }, true); dialog_ex->context = NULL; diff --git a/applications/services/gui/modules/number_input.c b/applications/services/gui/modules/number_input.c index 058710d51..ec801d2dc 100644 --- a/applications/services/gui/modules/number_input.c +++ b/applications/services/gui/modules/number_input.c @@ -3,6 +3,7 @@ #include #include #include +#include struct NumberInput { View* view; @@ -163,7 +164,11 @@ static void number_input_handle_right(NumberInputModel* model) { } static bool is_number_too_large(NumberInputModel* model) { - int64_t value = strtoll(furi_string_get_cstr(model->text_buffer), NULL, 10); + int64_t value; + if(strint_to_int64(furi_string_get_cstr(model->text_buffer), NULL, &value, 10) != + StrintParseNoError) { + return true; + } if(value > (int64_t)model->max_value) { return true; } @@ -171,7 +176,11 @@ static bool is_number_too_large(NumberInputModel* model) { } static bool is_number_too_small(NumberInputModel* model) { - int64_t value = strtoll(furi_string_get_cstr(model->text_buffer), NULL, 10); + int64_t value; + if(strint_to_int64(furi_string_get_cstr(model->text_buffer), NULL, &value, 10) != + StrintParseNoError) { + return true; + } if(value < (int64_t)model->min_value) { return true; } diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 61ce9c0e5..050163256 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -103,10 +103,20 @@ static void loader_show_gui_error( if(status.value == LoaderStatusErrorUnknownApp && loader_find_external_application_by_name(name, NULL) != NULL) { // Special case for external apps - dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop); + const char* header = NULL; + const char* text = NULL; + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_sd_status(storage) == FSE_OK) { + header = "Update needed"; + text = "Update firmware\nto run this app"; + } else { + header = "SD card needed"; + text = "Install SD card\nto run this app"; + } + furi_record_close(RECORD_STORAGE); + dialog_message_set_header(message, header, 64, 3, AlignCenter, AlignTop); dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22); - dialog_message_set_text( - message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop); + dialog_message_set_text(message, text, 3, 26, AlignLeft, AlignTop); dialog_message_show(dialogs, message); } else if(status.value == LoaderStatusErrorUnknownApp) { loader_dialog_prepare_and_show(dialogs, &err_app_not_found); @@ -777,7 +787,7 @@ static bool loader_do_signal(Loader* loader, uint32_t signal, void* arg) { static bool loader_do_get_application_name(Loader* loader, FuriString* name) { if(loader_is_application_running(loader)) { - furi_string_set(name, furi_thread_get_name(loader->app.thread)); + furi_string_set(name, furi_thread_get_name(furi_thread_get_id(loader->app.thread))); return true; } diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index ef530556d..45acefa68 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -4,6 +4,7 @@ #include #include #include +#include #include static void loader_cli_print_usage(void) { @@ -92,18 +93,22 @@ static void loader_cli_close(Loader* loader) { static void loader_cli_signal(FuriString* args, Loader* loader) { uint32_t signal; - void* arg = NULL; + uint32_t arg = 0; + StrintParseError parse_err = 0; + char* args_cstr = (char*)furi_string_get_cstr(args); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &signal, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &arg, 16); - if(!sscanf(furi_string_get_cstr(args), "%lu %p", &signal, &arg)) { + if(parse_err) { printf("Signal must be a decimal number\r\n"); } else if(!loader_is_locked(loader)) { printf("No application is running\r\n"); } else { - const bool is_handled = loader_signal(loader, signal, arg); + const bool is_handled = loader_signal(loader, signal, (void*)arg); printf( "Signal %lu with argument 0x%p was %s\r\n", signal, - arg, + (void*)arg, is_handled ? "handled" : "ignored"); } } diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index 8d80e89bc..5d7c9229d 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -4,9 +4,11 @@ #include #include #include +#include #include "loader.h" #include "loader_menu.h" +#include "loader_menu_storage_i.h" #include #include @@ -171,6 +173,21 @@ static void loader_menu_applications_callback(void* context, uint32_t index) { static void loader_menu_settings_menu_callback(void* context, uint32_t index) { UNUSED(context); const char* name = FLIPPER_SETTINGS_APPS[index].name; + + // Workaround for SD format when app can't be opened + if(!strcmp(name, "Storage")) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FS_Error status = storage_sd_status(storage); + furi_record_close(RECORD_STORAGE); + // If SD card not ready, cannot be formatted, so we want loader to give + // normal error message, with function below + if(status != FSE_NOT_READY) { + // Attempt to launch the app, and if failed offer to format SD card + run_parallel(loader_menu_storage_settings, storage, 512); + return; + } + } + loader_menu_start(name); } diff --git a/applications/services/loader/loader_menu_storage.c b/applications/services/loader/loader_menu_storage.c new file mode 100644 index 000000000..8823ec208 --- /dev/null +++ b/applications/services/loader/loader_menu_storage.c @@ -0,0 +1,109 @@ +#include "loader_menu_storage_i.h" + +#include +#include +#include +#include +#include + +#include "loader.h" + +typedef enum { + FormatFlagContinue = (1 << 0), + FormatFlagCancel = (1 << 1), + FormatFlagAll = (FormatFlagContinue | FormatFlagCancel), +} FormatFlag; + +static void loader_menu_storage_settings_callback(DialogExResult result, void* context) { + FuriThread* thread = context; + furi_thread_flags_set( + furi_thread_get_id(thread), + result == DialogExResultRight ? FormatFlagContinue : FormatFlagCancel); +} + +static void loader_menu_storage_settings_back(void* context) { + FuriThread* thread = context; + furi_thread_flags_set(furi_thread_get_id(thread), FormatFlagCancel); +} + +int32_t loader_menu_storage_settings(void* context) { + Storage* storage = context; + Loader* loader = furi_record_open(RECORD_LOADER); + LoaderStatus result = loader_start(loader, "Storage", NULL, NULL); + furi_record_close(RECORD_LOADER); + + if(result != LoaderStatusOk) { + DialogEx* dialog_ex = dialog_ex_alloc(); + Gui* gui = furi_record_open(RECORD_GUI); + ViewHolder* view_holder = view_holder_alloc(); + view_holder_attach_to_gui(view_holder, gui); + view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex)); + dialog_ex_set_context(dialog_ex, furi_thread_get_current()); + dialog_ex_set_result_callback(dialog_ex, loader_menu_storage_settings_callback); + view_holder_set_back_callback( + view_holder, loader_menu_storage_settings_back, furi_thread_get_current()); + + dialog_ex_set_header(dialog_ex, "Update needed", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, + "Update firmware\n" + "to run this app\n" + "Can format SD\n" + "here if needed", + 3, + 17, + AlignLeft, + AlignTop); + dialog_ex_set_icon(dialog_ex, 83, 11, &I_WarningDolphinFlip_45x42); + dialog_ex_set_right_button_text(dialog_ex, "Format SD"); + + FormatFlag flag = furi_thread_flags_wait(FormatFlagAll, FuriFlagWaitAny, FuriWaitForever); + if(flag == FormatFlagContinue) { + char text[39]; + dialog_ex_set_header(dialog_ex, "Format SD Card?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Format"); + for(uint8_t counter = 5; counter > 0; counter--) { + snprintf(text, sizeof(text), "All data will be lost!\n%d presses left", counter); + dialog_ex_set_text(dialog_ex, text, 64, 12, AlignCenter, AlignTop); + flag = furi_thread_flags_wait(FormatFlagAll, FuriFlagWaitAny, FuriWaitForever); + if(flag != FormatFlagContinue) break; + + if(counter == 1) { + dialog_ex_set_header( + dialog_ex, "Formatting...", 70, 32, AlignCenter, AlignCenter); + dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 15, 20, &I_LoadingHourglass_24x24); + dialog_ex_set_left_button_text(dialog_ex, NULL); + dialog_ex_set_right_button_text(dialog_ex, NULL); + + FS_Error error = storage_sd_format(storage); + if(error != FSE_OK) { + dialog_ex_set_header( + dialog_ex, "Cannot Format SD Card", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); + dialog_ex_set_text( + dialog_ex, + storage_error_get_desc(error), + 64, + 32, + AlignCenter, + AlignCenter); + } else { + dialog_ex_set_icon(dialog_ex, 48, 6, &I_DolphinDone_80x58); + dialog_ex_set_header(dialog_ex, "Formatted", 5, 10, AlignLeft, AlignTop); + } + dialog_ex_set_left_button_text(dialog_ex, "Finish"); + furi_thread_flags_wait(FormatFlagAll, FuriFlagWaitAny, FuriWaitForever); + } + } + } + + view_holder_set_view(view_holder, NULL); + view_holder_free(view_holder); + furi_record_close(RECORD_GUI); + dialog_ex_free(dialog_ex); + } + return 0; +} diff --git a/applications/services/loader/loader_menu_storage_i.h b/applications/services/loader/loader_menu_storage_i.h new file mode 100644 index 000000000..c5b29bc26 --- /dev/null +++ b/applications/services/loader/loader_menu_storage_i.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +int32_t loader_menu_storage_settings(void* context); diff --git a/applications/services/rpc/rpc_app.h b/applications/services/rpc/rpc_app.h index 4ee5a24d3..aa6fd81cc 100644 --- a/applications/services/rpc/rpc_app.h +++ b/applications/services/rpc/rpc_app.h @@ -13,6 +13,7 @@ #pragma once #include "rpc.h" +#include "rpc_app_error_codes.h" #ifdef __cplusplus extern "C" { diff --git a/applications/services/rpc/rpc_app_error_codes.h b/applications/services/rpc/rpc_app_error_codes.h new file mode 100644 index 000000000..fc0edd4d2 --- /dev/null +++ b/applications/services/rpc/rpc_app_error_codes.h @@ -0,0 +1,11 @@ +#pragma once + +/** + * @brief Enumeration of possible error codes for application which can be started through rpc + */ +typedef enum { + RpcAppSystemErrorCodeNone, /** There are no errors */ + RpcAppSystemErrorCodeParseFile, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */ + RpcAppSystemErrorCodeRegionLock, /** Requested function is blocked by regional settings */ + RpcAppSystemErrorCodeInternalParse, /** Error in protocol parameters description, or some data in opened file are unsupported */ +} RpcAppSystemErrorCode; diff --git a/applications/services/rpc/rpc_gpio.c b/applications/services/rpc/rpc_gpio.c index 09e738505..9952bec38 100644 --- a/applications/services/rpc/rpc_gpio.c +++ b/applications/services/rpc/rpc_gpio.c @@ -2,6 +2,7 @@ #include "rpc_i.h" #include "gpio.pb.h" #include +#include #include static const GpioPin* rpc_pin_to_hal_pin(PB_Gpio_GpioPin rpc_pin) { @@ -188,6 +189,44 @@ void rpc_system_gpio_set_input_pull(const PB_Main* request, void* context) { free(response); } +void rpc_system_gpio_get_otg_mode(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_get_otg_mode_tag); + + RpcSession* session = context; + + const bool otg_enabled = furi_hal_power_is_otg_enabled(); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->which_content = PB_Main_gpio_get_otg_mode_response_tag; + response->content.gpio_get_otg_mode_response.mode = otg_enabled ? PB_Gpio_GpioOtgMode_ON : + PB_Gpio_GpioOtgMode_OFF; + + rpc_send_and_release(session, response); + + free(response); +} + +void rpc_system_gpio_set_otg_mode(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_set_otg_mode_tag); + + RpcSession* session = context; + + const PB_Gpio_GpioOtgMode mode = request->content.gpio_set_otg_mode.mode; + + if(mode == PB_Gpio_GpioOtgMode_OFF) { + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); + } else { + if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg(); + } + + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); +} + void* rpc_system_gpio_alloc(RpcSession* session) { furi_assert(session); @@ -212,5 +251,11 @@ void* rpc_system_gpio_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_gpio_set_input_pull; rpc_add_handler(session, PB_Main_gpio_set_input_pull_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_gpio_get_otg_mode; + rpc_add_handler(session, PB_Main_gpio_get_otg_mode_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_set_otg_mode; + rpc_add_handler(session, PB_Main_gpio_set_otg_mode_tag, &rpc_handler); + return NULL; } diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 1128b7ff0..79b91d947 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -226,9 +226,7 @@ static void rpc_system_storage_list_root(const PB_Main* request, void* context) response.content.storage_list_response.file[i].data = NULL; response.content.storage_list_response.file[i].size = 0; response.content.storage_list_response.file[i].type = PB_Storage_File_FileType_DIR; - char* str = malloc(strlen(hard_coded_dirs[i]) + 1); - strcpy(str, hard_coded_dirs[i]); - response.content.storage_list_response.file[i].name = str; + response.content.storage_list_response.file[i].name = strdup(hard_coded_dirs[i]); } rpc_send_and_release(session, &response); @@ -656,7 +654,7 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi rpc_system_storage_reset_state(rpc_storage, session, true); - bool backup_ok = lfs_backup_create( + bool backup_ok = int_backup_create( rpc_storage->api, request->content.storage_backup_create_request.archive_path); rpc_send_and_release_empty( @@ -676,7 +674,7 @@ static void rpc_system_storage_backup_restore_process(const PB_Main* request, vo rpc_system_storage_reset_state(rpc_storage, session, true); - bool backup_ok = lfs_backup_unpack( + bool backup_ok = int_backup_unpack( rpc_storage->api, request->content.storage_backup_restore_request.archive_path); rpc_send_and_release_empty( diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index 4af3458d8..1fd99d30a 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -531,7 +531,7 @@ FS_Error storage_sd_info(Storage* storage, SDInfo* info); */ FS_Error storage_sd_status(Storage* storage); -/******************* Internal LFS Functions *******************/ +/************ Internal Storage Backup/Restore ************/ typedef void (*StorageNameConverter)(FuriString*); diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 73300ef84..67ee3ecb4 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -3,8 +3,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -267,9 +268,8 @@ static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args File* file = storage_file_alloc(api); uint32_t buffer_size; - int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - - if(parsed_count != 1) { + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &buffer_size, 10) != + StrintParseNoError) { storage_cli_print_usage(); } else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t file_size = storage_file_size(file); @@ -307,9 +307,8 @@ static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args File* file = storage_file_alloc(api); uint32_t buffer_size; - int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); - - if(parsed_count != 1) { + if(strint_to_uint32(furi_string_get_cstr(args), NULL, &buffer_size, 10) != + StrintParseNoError) { storage_cli_print_usage(); } else { if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index c5f839ec6..3052e63b8 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -165,7 +165,7 @@ static DialogMessageButton about_screen_fw_version(DialogsApp* dialogs, DialogMe buffer = furi_string_alloc(); const Version* ver = furi_hal_version_get_firmware_version(); const BleGlueC2Info* c2_ver = NULL; -#ifdef SRV_BT +#if defined(SRV_BT) || defined(FAP_VERSION) c2_ver = ble_glue_get_c2_info(); #endif diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index 71b42f81a..db44c5304 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -68,11 +69,11 @@ DesktopSettingsApp* desktop_settings_app_alloc(void) { app->popup = popup_alloc(); app->submenu = submenu_alloc(); - app->dialog_ex = dialog_ex_alloc(); app->variable_item_list = variable_item_list_alloc(); app->pin_input_view = desktop_view_pin_input_alloc(); app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc(); app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc(); + app->dialog_ex = dialog_ex_alloc(); view_dispatcher_add_view( app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); @@ -82,8 +83,6 @@ DesktopSettingsApp* desktop_settings_app_alloc(void) { variable_item_list_get_view(app->variable_item_list)); view_dispatcher_add_view( app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup)); - view_dispatcher_add_view( - app->view_dispatcher, DesktopSettingsAppViewDialogEx, dialog_ex_get_view(app->dialog_ex)); view_dispatcher_add_view( app->view_dispatcher, DesktopSettingsAppViewIdPinInput, @@ -96,6 +95,8 @@ DesktopSettingsApp* desktop_settings_app_alloc(void) { app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2, desktop_settings_view_pin_setup_howto2_get_view(app->pin_setup_howto2_view)); + view_dispatcher_add_view( + app->view_dispatcher, DesktopSettingsAppViewDialogEx, dialog_ex_get_view(app->dialog_ex)); return app; } @@ -105,17 +106,17 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); - view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewDialogEx); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2); + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewDialogEx); variable_item_list_free(app->variable_item_list); - dialog_ex_free(app->dialog_ex); submenu_free(app->submenu); popup_free(app->popup); desktop_view_pin_input_free(app->pin_input_view); desktop_settings_view_pin_setup_howto_free(app->pin_setup_howto_view); desktop_settings_view_pin_setup_howto2_free(app->pin_setup_howto2_view); + dialog_ex_free(app->dialog_ex); // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index d989426e4..e763ba75a 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -22,10 +22,10 @@ typedef enum { DesktopSettingsAppViewMenu, DesktopSettingsAppViewVarItemList, DesktopSettingsAppViewIdPopup, - DesktopSettingsAppViewDialogEx, DesktopSettingsAppViewIdPinInput, DesktopSettingsAppViewIdPinSetupHowto, DesktopSettingsAppViewIdPinSetupHowto2, + DesktopSettingsAppViewDialogEx, } DesktopSettingsAppView; typedef enum { @@ -50,10 +50,10 @@ typedef struct { VariableItemList* variable_item_list; Submenu* submenu; Popup* popup; - DialogEx* dialog_ex; DesktopViewPinInput* pin_input_view; DesktopSettingsViewPinSetupHowto* pin_setup_howto_view; DesktopSettingsViewPinSetupHowto2* pin_setup_howto2_view; + DialogEx* dialog_ex; DesktopPinCode pincode_buffer; bool pincode_buffer_filled; diff --git a/applications/settings/desktop_settings/desktop_settings_custom_event.h b/applications/settings/desktop_settings/desktop_settings_custom_event.h new file mode 100644 index 000000000..19fe2df2e --- /dev/null +++ b/applications/settings/desktop_settings/desktop_settings_custom_event.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + // reserve 100 for button presses, submenu selections, etc. + DesktopSettingsCustomEventExit = 100, + DesktopSettingsCustomEventDone, + + DesktopSettingsCustomEvent1stPinEntered, + DesktopSettingsCustomEventPinsEqual, + DesktopSettingsCustomEventPinsDifferent, + + DesktopSettingsCustomEventSetPin, + DesktopSettingsCustomEventChangePin, + DesktopSettingsCustomEventDisablePin, +} DesktopSettingsCustomEvent; + +#ifdef __cplusplus +} +#endif diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h index 9987705c8..4df5e1f7c 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h @@ -13,3 +13,5 @@ ADD_SCENE(desktop_settings, pin_setup, PinSetup) ADD_SCENE(desktop_settings, pin_setup_howto, PinSetupHowto) ADD_SCENE(desktop_settings, pin_setup_howto2, PinSetupHowto2) ADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone) + +ADD_SCENE(desktop_settings, happy_mode, HappyMode) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_happy_mode.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_happy_mode.c new file mode 100644 index 000000000..31fcbfd2a --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_happy_mode.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +#include "desktop_settings_scene.h" +#include "../desktop_settings_app.h" +#include "../desktop_settings_custom_event.h" + +static void desktop_settings_scene_happy_mode_done_callback(DialogExResult result, void* context) { + DesktopSettingsApp* app = context; + DolphinSettings settings; + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + dolphin_get_settings(dolphin, &settings); + settings.happy_mode = (result == DialogExResultRight); + dolphin_set_settings(dolphin, &settings); + furi_record_close(RECORD_DOLPHIN); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit); +} + +void desktop_settings_scene_happy_mode_on_enter(void* context) { + DesktopSettingsApp* app = context; + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + DolphinSettings settings; + dolphin_get_settings(dolphin, &settings); + furi_record_close(RECORD_DOLPHIN); + + dialog_ex_set_header(app->dialog_ex, "Happy Mode", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text( + app->dialog_ex, + "I will never get angry at you\nfor not spending time with me\nas long as this mode is enabled", + 64, + 30, + AlignCenter, + AlignCenter); + dialog_ex_set_left_button_text(app->dialog_ex, settings.happy_mode ? "Disable" : "Go back"); + dialog_ex_set_right_button_text( + app->dialog_ex, settings.happy_mode ? "Keep enabled" : "Enable"); + dialog_ex_set_result_callback(app->dialog_ex, desktop_settings_scene_happy_mode_done_callback); + dialog_ex_set_context(app->dialog_ex, app); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewDialogEx); +} + +bool desktop_settings_scene_happy_mode_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopSettingsCustomEventExit: + scene_manager_previous_scene(app->scene_manager); + consumed = true; + break; + default: + furi_crash(); + } + } + return consumed; +} + +void desktop_settings_scene_happy_mode_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c index 1e6416531..0d1543359 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -3,15 +3,12 @@ #include #include #include "../desktop_settings_app.h" +#include "../desktop_settings_custom_event.h" #include #include #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" -#define SCENE_EVENT_EXIT (0U) -#define SCENE_EVENT_PINS_EQUAL (1U) -#define SCENE_EVENT_PINS_DIFFERENT (2U) - static void pin_auth_done_callback(const DesktopPinCode* pin_code, void* context) { furi_assert(pin_code); furi_assert(context); @@ -20,15 +17,17 @@ static void pin_auth_done_callback(const DesktopPinCode* pin_code, void* context app->pincode_buffer = *pin_code; if(desktop_pin_code_check(pin_code)) { - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); + view_dispatcher_send_custom_event( + app->view_dispatcher, DesktopSettingsCustomEventPinsEqual); } else { - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); + view_dispatcher_send_custom_event( + app->view_dispatcher, DesktopSettingsCustomEventPinsDifferent); } } static void pin_auth_back_callback(void* context) { DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit); } void desktop_settings_scene_pin_auth_on_enter(void* context) { @@ -54,13 +53,13 @@ bool desktop_settings_scene_pin_auth_on_event(void* context, SceneManagerEvent e if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_EVENT_PINS_DIFFERENT: + case DesktopSettingsCustomEventPinsDifferent: scene_manager_set_scene_state( app->scene_manager, DesktopSettingsAppScenePinError, SCENE_STATE_PIN_ERROR_WRONG); scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError); consumed = true; break; - case SCENE_EVENT_PINS_EQUAL: { + case DesktopSettingsCustomEventPinsEqual: { uint32_t state = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinAuth); if(state == SCENE_STATE_PIN_AUTH_CHANGE_PIN) { @@ -73,7 +72,7 @@ bool desktop_settings_scene_pin_auth_on_event(void* context, SceneManagerEvent e consumed = true; break; } - case SCENE_EVENT_EXIT: + case DesktopSettingsCustomEventExit: scene_manager_search_and_switch_to_previous_scene( app->scene_manager, DesktopSettingsAppScenePinMenu); consumed = true; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index abcce66da..a97ce8aaa 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -3,15 +3,14 @@ #include #include "../desktop_settings_app.h" +#include "../desktop_settings_custom_event.h" #include #include "desktop_settings_scene.h" -#define SCENE_EVENT_EXIT (0U) - static void pin_disable_back_callback(void* context) { furi_assert(context); DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit); } void desktop_settings_scene_pin_disable_on_enter(void* context) { @@ -35,7 +34,7 @@ bool desktop_settings_scene_pin_disable_on_event(void* context, SceneManagerEven if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_EVENT_EXIT: + case DesktopSettingsCustomEventExit: scene_manager_search_and_switch_to_previous_scene( app->scene_manager, DesktopSettingsAppScenePinMenu); consumed = true; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c index 711683c3f..695f431a0 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c @@ -8,20 +8,19 @@ #include "desktop_settings_scene_i.h" #include #include "../desktop_settings_app.h" - -#define SCENE_EVENT_EXIT (0U) +#include "../desktop_settings_custom_event.h" static void pin_error_back_callback(void* context) { furi_assert(context); DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit); } static void pin_error_done_callback(const DesktopPinCode* pin_code, void* context) { UNUSED(pin_code); furi_assert(context); DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit); } void desktop_settings_scene_pin_error_on_enter(void* context) { @@ -55,7 +54,7 @@ bool desktop_settings_scene_pin_error_on_event(void* context, SceneManagerEvent if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_EVENT_EXIT: + case DesktopSettingsCustomEventExit: scene_manager_previous_scene(app->scene_manager); consumed = true; break; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index e0c66cb28..cf8436dc9 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -4,10 +4,7 @@ #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" - -#define SCENE_EVENT_SET_PIN 0 -#define SCENE_EVENT_CHANGE_PIN 1 -#define SCENE_EVENT_DISABLE_PIN 2 +#include "../desktop_settings_custom_event.h" static void desktop_settings_scene_pin_menu_submenu_callback(void* context, uint32_t index) { DesktopSettingsApp* app = context; @@ -23,7 +20,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { submenu_add_item( submenu, "Set PIN", - SCENE_EVENT_SET_PIN, + DesktopSettingsCustomEventSetPin, desktop_settings_scene_pin_menu_submenu_callback, app); @@ -31,14 +28,14 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { submenu_add_item( submenu, "Change PIN", - SCENE_EVENT_CHANGE_PIN, + DesktopSettingsCustomEventChangePin, desktop_settings_scene_pin_menu_submenu_callback, app); submenu_add_item( submenu, "Remove PIN", - SCENE_EVENT_DISABLE_PIN, + DesktopSettingsCustomEventDisablePin, desktop_settings_scene_pin_menu_submenu_callback, app); } @@ -54,11 +51,11 @@ bool desktop_settings_scene_pin_menu_on_event(void* context, SceneManagerEvent e if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_EVENT_SET_PIN: + case DesktopSettingsCustomEventSetPin: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); consumed = true; break; - case SCENE_EVENT_CHANGE_PIN: + case DesktopSettingsCustomEventChangePin: scene_manager_set_scene_state( app->scene_manager, DesktopSettingsAppScenePinAuth, @@ -66,7 +63,7 @@ bool desktop_settings_scene_pin_menu_on_event(void* context, SceneManagerEvent e scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth); consumed = true; break; - case SCENE_EVENT_DISABLE_PIN: + case DesktopSettingsCustomEventDisablePin: scene_manager_set_scene_state( app->scene_manager, DesktopSettingsAppScenePinAuth, SCENE_STATE_PIN_AUTH_DISABLE); scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c index 08f5fcb3f..95f50d2e1 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c @@ -8,11 +8,7 @@ #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" #include - -#define SCENE_EVENT_EXIT (0U) -#define SCENE_EVENT_1ST_PIN_ENTERED (1U) -#define SCENE_EVENT_PINS_EQUAL (2U) -#define SCENE_EVENT_PINS_DIFFERENT (3U) +#include "../desktop_settings_custom_event.h" static void pin_setup_done_callback(const DesktopPinCode* pin_code, void* context) { furi_assert(pin_code); @@ -22,20 +18,23 @@ static void pin_setup_done_callback(const DesktopPinCode* pin_code, void* contex if(!app->pincode_buffer_filled) { app->pincode_buffer = *pin_code; app->pincode_buffer_filled = true; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED); + view_dispatcher_send_custom_event( + app->view_dispatcher, DesktopSettingsCustomEvent1stPinEntered); } else { app->pincode_buffer_filled = false; if(desktop_pin_code_is_equal(&app->pincode_buffer, pin_code)) { - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); + view_dispatcher_send_custom_event( + app->view_dispatcher, DesktopSettingsCustomEventPinsEqual); } else { - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); + view_dispatcher_send_custom_event( + app->view_dispatcher, DesktopSettingsCustomEventPinsDifferent); } } } static void pin_setup_back_callback(void* context) { DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit); } void desktop_settings_scene_pin_setup_on_enter(void* context) { @@ -60,7 +59,7 @@ bool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_EVENT_1ST_PIN_ENTERED: + case DesktopSettingsCustomEvent1stPinEntered: desktop_view_pin_input_set_label_button(app->pin_input_view, "OK"); desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL); desktop_view_pin_input_set_label_secondary( @@ -69,7 +68,7 @@ bool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent desktop_view_pin_input_unlock_input(app->pin_input_view); consumed = true; break; - case SCENE_EVENT_PINS_DIFFERENT: + case DesktopSettingsCustomEventPinsDifferent: scene_manager_set_scene_state( app->scene_manager, DesktopSettingsAppScenePinError, @@ -77,11 +76,11 @@ bool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError); consumed = true; break; - case SCENE_EVENT_PINS_EQUAL: + case DesktopSettingsCustomEventPinsEqual: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto2); consumed = true; break; - case SCENE_EVENT_EXIT: { + case DesktopSettingsCustomEventExit: { uint32_t scene_found; scene_found = scene_manager_search_and_switch_to_previous_scene( app->scene_manager, DesktopSettingsAppScenePinMenu); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index aa3d2214e..ad5784b55 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -5,18 +5,17 @@ #include #include "../desktop_settings_app.h" +#include "../desktop_settings_custom_event.h" #include #include #include "desktop_settings_scene.h" -#define SCENE_EVENT_DONE (0U) - static void pin_setup_done_callback(const DesktopPinCode* pin_code, void* context) { furi_assert(pin_code); furi_assert(context); DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_DONE); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventDone); } void desktop_settings_scene_pin_setup_done_on_enter(void* context) { @@ -48,7 +47,7 @@ bool desktop_settings_scene_pin_setup_done_on_event(void* context, SceneManagerE if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_EVENT_DONE: { + case DesktopSettingsCustomEventDone: { bool scene_found = false; scene_found = scene_manager_search_and_switch_to_previous_scene( app->scene_manager, DesktopSettingsAppScenePinMenu); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c index 31eec3871..69cdcc074 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c @@ -5,12 +5,11 @@ #include "desktop_settings_scene.h" #include "../desktop_settings_app.h" #include "../views/desktop_settings_view_pin_setup_howto.h" - -#define SCENE_EXIT_EVENT (0U) +#include "../desktop_settings_custom_event.h" static void desktop_settings_scene_pin_lock_done_callback(void* context) { DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit); } void desktop_settings_scene_pin_setup_howto_on_enter(void* context) { @@ -27,7 +26,7 @@ bool desktop_settings_scene_pin_setup_howto_on_event(void* context, SceneManager if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_EXIT_EVENT: + case DesktopSettingsCustomEventExit: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetup); consumed = true; break; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c index efa39f1f0..c67ab4c79 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c @@ -4,18 +4,16 @@ #include "desktop_settings_scene.h" #include "../desktop_settings_app.h" #include "../views/desktop_settings_view_pin_setup_howto2.h" - -#define SCENE_EXIT_EVENT (0U) -#define SCENE_DONE_EVENT (1U) +#include "../desktop_settings_custom_event.h" static void desktop_settings_scene_pin_setup_howto2_done_callback(void* context) { DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_DONE_EVENT); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventDone); } static void desktop_settings_scene_pin_setup_howto2_exit_callback(void* context) { DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); + view_dispatcher_send_custom_event(app->view_dispatcher, DesktopSettingsCustomEventExit); } void desktop_settings_scene_pin_setup_howto2_on_enter(void* context) { @@ -35,12 +33,12 @@ bool desktop_settings_scene_pin_setup_howto2_on_event(void* context, SceneManage if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_DONE_EVENT: { + case DesktopSettingsCustomEventDone: { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupDone); consumed = true; break; } - case SCENE_EXIT_EVENT: { + case DesktopSettingsCustomEventExit: { bool scene_found = false; scene_found = scene_manager_search_and_switch_to_previous_scene( app->scene_manager, DesktopSettingsAppScenePinMenu); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 0edd48a58..16e32e324 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -11,6 +11,7 @@ typedef enum { DesktopSettingsAutoLockDelay, DesktopSettingsAutoLockPin, DesktopSettingsClockDisplay, + DesktopSettingsHappyMode, } DesktopSettingsEntry; #define AUTO_LOCK_DELAY_COUNT 9 @@ -104,7 +105,7 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_list, "Show Clock", CLOCK_ENABLE_COUNT, - desktop_settings_scene_start_clock_enable_changed, // + desktop_settings_scene_start_clock_enable_changed, app); value_index = @@ -112,6 +113,8 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, clock_enable_text[value_index]); + variable_item_list_add(variable_item_list, "Happy Mode", 1, NULL, NULL); + variable_item_list_set_enter_callback( variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); @@ -142,6 +145,10 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even consumed = true; break; + case DesktopSettingsHappyMode: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneHappyMode); + break; + default: break; } diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index d9c15c7c8..fb3b7575b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -1,4 +1,5 @@ #include "../power_settings_app.h" +#include void power_settings_scene_power_off_dialog_callback(DialogExResult result, void* context) { furi_assert(context); @@ -9,11 +10,23 @@ void power_settings_scene_power_off_dialog_callback(DialogExResult result, void* void power_settings_scene_power_off_on_enter(void* context) { PowerSettingsApp* app = context; DialogEx* dialog = app->dialog; + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + DolphinSettings settings; + dolphin_get_settings(dolphin, &settings); + furi_record_close(RECORD_DOLPHIN); - dialog_ex_set_header(dialog, "Turn Off Device?", 64, 0, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog, " I will be\nwaiting for\n you here...", 78, 14, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog, 14, 10, &I_dolph_cry_49x54); + dialog_ex_set_header( + dialog, + "Turn Off Device?", + 64, + settings.happy_mode ? 32 : 0, + AlignCenter, + settings.happy_mode ? AlignCenter : AlignTop); + if(!settings.happy_mode) { + dialog_ex_set_text( + dialog, " I will be\nwaiting for\n you here...", 78, 14, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog, 14, 10, &I_dolph_cry_49x54); + } dialog_ex_set_left_button_text(dialog, "Battery"); dialog_ex_set_right_button_text(dialog, "Power Off"); dialog_ex_set_result_callback(dialog, power_settings_scene_power_off_dialog_callback); diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index 0b734c0f4..56a16bd9d 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include typedef void (*cmd_handler)(FuriString* args); @@ -35,7 +35,7 @@ static void updater_cli_install(FuriString* manifest_path) { static void updater_cli_backup(FuriString* args) { printf("Backup /int to '%s'\r\n", furi_string_get_cstr(args)); Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = lfs_backup_create(storage, furi_string_get_cstr(args)); + bool success = int_backup_create(storage, furi_string_get_cstr(args)); furi_record_close(RECORD_STORAGE); printf("Result: %s\r\n", success ? "OK" : "FAIL"); } @@ -43,7 +43,7 @@ static void updater_cli_backup(FuriString* args) { static void updater_cli_restore(FuriString* args) { printf("Restore /int from '%s'\r\n", furi_string_get_cstr(args)); Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = lfs_backup_unpack(storage, furi_string_get_cstr(args)); + bool success = int_backup_unpack(storage, furi_string_get_cstr(args)); furi_record_close(RECORD_STORAGE); printf("Result: %s\r\n", success ? "OK" : "FAIL"); } diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c index eaddbeeab..c0349c1cf 100644 --- a/applications/system/updater/util/update_task.c +++ b/applications/system/updater/util/update_task.c @@ -22,8 +22,8 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageRadioInstall] = "Installing radio FW", [UpdateTaskStageRadioBusy] = "Core 2 busy", [UpdateTaskStageOBValidation] = "Validating opt. bytes", - [UpdateTaskStageLfsBackup] = "Backing up LFS", - [UpdateTaskStageLfsRestore] = "Restoring LFS", + [UpdateTaskStageIntBackup] = "Backing up configuration", + [UpdateTaskStageIntRestore] = "Restoring configuration", [UpdateTaskStageResourcesFileCleanup] = "Cleaning up files", [UpdateTaskStageResourcesDirCleanup] = "Cleaning up directories", [UpdateTaskStageResourcesFileUnpack] = "Extracting resources", @@ -82,7 +82,7 @@ static const struct { }, #ifndef FURI_RAM_EXEC { - .stage = UpdateTaskStageLfsBackup, + .stage = UpdateTaskStageIntBackup, .percent_min = 0, .percent_max = 100, .descr = "FS R/W error", @@ -193,10 +193,10 @@ static const struct { #endif #ifndef FURI_RAM_EXEC { - .stage = UpdateTaskStageLfsRestore, + .stage = UpdateTaskStageIntRestore, .percent_min = 0, .percent_max = 100, - .descr = "LFS I/O error", + .descr = "SD card I/O error", }, { .stage = UpdateTaskStageResourcesFileCleanup, @@ -245,7 +245,7 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = { [UpdateTaskStageProgress] = STAGE_DEF(UpdateTaskStageGroupMisc, 0), [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 45), - [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), + [UpdateTaskStageIntBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 25), @@ -259,7 +259,7 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = { [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 100), [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 20), - [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 5), + [UpdateTaskStageIntRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 5), [UpdateTaskStageResourcesFileCleanup] = STAGE_DEF(UpdateTaskStageGroupResources, 100), [UpdateTaskStageResourcesDirCleanup] = STAGE_DEF(UpdateTaskStageGroupResources, 50), diff --git a/applications/system/updater/util/update_task.h b/applications/system/updater/util/update_task.h index 52bdfdbd2..c346c55fa 100644 --- a/applications/system/updater/util/update_task.h +++ b/applications/system/updater/util/update_task.h @@ -16,7 +16,7 @@ typedef enum { UpdateTaskStageProgress = 0, UpdateTaskStageReadManifest, - UpdateTaskStageLfsBackup, + UpdateTaskStageIntBackup, UpdateTaskStageRadioImageValidate, UpdateTaskStageRadioErase, @@ -30,7 +30,7 @@ typedef enum { UpdateTaskStageFlashWrite, UpdateTaskStageFlashValidate, - UpdateTaskStageLfsRestore, + UpdateTaskStageIntRestore, UpdateTaskStageResourcesFileCleanup, UpdateTaskStageResourcesDirCleanup, UpdateTaskStageResourcesFileUnpack, diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index bcf1fd141..0081de21d 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -23,14 +23,14 @@ static bool update_task_pre_update(UpdateTask* update_task) { backup_file_path = furi_string_alloc(); path_concat( furi_string_get_cstr(update_task->update_path), - LFS_BACKUP_DEFAULT_FILENAME, + INT_BACKUP_DEFAULT_FILENAME, backup_file_path); - update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0); + update_task_set_progress(update_task, UpdateTaskStageIntBackup, 0); /* to avoid bootloops */ furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); if((success = - lfs_backup_create(update_task->storage, furi_string_get_cstr(backup_file_path)))) { + int_backup_create(update_task->storage, furi_string_get_cstr(backup_file_path)))) { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate); } @@ -131,12 +131,12 @@ static bool update_task_post_update(UpdateTask* update_task) { do { path_concat( furi_string_get_cstr(update_task->update_path), - LFS_BACKUP_DEFAULT_FILENAME, + INT_BACKUP_DEFAULT_FILENAME, file_path); - update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); + update_task_set_progress(update_task, UpdateTaskStageIntRestore, 0); - CHECK_RESULT(lfs_backup_unpack(update_task->storage, furi_string_get_cstr(file_path))); + CHECK_RESULT(int_backup_unpack(update_task->storage, furi_string_get_cstr(file_path))); // Fix flags for production / development #ifdef FURI_DEBUG diff --git a/applications/system/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c index 6ff00959d..f587f7447 100644 --- a/applications/system/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -341,7 +341,7 @@ int32_t update_task_worker_flash_writer(void* context) { } furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); - // Format LFS before restoring backup on next boot + // Clean up /int before restoring backup on next boot furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal); update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); diff --git a/assets/protobuf b/assets/protobuf index 8a799f3b9..b7d588169 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 8a799f3b9a130547e1f29e8120cfb8c0796d6011 +Subproject commit b7d5881690298de836df6305d4c71f96d5d34c61 diff --git a/documentation/ExpansionModules.md b/documentation/ExpansionModules.md index 470564e57..fd9703adc 100644 --- a/documentation/ExpansionModules.md +++ b/documentation/ExpansionModules.md @@ -73,7 +73,7 @@ If the requested baud rate is supported by the host, it SHALL respond with a STA ### Control frame -CONTROL frames are used to control various aspects of the communication. As of now, the sole purpose of CONTROL frames is to start and stop the RPC session. +CONTROL frames are used to control various aspects of the communication and enable/disable various device features. | Header (1 byte) | Contents (1 byte) | Checksum (1 byte) | |-----------------|-------------------|-------------------| @@ -81,10 +81,18 @@ CONTROL frames are used to control various aspects of the communication. As of n The `Command` field SHALL have one of the followind values: -| Command | Meaning | -|---------|-------------------| -| 0x00 | Start RPC session | -| 0x01 | Stop RPC session | +| Command | Meaning | Note | +|---------|--------------------------|:----:| +| 0x00 | Start RPC session | 1 | +| 0x01 | Stop RPC session | 2 | +| 0x02 | Enable OTG (5V) on GPIO | 3 | +| 0x03 | Disable OTG (5V) on GPIO | 3 | + +Notes: + +1. Must only be used while the RPC session NOT active. +2. Must only be used while the RPC session IS active. +3. See 1, otherwise OTG is to be controlled via RPC messages. ### Data frame diff --git a/documentation/OTA.md b/documentation/OTA.md index 0456eab1f..9783a7047 100644 --- a/documentation/OTA.md +++ b/documentation/OTA.md @@ -83,7 +83,7 @@ Even if something goes wrong, updater allows you to retry failed operations and | | | **50** | Package has mismatching HW target | | | | **60** | Missing DFU file | | | | **80** | Missing radio firmware file | -| Backing up LFS | **2** | **0-100** | FS read/write error | +| Backing up configuration| **2** | **0-100** | FS read/write error | | Checking radio FW | **3** | **0-99** | Error reading radio firmware file | | | | **100** | CRC mismatch | | Uninstalling radio FW | **4** | **0** | SHCI Delete command error | @@ -101,7 +101,7 @@ Even if something goes wrong, updater allows you to retry failed operations and | | | **99-100** | Corrupted DFU file | | Writing flash | **10** | **0-100** | Block read/write error | | Validating flash | **11** | **0-100** | Block read/write error | -| Restoring LFS | **12** | **0-100** | FS read/write error | +| Restoring configuration | **12** | **0-100** | FS read/write error | | Updating resources | **13-15** | **0-100** | SD card read/write error | ## Building update packages diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index f89c4e77f..19b28a500 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -48,7 +48,9 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { portYIELD_FROM_ISR(yield); } } else { + vTaskSuspendAll(); rflags = xEventGroupSetBits(hEventGroup, (EventBits_t)flags); + (void)xTaskResumeAll(); } /* Return event flags after setting */ diff --git a/furi/core/event_flag.h b/furi/core/event_flag.h index fbc3bb004..6408f47be 100644 --- a/furi/core/event_flag.h +++ b/furi/core/event_flag.h @@ -26,10 +26,15 @@ void furi_event_flag_free(FuriEventFlag* instance); /** Set flags * - * @param instance pointer to FuriEventFlag - * @param[in] flags The flags + * @warning result of this function can be flags that you've just asked to + * set or not if someone was waiting for them and asked to clear it. + * It is highly recommended to read this function and + * xEventGroupSetBits source code. * - * @return Resulting flags or error (FuriStatus) + * @param instance pointer to FuriEventFlag + * @param[in] flags The flags to set + * + * @return Resulting flags(see warning) or error (FuriStatus) */ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags); diff --git a/furi/core/event_loop.c b/furi/core/event_loop.c index 2a6cd51d3..f4f008a71 100644 --- a/furi/core/event_loop.c +++ b/furi/core/event_loop.c @@ -71,9 +71,9 @@ FuriEventLoop* furi_event_loop_alloc(void) { PendingQueue_init(instance->pending_queue); // Clear notification state and value - xTaskNotifyStateClearIndexed(instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX); - ulTaskNotifyValueClearIndexed( - instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0xFFFFFFFF); + TaskHandle_t task = (TaskHandle_t)instance->thread_id; + xTaskNotifyStateClearIndexed(task, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX); + ulTaskNotifyValueClearIndexed(task, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, 0xFFFFFFFF); return instance; } @@ -178,7 +178,7 @@ static void furi_event_loop_process_waiting_list(FuriEventLoop* instance) { static void furi_event_loop_restore_flags(FuriEventLoop* instance, uint32_t flags) { if(flags) { xTaskNotifyIndexed( - instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, flags, eSetBits); + (TaskHandle_t)instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, flags, eSetBits); } } @@ -186,10 +186,11 @@ void furi_event_loop_run(FuriEventLoop* instance) { furi_check(instance); furi_check(instance->thread_id == furi_thread_get_current_id()); + FuriThread* thread = furi_thread_get_current(); + // Set the default signal callback if none was previously set - if(furi_thread_get_signal_callback(instance->thread_id) == NULL) { - furi_thread_set_signal_callback( - instance->thread_id, furi_event_loop_signal_callback, instance); + if(furi_thread_get_signal_callback(thread) == NULL) { + furi_thread_set_signal_callback(thread, furi_event_loop_signal_callback, instance); } furi_event_loop_init_tick(instance); @@ -233,8 +234,8 @@ void furi_event_loop_run(FuriEventLoop* instance) { } // Disable the default signal callback - if(furi_thread_get_signal_callback(instance->thread_id) == furi_event_loop_signal_callback) { - furi_thread_set_signal_callback(instance->thread_id, NULL, NULL); + if(furi_thread_get_signal_callback(thread) == furi_event_loop_signal_callback) { + furi_thread_set_signal_callback(thread, NULL, NULL); } } @@ -242,7 +243,10 @@ void furi_event_loop_stop(FuriEventLoop* instance) { furi_check(instance); xTaskNotifyIndexed( - instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagStop, eSetBits); + (TaskHandle_t)instance->thread_id, + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, + FuriEventLoopFlagStop, + eSetBits); } /* @@ -265,7 +269,10 @@ void furi_event_loop_pend_callback( PendingQueue_push_front(instance->pending_queue, item); xTaskNotifyIndexed( - instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagPending, eSetBits); + (TaskHandle_t)instance->thread_id, + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, + FuriEventLoopFlagPending, + eSetBits); } /* @@ -473,7 +480,10 @@ static void furi_event_loop_item_notify(FuriEventLoopItem* instance) { FURI_CRITICAL_EXIT(); xTaskNotifyIndexed( - owner->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagEvent, eSetBits); + (TaskHandle_t)owner->thread_id, + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, + FuriEventLoopFlagEvent, + eSetBits); } static bool furi_event_loop_item_is_waiting(FuriEventLoopItem* instance) { diff --git a/furi/core/event_loop_timer.c b/furi/core/event_loop_timer.c index 03b6c5132..f4a79bb4f 100644 --- a/furi/core/event_loop_timer.c +++ b/furi/core/event_loop_timer.c @@ -65,7 +65,10 @@ static void furi_event_loop_timer_enqueue_request( TimerQueue_push_back(instance->timer_queue, timer); xTaskNotifyIndexed( - instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, FuriEventLoopFlagTimer, eSetBits); + (TaskHandle_t)instance->thread_id, + FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, + FuriEventLoopFlagTimer, + eSetBits); } /* diff --git a/furi/core/thread.c b/furi/core/thread.c index becadbc64..6a4c0763b 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -57,7 +57,7 @@ static void furi_thread_body(void* context) { furi_thread_set_state(thread, FuriThreadStateRunning); if(thread->heap_trace_enabled == true) { - memmgr_heap_enable_thread_trace(thread); + memmgr_heap_enable_thread_trace((FuriThreadId)thread); } thread->ret = thread->callback(thread->context); @@ -66,14 +66,14 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); - thread->heap_size = memmgr_heap_get_thread_memory(thread); + thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)thread); furi_log_print_format( thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, TAG, "%s allocation balance: %zu", thread->name ? thread->name : "Thread", thread->heap_size); - memmgr_heap_disable_thread_trace(thread); + memmgr_heap_disable_thread_trace((FuriThreadId)thread); } furi_check(thread->state == FuriThreadStateRunning); @@ -235,7 +235,7 @@ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) { FuriThreadPriority furi_thread_get_priority(FuriThread* thread) { furi_check(thread); - TaskHandle_t hTask = furi_thread_get_id(thread); + TaskHandle_t hTask = (TaskHandle_t)thread; return (FuriThreadPriority)uxTaskPriorityGet(hTask); } @@ -350,7 +350,7 @@ bool furi_thread_join(FuriThread* thread) { FuriThreadId furi_thread_get_id(FuriThread* thread) { furi_check(thread); - return thread; + return (FuriThreadId)thread; } void furi_thread_enable_heap_trace(FuriThread* thread) { @@ -378,7 +378,7 @@ int32_t furi_thread_get_return_code(FuriThread* thread) { } FuriThreadId furi_thread_get_current_id(void) { - return xTaskGetCurrentTaskHandle(); + return (FuriThreadId)xTaskGetCurrentTaskHandle(); } FuriThread* furi_thread_get_current(void) { @@ -584,15 +584,16 @@ bool furi_thread_enumerate(FuriThreadList* thread_list) { FuriThreadListItem* item = furi_thread_list_get_or_insert(thread_list, (FuriThread*)task[i].xHandle); - item->thread = (FuriThreadId)task[i].xHandle; - item->app_id = furi_thread_get_appid(item->thread); + FuriThreadId thread_id = (FuriThreadId)task[i].xHandle; + item->thread = (FuriThread*)thread_id; + item->app_id = furi_thread_get_appid(thread_id); item->name = task[i].pcTaskName; item->priority = task[i].uxCurrentPriority; item->stack_address = (uint32_t)tcb->pxStack; - size_t thread_heap = memmgr_heap_get_thread_memory(item->thread); + size_t thread_heap = memmgr_heap_get_thread_memory(thread_id); item->heap = thread_heap == MEMMGR_HEAP_UNKNOWN ? 0u : thread_heap; item->stack_size = (tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t); - item->stack_min_free = furi_thread_get_stack_space(item->thread); + item->stack_min_free = furi_thread_get_stack_space(thread_id); item->state = furi_thread_state_name(task[i].eCurrentState); item->counter_previous = item->counter_current; item->counter_current = task[i].ulRunTimeCounter; diff --git a/furi/core/timer.c b/furi/core/timer.c index 3f5920aed..5239a6f7c 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -4,6 +4,7 @@ #include "kernel.h" #include +#include #include const char* current_timer_name = NULL; @@ -12,12 +13,13 @@ struct FuriTimer { StaticTimer_t container; FuriTimerCallback cb_func; void* cb_context; - volatile bool can_be_removed; }; // IMPORTANT: container MUST be the FIRST struct member static_assert(offsetof(FuriTimer, container) == 0); +#define TIMER_DELETED_EVENT (1U << 0) + const char* furi_timer_get_current_name(void) { return current_timer_name; } @@ -54,9 +56,10 @@ static void furi_timer_epilogue(void* context, uint32_t arg) { furi_assert(context); UNUSED(arg); - FuriTimer* instance = context; - - instance->can_be_removed = true; + EventGroupHandle_t hEvent = context; + vTaskSuspendAll(); + xEventGroupSetBits(hEvent, TIMER_DELETED_EVENT); + (void)xTaskResumeAll(); } void furi_timer_free(FuriTimer* instance) { @@ -65,11 +68,15 @@ void furi_timer_free(FuriTimer* instance) { TimerHandle_t hTimer = (TimerHandle_t)instance; furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); - furi_check(xTimerPendFunctionCall(furi_timer_epilogue, instance, 0, portMAX_DELAY) == pdPASS); - while(!instance->can_be_removed) { - furi_delay_tick(2); - } + StaticEventGroup_t event_container = {}; + EventGroupHandle_t hEvent = xEventGroupCreateStatic(&event_container); + furi_check(xTimerPendFunctionCall(furi_timer_epilogue, hEvent, 0, portMAX_DELAY) == pdPASS); + + furi_check( + xEventGroupWaitBits(hEvent, TIMER_DELETED_EVENT, pdFALSE, pdTRUE, portMAX_DELAY) == + TIMER_DELETED_EVENT); + vEventGroupDelete(hEvent); free(instance); } diff --git a/lib/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index b9d33169c..8a16dbfd9 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -1,5 +1,6 @@ #include #include +#include #include #include "flipper_format_stream.h" #include "flipper_format_stream_i.h" @@ -396,14 +397,16 @@ bool flipper_format_stream_read_value_line( #endif case FlipperStreamValueInt32: { int32_t* data = _data; - scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]); + if(strint_to_int32(furi_string_get_cstr(value), NULL, &data[i], 10) == + StrintParseNoError) { + scan_values = 1; + } }; break; case FlipperStreamValueUint32: { uint32_t* data = _data; - // Minus sign is allowed in scanf() for unsigned numbers, resulting in unintentionally huge values with no error reported - if(!furi_string_start_with(value, "-")) { - scan_values = - sscanf(furi_string_get_cstr(value), "%" PRIu32, &data[i]); + if(strint_to_uint32(furi_string_get_cstr(value), NULL, &data[i], 10) == + StrintParseNoError) { + scan_values = 1; } }; break; case FlipperStreamValueHexUint64: { diff --git a/lib/ibutton/ibutton_worker_modes.c b/lib/ibutton/ibutton_worker_modes.c index 5900b10a2..8efb78f03 100644 --- a/lib/ibutton/ibutton_worker_modes.c +++ b/lib/ibutton/ibutton_worker_modes.c @@ -75,7 +75,7 @@ void ibutton_worker_mode_idle_stop(iButtonWorker* worker) { void ibutton_worker_mode_read_start(iButtonWorker* worker) { UNUSED(worker); - furi_hal_power_enable_otg(); + if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg(); } void ibutton_worker_mode_read_tick(iButtonWorker* worker) { @@ -90,7 +90,7 @@ void ibutton_worker_mode_read_tick(iButtonWorker* worker) { void ibutton_worker_mode_read_stop(iButtonWorker* worker) { UNUSED(worker); - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); } /*********************** EMULATE ***********************/ @@ -120,7 +120,7 @@ void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) { void ibutton_worker_mode_write_common_start(iButtonWorker* worker) { //-V524 UNUSED(worker); - furi_hal_power_enable_otg(); + if(!furi_hal_power_is_otg_enabled()) furi_hal_power_enable_otg(); } void ibutton_worker_mode_write_id_tick(iButtonWorker* worker) { @@ -149,5 +149,5 @@ void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker) { void ibutton_worker_mode_write_common_stop(iButtonWorker* worker) { //-V524 UNUSED(worker); - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); } diff --git a/lib/lfrfid/protocols/protocol_gproxii.c b/lib/lfrfid/protocols/protocol_gproxii.c index 73cbe8f39..341d092e9 100644 --- a/lib/lfrfid/protocols/protocol_gproxii.c +++ b/lib/lfrfid/protocols/protocol_gproxii.c @@ -38,12 +38,53 @@ void protocol_gproxii_free(ProtocolGProxII* protocol) { free(protocol); } -uint8_t* protocol_gproxii_get_data(ProtocolGProxII* proto) { - return proto->data; +uint8_t* protocol_gproxii_get_data(ProtocolGProxII* protocol) { + return protocol->data; +} + +bool wiegand_check(uint64_t fc_and_card, bool even_parity, bool odd_parity, int card_len) { + uint8_t even_parity_sum = 0; + uint8_t odd_parity_sum = 1; + switch(card_len) { + case 26: + for(int8_t i = 12; i < 24; i++) { + if(((fc_and_card >> i) & 1) == 1) { + even_parity_sum++; + } + } + if(even_parity_sum % 2 != even_parity) return false; + + for(int8_t i = 0; i < 12; i++) { + if(((fc_and_card >> i) & 1) == 1) { + odd_parity_sum++; + } + } + if(odd_parity_sum % 2 != odd_parity) return false; + break; + case 36: + for(int8_t i = 17; i < 34; i++) { + if(((fc_and_card >> i) & 1) == 1) { + even_parity_sum++; + } + } + if(even_parity_sum % 2 != even_parity) return false; + + for(int8_t i = 0; i < 17; i++) { + if(((fc_and_card >> i) & 1) == 1) { + odd_parity_sum++; + } + } + if(odd_parity_sum % 2 != odd_parity) return false; + break; + default: + furi_crash(); + } + return true; } void protocol_gproxii_decoder_start(ProtocolGProxII* protocol) { memset(protocol->data, 0, GPROXII_ENCODED_BYTE_FULL_SIZE); + memset(protocol->decoded_data, 0, GPROXII_DATA_SIZE); protocol->last_short = false; } @@ -73,13 +114,13 @@ static bool protocol_gproxii_can_be_decoded(ProtocolGProxII* protocol) { // XORVALUE LLLLLL DD PPPPPPPPPPPPPPPP E FFFFFFFF CCCCCCCCCCCCCCCC O UUUUUUUUUUUUUU // 10010000 011010 11 0000000100000000 0 00000000 0000000000000001 0 00000000000000 - Profile: 256 FC: 0 Card: 1 - // 72 Bit Guardall/Verex/Chubb GProx II 36 bit key with 26 bit profile - // 0 10 20 30 40 50 60 70 - // | | | | | | | | - // 01234567 890123 45 67890123456789012345678901 2 34567890 1234567890123456 7 8901 + // 72 Bit Guardall/Verex/Chubb GProx II 36 bit key with 16 bit profile + // 0 10 20 30 40 50 60 70 + // | | | | | | | | + // 01234567 890123 45 67890123 45678901 2 34567890123456 78901234567890123456 7 8901 // -------------------------------------------------------------------------------- - // XORVALUE LLLLLL DD PPPPPPPPPPPPPPPPPPPPPPPPPP E FFFFFFFF CCCCCCCCCCCCCCCC O UUUU - // 10111000 100100 10 00000001000000000000000000 1 01000000 1000100010111000 1 0000 - Profile: 262144 FC: 64 Card: 35000 + // XORVALUE LLLLLL DD PPPPPPPP PPPPPPPP E UUUUUUFFFFFFFF UUUUCCCCCCCCCCCCCCCC O UUUU + // 10111000 100100 10 00000001 00000000 0 00000000010100 00001000100010111000 1 0000 - Profile: 256 FC: 20 Card: 35000 // X = XOR Key, L = Message length, D = 2 bit check digits, P = Profile, E = Wiegand leading even parity // F = Faclity code, C = Card number, O = Wiegand trailing odd parity, U = Unused bits @@ -88,8 +129,9 @@ static bool protocol_gproxii_can_be_decoded(ProtocolGProxII* protocol) { if(bit_lib_get_bits(protocol->data, 0, 6) != 0b111110) return false; // Check always 0 parity on every 5th bit after preamble - if(bit_lib_test_parity(protocol->data, 5, GPROXII_ENCODED_BIT_SIZE, BitLibParityAlways0, 5)) + if(!bit_lib_test_parity(protocol->data, 6, GPROXII_ENCODED_BIT_SIZE, BitLibParityAlways0, 5)) { return false; + } // Start GProx II decode bit_lib_copy_bits(protocol->decoded_data, 0, GPROXII_ENCODED_BIT_SIZE, protocol->data, 6); @@ -109,11 +151,23 @@ static bool protocol_gproxii_can_be_decoded(ProtocolGProxII* protocol) { // Check card length is either 26 or 36 int card_len = bit_lib_get_bits(protocol->decoded_data, 8, 6); - if(card_len == 26 || card_len == 36) { - return true; + + // wiegand parity + if(card_len == 26) { + uint64_t fc_and_card = bit_lib_get_bits_64(protocol->decoded_data, 33, 24); + bool even_parity = bit_lib_get_bits(protocol->decoded_data, 32, 1); + bool odd_parity = bit_lib_get_bits(protocol->decoded_data, 57, 1); + if(!wiegand_check(fc_and_card, even_parity, odd_parity, card_len)) return false; + } else if(card_len == 36) { + uint64_t fc_and_card = bit_lib_get_bits_64(protocol->decoded_data, 33, 34); + uint8_t even_parity = bit_lib_get_bits(protocol->decoded_data, 32, 1); + uint8_t odd_parity = bit_lib_get_bits(protocol->decoded_data, 67, 1); + if(!wiegand_check(fc_and_card, even_parity, odd_parity, card_len)) return false; } else { return false; // If we don't get a 26 or 36 it's not a known card type } + + return true; } bool protocol_gproxii_decoder_feed(ProtocolGProxII* protocol, bool level, uint32_t duration) { @@ -181,6 +235,7 @@ LevelDuration protocol_gproxii_encoder_yield(ProtocolGProxII* protocol) { } void protocol_gproxii_render_data(ProtocolGProxII* protocol, FuriString* result) { + protocol_gproxii_can_be_decoded(protocol); int xor_code = bit_lib_get_bits(protocol->decoded_data, 0, 8); int card_len = bit_lib_get_bits(protocol->decoded_data, 8, 6); int crc_code = bit_lib_get_bits(protocol->decoded_data, 14, 2); @@ -189,7 +244,7 @@ void protocol_gproxii_render_data(ProtocolGProxII* protocol, FuriString* result) // Print FC, Card and Length furi_string_cat_printf( result, - "FC: %hhu Card: %hu LEN: %hhu\n", + "FC: %u Card: %u LEN: %hhu\n", bit_lib_get_bits(protocol->decoded_data, 33, 8), bit_lib_get_bits_16(protocol->decoded_data, 41, 16), card_len); @@ -204,17 +259,17 @@ void protocol_gproxii_render_data(ProtocolGProxII* protocol, FuriString* result) // Print FC, Card and Length furi_string_cat_printf( result, - "FC: %hhu Card: %hu LEN: %hhu\n", - bit_lib_get_bits(protocol->decoded_data, 43, 8), + "FC: %u Card: %u LEN: %hhu\n", + bit_lib_get_bits_16(protocol->decoded_data, 33, 14), bit_lib_get_bits_16(protocol->decoded_data, 51, 16), card_len); // XOR Key, CRC and Profile furi_string_cat_printf( result, - "XOR: %hhu CRC: %hhu P: %06lX", + "XOR: %hhu CRC: %hhu P: %04hX", xor_code, crc_code, - bit_lib_get_bits_32(protocol->decoded_data, 16, 26)); + bit_lib_get_bits_16(protocol->decoded_data, 16, 16)); } else { furi_string_cat_printf(result, "Read Error\n"); } diff --git a/lib/mjs/common/frozen/frozen.c b/lib/mjs/common/frozen/frozen.c index 923e6a556..7646864c0 100644 --- a/lib/mjs/common/frozen/frozen.c +++ b/lib/mjs/common/frozen/frozen.c @@ -37,7 +37,7 @@ #ifdef _WIN32 #undef snprintf #undef vsnprintf -#define snprintf cs_win_snprintf +#define snprintf cs_win_snprintf #define vsnprintf cs_win_vsnprintf int cs_win_snprintf(char* str, size_t size, const char* format, ...); int cs_win_vsnprintf(char* str, size_t size, const char* format, va_list ap); @@ -150,7 +150,8 @@ static int json_isspace(int ch) { } static void json_skip_whitespaces(struct frozen* f) { - while(f->cur < f->end && json_isspace(*f->cur)) f->cur++; + while(f->cur < f->end && json_isspace(*f->cur)) + f->cur++; } static int json_cur(struct frozen* f) { @@ -263,15 +264,18 @@ static int json_parse_number(struct frozen* f) { f->cur += 2; EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); EXPECT(json_isxdigit(f->cur[0]), JSON_STRING_INVALID); - while(f->cur < f->end && json_isxdigit(f->cur[0])) f->cur++; + while(f->cur < f->end && json_isxdigit(f->cur[0])) + f->cur++; } else { EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); - while(f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; + while(f->cur < f->end && json_isdigit(f->cur[0])) + f->cur++; if(f->cur < f->end && f->cur[0] == '.') { f->cur++; EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); - while(f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; + while(f->cur < f->end && json_isdigit(f->cur[0])) + f->cur++; } if(f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) { f->cur++; @@ -279,7 +283,8 @@ static int json_parse_number(struct frozen* f) { if((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++; EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); - while(f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; + while(f->cur < f->end && json_isdigit(f->cur[0])) + f->cur++; } } json_truncate_path(f, fstate.path_len); @@ -639,8 +644,7 @@ int json_vprintf(struct json_out* out, const char* fmt, va_list xap) { int need_len, size = sizeof(buf); char fmt2[20]; va_list ap_copy; - strncpy(fmt2, fmt, n + 1 > (int)sizeof(fmt2) ? sizeof(fmt2) : (size_t)n + 1); - fmt2[n + 1] = '\0'; + strlcpy(fmt2, fmt, sizeof(fmt2)); va_copy(ap_copy, ap); need_len = vsnprintf(pbuf, size, fmt2, ap_copy); @@ -1047,7 +1051,7 @@ int json_vscanf(const char* s, int len, const char* fmt, va_list ap) { while(fmt[i] != '\0') { if(fmt[i] == '{') { - strcat(path, "."); + strlcat(path, ".", sizeof(path)); i++; } else if(fmt[i] == '}') { if((p = strrchr(path, '.')) != NULL) *p = '\0'; @@ -1160,7 +1164,8 @@ struct json_setf_data { static int get_matched_prefix_len(const char* s1, const char* s2) { int i = 0; - while(s1[i] && s2[i] && s1[i] == s2[i]) i++; + while(s1[i] && s2[i] && s1[i] == s2[i]) + i++; return i; } @@ -1235,7 +1240,8 @@ int json_vsetf( /* Trim comma after the value that begins at object/array start */ if(s[data.prev - 1] == '{' || s[data.prev - 1] == '[') { int i = data.end; - while(i < len && json_isspace(s[i])) i++; + while(i < len && json_isspace(s[i])) + i++; if(s[i] == ',') data.end = i + 1; /* Point after comma */ } json_printf(out, "%.*s", len - data.end, s + data.end); @@ -1305,7 +1311,8 @@ struct prettify_data { }; static void indent(struct json_out* out, int level) { - while(level-- > 0) out->printer(out, " ", 2); + while(level-- > 0) + out->printer(out, " ", 2); } static void print_key(struct prettify_data* pd, const char* path, const char* name, int name_len) { diff --git a/lib/mjs/mjs_json.c b/lib/mjs/mjs_json.c index 829b3b4c0..654eeb3d6 100644 --- a/lib/mjs/mjs_json.c +++ b/lib/mjs/mjs_json.c @@ -138,8 +138,7 @@ MJS_PRIVATE mjs_err_t to_json_or_debug( vp < mjs->json_visited_stack.buf + mjs->json_visited_stack.len; vp += sizeof(mjs_val_t)) { if(*(mjs_val_t*)vp == v) { - strncpy(buf, "[Circular]", size); - len = 10; + len = strlcpy(buf, "[Circular]", size); goto clean; } } diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c index d0d342a6b..90e65b282 100644 --- a/lib/nfc/nfc.c +++ b/lib/nfc/nfc.c @@ -651,11 +651,12 @@ NfcError nfc_felica_listener_set_sensf_res_data( const uint8_t* idm, const uint8_t idm_len, const uint8_t* pmm, - const uint8_t pmm_len) { + const uint8_t pmm_len, + const uint16_t sys_code) { furi_check(instance); FuriHalNfcError error = - furi_hal_nfc_felica_listener_set_sensf_res_data(idm, idm_len, pmm, pmm_len); + furi_hal_nfc_felica_listener_set_sensf_res_data(idm, idm_len, pmm, pmm_len, sys_code); instance->comm_state = NfcCommStateIdle; return nfc_process_hal_error(error); } diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h index 959bd018f..8fbf90d1f 100644 --- a/lib/nfc/nfc.h +++ b/lib/nfc/nfc.h @@ -361,6 +361,7 @@ NfcError nfc_iso14443a_listener_set_col_res_data( * @param[in] idm_len IDm length in bytes. * @param[in] pmm pointer to a byte array containing the PMm. * @param[in] pmm_len PMm length in bytes. + * @param[in] sys_code System code from SYS_C block * @returns NfcErrorNone on success, any other error code on failure. */ NfcError nfc_felica_listener_set_sensf_res_data( @@ -368,7 +369,8 @@ NfcError nfc_felica_listener_set_sensf_res_data( const uint8_t* idm, const uint8_t idm_len, const uint8_t* pmm, - const uint8_t pmm_len); + const uint8_t pmm_len, + const uint16_t sys_code); /** * @brief Send ISO15693 Start of Frame pattern in listener mode diff --git a/lib/nfc/nfc_mock.c b/lib/nfc/nfc_mock.c index a17f0e104..ee4bb09cc 100644 --- a/lib/nfc/nfc_mock.c +++ b/lib/nfc/nfc_mock.c @@ -270,7 +270,8 @@ static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, ui } } else if(rx_bits == 8 * 8) { FelicaPollingRequest* request = (FelicaPollingRequest*)rx_data; - if(request->system_code == instance->pt_memory.system_code) { + if(request->system_code == 0xFFFF || + request->system_code == instance->pt_memory.system_code) { uint8_t response_size = sizeof(FelicaSensfResData) + 1; bit_buffer_reset(tx_buffer); bit_buffer_append_byte(tx_buffer, response_size); @@ -501,19 +502,19 @@ NfcError nfc_felica_listener_set_sensf_res_data( const uint8_t* idm, const uint8_t idm_len, const uint8_t* pmm, - const uint8_t pmm_len) { + const uint8_t pmm_len, + const uint16_t sys_code) { furi_assert(instance); furi_assert(idm); furi_assert(pmm); furi_assert(idm_len == 8); furi_assert(pmm_len == 8); - instance->pt_memory.system_code = 0xFFFF; + instance->pt_memory.system_code = sys_code; instance->pt_memory.sens_res.code = 0x01; instance->software_col_res_required = true; memcpy(instance->pt_memory.sens_res.idm.data, idm, idm_len); memcpy(instance->pt_memory.sens_res.pmm.data, pmm, pmm_len); - return NfcErrorNone; } diff --git a/lib/nfc/protocols/felica/felica_listener.c b/lib/nfc/protocols/felica/felica_listener.c index d019eb21d..90954de97 100644 --- a/lib/nfc/protocols/felica/felica_listener.c +++ b/lib/nfc/protocols/felica/felica_listener.c @@ -26,8 +26,9 @@ FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) { memcpy(instance->mc_shadow.data, instance->data->data.fs.mc.data, FELICA_DATA_BLOCK_SIZE); instance->data->data.fs.state.data[0] = 0; nfc_config(instance->nfc, NfcModeListener, NfcTechFelica); + const uint16_t system_code = *(uint16_t*)data->data.fs.sys_c.data; nfc_felica_listener_set_sensf_res_data( - nfc, data->idm.data, sizeof(data->idm), data->pmm.data, sizeof(data->pmm)); + nfc, data->idm.data, sizeof(data->idm), data->pmm.data, sizeof(data->pmm), system_code); return instance; } diff --git a/lib/subghz/protocols/gangqi.c b/lib/subghz/protocols/gangqi.c new file mode 100644 index 000000000..a2195dd33 --- /dev/null +++ b/lib/subghz/protocols/gangqi.c @@ -0,0 +1,520 @@ +#include "gangqi.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#include "../blocks/custom_btn_i.h" + +#define TAG "SubGhzProtocolGangQi" + +static const SubGhzBlockConst subghz_protocol_gangqi_const = { + .te_short = 500, + .te_long = 1200, + .te_delta = 200, + .min_count_bit_for_found = 34, +}; + +struct SubGhzProtocolDecoderGangQi { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderGangQi { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + GangQiDecoderStepReset = 0, + GangQiDecoderStepSaveDuration, + GangQiDecoderStepCheckDuration, +} GangQiDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_gangqi_decoder = { + .alloc = subghz_protocol_decoder_gangqi_alloc, + .free = subghz_protocol_decoder_gangqi_free, + + .feed = subghz_protocol_decoder_gangqi_feed, + .reset = subghz_protocol_decoder_gangqi_reset, + + .get_hash_data = NULL, + .get_hash_data_long = subghz_protocol_decoder_gangqi_get_hash_data, + .serialize = subghz_protocol_decoder_gangqi_serialize, + .deserialize = subghz_protocol_decoder_gangqi_deserialize, + .get_string = subghz_protocol_decoder_gangqi_get_string, + .get_string_brief = NULL, +}; + +const SubGhzProtocolEncoder subghz_protocol_gangqi_encoder = { + .alloc = subghz_protocol_encoder_gangqi_alloc, + .free = subghz_protocol_encoder_gangqi_free, + + .deserialize = subghz_protocol_encoder_gangqi_deserialize, + .stop = subghz_protocol_encoder_gangqi_stop, + .yield = subghz_protocol_encoder_gangqi_yield, +}; + +const SubGhzProtocol subghz_protocol_gangqi = { + .name = SUBGHZ_PROTOCOL_GANGQI_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_gangqi_decoder, + .encoder = &subghz_protocol_gangqi_encoder, +}; + +void* subghz_protocol_encoder_gangqi_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderGangQi* instance = malloc(sizeof(SubGhzProtocolEncoderGangQi)); + + instance->base.protocol = &subghz_protocol_gangqi; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_gangqi_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderGangQi* instance = context; + free(instance->encoder.upload); + free(instance); +} + +// Get custom button code +static uint8_t subghz_protocol_gangqi_get_btn_code(void) { + uint8_t custom_btn_id = subghz_custom_btn_get(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); + uint8_t btn = original_btn_code; + + // Set custom button + if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { + // Restore original button code + btn = original_btn_code; + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { + switch(original_btn_code) { + case 0xD: + btn = 0xE; + break; + case 0xE: + btn = 0xD; + break; + case 0xB: + btn = 0xD; + break; + case 0x7: + btn = 0xD; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0xD: + btn = 0xB; + break; + case 0xE: + btn = 0xB; + break; + case 0xB: + btn = 0xE; + break; + case 0x7: + btn = 0xE; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0xD: + btn = 0x7; + break; + case 0xE: + btn = 0x7; + break; + case 0xB: + btn = 0x7; + break; + case 0x7: + btn = 0xB; + break; + + default: + break; + } + } + + return btn; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderGangQi instance + */ +static void subghz_protocol_encoder_gangqi_get_upload(SubGhzProtocolEncoderGangQi* instance) { + furi_assert(instance); + + // Generate new key using custom or default button + instance->generic.btn = subghz_protocol_gangqi_get_btn_code(); + + uint64_t new_key = (instance->generic.data >> 14) << 14 | (instance->generic.btn << 10) | + (0b01 << 8); + + uint8_t crc = -0xD7 - ((new_key >> 32) & 0xFF) - ((new_key >> 24) & 0xFF) - + ((new_key >> 16) & 0xFF) - ((new_key >> 8) & 0xFF); + + instance->generic.data = (new_key | crc); + + size_t index = 0; + + // Send key and GAP between parcels + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + // Send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_gangqi_const.te_long); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_gangqi_const.te_short * 4 + + subghz_protocol_gangqi_const.te_delta); + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_gangqi_const.te_short); + } + } else { + // Send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_gangqi_const.te_short); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_gangqi_const.te_short * 4 + + subghz_protocol_gangqi_const.te_delta); + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_gangqi_const.te_long); + } + } + } + + instance->encoder.size_upload = index; + return; +} + +/** + * Analysis of received data and parsing serial number + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_gangqi_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = (instance->data >> 10) & 0xF; + instance->serial = (instance->data & 0xFFFFF0000) >> 16; + + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->btn); + } + subghz_custom_btn_set_max(3); + + // GangQi Decoder + // 09.2024 - @xMasterX (MMX) + // Thanks @Skorpionm for support! + + //// 4D=F8=171=229 byte sum should be always the same + // Button + // Serial || BBBB || CRC (byte sum) with overflow and starting point 0xD7 + //034AAB75BC = 00110100101010101011 01 1101 01 101111 00 // A (0xD) + //034AAB79B8 = 00110100101010101011 01 1110 01 101110 00 // B (0xE) + //034AAB6DC4 = 00110100101010101011 01 1011 01 110001 00 // C (0xB) + //034AAB5DD4 = 00110100101010101011 01 0111 01 110101 00 // D (0x7) + //034AAB55DC = 00110100101010101011 01 0101 01 110111 00 // Settings (0x5) + //034AAB51E0 = 00110100101010101011 01 0100 01 111000 00 // A (0x4) + //034AAB49E8 = 00110100101010101011 01 0010 01 111010 00 // C (0x2) + //034AAB59D8 = 00110100101010101011 01 0110 01 110110 00 // D (0x6) + //034AAB45EC = 00110100101010101011 01 0001 01 111011 00 // Settings exit (0x1) + // + // Serial 3 bytes should meet requirements see validation example at subghz_protocol_decoder_gangqi_get_string + // + // Code for finding start byte for crc sum + // + //uint64_t test = 0x034AAB79B8; //B8 + //for(size_t byte = 0; byte < 0xFF; ++byte) { + // uint8_t crc_res = -byte - ((test >> 32) & 0xFF) - ((test >> 24) & 0xFF) - + // ((test >> 16) & 0xFF) - ((test >> 8) & 0xFF); + // if(crc_res == 0xB8) { + // uint64_t test2 = 0x034AAB6DC4; //C4 + // uint8_t crc_res2 = -byte - ((test2 >> 32) & 0xFF) - ((test2 >> 24) & 0xFF) - + // ((test2 >> 16) & 0xFF) - ((test2 >> 8) & 0xFF); + // if(crc_res2 == 0xC4) { + // printf("Start byte for CRC = %02lX / CRC = %02X \n", byte, crc_res); + // + // printf("Testing second parcel CRC = %02X", crc_res2); + // } + // } + // } +} + +SubGhzProtocolStatus + subghz_protocol_encoder_gangqi_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderGangQi* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_gangqi_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_gangqi_remote_controller(&instance->generic); + subghz_protocol_encoder_gangqi_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_gangqi_stop(void* context) { + SubGhzProtocolEncoderGangQi* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_gangqi_yield(void* context) { + SubGhzProtocolEncoderGangQi* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_gangqi_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderGangQi* instance = malloc(sizeof(SubGhzProtocolDecoderGangQi)); + instance->base.protocol = &subghz_protocol_gangqi; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_gangqi_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGangQi* instance = context; + free(instance); +} + +void subghz_protocol_decoder_gangqi_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGangQi* instance = context; + instance->decoder.parser_step = GangQiDecoderStepReset; +} + +void subghz_protocol_decoder_gangqi_feed(void* context, bool level, volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderGangQi* instance = context; + + switch(instance->decoder.parser_step) { + case GangQiDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_long * 2) < + subghz_protocol_gangqi_const.te_delta * 3)) { + //Found GAP + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = GangQiDecoderStepSaveDuration; + } + break; + case GangQiDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = GangQiDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = GangQiDecoderStepReset; + } + break; + case GangQiDecoderStepCheckDuration: + if(!level) { + // Bit 0 is short and long timing + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gangqi_const.te_short) < + subghz_protocol_gangqi_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_long) < + subghz_protocol_gangqi_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = GangQiDecoderStepSaveDuration; + // Bit 1 is long and short timing + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gangqi_const.te_long) < + subghz_protocol_gangqi_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_short) < + subghz_protocol_gangqi_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = GangQiDecoderStepSaveDuration; + } else if( + // End of the key + DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_short * 4) < + subghz_protocol_gangqi_const.te_delta) { + //Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes) + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_gangqi_const.te_short) < + subghz_protocol_gangqi_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_short * 4) < + subghz_protocol_gangqi_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gangqi_const.te_long) < + subghz_protocol_gangqi_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_gangqi_const.te_short * 4) < + subghz_protocol_gangqi_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + // If got 34 bits key reading is finished + if(instance->decoder.decode_count_bit == + subghz_protocol_gangqi_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = GangQiDecoderStepReset; + } else { + instance->decoder.parser_step = GangQiDecoderStepReset; + } + } else { + instance->decoder.parser_step = GangQiDecoderStepReset; + } + break; + } +} + +/** + * Get button name. + * @param btn Button number, 4 bit + */ +static const char* subghz_protocol_gangqi_get_button_name(uint8_t btn) { + const char* name_btn[16] = { + "Unknown", + "Exit settings", + "Volume setting", + "0x3", + "Vibro sens. setting", + "Settings mode", + "Ringtone setting", + "Ring", // D + "0x8", + "0x9", + "0xA", + "Alarm", // C + "0xC", + "Arm", // A + "Disarm", // B + "0xF"}; + return btn <= 0xf ? name_btn[btn] : name_btn[0]; +} + +uint32_t subghz_protocol_decoder_gangqi_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGangQi* instance = context; + return subghz_protocol_blocks_get_hash_data_long( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_gangqi_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderGangQi* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_gangqi_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderGangQi* instance = context; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_gangqi_const.min_count_bit_for_found); +} + +void subghz_protocol_decoder_gangqi_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderGangQi* instance = context; + + // Parse serial + subghz_protocol_gangqi_remote_controller(&instance->generic); + + // Get CRC + uint8_t crc = -0xD7 - ((instance->generic.data >> 32) & 0xFF) - + ((instance->generic.data >> 24) & 0xFF) - + ((instance->generic.data >> 16) & 0xFF) - ((instance->generic.data >> 8) & 0xFF); + + // Get 3 bytes sum + uint16_t sum_3bytes_serial = ((instance->generic.serial >> 16) & 0xFF) + + ((instance->generic.serial >> 8) & 0xFF) + + (instance->generic.serial & 0xFF); + // Returns true if serial is valid + bool serial_is_valid = + (((!(sum_3bytes_serial & 0x3)) && + ((0xB < sum_3bytes_serial) && (sum_3bytes_serial < 0x141))) && + ((((instance->generic.serial >> 16) & 0xFF) == 0x2) || + (((instance->generic.serial >> 16) & 0xFF) == 0x3))); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key: 0x%X%08lX\r\n" + "Serial: 0x%05lX CRC: 0x%02X\r\n" + "Btn: 0x%01X - %s\r\n" + "Serial is %s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint8_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + crc, + instance->generic.btn, + subghz_protocol_gangqi_get_button_name(instance->generic.btn), + serial_is_valid ? "valid" : "invalid"); +} diff --git a/lib/subghz/protocols/gangqi.h b/lib/subghz/protocols/gangqi.h new file mode 100644 index 000000000..3281117b5 --- /dev/null +++ b/lib/subghz/protocols/gangqi.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_GANGQI_NAME "GangQi" + +typedef struct SubGhzProtocolDecoderGangQi SubGhzProtocolDecoderGangQi; +typedef struct SubGhzProtocolEncoderGangQi SubGhzProtocolEncoderGangQi; + +extern const SubGhzProtocolDecoder subghz_protocol_gangqi_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_gangqi_encoder; +extern const SubGhzProtocol subghz_protocol_gangqi; + +/** + * Allocate SubGhzProtocolEncoderGangQi. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderGangQi* pointer to a SubGhzProtocolEncoderGangQi instance + */ +void* subghz_protocol_encoder_gangqi_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderGangQi. + * @param context Pointer to a SubGhzProtocolEncoderGangQi instance + */ +void subghz_protocol_encoder_gangqi_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderGangQi instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_gangqi_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderGangQi instance + */ +void subghz_protocol_encoder_gangqi_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderGangQi instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_gangqi_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderGangQi. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderGangQi* pointer to a SubGhzProtocolDecoderGangQi instance + */ +void* subghz_protocol_decoder_gangqi_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderGangQi. + * @param context Pointer to a SubGhzProtocolDecoderGangQi instance + */ +void subghz_protocol_decoder_gangqi_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderGangQi. + * @param context Pointer to a SubGhzProtocolDecoderGangQi instance + */ +void subghz_protocol_decoder_gangqi_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderGangQi instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_gangqi_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderGangQi instance + * @return hash Hash sum + */ +uint32_t subghz_protocol_decoder_gangqi_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderGangQi. + * @param context Pointer to a SubGhzProtocolDecoderGangQi instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_gangqi_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderGangQi. + * @param context Pointer to a SubGhzProtocolDecoderGangQi instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_gangqi_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderGangQi instance + * @param output Resulting text + */ +void subghz_protocol_decoder_gangqi_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/hay21.c b/lib/subghz/protocols/hay21.c new file mode 100644 index 000000000..3945fcea7 --- /dev/null +++ b/lib/subghz/protocols/hay21.c @@ -0,0 +1,470 @@ +#include "hay21.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#include "../blocks/custom_btn_i.h" + +#define TAG "SubGhzProtocolHay21" + +static const SubGhzBlockConst subghz_protocol_hay21_const = { + .te_short = 300, + .te_long = 700, + .te_delta = 150, + .min_count_bit_for_found = 21, +}; + +struct SubGhzProtocolDecoderHay21 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderHay21 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Hay21DecoderStepReset = 0, + Hay21DecoderStepSaveDuration, + Hay21DecoderStepCheckDuration, +} Hay21DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_hay21_decoder = { + .alloc = subghz_protocol_decoder_hay21_alloc, + .free = subghz_protocol_decoder_hay21_free, + + .feed = subghz_protocol_decoder_hay21_feed, + .reset = subghz_protocol_decoder_hay21_reset, + + .get_hash_data = NULL, + .get_hash_data_long = subghz_protocol_decoder_hay21_get_hash_data, + .serialize = subghz_protocol_decoder_hay21_serialize, + .deserialize = subghz_protocol_decoder_hay21_deserialize, + .get_string = subghz_protocol_decoder_hay21_get_string, + .get_string_brief = NULL, +}; + +const SubGhzProtocolEncoder subghz_protocol_hay21_encoder = { + .alloc = subghz_protocol_encoder_hay21_alloc, + .free = subghz_protocol_encoder_hay21_free, + + .deserialize = subghz_protocol_encoder_hay21_deserialize, + .stop = subghz_protocol_encoder_hay21_stop, + .yield = subghz_protocol_encoder_hay21_yield, +}; + +const SubGhzProtocol subghz_protocol_hay21 = { + .name = SUBGHZ_PROTOCOL_HAY21_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_hay21_decoder, + .encoder = &subghz_protocol_hay21_encoder, +}; + +void* subghz_protocol_encoder_hay21_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHay21* instance = malloc(sizeof(SubGhzProtocolEncoderHay21)); + + instance->base.protocol = &subghz_protocol_hay21; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_hay21_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHay21* instance = context; + free(instance->encoder.upload); + free(instance); +} + +// Get custom button code +static uint8_t subghz_protocol_hay21_get_btn_code(void) { + uint8_t custom_btn_id = subghz_custom_btn_get(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); + uint8_t btn = original_btn_code; + + // Set custom button + if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { + // Restore original button code + btn = original_btn_code; + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { + switch(original_btn_code) { + case 0x5A: + btn = 0xC3; + break; + case 0xC3: + btn = 0x5A; + break; + case 0x88: + btn = 0x5A; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x5A: + btn = 0x88; + break; + case 0xC3: + btn = 0x88; + break; + case 0x88: + btn = 0xC3; + break; + + default: + break; + } + } + + return btn; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHay21 instance + */ +static void subghz_protocol_encoder_hay21_get_upload(SubGhzProtocolEncoderHay21* instance) { + furi_assert(instance); + + // Generate new key using custom or default button + instance->generic.btn = subghz_protocol_hay21_get_btn_code(); + + // Counter increment + if(instance->generic.cnt < 0xF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if(instance->generic.cnt >= 0xF) { + instance->generic.cnt = 0; + } + + // Reconstruction of the data + instance->generic.data = + ((uint64_t)instance->generic.btn << 13 | (uint64_t)instance->generic.serial << 5 | + instance->generic.cnt << 1) | + 0b1; + + size_t index = 0; + + // Send key and GAP between parcels + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + // Send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hay21_const.te_long); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hay21_const.te_long * 6); + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hay21_const.te_short); + } + } else { + // Send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hay21_const.te_short); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hay21_const.te_long * 6); + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hay21_const.te_long); + } + } + } + + instance->encoder.size_upload = index; + return; +} + +/** + * Analysis of received data and parsing serial number + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_hay21_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = (instance->data >> 13) & 0xFF; + instance->serial = (instance->data >> 5) & 0xFF; + instance->cnt = (instance->data >> 1) & 0xF; + + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->btn); + } + subghz_custom_btn_set_max(2); + + // Hay21 Decoder + // 09.2024 - @xMasterX (MMX) + + // Key samples (inverted) + // button serial CNT (goes lower since 0/1 are inverted) + //14A84A = 000 10100101 01000010 0101 0 (cnt 5) + //14A848 = 000 10100101 01000010 0100 0 (cnt 4) + //14A846 = 000 10100101 01000010 0011 0 (cnt 3) + //14A844 = 000 10100101 01000010 0010 0 (cnt 2) + //14A842 = 000 10100101 01000010 0001 0 (cnt 1) + //14A840 = 000 10100101 01000010 0000 0 (cnt 0) + //14A85E = 000 10100101 01000010 1111 0 (cnt F) + //14A85C = 000 10100101 01000010 1110 0 (cnt E) + //14A85A = 000 10100101 01000010 1101 0 (cnt D) + //14A858 = 000 10100101 01000010 1100 0 (cnt C) + //14A856 = 000 10100101 01000010 1011 0 (cnt B) + // 0xA5 (Labeled as On/Off on the remote board) + // 0x3C (Labeled as Mode on the remote board) + // 0x42 (Serial) + // BTN Serial CNT + //078854 = 000 00111100 01000010 1010 0 (cnt A) + //078852 = 000 00111100 01000010 1001 0 (cnt 9) + //078850 = 000 00111100 01000010 1000 0 (cnt 8) + //07884E = 000 00111100 01000010 0111 0 (cnt 7) + // Inverted back + //1877B9 = 000 11000011 10111101 1100 1 + //1877BB = 000 11000011 10111101 1101 1 + //1877BD = 000 11000011 10111101 1110 1 + //0B57BF = 000 01011010 10111101 1111 1 +} + +SubGhzProtocolStatus + subghz_protocol_encoder_hay21_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHay21* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_hay21_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_hay21_remote_controller(&instance->generic); + subghz_protocol_encoder_hay21_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_hay21_stop(void* context) { + SubGhzProtocolEncoderHay21* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_hay21_yield(void* context) { + SubGhzProtocolEncoderHay21* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_hay21_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHay21* instance = malloc(sizeof(SubGhzProtocolDecoderHay21)); + instance->base.protocol = &subghz_protocol_hay21; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_hay21_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHay21* instance = context; + free(instance); +} + +void subghz_protocol_decoder_hay21_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHay21* instance = context; + instance->decoder.parser_step = Hay21DecoderStepReset; +} + +void subghz_protocol_decoder_hay21_feed(void* context, bool level, volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHay21* instance = context; + + switch(instance->decoder.parser_step) { + case Hay21DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_hay21_const.te_long * 6) < + subghz_protocol_hay21_const.te_delta * 3)) { + //Found GAP + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = Hay21DecoderStepSaveDuration; + } + break; + case Hay21DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Hay21DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Hay21DecoderStepReset; + } + break; + case Hay21DecoderStepCheckDuration: + if(!level) { + // Bit 1 is long + short timing + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hay21_const.te_long) < + subghz_protocol_hay21_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_hay21_const.te_short) < + subghz_protocol_hay21_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Hay21DecoderStepSaveDuration; + // Bit 0 is short + long timing + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hay21_const.te_short) < + subghz_protocol_hay21_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_hay21_const.te_long) < + subghz_protocol_hay21_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Hay21DecoderStepSaveDuration; + } else if( + // End of the key + DURATION_DIFF(duration, subghz_protocol_hay21_const.te_long * 6) < + subghz_protocol_hay21_const.te_delta * 2) { + //Found next GAP and add bit 0 or 1 + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hay21_const.te_long) < + subghz_protocol_hay21_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hay21_const.te_short) < + subghz_protocol_hay21_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + // If got 21 bits key reading is finished + if(instance->decoder.decode_count_bit == + subghz_protocol_hay21_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = Hay21DecoderStepReset; + } else { + instance->decoder.parser_step = Hay21DecoderStepReset; + } + } else { + instance->decoder.parser_step = Hay21DecoderStepReset; + } + break; + } +} + +/** + * Get button name. + * @param btn Button number, 4 bit + */ +static const char* subghz_protocol_hay21_get_button_name(uint8_t btn) { + const char* btn_name; + switch(btn) { + case 0x5A: + btn_name = "On/Off"; + break; + case 0xC3: + btn_name = "Mode"; + break; + case 0x88: + btn_name = "Hold"; + break; + default: + btn_name = "Unknown"; + break; + } + return btn_name; +} + +uint32_t subghz_protocol_decoder_hay21_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHay21* instance = context; + return subghz_protocol_blocks_get_hash_data_long( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_hay21_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHay21* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_hay21_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHay21* instance = context; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_hay21_const.min_count_bit_for_found); +} + +void subghz_protocol_decoder_hay21_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHay21* instance = context; + + // Parse serial, button, counter + subghz_protocol_hay21_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s - %dbit\r\n" + "Key: 0x%06lX\r\n" + "Serial: 0x%02X\r\n" + "Btn: 0x%01X - %s\r\n" + "Cnt: 0x%01X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + (uint8_t)(instance->generic.serial & 0xFF), + instance->generic.btn, + subghz_protocol_hay21_get_button_name(instance->generic.btn), + (uint8_t)(instance->generic.cnt & 0xF)); +} diff --git a/lib/subghz/protocols/hay21.h b/lib/subghz/protocols/hay21.h new file mode 100644 index 000000000..65ecd856e --- /dev/null +++ b/lib/subghz/protocols/hay21.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HAY21_NAME "Hay21" + +typedef struct SubGhzProtocolDecoderHay21 SubGhzProtocolDecoderHay21; +typedef struct SubGhzProtocolEncoderHay21 SubGhzProtocolEncoderHay21; + +extern const SubGhzProtocolDecoder subghz_protocol_hay21_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_hay21_encoder; +extern const SubGhzProtocol subghz_protocol_hay21; + +/** + * Allocate SubGhzProtocolEncoderHay21. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHay21* pointer to a SubGhzProtocolEncoderHay21 instance + */ +void* subghz_protocol_encoder_hay21_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHay21. + * @param context Pointer to a SubGhzProtocolEncoderHay21 instance + */ +void subghz_protocol_encoder_hay21_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHay21 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_hay21_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHay21 instance + */ +void subghz_protocol_encoder_hay21_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHay21 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_hay21_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHay21. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHay21* pointer to a SubGhzProtocolDecoderHay21 instance + */ +void* subghz_protocol_decoder_hay21_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHay21. + * @param context Pointer to a SubGhzProtocolDecoderHay21 instance + */ +void subghz_protocol_decoder_hay21_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHay21. + * @param context Pointer to a SubGhzProtocolDecoderHay21 instance + */ +void subghz_protocol_decoder_hay21_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHay21 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_hay21_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHay21 instance + * @return hash Hash sum + */ +uint32_t subghz_protocol_decoder_hay21_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHay21. + * @param context Pointer to a SubGhzProtocolDecoderHay21 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_hay21_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHay21. + * @param context Pointer to a SubGhzProtocolDecoderHay21 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_hay21_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHay21 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_hay21_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/hollarm.c b/lib/subghz/protocols/hollarm.c new file mode 100644 index 000000000..292b7054a --- /dev/null +++ b/lib/subghz/protocols/hollarm.c @@ -0,0 +1,471 @@ +#include "hollarm.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#include "../blocks/custom_btn_i.h" + +#define TAG "SubGhzProtocolHollarm" + +static const SubGhzBlockConst subghz_protocol_hollarm_const = { + .te_short = 200, + .te_long = 1000, + .te_delta = 200, + .min_count_bit_for_found = 42, +}; + +struct SubGhzProtocolDecoderHollarm { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderHollarm { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + HollarmDecoderStepReset = 0, + HollarmDecoderStepSaveDuration, + HollarmDecoderStepCheckDuration, +} HollarmDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_hollarm_decoder = { + .alloc = subghz_protocol_decoder_hollarm_alloc, + .free = subghz_protocol_decoder_hollarm_free, + + .feed = subghz_protocol_decoder_hollarm_feed, + .reset = subghz_protocol_decoder_hollarm_reset, + + .get_hash_data = NULL, + .get_hash_data_long = subghz_protocol_decoder_hollarm_get_hash_data, + .serialize = subghz_protocol_decoder_hollarm_serialize, + .deserialize = subghz_protocol_decoder_hollarm_deserialize, + .get_string = subghz_protocol_decoder_hollarm_get_string, + .get_string_brief = NULL, +}; + +const SubGhzProtocolEncoder subghz_protocol_hollarm_encoder = { + .alloc = subghz_protocol_encoder_hollarm_alloc, + .free = subghz_protocol_encoder_hollarm_free, + + .deserialize = subghz_protocol_encoder_hollarm_deserialize, + .stop = subghz_protocol_encoder_hollarm_stop, + .yield = subghz_protocol_encoder_hollarm_yield, +}; + +const SubGhzProtocol subghz_protocol_hollarm = { + .name = SUBGHZ_PROTOCOL_HOLLARM_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_hollarm_decoder, + .encoder = &subghz_protocol_hollarm_encoder, +}; + +void* subghz_protocol_encoder_hollarm_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHollarm* instance = malloc(sizeof(SubGhzProtocolEncoderHollarm)); + + instance->base.protocol = &subghz_protocol_hollarm; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_hollarm_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHollarm* instance = context; + free(instance->encoder.upload); + free(instance); +} + +// Get custom button code +static uint8_t subghz_protocol_hollarm_get_btn_code(void) { + uint8_t custom_btn_id = subghz_custom_btn_get(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); + uint8_t btn = original_btn_code; + + // Set custom button + if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { + // Restore original button code + btn = original_btn_code; + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { + switch(original_btn_code) { + case 0x1: + btn = 0x2; + break; + case 0x2: + btn = 0x1; + break; + case 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = 0x4; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + + default: + break; + } + } + + return btn; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHollarm instance + */ +static void subghz_protocol_encoder_hollarm_get_upload(SubGhzProtocolEncoderHollarm* instance) { + furi_assert(instance); + + // Generate new key using custom or default button + instance->generic.btn = subghz_protocol_hollarm_get_btn_code(); + + uint64_t new_key = (instance->generic.data >> 12) << 12 | (instance->generic.btn << 8); + + uint8_t crc = ((new_key >> 32) & 0xFF) + ((new_key >> 24) & 0xFF) + ((new_key >> 16) & 0xFF) + + ((new_key >> 8) & 0xFF); + + instance->generic.data = (new_key | crc); + + size_t index = 0; + + // Send key and GAP between parcels + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + // Read and prepare levels with 2 bit (was saved for better parsing) to the left offset to fit with the original remote transmission + if(bit_read((instance->generic.data << 2), i - 1)) { + // Send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hollarm_const.te_short); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_hollarm_const.te_short * 12); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_hollarm_const.te_short * 8); + } + } else { + // Send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hollarm_const.te_short); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_hollarm_const.te_short * 12); + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hollarm_const.te_long); + } + } + } + + instance->encoder.size_upload = index; + return; +} + +/** + * Analysis of received data and parsing serial number + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_hollarm_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = (instance->data >> 8) & 0xF; + instance->serial = (instance->data & 0xFFFFFFF0000) >> 16; + + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->btn); + } + subghz_custom_btn_set_max(3); + + // Hollarm Decoder + // 09.2024 - @xMasterX (MMX) + // Thanks @Skorpionm for support! + + // F0B93422FF = FF 8bit Sum + // F0B93421FE = FE 8bit Sum + // F0B9342401 = 01 8bit Sum + // F0B9342805 = 05 8bit Sum + + // Serial (moved 2bit to right) | Btn | 8b CRC (previous 4 bytes sum) + // 00001111000010111001001101000010 0010 11111111 btn = (0x2) + // 00001111000010111001001101000010 0001 11111110 btn = (0x1) + // 00001111000010111001001101000010 0100 00000001 btn = (0x4) + // 00001111000010111001001101000010 1000 00000101 btn = (0x8) +} + +SubGhzProtocolStatus + subghz_protocol_encoder_hollarm_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHollarm* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_hollarm_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_hollarm_remote_controller(&instance->generic); + subghz_protocol_encoder_hollarm_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_hollarm_stop(void* context) { + SubGhzProtocolEncoderHollarm* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_hollarm_yield(void* context) { + SubGhzProtocolEncoderHollarm* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_hollarm_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHollarm* instance = malloc(sizeof(SubGhzProtocolDecoderHollarm)); + instance->base.protocol = &subghz_protocol_hollarm; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_hollarm_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHollarm* instance = context; + free(instance); +} + +void subghz_protocol_decoder_hollarm_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHollarm* instance = context; + instance->decoder.parser_step = HollarmDecoderStepReset; +} + +void subghz_protocol_decoder_hollarm_feed(void* context, bool level, volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHollarm* instance = context; + + switch(instance->decoder.parser_step) { + case HollarmDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_hollarm_const.te_short * 12) < + subghz_protocol_hollarm_const.te_delta * 2)) { + //Found GAP between parcels + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = HollarmDecoderStepSaveDuration; + } + break; + case HollarmDecoderStepSaveDuration: + // Save HIGH level timing for next step + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = HollarmDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = HollarmDecoderStepReset; + } + break; + case HollarmDecoderStepCheckDuration: + if(!level) { + // Bit 0 is short 200us HIGH + long 1000us LOW timing + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hollarm_const.te_short) < + subghz_protocol_hollarm_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_hollarm_const.te_long) < + subghz_protocol_hollarm_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = HollarmDecoderStepSaveDuration; + // Bit 1 is short 200us HIGH + short x8 = 1600us LOW timing + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hollarm_const.te_short) < + subghz_protocol_hollarm_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_hollarm_const.te_short * 8) < + subghz_protocol_hollarm_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = HollarmDecoderStepSaveDuration; + } else if( + // End of the key + DURATION_DIFF(duration, subghz_protocol_hollarm_const.te_short * 12) < + subghz_protocol_hollarm_const.te_delta) { + // When next GAP is found add bit 0 and do check for read finish + // (we have 42 high level pulses, last or first one may be a stop/start bit but we will parse it as zero) + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + + // If got 42 bits key reading is finished + if(instance->decoder.decode_count_bit == + subghz_protocol_hollarm_const.min_count_bit_for_found) { + // Saving with 2bit to the right offset for proper parsing + instance->generic.data = (instance->decoder.decode_data >> 2); + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = HollarmDecoderStepReset; + } else { + instance->decoder.parser_step = HollarmDecoderStepReset; + } + } else { + instance->decoder.parser_step = HollarmDecoderStepReset; + } + break; + } +} + +/** + * Get button name. + * @param btn Button number, 4 bit + */ +static const char* subghz_protocol_hollarm_get_button_name(uint8_t btn) { + const char* name_btn[16] = { + "Unknown", + "Disarm", // B (2) + "Arm", // A (1) + "0x3", + "Alarm", // C (3) + "0x5", + "0x6", + "0x7", + "Ring", // D (4) + "0x9", + "0xA", + "0xB", + "0xC", + "0xD", + "0xE", + "0xF"}; + return btn <= 0xf ? name_btn[btn] : name_btn[0]; +} + +uint32_t subghz_protocol_decoder_hollarm_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHollarm* instance = context; + return subghz_protocol_blocks_get_hash_data_long( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_hollarm_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHollarm* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_hollarm_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHollarm* instance = context; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_hollarm_const.min_count_bit_for_found); +} + +void subghz_protocol_decoder_hollarm_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHollarm* instance = context; + + // Parse serial + subghz_protocol_hollarm_remote_controller(&instance->generic); + // Get CRC + uint8_t crc = ((instance->generic.data >> 32) & 0xFF) + + ((instance->generic.data >> 24) & 0xFF) + + ((instance->generic.data >> 16) & 0xFF) + ((instance->generic.data >> 8) & 0xFF); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key: 0x%02lX%08lX\r\n" + "Serial: 0x%06lX CRC: %02X\r\n" + "Btn: 0x%01X - %s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.serial, + crc, + instance->generic.btn, + subghz_protocol_hollarm_get_button_name(instance->generic.btn)); +} diff --git a/lib/subghz/protocols/hollarm.h b/lib/subghz/protocols/hollarm.h new file mode 100644 index 000000000..4d73ba660 --- /dev/null +++ b/lib/subghz/protocols/hollarm.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HOLLARM_NAME "Hollarm" + +typedef struct SubGhzProtocolDecoderHollarm SubGhzProtocolDecoderHollarm; +typedef struct SubGhzProtocolEncoderHollarm SubGhzProtocolEncoderHollarm; + +extern const SubGhzProtocolDecoder subghz_protocol_hollarm_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_hollarm_encoder; +extern const SubGhzProtocol subghz_protocol_hollarm; + +/** + * Allocate SubGhzProtocolEncoderHollarm. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHollarm* pointer to a SubGhzProtocolEncoderHollarm instance + */ +void* subghz_protocol_encoder_hollarm_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHollarm. + * @param context Pointer to a SubGhzProtocolEncoderHollarm instance + */ +void subghz_protocol_encoder_hollarm_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHollarm instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_hollarm_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHollarm instance + */ +void subghz_protocol_encoder_hollarm_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHollarm instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_hollarm_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHollarm. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHollarm* pointer to a SubGhzProtocolDecoderHollarm instance + */ +void* subghz_protocol_decoder_hollarm_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHollarm. + * @param context Pointer to a SubGhzProtocolDecoderHollarm instance + */ +void subghz_protocol_decoder_hollarm_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHollarm. + * @param context Pointer to a SubGhzProtocolDecoderHollarm instance + */ +void subghz_protocol_decoder_hollarm_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHollarm instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_hollarm_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHollarm instance + * @return hash Hash sum + */ +uint32_t subghz_protocol_decoder_hollarm_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHollarm. + * @param context Pointer to a SubGhzProtocolDecoderHollarm instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_hollarm_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHollarm. + * @param context Pointer to a SubGhzProtocolDecoderHollarm instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_hollarm_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHollarm instance + * @param output Resulting text + */ +void subghz_protocol_decoder_hollarm_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/marantec24.c b/lib/subghz/protocols/marantec24.c new file mode 100644 index 000000000..2f9acfe73 --- /dev/null +++ b/lib/subghz/protocols/marantec24.c @@ -0,0 +1,344 @@ +#include "marantec24.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMarantec24" + +static const SubGhzBlockConst subghz_protocol_marantec24_const = { + .te_short = 800, + .te_long = 1600, + .te_delta = 200, + .min_count_bit_for_found = 24, +}; + +struct SubGhzProtocolDecoderMarantec24 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderMarantec24 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Marantec24DecoderStepReset = 0, + Marantec24DecoderStepSaveDuration, + Marantec24DecoderStepCheckDuration, +} Marantec24DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_marantec24_decoder = { + .alloc = subghz_protocol_decoder_marantec24_alloc, + .free = subghz_protocol_decoder_marantec24_free, + + .feed = subghz_protocol_decoder_marantec24_feed, + .reset = subghz_protocol_decoder_marantec24_reset, + + .get_hash_data = NULL, + .get_hash_data_long = subghz_protocol_decoder_marantec24_get_hash_data, + .serialize = subghz_protocol_decoder_marantec24_serialize, + .deserialize = subghz_protocol_decoder_marantec24_deserialize, + .get_string = subghz_protocol_decoder_marantec24_get_string, + .get_string_brief = NULL, +}; + +const SubGhzProtocolEncoder subghz_protocol_marantec24_encoder = { + .alloc = subghz_protocol_encoder_marantec24_alloc, + .free = subghz_protocol_encoder_marantec24_free, + + .deserialize = subghz_protocol_encoder_marantec24_deserialize, + .stop = subghz_protocol_encoder_marantec24_stop, + .yield = subghz_protocol_encoder_marantec24_yield, +}; + +const SubGhzProtocol subghz_protocol_marantec24 = { + .name = SUBGHZ_PROTOCOL_MARANTEC24_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_marantec24_decoder, + .encoder = &subghz_protocol_marantec24_encoder, +}; + +void* subghz_protocol_encoder_marantec24_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMarantec24* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec24)); + + instance->base.protocol = &subghz_protocol_marantec24; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_marantec24_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMarantec24* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMarantec24 instance + */ +static void + subghz_protocol_encoder_marantec24_get_upload(SubGhzProtocolEncoderMarantec24* instance) { + furi_assert(instance); + size_t index = 0; + + // Send key and GAP + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + // Send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_marantec24_const.te_short); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_marantec24_const.te_long * 9 + + subghz_protocol_marantec24_const.te_short); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_marantec24_const.te_long * 2); + } + } else { + // Send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_marantec24_const.te_long); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_marantec24_const.te_long * 9 + + subghz_protocol_marantec24_const.te_short); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_marantec24_const.te_short * 3); + } + } + } + + instance->encoder.size_upload = index; + return; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_marantec24_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->serial = instance->data >> 4; + instance->btn = instance->data & 0xF; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_marantec24_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMarantec24* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_marantec24_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_marantec24_check_remote_controller(&instance->generic); + subghz_protocol_encoder_marantec24_get_upload(instance); + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_marantec24_stop(void* context) { + SubGhzProtocolEncoderMarantec24* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_marantec24_yield(void* context) { + SubGhzProtocolEncoderMarantec24* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_marantec24_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMarantec24* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec24)); + instance->base.protocol = &subghz_protocol_marantec24; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_marantec24_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec24* instance = context; + free(instance); +} + +void subghz_protocol_decoder_marantec24_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec24* instance = context; + instance->decoder.parser_step = Marantec24DecoderStepReset; +} + +void subghz_protocol_decoder_marantec24_feed(void* context, bool level, volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMarantec24* instance = context; + + // Key samples + // 101011000000010111001000 = AC05C8 + // 101011000000010111000100 = AC05C4 + // 101011000000010111001100 = AC05CC + // 101011000000010111000000 = AC05C0 + + switch(instance->decoder.parser_step) { + case Marantec24DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) < + subghz_protocol_marantec24_const.te_delta * 4)) { + //Found GAP + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = Marantec24DecoderStepSaveDuration; + } + break; + case Marantec24DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Marantec24DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Marantec24DecoderStepReset; + } + break; + case Marantec24DecoderStepCheckDuration: + if(!level) { + // Bit 0 is long and short x2 timing = 1600us HIGH (te_last) and 2400us LOW + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_marantec24_const.te_long) < + subghz_protocol_marantec24_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_short * 3) < + subghz_protocol_marantec24_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Marantec24DecoderStepSaveDuration; + // Bit 1 is short and long x2 timing = 800us HIGH (te_last) and 3200us LOW + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_marantec24_const.te_short) < + subghz_protocol_marantec24_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 2) < + subghz_protocol_marantec24_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Marantec24DecoderStepSaveDuration; + } else if( + // End of the key + DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) < + subghz_protocol_marantec24_const.te_delta * 4) { + //Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes) + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_marantec24_const.te_long) < + subghz_protocol_marantec24_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) < + subghz_protocol_marantec24_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_marantec24_const.te_short) < + subghz_protocol_marantec24_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) < + subghz_protocol_marantec24_const.te_delta * 4)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + // If got 24 bits key reading is finished + if(instance->decoder.decode_count_bit == + subghz_protocol_marantec24_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = Marantec24DecoderStepReset; + } else { + instance->decoder.parser_step = Marantec24DecoderStepReset; + } + } else { + instance->decoder.parser_step = Marantec24DecoderStepReset; + } + break; + } +} + +uint32_t subghz_protocol_decoder_marantec24_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec24* instance = context; + return subghz_protocol_blocks_get_hash_data_long( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_marantec24_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMarantec24* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_marantec24_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMarantec24* instance = context; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_marantec24_const.min_count_bit_for_found); +} + +void subghz_protocol_decoder_marantec24_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMarantec24* instance = context; + + subghz_protocol_marantec24_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key: 0x%06lX\r\n" + "Serial: 0x%05lX\r\n" + "Btn: %01X", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/lib/subghz/protocols/marantec24.h b/lib/subghz/protocols/marantec24.h new file mode 100644 index 000000000..fbfff268e --- /dev/null +++ b/lib/subghz/protocols/marantec24.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MARANTEC24_NAME "Marantec24" + +typedef struct SubGhzProtocolDecoderMarantec24 SubGhzProtocolDecoderMarantec24; +typedef struct SubGhzProtocolEncoderMarantec24 SubGhzProtocolEncoderMarantec24; + +extern const SubGhzProtocolDecoder subghz_protocol_marantec24_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_marantec24_encoder; +extern const SubGhzProtocol subghz_protocol_marantec24; + +/** + * Allocate SubGhzProtocolEncoderMarantec24. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMarantec24* pointer to a SubGhzProtocolEncoderMarantec24 instance + */ +void* subghz_protocol_encoder_marantec24_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMarantec24. + * @param context Pointer to a SubGhzProtocolEncoderMarantec24 instance + */ +void subghz_protocol_encoder_marantec24_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMarantec24 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_marantec24_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMarantec24 instance + */ +void subghz_protocol_encoder_marantec24_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMarantec24 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_marantec24_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMarantec24. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMarantec24* pointer to a SubGhzProtocolDecoderMarantec24 instance + */ +void* subghz_protocol_decoder_marantec24_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMarantec24. + * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance + */ +void subghz_protocol_decoder_marantec24_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMarantec24. + * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance + */ +void subghz_protocol_decoder_marantec24_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_marantec24_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance + * @return hash Hash sum + */ +uint32_t subghz_protocol_decoder_marantec24_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMarantec24. + * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_marantec24_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMarantec24. + * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_marantec24_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMarantec24 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_marantec24_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 13665cf0b..42d87bd8d 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -74,6 +74,10 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_hormann_bisecur, &subghz_protocol_legrand, &subghz_protocol_dickert_mahs, + &subghz_protocol_gangqi, + &subghz_protocol_marantec24, + &subghz_protocol_hollarm, + &subghz_protocol_hay21, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 133d89135..12ce8bd42 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -75,3 +75,7 @@ #include "hormann_bisecur.h" #include "legrand.h" #include "dickert_mahs.h" +#include "gangqi.h" +#include "marantec24.h" +#include "hollarm.h" +#include "hay21.h" diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 30654567e..1b8d4b667 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -4,6 +4,7 @@ #include #include #include +#include #define TAG "SubGhzFileEncoderWorker" @@ -45,39 +46,35 @@ void subghz_file_encoder_worker_add_level_duration( } bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { - char* str1; - int32_t temp_ds = 0; - bool res = false; // Line sample: "RAW_Data: -1, 2, -2..." - // Look for a key in the line - str1 = strstr(strStart, "RAW_Data: "); + // Look for the key in the line + char* str = strstr(strStart, "RAW_Data: "); + bool res = false; - if(str1 != NULL) { + if(str) { // Skip key - str1 = strchr(str1, ' '); + str = strchr(str, ' '); - // Check that there is still an element in the line - while(strchr(str1, ' ') != NULL) { - str1 = strchr(str1, ' '); - - // Skip space - str1 += 1; - // - temp_ds = atoi(str1); - if((temp_ds < -1000000) || (temp_ds > 1000000)) { - if(temp_ds > 0) { + // Parse next element + int32_t duration; + while(strint_to_int32(str, &str, &duration, 10) == StrintParseNoError) { + if((duration < -1000000) || (duration > 1000000)) { + if(duration > 0) { subghz_file_encoder_worker_add_level_duration(instance, (int32_t)100); } else { subghz_file_encoder_worker_add_level_duration(instance, (int32_t)-100); } - //FURI_LOG_I("PARSE", "Number overflow - %d", atoi(str1)); + //FURI_LOG_I("PARSE", "Number overflow - %d", duration); } else { - subghz_file_encoder_worker_add_level_duration(instance, temp_ds); + subghz_file_encoder_worker_add_level_duration(instance, duration); } + if(*str == ',') str++; // could also be `\0` } + res = true; } + return res; } diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 11e01a8c9..03b8999c4 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -29,6 +29,7 @@ env.Append( File("stream/file_stream.h"), File("stream/string_stream.h"), File("stream/buffered_file_stream.h"), + File("strint.h"), File("protocols/protocol_dict.h"), File("pretty_format.h"), File("hex.h"), diff --git a/lib/toolbox/args.c b/lib/toolbox/args.c index aa790ad68..914b093ba 100644 --- a/lib/toolbox/args.c +++ b/lib/toolbox/args.c @@ -1,5 +1,7 @@ #include "args.h" #include "hex.h" +#include "strint.h" +#include "m-core.h" size_t args_get_first_word_length(FuriString* args) { size_t ws = furi_string_search_char(args, ' '); @@ -21,7 +23,9 @@ bool args_read_int_and_trim(FuriString* args, int* value) { return false; } - if(sscanf(furi_string_get_cstr(args), "%d", value) == 1) { + int32_t temp; + if(strint_to_int32(furi_string_get_cstr(args), NULL, &temp, 10) == StrintParseNoError) { + *value = temp; furi_string_right(args, cmd_length); furi_string_trim(args); return true; diff --git a/lib/toolbox/strint.c b/lib/toolbox/strint.c new file mode 100644 index 000000000..8c7f36976 --- /dev/null +++ b/lib/toolbox/strint.c @@ -0,0 +1,121 @@ +#include "strint.h" + +#include + +// Splitting out the actual parser helps reduce code size. The manually +// monomorphized `strint_to_*`s are just wrappers around this generic +// implementation. +/** + * @brief Converts a string to a `uint64_t` and an auxillary sign bit, checking + * the bounds of the integer. + * @param [in] str Input string + * @param [out] end Pointer to first character after the number in input string + * @param [out] abs_out Absolute part of result + * @param [out] negative_out Sign part of result (true=negative, false=positive) + * @param [in] base Integer base + * @param [in] max_abs_negative Largest permissible absolute part of result if + * the sign is negative + * @param [in] max_positive Largest permissible absolute part of result if the + * sign is positive + */ +StrintParseError strint_to_uint64_internal( + const char* str, + char** end, + uint64_t* abs_out, + bool* negative_out, + uint8_t base, + uint64_t max_abs_negative, + uint64_t max_positive) { + // skip whitespace + while(((*str >= '\t') && (*str <= '\r')) || *str == ' ') { + str++; + } + + // read sign + bool negative = false; + if(*str == '+' || *str == '-') { + if(*str == '-') negative = true; + str++; + } + if(*str == '+' || *str == '-') return StrintParseSignError; + if(max_abs_negative == 0 && negative) return StrintParseSignError; + + // infer base + // not assigning directly to `base' to permit prefixes with explicit bases + uint8_t inferred_base = 0; + if(strncasecmp(str, "0x", 2) == 0) { + inferred_base = 16; + str += 2; + } else if(strncasecmp(str, "0b", 2) == 0) { + inferred_base = 2; + str += 2; + } else if(*str == '0') { + inferred_base = 8; + str++; + } else { + inferred_base = 10; + } + if(base == 0) base = inferred_base; + + // read digits + uint64_t limit = negative ? max_abs_negative : max_positive; + uint64_t mul_limit = limit / base; + uint64_t result = 0; + int read_total = 0; + while(*str != 0) { + int digit_value; + if(*str >= '0' && *str <= '9') { + digit_value = *str - '0'; + } else if(*str >= 'A' && *str <= 'Z') { + digit_value = *str - 'A' + 10; + } else if(*str >= 'a' && *str <= 'z') { + digit_value = *str - 'a' + 10; + } else { + break; + } + + if(digit_value >= base) { + break; + } + + if(result > mul_limit) return StrintParseOverflowError; + result *= base; + if(result > limit - digit_value) return StrintParseOverflowError; + result += digit_value; + + read_total++; + str++; + } + + if(read_total == 0) { + if(inferred_base == 8) { + // there's just a single zero + result = 0; + } else { + return StrintParseAbsentError; + } + } + + if(abs_out) *abs_out = result; + if(negative_out) *negative_out = negative; + if(end) *end = (char*)str; // rabbit hole: https://c-faq.com/ansi/constmismatch.html + return StrintParseNoError; +} + +#define STRINT_MONO(name, ret_type, neg_abs_limit, pos_limit) \ + StrintParseError name(const char* str, char** end, ret_type* out, uint8_t base) { \ + uint64_t absolute; \ + bool negative; \ + StrintParseError err = strint_to_uint64_internal( \ + str, end, &absolute, &negative, base, (neg_abs_limit), (pos_limit)); \ + if(err) return err; \ + if(out) *out = (negative ? (-(ret_type)absolute) : ((ret_type)absolute)); \ + return StrintParseNoError; \ + } + +STRINT_MONO(strint_to_uint64, uint64_t, 0, UINT64_MAX) +STRINT_MONO(strint_to_int64, int64_t, (uint64_t)INT64_MAX + 1, INT64_MAX) +STRINT_MONO(strint_to_uint32, uint32_t, 0, UINT32_MAX) +STRINT_MONO(strint_to_int32, int32_t, (uint64_t)INT32_MAX + 1, INT32_MAX) +STRINT_MONO(strint_to_uint16, uint16_t, 0, UINT16_MAX) +STRINT_MONO(strint_to_int16, int16_t, (uint64_t)INT16_MAX + 1, INT16_MAX) diff --git a/lib/toolbox/strint.h b/lib/toolbox/strint.h new file mode 100644 index 000000000..c27cdcb4e --- /dev/null +++ b/lib/toolbox/strint.h @@ -0,0 +1,70 @@ +/** + * @file strint.h + * Performs conversions between strings and integers. + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** String to integer conversion error */ +typedef enum { + StrintParseNoError, //!< Conversion performed successfully + StrintParseSignError, //!< Multiple leading `+` or `-` characters, or leading `-` character if the type is unsigned + StrintParseAbsentError, //!< No valid digits after the leading whitespace, sign and prefix + StrintParseOverflowError, //!< Result does not fit in the requested type +} StrintParseError; + +/** See `strint_to_uint32` */ +StrintParseError strint_to_uint64(const char* str, char** end, uint64_t* out, uint8_t base); + +/** See `strint_to_uint32` */ +StrintParseError strint_to_int64(const char* str, char** end, int64_t* out, uint8_t base); + +/** Converts a string to a `uint32_t` + * + * @param[in] str Input string + * @param[out] end Pointer to first character after the number in input string + * @param[out] out Parse result + * @param[in] base Integer base + * + * @return Parse error + * + * Parses the number in the input string. The number may be surrounded by + * whitespace characters to the left and any non-digit characters to the right. + * What's considered a digit is determined by the input base in the following + * order: `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`. The number may be prefixed + * with either a `+` or a `-` to indicate its sign. The pointer to the first + * character after the leading whitespace, allowed prefixes and digits is + * assigned to `end`. + * + * If the input base is 0, the base is inferred from the leading characters of + * the number: + * - If it starts with `0x`, it's read in base 16; + * - If it starts with a `0`, it's read in base 8; + * - If it starts with `0b`, it's read in base 2. + * - Otherwise, it's read in base 10. + * + * For a description of the return codes, see `StrintParseError`. If the return + * code is something other than `StrintParseNoError`, the values at `end` and + * `out` are unaltered. + */ +StrintParseError strint_to_uint32(const char* str, char** end, uint32_t* out, uint8_t base); + +/** See `strint_to_uint32` */ +StrintParseError strint_to_int32(const char* str, char** end, int32_t* out, uint8_t base); + +/** See `strint_to_uint32` */ +StrintParseError strint_to_uint16(const char* str, char** end, uint16_t* out, uint8_t base); + +/** See `strint_to_uint32` */ +StrintParseError strint_to_int16(const char* str, char** end, int16_t* out, uint8_t base); + +#ifdef __cplusplus +} +#endif diff --git a/lib/update_util/int_backup.c b/lib/update_util/int_backup.c new file mode 100644 index 000000000..a904db247 --- /dev/null +++ b/lib/update_util/int_backup.c @@ -0,0 +1,51 @@ +#include "int_backup.h" + +#include + +#include +#include +#include +#include +#include +#include + +#define INT_BACKUP_DEFAULT_LOCATION EXT_PATH(INT_BACKUP_DEFAULT_FILENAME) + +static void backup_name_converter(FuriString* filename) { + if(furi_string_empty(filename) || (furi_string_get_char(filename, 0) == '.')) { + return; + } + + /* Filenames are already prefixed with '.' */ + const char* const names[] = { + BT_SETTINGS_FILE_NAME, + BT_KEYS_STORAGE_FILE_NAME, + DESKTOP_SETTINGS_FILE_NAME, + NOTIFICATION_SETTINGS_FILE_NAME, + SLIDESHOW_FILE_NAME, + DOLPHIN_STATE_FILE_NAME, + }; + + for(size_t i = 0; i < COUNT_OF(names); i++) { + if(furi_string_equal(filename, &names[i][1])) { + furi_string_set(filename, names[i]); + return; + } + } +} + +bool int_backup_create(Storage* storage, const char* destination) { + const char* final_destination = + destination && strlen(destination) ? destination : INT_BACKUP_DEFAULT_LOCATION; + return storage_int_backup(storage, final_destination) == FSE_OK; +} + +bool int_backup_exists(Storage* storage, const char* source) { + const char* final_source = source && strlen(source) ? source : INT_BACKUP_DEFAULT_LOCATION; + return storage_common_stat(storage, final_source, NULL) == FSE_OK; +} + +bool int_backup_unpack(Storage* storage, const char* source) { + const char* final_source = source && strlen(source) ? source : INT_BACKUP_DEFAULT_LOCATION; + return storage_int_restore(storage, final_source, backup_name_converter) == FSE_OK; +} diff --git a/lib/update_util/int_backup.h b/lib/update_util/int_backup.h new file mode 100644 index 000000000..168efda50 --- /dev/null +++ b/lib/update_util/int_backup.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#define INT_BACKUP_DEFAULT_FILENAME "backup.tar" + +#ifdef __cplusplus +extern "C" { +#endif + +bool int_backup_create(Storage* storage, const char* destination); +bool int_backup_exists(Storage* storage, const char* source); +bool int_backup_unpack(Storage* storage, const char* source); + +#ifdef __cplusplus +} +#endif diff --git a/lib/update_util/lfs_backup.c b/lib/update_util/lfs_backup.c deleted file mode 100644 index a441ccabd..000000000 --- a/lib/update_util/lfs_backup.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "lfs_backup.h" - -#include - -#define LFS_BACKUP_DEFAULT_LOCATION EXT_PATH(LFS_BACKUP_DEFAULT_FILENAME) - -bool lfs_backup_create(Storage* storage, const char* destination) { - const char* final_destination = - destination && strlen(destination) ? destination : LFS_BACKUP_DEFAULT_LOCATION; - return storage_int_backup(storage, final_destination) == FSE_OK; -} - -bool lfs_backup_exists(Storage* storage, const char* source) { - const char* final_source = source && strlen(source) ? source : LFS_BACKUP_DEFAULT_LOCATION; - return storage_common_stat(storage, final_source, NULL) == FSE_OK; -} - -bool lfs_backup_unpack(Storage* storage, const char* source) { - const char* final_source = source && strlen(source) ? source : LFS_BACKUP_DEFAULT_LOCATION; - return storage_int_restore(storage, final_source, NULL) == FSE_OK; -} diff --git a/lib/update_util/lfs_backup.h b/lib/update_util/lfs_backup.h deleted file mode 100644 index 5a7738c86..000000000 --- a/lib/update_util/lfs_backup.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include - -#define LFS_BACKUP_DEFAULT_FILENAME "backup.tar" - -#ifdef __cplusplus -extern "C" { -#endif - -bool lfs_backup_create(Storage* storage, const char* destination); -bool lfs_backup_exists(Storage* storage, const char* source); -bool lfs_backup_unpack(Storage* storage, const char* source); - -#ifdef __cplusplus -} -#endif diff --git a/lib/update_util/resources/manifest.c b/lib/update_util/resources/manifest.c index 580a76d45..92d84a779 100644 --- a/lib/update_util/resources/manifest.c +++ b/lib/update_util/resources/manifest.c @@ -1,6 +1,7 @@ #include "manifest.h" #include +#include #include struct ResourceManifestReader { @@ -97,7 +98,12 @@ ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* res furi_string_right( resource_manifest->linebuf, sizeof(resource_manifest->entry.hash) * 2 + 1); - resource_manifest->entry.size = atoi(furi_string_get_cstr(resource_manifest->linebuf)); + if(strint_to_uint32( + furi_string_get_cstr(resource_manifest->linebuf), + NULL, + &resource_manifest->entry.size, + 10) != StrintParseNoError) + break; /* Remove size */ size_t offs = furi_string_search_char(resource_manifest->linebuf, ':'); diff --git a/scripts/debug/flipperapps.py b/scripts/debug/flipperapps.py index 6dba89a56..81aa43c34 100644 --- a/scripts/debug/flipperapps.py +++ b/scripts/debug/flipperapps.py @@ -124,7 +124,7 @@ class SetFapDebugElfRoot(gdb.Command): print(f"Set '{arg}' as debug info lookup path for Flipper external apps") helper.attach_to_fw() gdb.events.stop.connect(helper.handle_stop) - gdb.events.exited.connect(helper.handle_exit) + gdb.events.gdb_exiting.connect(helper.handle_exit) except gdb.error as e: print(f"Support for Flipper external apps debug is not available: {e}") diff --git a/scripts/fbt_tools/pvsstudio.py b/scripts/fbt_tools/pvsstudio.py index 290531321..1a55278dc 100644 --- a/scripts/fbt_tools/pvsstudio.py +++ b/scripts/fbt_tools/pvsstudio.py @@ -47,6 +47,7 @@ def generate(env): PVSOPTIONS=[ "@.pvsoptions", "-j${PVSNCORES}", + "--disableLicenseExpirationCheck", # "--incremental", # kinda broken on PVS side ], PVSCONVOPTIONS=[ diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index ecc3849c9..553cf1472 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -13,13 +13,13 @@ Header,+,applications/services/gui/icon_i.h,, Header,+,applications/services/gui/modules/button_menu.h,, Header,+,applications/services/gui/modules/button_panel.h,, Header,+,applications/services/gui/modules/byte_input.h,, -Header,+,applications/services/gui/modules/number_input.h,, Header,+,applications/services/gui/modules/dialog_ex.h,, Header,+,applications/services/gui/modules/empty_screen.h,, Header,+,applications/services/gui/modules/file_browser.h,, Header,+,applications/services/gui/modules/file_browser_worker.h,, Header,+,applications/services/gui/modules/loading.h,, Header,+,applications/services/gui/modules/menu.h,, +Header,+,applications/services/gui/modules/number_input.h,, Header,+,applications/services/gui/modules/popup.h,, Header,+,applications/services/gui/modules/submenu.h,, Header,+,applications/services/gui/modules/text_box.h,, @@ -170,6 +170,7 @@ Header,+,lib/toolbox/stream/buffered_file_stream.h,, Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, Header,+,lib/toolbox/stream/string_stream.h,, +Header,+,lib/toolbox/strint.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/varint.h,, @@ -723,11 +724,6 @@ Function,+,byte_input_free,void,ByteInput* Function,+,byte_input_get_view,View*,ByteInput* Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" -Function,+,number_input_alloc,NumberInput*, -Function,+,number_input_free,void,NumberInput* -Function,+,number_input_get_view,View*,NumberInput* -Function,+,number_input_set_header_text,void,"NumberInput*, const char*" -Function,+,number_input_set_result_callback,void,"NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t" Function,-,bzero,void,"void*, size_t" Function,+,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* @@ -873,6 +869,8 @@ Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed Function,+,dolphin_flush,void,Dolphin* Function,+,dolphin_get_pubsub,FuriPubSub*,Dolphin* +Function,+,dolphin_get_settings,void,"Dolphin*, DolphinSettings*" +Function,+,dolphin_set_settings,void,"Dolphin*, DolphinSettings*" Function,+,dolphin_stats,DolphinStats,Dolphin* Function,+,dolphin_upgrade_level,void,Dolphin* Function,-,dprintf,int,"int, const char*, ..." @@ -883,8 +881,10 @@ Function,+,elements_bold_rounded_frame,void,"Canvas*, int32_t, int32_t, size_t, Function,+,elements_bubble,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,elements_bubble_str,void,"Canvas*, int32_t, int32_t, const char*, Align, Align" Function,+,elements_button_center,void,"Canvas*, const char*" +Function,+,elements_button_down,void,"Canvas*, const char*" Function,+,elements_button_left,void,"Canvas*, const char*" Function,+,elements_button_right,void,"Canvas*, const char*" +Function,+,elements_button_up,void,"Canvas*, const char*" Function,+,elements_frame,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,elements_multiline_text,void,"Canvas*, int32_t, int32_t, const char*" Function,+,elements_multiline_text_aligned,void,"Canvas*, int32_t, int32_t, Align, Align, const char*" @@ -2197,6 +2197,11 @@ Function,+,notification_internal_message_block,void,"NotificationApp*, const Not Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" Function,-,nrand48,long,unsigned short[3] +Function,+,number_input_alloc,NumberInput*, +Function,+,number_input_free,void,NumberInput* +Function,+,number_input_get_view,View*,NumberInput* +Function,+,number_input_set_header_text,void,"NumberInput*, const char*" +Function,+,number_input_set_result_callback,void,"NumberInput*, NumberInputCallback, void*, int32_t, int32_t, int32_t" Function,-,on_exit,int,"void (*)(int, void*), void*" Function,+,onewire_host_alloc,OneWireHost*,const GpioPin* Function,+,onewire_host_free,void,OneWireHost* @@ -2587,7 +2592,13 @@ Function,-,strerror,char*,int Function,-,strerror_l,char*,"int, locale_t" Function,-,strerror_r,char*,"int, char*, size_t" Function,+,string_stream_alloc,Stream*, -Function,-,strlcat,size_t,"char*, const char*, size_t" +Function,+,strint_to_int16,StrintParseError,"const char*, char**, int16_t*, uint8_t" +Function,+,strint_to_int32,StrintParseError,"const char*, char**, int32_t*, uint8_t" +Function,+,strint_to_int64,StrintParseError,"const char*, char**, int64_t*, uint8_t" +Function,+,strint_to_uint16,StrintParseError,"const char*, char**, uint16_t*, uint8_t" +Function,+,strint_to_uint32,StrintParseError,"const char*, char**, uint32_t*, uint8_t" +Function,+,strint_to_uint64,StrintParseError,"const char*, char**, uint64_t*, uint8_t" +Function,+,strlcat,size_t,"char*, const char*, size_t" Function,+,strlcpy,size_t,"char*, const char*, size_t" Function,+,strlen,size_t,const char* Function,-,strlwr,char*,char* @@ -2750,7 +2761,7 @@ Function,+,view_dispatcher_alloc,ViewDispatcher*, Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* Function,+,view_dispatcher_free,void,ViewDispatcher* -Function,-,view_dispatcher_get_event_loop,FuriEventLoop*,ViewDispatcher* +Function,+,view_dispatcher_get_event_loop,FuriEventLoop*,ViewDispatcher* Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" Function,+,view_dispatcher_run,void,ViewDispatcher* Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" diff --git a/targets/f18/furi_hal/furi_hal.c b/targets/f18/furi_hal/furi_hal.c index 247c2ee2d..b28606921 100644 --- a/targets/f18/furi_hal/furi_hal.c +++ b/targets/f18/furi_hal/furi_hal.c @@ -40,6 +40,7 @@ void furi_hal_init(void) { furi_hal_interrupt_init(); furi_hal_flash_init(); furi_hal_resources_init(); + furi_hal_region_init(); furi_hal_spi_config_init(); furi_hal_spi_dma_init(); furi_hal_speaker_init(); diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 54b61985a..5a9b9cff7 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -251,6 +251,7 @@ Header,+,lib/toolbox/stream/buffered_file_stream.h,, Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, Header,+,lib/toolbox/stream/string_stream.h,, +Header,+,lib/toolbox/strint.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/varint.h,, @@ -976,6 +977,8 @@ Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed Function,+,dolphin_flush,void,Dolphin* Function,+,dolphin_get_pubsub,FuriPubSub*,Dolphin* +Function,+,dolphin_get_settings,void,"Dolphin*, DolphinSettings*" +Function,+,dolphin_set_settings,void,"Dolphin*, DolphinSettings*" Function,+,dolphin_reload_state,void,Dolphin* Function,+,dolphin_stats,DolphinStats,Dolphin* Function,+,dolphin_upgrade_level,void,Dolphin* @@ -987,8 +990,10 @@ Function,+,elements_bold_rounded_frame,void,"Canvas*, int32_t, int32_t, size_t, Function,+,elements_bubble,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,elements_bubble_str,void,"Canvas*, int32_t, int32_t, const char*, Align, Align" Function,+,elements_button_center,void,"Canvas*, const char*" +Function,+,elements_button_down,void,"Canvas*, const char*" Function,+,elements_button_left,void,"Canvas*, const char*" Function,+,elements_button_right,void,"Canvas*, const char*" +Function,+,elements_button_up,void,"Canvas*, const char*" Function,+,elements_frame,void,"Canvas*, int32_t, int32_t, size_t, size_t" Function,+,elements_multiline_text,void,"Canvas*, int32_t, int32_t, const char*" Function,+,elements_multiline_text_aligned,void,"Canvas*, int32_t, int32_t, Align, Align, const char*" @@ -1494,7 +1499,7 @@ Function,+,furi_hal_nfc_abort,FuriHalNfcError, Function,+,furi_hal_nfc_acquire,FuriHalNfcError, Function,+,furi_hal_nfc_event_start,FuriHalNfcError, Function,+,furi_hal_nfc_event_stop,FuriHalNfcError, -Function,+,furi_hal_nfc_felica_listener_set_sensf_res_data,FuriHalNfcError,"const uint8_t*, const uint8_t, const uint8_t*, const uint8_t" +Function,+,furi_hal_nfc_felica_listener_set_sensf_res_data,FuriHalNfcError,"const uint8_t*, const uint8_t, const uint8_t*, const uint8_t, const uint16_t" Function,+,furi_hal_nfc_field_detect_start,FuriHalNfcError, Function,+,furi_hal_nfc_field_detect_stop,FuriHalNfcError, Function,+,furi_hal_nfc_field_is_present,_Bool, @@ -2853,7 +2858,7 @@ Function,+,nfc_device_save,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceData*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t" -Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t" +Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t, const uint16_t" Function,+,nfc_free,void,Nfc* Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t" Function,+,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuffer*" @@ -3373,7 +3378,13 @@ Function,-,strerror,char*,int Function,-,strerror_l,char*,"int, locale_t" Function,-,strerror_r,char*,"int, char*, size_t" Function,+,string_stream_alloc,Stream*, -Function,-,strlcat,size_t,"char*, const char*, size_t" +Function,+,strint_to_int16,StrintParseError,"const char*, char**, int16_t*, uint8_t" +Function,+,strint_to_int32,StrintParseError,"const char*, char**, int32_t*, uint8_t" +Function,+,strint_to_int64,StrintParseError,"const char*, char**, int64_t*, uint8_t" +Function,+,strint_to_uint16,StrintParseError,"const char*, char**, uint16_t*, uint8_t" +Function,+,strint_to_uint32,StrintParseError,"const char*, char**, uint32_t*, uint8_t" +Function,+,strint_to_uint64,StrintParseError,"const char*, char**, uint64_t*, uint8_t" +Function,+,strlcat,size_t,"char*, const char*, size_t" Function,+,strlcpy,size_t,"char*, const char*, size_t" Function,+,strlen,size_t,const char* Function,-,strlwr,char*,char* @@ -3735,7 +3746,7 @@ Function,+,view_dispatcher_alloc,ViewDispatcher*, Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* Function,+,view_dispatcher_free,void,ViewDispatcher* -Function,-,view_dispatcher_get_event_loop,FuriEventLoop*,ViewDispatcher* +Function,+,view_dispatcher_get_event_loop,FuriEventLoop*,ViewDispatcher* Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" Function,+,view_dispatcher_run,void,ViewDispatcher* Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" diff --git a/targets/f7/furi_hal/furi_hal_nfc_felica.c b/targets/f7/furi_hal/furi_hal_nfc_felica.c index 89305877e..6c3f55525 100644 --- a/targets/f7/furi_hal/furi_hal_nfc_felica.c +++ b/targets/f7/furi_hal/furi_hal_nfc_felica.c @@ -4,6 +4,20 @@ // Prevent FDT timer from starting #define FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC (INT32_MAX) +#define FURI_HAL_FELICA_COMMUNICATION_PERFORMANCE (0x0083U) +#define FURI_HAL_FELICA_RESPONSE_CODE (0x01) +#define FURI_HAL_FELICA_IDM_PMM_LENGTH (8) + +#pragma pack(push, 1) +typedef struct { + uint16_t system_code; + uint8_t response_code; + uint8_t Idm[FURI_HAL_FELICA_IDM_PMM_LENGTH]; + uint8_t Pmm[FURI_HAL_FELICA_IDM_PMM_LENGTH]; + uint16_t communication_performance; +} FuriHalFelicaPtMemory; +#pragma pack(pop) + static FuriHalNfcError furi_hal_nfc_felica_poller_init(FuriHalSpiBusHandle* handle) { // Enable Felica mode, AM modulation st25r3916_change_reg_bits( @@ -161,17 +175,23 @@ FuriHalNfcError furi_hal_nfc_felica_listener_set_sensf_res_data( const uint8_t* idm, const uint8_t idm_len, const uint8_t* pmm, - const uint8_t pmm_len) { + const uint8_t pmm_len, + const uint16_t sys_code) { furi_check(idm); furi_check(pmm); + furi_check(idm_len == FURI_HAL_FELICA_IDM_PMM_LENGTH); + furi_check(pmm_len == FURI_HAL_FELICA_IDM_PMM_LENGTH); FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; // Write PT Memory - uint8_t pt_memory[19] = {}; - pt_memory[2] = 0x01; - memcpy(pt_memory + 3, idm, idm_len); - memcpy(pt_memory + 3 + idm_len, pmm, pmm_len); - st25r3916_write_ptf_mem(handle, pt_memory, sizeof(pt_memory)); + FuriHalFelicaPtMemory pt; + pt.system_code = sys_code; + pt.response_code = FURI_HAL_FELICA_RESPONSE_CODE; + pt.communication_performance = __builtin_bswap16(FURI_HAL_FELICA_COMMUNICATION_PERFORMANCE); + memcpy(pt.Idm, idm, idm_len); + memcpy(pt.Pmm, pmm, pmm_len); + + st25r3916_write_ptf_mem(handle, (uint8_t*)&pt, sizeof(FuriHalFelicaPtMemory)); return FuriHalNfcErrorNone; } diff --git a/targets/f7/furi_hal/furi_hal_rtc.h b/targets/f7/furi_hal/furi_hal_rtc.h index 96c9f80de..9b7022eab 100644 --- a/targets/f7/furi_hal/furi_hal_rtc.h +++ b/targets/f7/furi_hal/furi_hal_rtc.h @@ -9,6 +9,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -44,7 +45,7 @@ typedef enum { FuriHalRtcRegisterHeader, /**< RTC structure header */ FuriHalRtcRegisterSystem, /**< Various system bits */ FuriHalRtcRegisterVersion, /**< Pointer to Version */ - FuriHalRtcRegisterLfsFingerprint, /**< LFS geometry fingerprint */ + FuriHalRtcRegisterLfsFingerprint FURI_DEPRECATED, /**< LFS geometry fingerprint */ FuriHalRtcRegisterFaultData, /**< Pointer to last fault message */ FuriHalRtcRegisterPinFails, /**< Failed PINs count */ /* Index of FS directory entry corresponding to FW update to be applied */ diff --git a/targets/f7/furi_hal/furi_hal_version.c b/targets/f7/furi_hal/furi_hal_version.c index 65faf0a6d..04929390f 100644 --- a/targets/f7/furi_hal/furi_hal_version.c +++ b/targets/f7/furi_hal/furi_hal_version.c @@ -109,7 +109,7 @@ void furi_hal_version_set_name(const char* name) { "x%s", // Someone tell me why that X is needed - it's for BLE adv name type (6 lines below) furi_hal_version.name); } else { - snprintf(furi_hal_version.device_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "xFlipper"); + strlcpy(furi_hal_version.device_name, "xFlipper", FURI_HAL_VERSION_DEVICE_NAME_LENGTH); } furi_hal_version.device_name[0] = AD_TYPE_COMPLETE_LOCAL_NAME; diff --git a/targets/furi_hal_include/furi_hal_nfc.h b/targets/furi_hal_include/furi_hal_nfc.h index 1c9f54d35..3a81de6f5 100644 --- a/targets/furi_hal_include/furi_hal_nfc.h +++ b/targets/furi_hal_include/furi_hal_nfc.h @@ -461,13 +461,15 @@ FuriHalNfcError furi_hal_nfc_iso15693_listener_tx_sof(void); * @param[in] idm_len IDm length in bytes. * @param[in] pmm pointer to a byte array containing the PMm. * @param[in] pmm_len PMm length in bytes. + * @param[in] sys_code System code from SYS_C block * @returns NfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_felica_listener_set_sensf_res_data( const uint8_t* idm, const uint8_t idm_len, const uint8_t* pmm, - const uint8_t pmm_len); + const uint8_t pmm_len, + const uint16_t sys_code); #ifdef __cplusplus }