Merge branch 'dev' into release

This commit is contained in:
MX 2024-09-11 02:45:59 +03:00
commit 572cf1b718
No known key found for this signature in database
GPG key ID: 7CCC66B7DBDD1C83
205 changed files with 5134 additions and 883 deletions

View file

@ -1,41 +1,46 @@
## Main changes
- SubGHz:
- OFW: Added protocol for Dickert MAHS garage door remote control
- Fix rare crash when opening Read mode via Frequency analyzer
- Refactor frequency analyzer code for better readability (by @derskythe | PR #782)
- Add new protocols (by @xMasterX) (big thanks to @Skorpionm for help with GangQi and Hollarm protocols!):
- Marantec24 (static 24 bit) with add manually support
- GangQi (static 34 bit) with button parsing and add manually support (thanks to @mishamyte for captures and testing)
- Hollarm (static 42 bit) with button parsing and add manually support (thanks to @mishamyte for captures)
- Hay21 (dynamic 21 bit) with button parsing
- Princeton custom buttons support (0x1, 0x2, 0x4, 0x8, 0xF)
- 125kHz RFID:
- OFW: Add lfrfid GProxII support
- 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
- NFC:
- OFW: Fix plantain balance string
- OFW: Now fifo size in ST25 chip is calculated properly
* Docs: Remove not printable symbols and update docs (by @derskythe | PR #783)
* OFW: Fix cumulative error in infrared signals
* OFW: iButton ID writing (Enable ID writing for ds1971 and ds1996)
- Saflok parser improvements (by @zinongli & @xtruan & @zacharyweiss & @evilmog & @Arkwin)
- OFW: Fix crash on Ultralight unlock (by @Astrrra)
- OFW: FeliCa anti-collision fix
* OFW: Rename 'Detect Reader' to 'Extract MF Keys'
* OFW: Happy mode
* OFW: Infrared: Universal AC - Add Airwell AW-HKD012-N91
* OFW: Broken file interaction fixes
* OFW: Add the Procrastination animation
* OFW PR 3892: Fix USB-UART bridge exit screen stopping the bridge prematurely (by @portasynthinca3)
* Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
## Other changes
* Archive: Fix BadUSB favourite path check
* Settings: Show free flash amount in internal storage info (by @Willy-JL)
* Misc: Fix typo in comment in QueueTools.py (by @eltociear | PR #785)
* OFW PR 3840: GUI: NumberInput small improvements (by @Willy-JL)
* OFW PR 3838: SubGhz: Fix RPC status for ButtonRelease event (by @Skorpionm)
* OFW: scripts: improved size validator for updater image
* OFW: Desktop: seaprate callbacks for dolphin and storage subscriptions
* OFW: Make file extensions case-insensitive
* OFW: Remove internal storage folder if corresponding flag set
* OFW: **Added a text input that only accepts full numbers (int)**
* OFW: FuriEventLoop Pt.2
* OFW: Images linting: ensure that all images conform specification
* OFW: **Storage: remove LFS**
* OFW: NFC: Change the plantain last number display from "?" to "X"
* OFW: CCID App: Refactor
* OFW: Refactor detected protocols list
* OFW: fix: Ensure proper closure of variadic function in `mjs_array`
* OFW: **Added** `-Wundef` **to compiler options**
* OFW: toolchain: v39
* OFW: Furi: update string documentation
* OFW: Fix typo in "charge me" screen.
* OFW: Reordered VS-Code Tasks to follow the `Release` > `Debug` schema
* OFW: Remove unused entries from .editorconfig
* Docs: Improved the description steps to create a new remote BFT Mitto with more detailed and accurate instructions (by @chrostino | PR #805)
* OFW: FuriTimer: Use an event instead of a volatile bool to wait for deletion
* OFW: Threading, Timers improvements
* OFW: Replace all calls to strncpy with strlcpy, use strdup more, expose strlcat
* OFW: feat: add linux/gnome badusb demo resource files
* OFW: Exposed `view_dispatcher_get_event_loop`
* OFW: Infrared button operation fails now shows more informative messages
* OFW: Loader: Warn about missing SD card for main apps
* OFW: Desktop: Sanity check PIN length for good measure
* OFW: DialogEx: Fix NULL ptr crash
* OFW: Debug: use proper hook for handle_exit in flipperapps
* OFW: Clean up of LFS traces
* OFW: Proper integer parsing
* OFW: SubGhz: Fix RPC status for ButtonRelease event
* OFW: CCID: App changes
* OFW: 5V on GPIO control for ext. modules
* OFW: Gui: Add up and down button drawing functions to GUI elements
* OFW: Gui: change dialog_ex text ownership model
* OFW: Publishing T5577 page 1 block count macro
<br><br>
#### Known NFC post-refactor regressions list:
- Mifare Mini clones reading is broken (original mini working fine) (OFW)

View file

@ -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 = [

View file

@ -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

View file

@ -6,11 +6,17 @@
#include <furi.h>
#include <furi_hal.h>
#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);
}

View file

@ -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();

View file

@ -1,49 +1,48 @@
/* Implements rudimentary iso7816-3 support for APDU (T=0) */
#include <stdint.h>
#include <string.h>
#include <furi.h>
#include <furi_hal.h>
#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;

View file

@ -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,

View file

@ -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 <furi.h>
@ -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);

View file

@ -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, "<Data empty>", buf_size);
strlcpy(buf, "<Data empty>", buf_size);
return;
}

View file

@ -1,5 +1,7 @@
#include "../rpc_debug_app.h"
#include <lib/toolbox/strint.h>
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);

View file

@ -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,

View file

@ -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);

View file

@ -6,6 +6,8 @@
#include <gui/view_dispatcher.h>
#include <gui/modules/dialog_ex.h>
#include <lib/toolbox/strint.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
@ -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;
}

View file

@ -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"],
)

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,142 @@
#include <furi.h>
#include <furi_hal.h>
#include "../test.h" // IWYU pragma: keep
#include <toolbox/strint.h>
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)

View file

@ -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);

View file

@ -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);

View file

@ -3,6 +3,7 @@
#include <gui/gui.h>
#include <input/input.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/strint.h>
#include <storage/storage.h>
#include "ducky_script.h"
#include "ducky_script_i.h"
@ -64,7 +65,7 @@ uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept
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;
}

View file

@ -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 "

View file

@ -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

View file

@ -10,4 +10,5 @@ typedef enum {
GpioUsbUartEventConfig,
GpioUsbUartEventConfigSet,
GpioUsbUartEventStop,
} GpioCustomEvent;

View file

@ -24,10 +24,9 @@ bool gpio_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultRight) {
consumed = scene_manager_previous_scene(app->scene_manager);
} else if(event.event == DialogExResultLeft) {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart);
consumed = scene_manager_previous_scene(app->scene_manager);
if(consumed && event.event == DialogExResultLeft) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventStop);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;

View file

@ -80,9 +80,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);

View file

@ -8,6 +8,11 @@ typedef struct {
static SceneUsbUartBridge* scene_usb_uart;
typedef enum {
UsbUartSceneStateInitialize,
UsbUartSceneStateKeep,
} UsbUartSceneState;
void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {
furi_assert(context);
GpioApp* app = context;
@ -16,8 +21,9 @@ void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) {
void gpio_scene_usb_uart_on_enter(void* context) {
GpioApp* app = context;
uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
if(prev_state == 0) {
UsbUartSceneState state =
scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart);
if(state == UsbUartSceneStateInitialize) {
scene_usb_uart = malloc(sizeof(SceneUsbUartBridge));
scene_usb_uart->cfg.vcp_ch = 0;
scene_usb_uart->cfg.uart_ch = 0;
@ -39,10 +45,18 @@ void gpio_scene_usb_uart_on_enter(void* context) {
bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg);
if(event.event == GpioUsbUartEventConfig) {
scene_manager_set_scene_state(
app->scene_manager, GpioSceneUsbUart, UsbUartSceneStateKeep);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg);
} else if(event.event == GpioUsbUartEventStop) {
scene_manager_set_scene_state(
app->scene_manager, GpioSceneUsbUart, UsbUartSceneStateInitialize);
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart);
}
return true;
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, UsbUartSceneStateKeep);
scene_manager_next_scene(app->scene_manager, GpioSceneExitConfirm);
return true;
} else if(event.type == SceneManagerEventTypeTick) {
@ -61,8 +75,8 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
void gpio_scene_usb_uart_on_exit(void* context) {
GpioApp* app = context;
uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart);
if(prev_state == 0) {
uint32_t state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart);
if(state == UsbUartSceneStateInitialize) {
usb_uart_disable(app->usb_uart_bridge);
free(scene_usb_uart);
}

View file

@ -183,7 +183,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) {
@ -243,7 +243,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);
}

View file

@ -32,7 +32,7 @@
#define IBUTTON_APP_FILENAME_PREFIX "iBtn"
#define IBUTTON_APP_FILENAME_EXTENSION ".ibtn"
#define IBUTTON_KEY_NAME_SIZE 22
#define IBUTTON_KEY_NAME_SIZE 23
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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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.

View file

@ -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);
}

View file

@ -10,6 +10,7 @@
#include <stdint.h>
#include <stdbool.h>
#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.

View file

@ -5,6 +5,7 @@
#include <furi_hal_infrared.h>
#include <flipper_format.h>
#include <toolbox/args.h>
#include <toolbox/strint.h>
#include <m-dict.h>
#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;
}

View file

@ -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))

View file

@ -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;
}

View file

@ -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);

View file

@ -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) {

View file

@ -8,6 +8,7 @@
*/
#pragma once
#include "infrared_error_code.h"
#include <flipper_format/flipper_format.h>
#include <infrared/encoder_decoder/infrared.h>
@ -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.

View file

@ -1053,3 +1053,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

View file

@ -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);

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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 {

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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]);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -721,6 +721,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);

View file

@ -494,7 +494,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_show_initial_scene_for_device(nfc);
} else {
view_dispatcher_stop(nfc->view_dispatcher);

View file

@ -3,6 +3,7 @@
#include <flipper_application.h>
#include <lib/nfc/protocols/mf_desfire/mf_desfire.h>
#include <lib/toolbox/strint.h>
#include <applications/services/locale/locale.h>
#include <datetime.h>
@ -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");

View file

@ -1,26 +1,64 @@
// From: https://gitee.com/jadenwu/Saflok_KDF/blob/master/saflok.c
// KDF from: https://gitee.com/jadenwu/Saflok_KDF/blob/master/saflok.c
// KDF published and reverse engineered by Jaden Wu
// FZ plugin by @noproto
// Decryption and parsing from: https://gitee.com/wangshuoyue/unsaflok
// Decryption algorithm and parsing published by Shuoyue Wang
// Parsing also inspired by Lennert Wouters and Ian Carroll's DEFCON 32 talk
// https://defcon.org/html/defcon-32/dc-32-speakers.html
// FZ parser by @Torron, with help from @xtruan, @zacharyweiss, @evilmog and kara (@Arkwin)
#include "nfc_supported_card_plugin.h"
#include <flipper_application.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <nfc_app_i.h>
#include <bit_lib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAG "Saflok"
#define MAGIC_TABLE_SIZE 192
#define KEY_LENGTH 6
#define UID_LENGTH 4
#define CHECK_SECTOR 1
#define MAGIC_TABLE_SIZE 192
#define KEY_LENGTH 6
#define UID_LENGTH 4
#define CHECK_SECTOR 1
#define BASIC_ACCESS_BYTE_NUM 17
#define SAFLOK_YEAR_OFFSET 1980
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
typedef struct {
uint8_t level_num;
char* level_name;
} SaflokKeyLevel;
static SaflokKeyLevel key_levels[] = {
{1, "Guest Key"},
{2, "Connectors"},
{3, "Suite"},
{4, "Limited Use"},
{5, "Failsafe"},
{6, "Inhibit"},
{7, "Pool/Meeting Master"},
{8, "Housekeeping"},
{9, "Floor Key"},
{10, "Section Key"},
{11, "Rooms Master"},
{12, "Grand Master"},
{13, "Emergency"},
{14, "Electronic Lockout"},
{15, "Secondary Programming Key (SPK)"},
{16, "Primary Programming Key (PPK)"},
};
const char* weekdays[] =
{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
static MfClassicKeyPair saflok_1k_keys[] = {
{.a = 0x000000000000, .b = 0xffffffffffff}, // 000
{.a = 0x2a2c13cc242a, .b = 0xffffffffffff}, // 001
@ -71,6 +109,68 @@ void generate_saflok_key(const uint8_t* uid, uint8_t* key) {
memcpy(key, temp_key, KEY_LENGTH);
}
// Lookup table
static const uint8_t c_aDecode[256] = {
0xEA, 0x0D, 0xD9, 0x74, 0x4E, 0x28, 0xFD, 0xBA, 0x7B, 0x98, 0x87, 0x78, 0xDD, 0x8D, 0xB5,
0x1A, 0x0E, 0x30, 0xF3, 0x2F, 0x6A, 0x3B, 0xAC, 0x09, 0xB9, 0x20, 0x6E, 0x5B, 0x2B, 0xB6,
0x21, 0xAA, 0x17, 0x44, 0x5A, 0x54, 0x57, 0xBE, 0x0A, 0x52, 0x67, 0xC9, 0x50, 0x35, 0xF5,
0x41, 0xA0, 0x94, 0x60, 0xFE, 0x24, 0xA2, 0x36, 0xEF, 0x1E, 0x6B, 0xF7, 0x9C, 0x69, 0xDA,
0x9B, 0x6F, 0xAD, 0xD8, 0xFB, 0x97, 0x62, 0x5F, 0x1F, 0x38, 0xC2, 0xD7, 0x71, 0x31, 0xF0,
0x13, 0xEE, 0x0F, 0xA3, 0xA7, 0x1C, 0xD5, 0x11, 0x4C, 0x45, 0x2C, 0x04, 0xDB, 0xA6, 0x2E,
0xF8, 0x64, 0x9A, 0xB8, 0x53, 0x66, 0xDC, 0x7A, 0x5D, 0x03, 0x07, 0x80, 0x37, 0xFF, 0xFC,
0x06, 0xBC, 0x26, 0xC0, 0x95, 0x4A, 0xF1, 0x51, 0x2D, 0x22, 0x18, 0x01, 0x79, 0x5E, 0x76,
0x1D, 0x7F, 0x14, 0xE3, 0x9E, 0x8A, 0xBB, 0x34, 0xBF, 0xF4, 0xAB, 0x48, 0x63, 0x55, 0x3E,
0x56, 0x8C, 0xD1, 0x12, 0xED, 0xC3, 0x49, 0x8E, 0x92, 0x9D, 0xCA, 0xB1, 0xE5, 0xCE, 0x4D,
0x3F, 0xFA, 0x73, 0x05, 0xE0, 0x4B, 0x93, 0xB2, 0xCB, 0x08, 0xE1, 0x96, 0x19, 0x3D, 0x83,
0x39, 0x75, 0xEC, 0xD6, 0x3C, 0xD0, 0x70, 0x81, 0x16, 0x29, 0x15, 0x6C, 0xC7, 0xE7, 0xE2,
0xF6, 0xB7, 0xE8, 0x25, 0x6D, 0x3A, 0xE6, 0xC8, 0x99, 0x46, 0xB0, 0x85, 0x02, 0x61, 0x1B,
0x8B, 0xB3, 0x9F, 0x0B, 0x2A, 0xA8, 0x77, 0x10, 0xC1, 0x88, 0xCC, 0xA4, 0xDE, 0x43, 0x58,
0x23, 0xB4, 0xA1, 0xA5, 0x5C, 0xAE, 0xA9, 0x7E, 0x42, 0x40, 0x90, 0xD2, 0xE9, 0x84, 0xCF,
0xE4, 0xEB, 0x47, 0x4F, 0x82, 0xD4, 0xC5, 0x8F, 0xCD, 0xD3, 0x86, 0x00, 0x59, 0xDF, 0xF2,
0x0C, 0x7C, 0xC6, 0xBD, 0xF9, 0x7D, 0xC4, 0x91, 0x27, 0x89, 0x32, 0x72, 0x33, 0x65, 0x68,
0xAF};
void DecryptCard(
uint8_t strCard[BASIC_ACCESS_BYTE_NUM],
int length,
uint8_t decryptedCard[BASIC_ACCESS_BYTE_NUM]) {
int i, num, num2, num3, num4, b = 0, b2 = 0;
for(i = 0; i < length; i++) {
num = c_aDecode[strCard[i]] - (i + 1);
if(num < 0) num += 256;
decryptedCard[i] = num;
}
if(length == 17) {
b = decryptedCard[10];
b2 = b & 1;
}
for(num2 = length; num2 > 0; num2--) {
b = decryptedCard[num2 - 1];
for(num3 = 8; num3 > 0; num3--) {
num4 = num2 + num3;
if(num4 > length) num4 -= length;
int b3 = decryptedCard[num4 - 1];
int b4 = (b3 & 0x80) >> 7;
b3 = ((b3 << 1) & 0xFF) | b2;
b2 = (b & 0x80) >> 7;
b = ((b << 1) & 0xFF) | b4;
decryptedCard[num4 - 1] = b3;
}
decryptedCard[num2 - 1] = b;
}
}
uint8_t CalculateCheckSum(uint8_t data[BASIC_ACCESS_BYTE_NUM]) {
int sum = 0;
for(int i = 0; i < BASIC_ACCESS_BYTE_NUM - 1; i++) {
sum += data[i];
}
sum = 255 - (sum & 0xFF);
return sum & 0xFF;
}
static bool saflok_verify(Nfc* nfc) {
bool verified = false;
@ -153,13 +253,196 @@ static bool saflok_read(Nfc* nfc, NfcDevice* device) {
return is_read;
}
bool saflok_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Check card type
if(data->type != MfClassicType1k) break;
// Verify key
const MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, CHECK_SECTOR);
const uint64_t key_a =
bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
if(key_a != saflok_1k_keys[CHECK_SECTOR].a) break;
// Decrypt basic access
uint8_t basicAccess[BASIC_ACCESS_BYTE_NUM];
memcpy(&basicAccess, &data->block[1].data, 16);
memcpy(&basicAccess[16], &data->block[2].data[0], 1);
uint8_t decodedBA[BASIC_ACCESS_BYTE_NUM];
DecryptCard(basicAccess, BASIC_ACCESS_BYTE_NUM, decodedBA);
// Byte 0: Key level, LED warning bit, and subgroup functions
uint8_t key_level = (decodedBA[0] & 0xF0) >> 4;
uint8_t led_warning = (decodedBA[0] & 0x08) >> 3;
// Byte 1: Key ID
uint8_t key_id = decodedBA[1];
// Byte 2 & 3: KeyRecord, including OpeningKey flag
uint8_t key_record_high = decodedBA[2] & 0x7F;
uint8_t opening_key = (decodedBA[2] & 0x80) >> 7;
uint16_t key_record = (key_record_high << 8) | decodedBA[3];
// Byte 4 & 5: Pass level in reversed binary
// This part is commented because the relevance of this info is still unknown
// uint16_t pass_level = ((decodedBA[4] & 0xFF) << 8) | decodedBA[5];
// uint8_t pass_levels[12];
// int pass_levels_count = 0;
// for (int i = 0; i < 12; i++) {
// if ((pass_level >> i) & 1) {
// pass_levels[pass_levels_count++] = i + 1;
// }
// }
// Byte 5 & 6: EncryptSequence + Combination
uint16_t sequence_combination_number = ((decodedBA[5] & 0x0F) << 8) | decodedBA[6];
// Bytes 14-15: Property number and year
uint8_t creation_year_bits = (decodedBA[14] & 0xF0);
uint16_t property_id = ((decodedBA[14] & 0x0F) << 8) | decodedBA[15];
// Byte 7: OverrideDeadbolt and Days
uint8_t override_deadbolt = (decodedBA[7] & 0x80) >> 7;
uint8_t restricted_weekday = decodedBA[7] & 0x7F;
// Counter to keep track of the number of restricted days
int restricted_count = 0;
// Buffer to store the resulting string
FuriString* restricted_weekday_string = furi_string_alloc();
// Check each bit from Monday to Sunday
for(int i = 0; i < 7; i++) {
if(restricted_weekday & (0b01000000 >> i)) {
// If the bit is set, append the corresponding weekday to the buffer
if(restricted_count > 0) {
furi_string_cat_printf(restricted_weekday_string, ", ");
}
furi_string_cat_printf(restricted_weekday_string, "%s", weekdays[i]);
restricted_count++;
}
}
// Determine if all weekdays are restricted
if(restricted_weekday == 0b01111100) {
furi_string_printf(restricted_weekday_string, "weekdays");
}
// If there are specific restricted days
else if(restricted_weekday == 0b00000011) {
furi_string_printf(restricted_weekday_string, "weekends");
}
// If no weekdays are restricted
else if(restricted_weekday == 0) {
furi_string_printf(restricted_weekday_string, "none");
}
// Bytes 8-10: Expiry interval
uint16_t interval_year = (decodedBA[8] >> 4);
uint8_t interval_month = decodedBA[8] & 0x0F;
uint8_t interval_day = (decodedBA[9] >> 3) & 0x1F;
uint8_t interval_hour = ((decodedBA[9] & 0x07) << 2) | (decodedBA[10] >> 6);
uint8_t interval_minute = decodedBA[10] & 0x3F;
// Bytes 11-13: Creation date since 1980 Jan 1st
uint16_t creation_year = (((decodedBA[11] & 0xF0) >> 4) + SAFLOK_YEAR_OFFSET) |
creation_year_bits;
uint8_t creation_month = decodedBA[11] & 0x0F;
uint8_t creation_day = (decodedBA[12] >> 3) & 0x1F;
uint8_t creation_hour = ((decodedBA[12] & 0x07) << 2) | (decodedBA[13] >> 6);
uint8_t creation_minute = decodedBA[13] & 0x3F;
uint16_t expire_year = creation_year + interval_year;
uint8_t expire_month = creation_month + interval_month;
uint8_t expire_day = creation_day + interval_day;
uint8_t expire_hour = interval_hour;
uint8_t expire_minute = interval_minute;
// Handle month rollover
while(expire_month > 12) {
expire_month -= 12;
expire_year++;
}
// Handle day rollover
static const uint8_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
while(true) {
uint8_t max_days = days_in_month[expire_month - 1];
// Adjust for leap years
if(expire_month == 2 &&
(expire_year % 4 == 0 && (expire_year % 100 != 0 || expire_year % 400 == 0))) {
max_days = 29;
}
if(expire_day <= max_days) {
break;
}
expire_day -= max_days;
expire_month++;
if(expire_month > 12) {
expire_month = 1;
expire_year++;
}
}
// Byte 16: Checksum
uint8_t checksum = decodedBA[16];
uint8_t checksum_calculated = CalculateCheckSum(decodedBA);
bool checksum_valid = (checksum_calculated == checksum);
for(int i = 0; i < 17; i++) {
FURI_LOG_D(TAG, "%02X", decodedBA[i]);
}
FURI_LOG_D(TAG, "CS decrypted: %02X", checksum);
FURI_LOG_D(TAG, "CS calculated: %02X", checksum_calculated);
furi_string_cat_printf(parsed_data, "\e#Saflok Card\n");
furi_string_cat_printf(
parsed_data,
"Key Level: %u, %s\n",
key_levels[key_level].level_num,
key_levels[key_level].level_name);
furi_string_cat_printf(parsed_data, "LED Exp. Warning: %s\n", led_warning ? "Yes" : "No");
furi_string_cat_printf(parsed_data, "Key ID: %02X\n", key_id);
furi_string_cat_printf(parsed_data, "Key Record: %04X\n", key_record);
furi_string_cat_printf(parsed_data, "Opening key: %s\n", opening_key ? "Yes" : "No");
furi_string_cat_printf(
parsed_data, "Seq. & Combination: %04X\n", sequence_combination_number);
furi_string_cat_printf(
parsed_data, "Override Deadbolt: %s\n", override_deadbolt ? "Yes" : "No");
furi_string_cat_printf(
parsed_data,
"Restricted Weekday: %s\n",
furi_string_get_cstr(restricted_weekday_string));
furi_string_cat_printf(
parsed_data,
"Valid Start Date: \n%u-%02d-%02d\n%02d:%02d:00\n",
creation_year,
creation_month,
creation_day,
creation_hour,
creation_minute);
furi_string_cat_printf(
parsed_data,
"Expiration Date: \n%u-%02d-%02d\n%02d:%02d:00\n",
expire_year,
expire_month,
expire_day,
expire_hour,
expire_minute);
furi_string_cat_printf(parsed_data, "Property Number: %u\n", property_id);
furi_string_cat_printf(parsed_data, "Checksum Valid: %s", checksum_valid ? "Yes" : "No");
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin saflok_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = saflok_verify,
.read = saflok_read,
// KDF mode
.parse = NULL,
.parse = saflok_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
@ -170,6 +453,6 @@ static const FlipperAppPluginDescriptor saflok_plugin_descriptor = {
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* saflok_plugin_ep() {
const FlipperAppPluginDescriptor* saflok_plugin_ep(void) {
return &saflok_plugin_descriptor;
}

View file

@ -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);

View file

@ -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);
}

View file

@ -117,6 +117,9 @@ typedef enum {
SetTypePricenton315,
SetTypePricenton433,
SetTypeBETT_433,
SetTypeGangQi_433,
SetTypeHollarm_433,
SetTypeMarantec24_868,
SetTypeLinear_300_00,
// SetTypeNeroSketch, //Deleted in OFW
// SetTypeNeroRadio, //Deleted in OFW

View file

@ -1,14 +0,0 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
/** 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;

View file

@ -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);
}

View file

@ -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);

View file

@ -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");
}
}

View file

@ -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:

View file

@ -3,18 +3,20 @@
#include <furi.h>
#include <furi_hal.h>
#include <lib/toolbox/args.h>
#include <lib/subghz/subghz_keystore.h>
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/subghz_keystore.h>
#include <lib/subghz/receiver.h>
#include <lib/subghz/transmitter.h>
#include <lib/subghz/subghz_file_encoder_worker.h>
#include <lib/subghz/protocols/protocol_items.h>
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
#include <lib/subghz/devices/devices.h>
#include <lib/subghz/devices/cc1101_configs.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/strint.h>
#include "helpers/subghz_chat.h"
#include <notification/notification_messages.h>
@ -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", "<Frequency: in Hz>", 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", "<Frequency: in Hz>", 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> <Frequency: in Hz> <Te us> <Repeat count> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>",
@ -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",
"<Frequency: in Hz> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>",
@ -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", "<Frequency: in Hz>", 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:",
"<file_name: path_file> <Repeat count> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>",
@ -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",
"<Frequency: in Hz> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>",

View file

@ -1,7 +1,6 @@
#pragma once
#include "helpers/subghz_types.h"
#include "helpers/subghz_error_type.h"
#include <lib/subghz/types.h>
#include "subghz.h"
#include "views/receiver.h"

View file

@ -9,6 +9,7 @@
#include <notification/notification_messages.h>
#include <loader/loader.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/strint.h>
// 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", "<r|g|b|bl> <0-255>", furi_string_get_cstr(args));
return;
}

View file

@ -51,8 +51,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;
@ -104,9 +103,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;
@ -400,8 +397,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;

View file

@ -58,7 +58,8 @@ static uint32_t desktop_pin_code_pack(const DesktopPinCode* pin_code) {
}
bool desktop_pin_code_is_set(void) {
return furi_hal_rtc_get_pin_value() >> DESKTOP_PIN_CODE_LENGTH_OFFSET;
uint8_t length = furi_hal_rtc_get_pin_value() >> DESKTOP_PIN_CODE_LENGTH_OFFSET;
return length >= DESKTOP_PIN_CODE_MIN_LEN && length <= DESKTOP_PIN_CODE_MAX_LEN;
}
void desktop_pin_code_set(const DesktopPinCode* pin_code) {

View file

@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
#define DESKTOP_PIN_CODE_MIN_LEN (4)
#define DESKTOP_PIN_CODE_MAX_LEN (10)
typedef struct {

View file

@ -13,7 +13,7 @@
#define DEFAULT_PIN_X 64
#define DEFAULT_PIN_Y 32
#define MIN_PIN_LENGTH 4
#define MIN_PIN_LENGTH DESKTOP_PIN_CODE_MIN_LEN
#define MAX_PIN_LENGTH DESKTOP_PIN_CODE_MAX_LEN
struct DesktopViewPinInput {
@ -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;
}

View file

@ -47,6 +47,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);
@ -211,7 +231,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 =
@ -228,6 +250,15 @@ static bool dolphin_process_event(FuriEventLoopObject* object, void* context) {
dolphin_state_load(dolphin->state);
furi_event_loop_timer_start(dolphin->butthurt_timer, BUTTHURT_INCREASE_PERIOD_TICKS);
} 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();
}

View file

@ -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
*/

View file

@ -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;

View file

@ -5,6 +5,10 @@
#include "dolphin_deed.h"
typedef enum {
DolphinFlagHappyMode = 1,
} DolphinFlags;
typedef struct DolphinState DolphinState;
typedef struct {
uint8_t icounter_daily_limit[DolphinAppMAX];

View file

@ -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)

View file

@ -245,9 +245,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) {
@ -279,9 +288,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) {

View file

@ -185,6 +185,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);

View file

@ -96,6 +96,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

View file

@ -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;
@ -649,11 +649,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++) {
if(keys[column].value == enter_symbol) {
canvas_set_color(canvas, ColorBlack);
if(model->selected_row == row && model->selected_column == column) {

View file

@ -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;

View file

@ -3,6 +3,7 @@
#include <gui/elements.h>
#include <furi.h>
#include <assets_icons.h>
#include <lib/toolbox/strint.h>
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;
}

View file

@ -94,10 +94,20 @@ static void loader_show_gui_error(
if(status.value == LoaderStatusErrorUnknownApp &&
loader_find_external_application_by_name(name) != 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);
@ -757,7 +767,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;
}

View file

@ -4,6 +4,7 @@
#include <cli/cli.h>
#include <applications.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/strint.h>
#include <notification/notification_messages.h>
static void loader_cli_print_usage(void) {
@ -89,18 +90,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");
}
}

View file

@ -13,6 +13,7 @@
#pragma once
#include "rpc.h"
#include "rpc_app_error_codes.h"
#ifdef __cplusplus
extern "C" {

View file

@ -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;

View file

@ -2,6 +2,7 @@
#include "rpc_i.h"
#include "gpio.pb.h"
#include <furi_hal_gpio.h>
#include <furi_hal_power.h>
#include <furi_hal_resources.h>
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;
}

View file

@ -7,7 +7,7 @@
#include <storage/storage.h>
#include <lib/toolbox/md5_calc.h>
#include <lib/toolbox/path.h>
#include <update_util/lfs_backup.h>
#include <update_util/int_backup.h>
#include <toolbox/tar/tar_archive.h>
#include <pb_decode.h>
@ -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(

View file

@ -504,7 +504,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*);

View file

@ -3,8 +3,9 @@
#include <cli/cli.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/md5_calc.h>
#include <lib/toolbox/dir_walk.h>
#include <lib/toolbox/md5_calc.h>
#include <lib/toolbox/strint.h>
#include <lib/toolbox/tar/tar_archive.h>
#include <storage/storage.h>
#include <storage/storage_sd_api.h>
@ -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)) {

View file

@ -1,5 +1,6 @@
#include <furi.h>
#include <gui/modules/popup.h>
#include <gui/modules/dialog_ex.h>
#include <gui/scene_manager.h>
#include <namechanger/namechanger.h>
#include <flipper_format/flipper_format.h>
@ -48,6 +49,7 @@ DesktopSettingsApp* desktop_settings_app_alloc(void) {
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));
@ -69,6 +71,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));
// Text Input
app->text_input = text_input_alloc();
@ -111,6 +115,7 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
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);
// TextInput
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewTextInput);
text_input_free(app->text_input);
@ -121,6 +126,7 @@ void desktop_settings_app_free(DesktopSettingsApp* app) {
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);

View file

@ -7,6 +7,7 @@
#include <gui/modules/submenu.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/text_input.h>
#include <gui/modules/dialog_ex.h>
#include <dialogs/dialogs.h>
#include <assets_icons.h>
@ -25,6 +26,7 @@ typedef enum {
DesktopSettingsAppViewIdPinSetupHowto,
DesktopSettingsAppViewIdPinSetupHowto2,
DesktopSettingsAppViewTextInput,
DesktopSettingsAppViewDialogEx,
} DesktopSettingsAppView;
typedef struct {
@ -41,6 +43,7 @@ typedef struct {
DesktopViewPinInput* pin_input_view;
DesktopSettingsViewPinSetupHowto* pin_setup_howto_view;
DesktopSettingsViewPinSetupHowto2* pin_setup_howto2_view;
DialogEx* dialog_ex;
DesktopPinCode pincode_buffer;
bool pincode_buffer_filled;

View file

@ -0,0 +1,26 @@
#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,
DesktopSettingsCustomEventSetDefault,
DesktopSettingsCustomEventSetDummy,
} DesktopSettingsCustomEvent;
#ifdef __cplusplus
}
#endif

View file

@ -12,3 +12,5 @@ ADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone)
ADD_SCENE(desktop_settings, change_name, ChangeName)
ADD_SCENE(desktop_settings, name_popup, NamePopup)
ADD_SCENE(desktop_settings, happy_mode, HappyMode)

View file

@ -191,7 +191,7 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {
submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene
strncpy(
strlcpy(
curr_favorite_app->name_or_path,
furi_string_get_cstr(temp_path),
sizeof(curr_favorite_app->name_or_path));
@ -201,7 +201,7 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
size_t app_index = event.event - MAIN_LIST_APPLICATION_OFFSET;
const char* name = favorite_fap_get_app_name(app_index);
if(name)
strncpy(
strlcpy(
curr_favorite_app->name_or_path,
name,
sizeof(curr_favorite_app->name_or_path));

View file

@ -0,0 +1,64 @@
#include <furi.h>
#include <gui/scene_manager.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/dialog_ex.h>
#include <dolphin/dolphin.h>
#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);
}

View file

@ -3,15 +3,12 @@
#include <gui/scene_manager.h>
#include <desktop/helpers/pin_code.h>
#include "../desktop_settings_app.h"
#include "../desktop_settings_custom_event.h"
#include <desktop/desktop_settings.h>
#include <desktop/views/desktop_view_pin_input.h>
#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;

View file

@ -3,15 +3,14 @@
#include <gui/modules/popup.h>
#include "../desktop_settings_app.h"
#include "../desktop_settings_custom_event.h"
#include <desktop/desktop_settings.h>
#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;

View file

@ -8,20 +8,19 @@
#include "desktop_settings_scene_i.h"
#include <desktop/helpers/pin_code.h>
#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;

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