Merge remote-tracking branch 'ofw/dev' into mntm-dev

This commit is contained in:
Willy-JL
2025-01-11 04:11:49 +00:00
108 changed files with 1600 additions and 139 deletions

View File

@@ -3,6 +3,11 @@
- Games: Pinball0 (by @rdefeo) - Games: Pinball0 (by @rdefeo)
- NFC: Metroflip (by @luu176) - NFC: Metroflip (by @luu176)
- UL: Sub-GHz: Jolly Motors support with add manually (by @pkooiman & @xMasterX) - UL: Sub-GHz: Jolly Motors support with add manually (by @pkooiman & @xMasterX)
- OFW: Desktop: Add winter animations (by @Astrrra)
- OFW: Furi: Pipe support (by @portasynthinca3)
- OFW: Furi: Thread stdin support (by @portasynthinca3)
- OFW: RPC: Command to send a signal once (by @Astrrra)
- OFW: API: Added `flipper_format_write_empty_line()` (by @janwiesemann)
- OFW: Add VCP break support (by @gsurkov) - OFW: Add VCP break support (by @gsurkov)
### Updated: ### Updated:
@@ -32,10 +37,19 @@
### Fixed: ### Fixed:
- Desktop: Fixed Wardriving animation design (by @Davim09) - Desktop: Fixed Wardriving animation design (by @Davim09)
- OFW: Fix lost BadBLE keystrokes (by @Astrrra)
- OFW: GPIO: Fix USB UART Bridge Crash by increasing system stack size (by @Astrrra) - OFW: GPIO: Fix USB UART Bridge Crash by increasing system stack size (by @Astrrra)
- OFW: Loader: Fix BusFault in handling of OOM (by @Willy-JL)
- NFC: - NFC:
- OFW: Plantain parser Last payment amount fix (by @mxcdoam) - OFW: Plantain parser Last payment amount fix (by @mxcdoam)
- OFW: Fix typo for mf_classic_key_cahce_get_next_key() function (by @luu176) - OFW: Fix skylander ID reading (by @bettse)
- OFW: Fix ISO15693 stuck in wrong mode (by @RebornedBrain)
- OFW: Fix MFUL PWD_AUTH command creation when 0x00 in password (by @GMMan)
- OFW: Fix typo for `mf_classic_key_cahce_get_next_key()` function (by @luu176)
- OFW: U2F: Fix message digest memory leak (by @GMMan)
- OFW: JS: SDK workaround incorrect serial port handling by OS (by @portasynthinca3)
- OFW: FBT: Fix invalid path errors on Windows with UTF8 paths (by @Alex4386)
### Removed: ### Removed:
- Nothing - NFC: Previous fix for ISO15693 stuck in wrong mode (#225)
- Removes APIs `nfc_iso15693_detect_mode()`, `nfc_iso15693_force_1outof4()`, `nfc_iso15693_force_1outof256()`

View File

@@ -236,3 +236,11 @@ App(
entry_point="get_api", entry_point="get_api",
requires=["unit_tests"], requires=["unit_tests"],
) )
App(
appid="test_pipe",
sources=["tests/common/*.c", "tests/pipe/*.c"],
apptype=FlipperAppType.PLUGIN,
entry_point="get_api",
requires=["unit_tests"],
)

View File

@@ -265,6 +265,7 @@ static bool test_write(const char* file_name) {
if(!flipper_format_file_open_always(file, file_name)) break; if(!flipper_format_file_open_always(file, file_name)) break;
if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break; if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break;
if(!flipper_format_write_comment_cstr(file, "This is comment")) break; if(!flipper_format_write_comment_cstr(file, "This is comment")) break;
if(!flipper_format_write_empty_line(file)) break;
if(!flipper_format_write_string_cstr(file, test_string_key, test_string_data)) break; if(!flipper_format_write_string_cstr(file, test_string_key, test_string_data)) break;
if(!flipper_format_write_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data))) if(!flipper_format_write_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data)))
break; break;

View File

@@ -0,0 +1,108 @@
#include <furi.h>
#include <errno.h>
#include <stdio.h>
#include "../test.h" // IWYU pragma: keep
#define TAG "StdioTest"
#define CONTEXT_MAGIC ((void*)0xDEADBEEF)
// stdin
static char mock_in[256];
static size_t mock_in_len, mock_in_pos;
static void set_mock_in(const char* str) {
size_t len = strlen(str);
strcpy(mock_in, str);
mock_in_len = len;
mock_in_pos = 0;
}
static size_t mock_in_cb(char* buffer, size_t size, FuriWait wait, void* context) {
UNUSED(wait);
furi_check(context == CONTEXT_MAGIC);
size_t remaining = mock_in_len - mock_in_pos;
size = MIN(remaining, size);
memcpy(buffer, mock_in + mock_in_pos, size);
mock_in_pos += size;
return size;
}
void test_stdin(void) {
FuriThreadStdinReadCallback in_cb = furi_thread_get_stdin_callback();
furi_thread_set_stdin_callback(mock_in_cb, CONTEXT_MAGIC);
char buf[256];
// plain in
set_mock_in("Hello, World!\n");
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq("Hello, World!\n", buf);
mu_assert_int_eq(EOF, getchar());
// ungetc
ungetc('i', stdin);
ungetc('H', stdin);
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq("Hi", buf);
mu_assert_int_eq(EOF, getchar());
// ungetc + plain in
set_mock_in(" World");
ungetc('i', stdin);
ungetc('H', stdin);
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq("Hi World", buf);
mu_assert_int_eq(EOF, getchar());
// partial plain in
set_mock_in("Hello, World!\n");
fgets(buf, strlen("Hello") + 1, stdin);
mu_assert_string_eq("Hello", buf);
mu_assert_int_eq(',', getchar());
fgets(buf, sizeof(buf), stdin);
mu_assert_string_eq(" World!\n", buf);
furi_thread_set_stdin_callback(in_cb, CONTEXT_MAGIC);
}
// stdout
static FuriString* mock_out;
FuriThreadStdoutWriteCallback original_out_cb;
static void mock_out_cb(const char* data, size_t size, void* context) {
furi_check(context == CONTEXT_MAGIC);
// there's no furi_string_cat_strn :(
for(size_t i = 0; i < size; i++) {
furi_string_push_back(mock_out, data[i]);
}
}
static void assert_and_clear_mock_out(const char* expected) {
// return the original stdout callback for the duration of the check
// if the check fails, we don't want the error to end up in our buffer,
// we want to be able to see it!
furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC);
mu_assert_string_eq(expected, furi_string_get_cstr(mock_out));
furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC);
furi_string_reset(mock_out);
}
void test_stdout(void) {
original_out_cb = furi_thread_get_stdout_callback();
furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC);
mock_out = furi_string_alloc();
puts("Hello, World!");
assert_and_clear_mock_out("Hello, World!\n");
printf("He");
printf("llo!");
fflush(stdout);
assert_and_clear_mock_out("Hello!");
furi_string_free(mock_out);
furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC);
}

View File

@@ -10,6 +10,8 @@ void test_furi_memmgr(void);
void test_furi_event_loop(void); void test_furi_event_loop(void);
void test_errno_saving(void); void test_errno_saving(void);
void test_furi_primitives(void); void test_furi_primitives(void);
void test_stdin(void);
void test_stdout(void);
static int foo = 0; static int foo = 0;
@@ -52,6 +54,11 @@ MU_TEST(mu_test_furi_primitives) {
test_furi_primitives(); test_furi_primitives();
} }
MU_TEST(mu_test_stdio) {
test_stdin();
test_stdout();
}
MU_TEST_SUITE(test_suite) { MU_TEST_SUITE(test_suite) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown); MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_check); MU_RUN_TEST(test_check);
@@ -61,6 +68,7 @@ MU_TEST_SUITE(test_suite) {
MU_RUN_TEST(mu_test_furi_pubsub); MU_RUN_TEST(mu_test_furi_pubsub);
MU_RUN_TEST(mu_test_furi_memmgr); MU_RUN_TEST(mu_test_furi_memmgr);
MU_RUN_TEST(mu_test_furi_event_loop); MU_RUN_TEST(mu_test_furi_event_loop);
MU_RUN_TEST(mu_test_stdio);
MU_RUN_TEST(mu_test_errno_saving); MU_RUN_TEST(mu_test_errno_saving);
MU_RUN_TEST(mu_test_furi_primitives); MU_RUN_TEST(mu_test_furi_primitives);
} }

View File

@@ -0,0 +1,153 @@
#include "../test.h" // IWYU pragma: keep
#include <furi.h>
#include <lib/toolbox/pipe.h>
#define PIPE_SIZE 128U
#define PIPE_TRG_LEVEL 1U
MU_TEST(pipe_test_trivial) {
PipeSideBundle bundle = pipe_alloc(PIPE_SIZE, PIPE_TRG_LEVEL);
PipeSide* alice = bundle.alices_side;
PipeSide* bob = bundle.bobs_side;
mu_assert_int_eq(PipeRoleAlice, pipe_role(alice));
mu_assert_int_eq(PipeRoleBob, pipe_role(bob));
mu_assert_int_eq(PipeStateOpen, pipe_state(alice));
mu_assert_int_eq(PipeStateOpen, pipe_state(bob));
mu_assert_int_eq(PIPE_SIZE, pipe_spaces_available(alice));
mu_assert_int_eq(PIPE_SIZE, pipe_spaces_available(bob));
mu_assert_int_eq(0, pipe_bytes_available(alice));
mu_assert_int_eq(0, pipe_bytes_available(bob));
for(uint8_t i = 0;; ++i) {
mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(alice));
mu_assert_int_eq(i, pipe_bytes_available(bob));
if(pipe_send(alice, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) {
break;
}
mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(bob));
mu_assert_int_eq(i, pipe_bytes_available(alice));
if(pipe_send(bob, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) {
break;
}
}
pipe_free(alice);
mu_assert_int_eq(PipeStateBroken, pipe_state(bob));
for(uint8_t i = 0;; ++i) {
mu_assert_int_eq(PIPE_SIZE - i, pipe_bytes_available(bob));
uint8_t value;
if(pipe_receive(bob, &value, sizeof(uint8_t), 0) != sizeof(uint8_t)) {
break;
}
mu_assert_int_eq(i, value);
}
pipe_free(bob);
}
typedef enum {
TestFlagDataArrived = 1 << 0,
TestFlagSpaceFreed = 1 << 1,
TestFlagBecameBroken = 1 << 2,
} TestFlag;
typedef struct {
TestFlag flag;
FuriEventLoop* event_loop;
} AncillaryThreadContext;
static void on_data_arrived(PipeSide* pipe, void* context) {
AncillaryThreadContext* ctx = context;
ctx->flag |= TestFlagDataArrived;
uint8_t buffer[PIPE_SIZE];
size_t size = pipe_receive(pipe, buffer, sizeof(buffer), 0);
pipe_send(pipe, buffer, size, 0);
}
static void on_space_freed(PipeSide* pipe, void* context) {
AncillaryThreadContext* ctx = context;
ctx->flag |= TestFlagSpaceFreed;
const char* message = "Hi!";
pipe_send(pipe, message, strlen(message), 0);
}
static void on_became_broken(PipeSide* pipe, void* context) {
UNUSED(pipe);
AncillaryThreadContext* ctx = context;
ctx->flag |= TestFlagBecameBroken;
furi_event_loop_stop(ctx->event_loop);
}
static int32_t ancillary_thread(void* context) {
PipeSide* pipe = context;
AncillaryThreadContext thread_ctx = {
.flag = 0,
.event_loop = furi_event_loop_alloc(),
};
pipe_attach_to_event_loop(pipe, thread_ctx.event_loop);
pipe_set_callback_context(pipe, &thread_ctx);
pipe_set_data_arrived_callback(pipe, on_data_arrived, 0);
pipe_set_space_freed_callback(pipe, on_space_freed, FuriEventLoopEventFlagEdge);
pipe_set_broken_callback(pipe, on_became_broken, 0);
furi_event_loop_run(thread_ctx.event_loop);
pipe_detach_from_event_loop(pipe);
pipe_free(pipe);
furi_event_loop_free(thread_ctx.event_loop);
return thread_ctx.flag;
}
MU_TEST(pipe_test_event_loop) {
PipeSideBundle bundle = pipe_alloc(PIPE_SIZE, PIPE_TRG_LEVEL);
PipeSide* alice = bundle.alices_side;
PipeSide* bob = bundle.bobs_side;
FuriThread* thread = furi_thread_alloc_ex("PipeTestAnc", 2048, ancillary_thread, bob);
furi_thread_start(thread);
const char* message = "Hello!";
pipe_send(alice, message, strlen(message), FuriWaitForever);
char buffer_1[16];
size_t size = pipe_receive(alice, buffer_1, sizeof(buffer_1), FuriWaitForever);
buffer_1[size] = 0;
char buffer_2[16];
const char* expected_reply = "Hi!";
size = pipe_receive(alice, buffer_2, sizeof(buffer_2), FuriWaitForever);
buffer_2[size] = 0;
pipe_free(alice);
furi_thread_join(thread);
mu_assert_string_eq(message, buffer_1);
mu_assert_string_eq(expected_reply, buffer_2);
mu_assert_int_eq(
TestFlagDataArrived | TestFlagSpaceFreed | TestFlagBecameBroken,
furi_thread_get_return_code(thread));
furi_thread_free(thread);
}
MU_TEST_SUITE(test_pipe) {
MU_RUN_TEST(pipe_test_trivial);
MU_RUN_TEST(pipe_test_event_loop);
}
int run_minunit_test_pipe(void) {
MU_RUN_SUITE(test_pipe);
return MU_EXIT_CODE;
}
TEST_API_DEFINE(run_minunit_test_pipe)

View File

@@ -157,6 +157,7 @@ static BleEventAckStatus ble_svc_hid_event_handler(void* event, void* context) {
hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);
evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data;
// aci_gatt_attribute_modified_event_rp0* attribute_modified; // aci_gatt_attribute_modified_event_rp0* attribute_modified;
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
// Process modification events // Process modification events
@@ -274,6 +275,7 @@ bool ble_svc_hid_update_input_report(
.data_ptr = data, .data_ptr = data,
.data_len = len, .data_len = len,
}; };
return ble_gatt_characteristic_update( return ble_gatt_characteristic_update(
hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data);
} }

View File

@@ -79,6 +79,19 @@ static void infrared_rpc_command_callback(const RpcAppSystemEvent* event, void*
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressIndex); infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressIndex);
} }
} else if(event->type == RpcAppEventTypeButtonPressRelease) {
furi_assert(
event->data.type == RpcAppSystemEventDataTypeString ||
event->data.type == RpcAppSystemEventDataTypeInt32);
if(event->data.type == RpcAppSystemEventDataTypeString) {
furi_string_set(infrared->button_name, event->data.string);
view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressReleaseName);
} else {
infrared->app_state.current_button_index = event->data.i32;
view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressReleaseIndex);
}
} else if(event->type == RpcAppEventTypeButtonRelease) { } else if(event->type == RpcAppEventTypeButtonRelease) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease);
@@ -402,6 +415,26 @@ void infrared_tx_stop(InfraredApp* infrared) {
infrared->app_state.last_transmit_time = furi_get_tick(); infrared->app_state.last_transmit_time = furi_get_tick();
} }
void infrared_tx_send_once(InfraredApp* infrared) {
if(infrared->app_state.is_transmitting) {
return;
}
dolphin_deed(DolphinDeedIrSend);
infrared_signal_transmit(infrared->current_signal);
}
InfraredErrorCode infrared_tx_send_once_button_index(InfraredApp* infrared, size_t button_index) {
furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote));
InfraredErrorCode error = infrared_remote_load_signal(
infrared->remote, infrared->current_signal, infrared->app_state.current_button_index);
if(!INFRARED_ERROR_PRESENT(error)) {
infrared_tx_send_once(infrared);
}
return error;
}
void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback) { void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback) {
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewLoading); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewLoading);
furi_thread_set_callback(infrared->task_thread, callback); furi_thread_set_callback(infrared->task_thread, callback);

View File

@@ -218,6 +218,20 @@ InfraredErrorCode infrared_tx_start_button_index(InfraredApp* infrared, size_t b
*/ */
void infrared_tx_stop(InfraredApp* infrared); void infrared_tx_stop(InfraredApp* infrared);
/**
* @brief Transmit the currently loaded signal once.
*
* @param[in,out] infrared pointer to the application instance.
*/
void infrared_tx_send_once(InfraredApp* infrared);
/**
* @brief Load the signal under the given index and transmit it once.
*
* @param[in,out] infrared pointer to the application instance.
*/
InfraredErrorCode infrared_tx_send_once_button_index(InfraredApp* infrared, size_t button_index);
/** /**
* @brief Start a blocking task in a separate thread. * @brief Start a blocking task in a separate thread.
* *

View File

@@ -21,6 +21,8 @@ enum InfraredCustomEventType {
InfraredCustomEventTypeRpcButtonPressName, InfraredCustomEventTypeRpcButtonPressName,
InfraredCustomEventTypeRpcButtonPressIndex, InfraredCustomEventTypeRpcButtonPressIndex,
InfraredCustomEventTypeRpcButtonRelease, InfraredCustomEventTypeRpcButtonRelease,
InfraredCustomEventTypeRpcButtonPressReleaseName,
InfraredCustomEventTypeRpcButtonPressReleaseIndex,
InfraredCustomEventTypeRpcSessionClose, InfraredCustomEventTypeRpcSessionClose,
InfraredCustomEventTypeGpioTxPinChanged, InfraredCustomEventTypeGpioTxPinChanged,

View File

@@ -124,6 +124,49 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) {
rpc_system_app_confirm(infrared->rpc_ctx, result); rpc_system_app_confirm(infrared->rpc_ctx, result);
} else if(
event.event == InfraredCustomEventTypeRpcButtonPressReleaseName ||
event.event == InfraredCustomEventTypeRpcButtonPressReleaseIndex) {
bool result = false;
// Send the signal once and stop
if(rpc_state == InfraredRpcStateLoaded) {
if(event.event == InfraredCustomEventTypeRpcButtonPressReleaseName) {
const char* button_name = furi_string_get_cstr(infrared->button_name);
size_t index;
const bool index_found =
infrared_remote_get_signal_index(infrared->remote, button_name, &index);
app_state->current_button_index = index_found ? (signed)index :
InfraredButtonIndexNone;
FURI_LOG_D(TAG, "Sending signal with name \"%s\"", button_name);
} else {
FURI_LOG_D(
TAG, "Sending signal with index \"%ld\"", app_state->current_button_index);
}
if(infrared->app_state.current_button_index != InfraredButtonIndexNone) {
InfraredErrorCode error = infrared_tx_send_once_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;
}
}
}
if(result) {
scene_manager_set_scene_state(
infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded);
}
rpc_system_app_confirm(infrared->rpc_ctx, result);
} else if( } else if(
event.event == InfraredCustomEventTypeRpcExit || event.event == InfraredCustomEventTypeRpcExit ||
event.event == InfraredCustomEventTypeRpcSessionClose || event.event == InfraredCustomEventTypeRpcSessionClose ||

View File

@@ -7,13 +7,36 @@
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h> #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <flipper_format/flipper_format.h> #include <flipper_format/flipper_format.h>
#define TAG "Skylanders" #define TAG "Skylanders"
#define POLY UINT64_C(0x42f0e1eba9ea3693)
#define TOP UINT64_C(0x800000000000)
#define UID_LEN 4
#define KEY_MASK 0xFFFFFFFFFFFF
static const uint64_t skylanders_key = 0x4b0b20107ccb; static const uint64_t skylanders_key = 0x4b0b20107ccb;
static const char* nfc_resources_header = "Flipper NFC resources"; static const char* nfc_resources_header = "Flipper NFC resources";
static const uint32_t nfc_resources_file_version = 1; static const uint32_t nfc_resources_file_version = 1;
uint64_t crc64_like(uint64_t result, uint8_t sector) {
result ^= (uint64_t)sector << 40;
for(int i = 0; i < 8; i++) {
result = (result & TOP) ? (result << 1) ^ POLY : result << 1;
}
return result;
}
uint64_t taghash(uint32_t uid) {
uint64_t result = 0x9AE903260CC4;
uint8_t uidBytes[UID_LEN] = {0};
memcpy(uidBytes, &uid, UID_LEN);
for(int i = 0; i < UID_LEN; i++) {
result = crc64_like(result, uidBytes[i]);
}
return result;
}
static bool skylanders_search_data( static bool skylanders_search_data(
Storage* storage, Storage* storage,
const char* file_name, const char* file_name,
@@ -88,6 +111,12 @@ static bool skylanders_read(Nfc* nfc, NfcDevice* device) {
MfClassicData* data = mf_classic_alloc(); MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data); nfc_device_copy_data(device, NfcProtocolMfClassic, data);
size_t* uid_len = 0;
const uint8_t* uid_bytes = mf_classic_get_uid(data, uid_len);
uint32_t uid = 0;
memcpy(&uid, uid_bytes, sizeof(uid));
uint64_t hash = taghash(uid);
do { do {
MfClassicType type = MfClassicType1k; MfClassicType type = MfClassicType1k;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
@@ -96,10 +125,18 @@ static bool skylanders_read(Nfc* nfc, NfcDevice* device) {
data->type = type; data->type = type;
MfClassicDeviceKeys keys = {}; MfClassicDeviceKeys keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_a[i].data); if(i == 0) {
FURI_BIT_SET(keys.key_a_mask, i); bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_a[i].data);
bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_b[i].data); FURI_BIT_SET(keys.key_a_mask, i);
FURI_BIT_SET(keys.key_b_mask, i); } else {
uint64_t sectorhash = crc64_like(hash, i);
uint64_t key = sectorhash & KEY_MASK;
uint8_t* keyBytes = (uint8_t*)&key;
memcpy(keys.key_a[i].data, keyBytes, sizeof(MfClassicKey));
FURI_BIT_SET(keys.key_a_mask, i);
memset(keys.key_b[i].data, 0, sizeof(MfClassicKey));
FURI_BIT_SET(keys.key_b_mask, i);
}
} }
error = mf_classic_poller_sync_read(nfc, &keys, data); error = mf_classic_poller_sync_read(nfc, &keys, data);
@@ -134,7 +171,7 @@ static bool skylanders_parse(const NfcDevice* device, FuriString* parsed_data) {
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6); uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
if(key != skylanders_key) break; if(key != skylanders_key) break;
const uint16_t id = (uint16_t)*data->block[1].data; const uint16_t id = data->block[1].data[1] << 8 | data->block[1].data[0];
if(id == 0) break; if(id == 0) break;
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);

View File

@@ -35,6 +35,7 @@ typedef enum {
SubGhzCustomEventSceneRpcLoad, SubGhzCustomEventSceneRpcLoad,
SubGhzCustomEventSceneRpcButtonPress, SubGhzCustomEventSceneRpcButtonPress,
SubGhzCustomEventSceneRpcButtonRelease, SubGhzCustomEventSceneRpcButtonRelease,
SubGhzCustomEventSceneRpcButtonPressRelease,
SubGhzCustomEventSceneRpcSessionClose, SubGhzCustomEventSceneRpcSessionClose,
SubGhzCustomEventViewReceiverOK, SubGhzCustomEventViewReceiverOK,

View File

@@ -87,6 +87,43 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle);
rpc_system_app_confirm(subghz->rpc_ctx, result); rpc_system_app_confirm(subghz->rpc_ctx, result);
} else if(event.event == SubGhzCustomEventSceneRpcButtonPressRelease) {
bool result = false;
if(state == SubGhzRpcStateLoaded) {
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, RpcAppSystemErrorCodeRegionLock);
rpc_system_app_set_error_text(
subghz->rpc_ctx,
"Transmission on this frequency is restricted in your region");
break;
case SubGhzTxRxStartTxStateErrorParserOthers:
rpc_system_app_set_error_code(
subghz->rpc_ctx, RpcAppSystemErrorCodeInternalParse);
rpc_system_app_set_error_text(
subghz->rpc_ctx, "Error in protocol parameters description");
break;
default: //if(SubGhzTxRxStartTxStateOk)
result = true;
subghz_blink_start(subghz);
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateTx);
break;
}
}
// Stop transmission
if(state == SubGhzRpcStateTx) {
subghz_txrx_stop(subghz->txrx);
subghz_blink_stop(subghz);
result = true;
}
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle);
rpc_system_app_confirm(subghz->rpc_ctx, result);
} else if(event.event == SubGhzCustomEventSceneRpcLoad) { } else if(event.event == SubGhzCustomEventSceneRpcLoad) {
bool result = false; bool result = false;
if(state == SubGhzRpcStateIdle) { if(state == SubGhzRpcStateIdle) {

View File

@@ -53,6 +53,9 @@ static void subghz_rpc_command_callback(const RpcAppSystemEvent* event, void* co
} else if(event->type == RpcAppEventTypeButtonRelease) { } else if(event->type == RpcAppEventTypeButtonRelease) {
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease); subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease);
} else if(event->type == RpcAppEventTypeButtonPressRelease) {
view_dispatcher_send_custom_event(
subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPressRelease);
} else { } else {
rpc_system_app_confirm(subghz->rpc_ctx, false); rpc_system_app_confirm(subghz->rpc_ctx, false);
} }

View File

@@ -280,6 +280,8 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, private, sizeof(private))); MCHECK(mbedtls_md_hmac_update(&hmac_ctx, private, sizeof(private)));
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id))); MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));
MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, handle.hash)); MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, handle.hash));
mbedtls_md_free(&hmac_ctx);
} }
// Generate public key // Generate public key
@@ -387,6 +389,8 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, priv_key, sizeof(priv_key))); MCHECK(mbedtls_md_hmac_update(&hmac_ctx, priv_key, sizeof(priv_key)));
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id))); MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));
MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, mac_control)); MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, mac_control));
mbedtls_md_free(&hmac_ctx);
} }
if(memcmp(req->key_handle.hash, mac_control, sizeof(mac_control)) != 0) { if(memcmp(req->key_handle.hash, mac_control, sizeof(mac_control)) != 0) {

View File

@@ -533,9 +533,9 @@ void cli_session_open(Cli* cli, void* session) {
cli->session = session; cli->session = session;
if(cli->session != NULL) { if(cli->session != NULL) {
cli->session->init(); cli->session->init();
furi_thread_set_stdout_callback(cli->session->tx_stdout); furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL);
} else { } else {
furi_thread_set_stdout_callback(NULL); furi_thread_set_stdout_callback(NULL, NULL);
} }
furi_semaphore_release(cli->idle_sem); furi_semaphore_release(cli->idle_sem);
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
@@ -549,7 +549,7 @@ void cli_session_close(Cli* cli) {
cli->session->deinit(); cli->session->deinit();
} }
cli->session = NULL; cli->session = NULL;
furi_thread_set_stdout_callback(NULL); furi_thread_set_stdout_callback(NULL, NULL);
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
} }
@@ -563,9 +563,9 @@ int32_t cli_srv(void* p) {
furi_record_create(RECORD_CLI, cli); furi_record_create(RECORD_CLI, cli);
if(cli->session != NULL) { if(cli->session != NULL) {
furi_thread_set_stdout_callback(cli->session->tx_stdout); furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL);
} else { } else {
furi_thread_set_stdout_callback(NULL); furi_thread_set_stdout_callback(NULL, NULL);
} }
if(furi_hal_is_normal_boot()) { if(furi_hal_is_normal_boot()) {

View File

@@ -28,8 +28,9 @@ struct CliSession {
void (*init)(void); void (*init)(void);
void (*deinit)(void); void (*deinit)(void);
size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout); size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout);
size_t (*rx_stdin)(uint8_t* buffer, size_t size, uint32_t timeout, void* context);
void (*tx)(const uint8_t* buffer, size_t size); void (*tx)(const uint8_t* buffer, size_t size);
void (*tx_stdout)(const char* data, size_t size); void (*tx_stdout)(const char* data, size_t size, void* context);
bool (*is_connected)(void); bool (*is_connected)(void);
}; };

View File

@@ -243,6 +243,11 @@ static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) {
return rx_cnt; return rx_cnt;
} }
static size_t cli_vcp_rx_stdin(uint8_t* data, size_t size, uint32_t timeout, void* context) {
UNUSED(context);
return cli_vcp_rx(data, size, timeout);
}
static void cli_vcp_tx(const uint8_t* buffer, size_t size) { static void cli_vcp_tx(const uint8_t* buffer, size_t size) {
furi_assert(vcp); furi_assert(vcp);
furi_assert(buffer); furi_assert(buffer);
@@ -268,7 +273,8 @@ static void cli_vcp_tx(const uint8_t* buffer, size_t size) {
VCP_DEBUG("tx %u end", size); VCP_DEBUG("tx %u end", size);
} }
static void cli_vcp_tx_stdout(const char* data, size_t size) { static void cli_vcp_tx_stdout(const char* data, size_t size, void* context) {
UNUSED(context);
cli_vcp_tx((const uint8_t*)data, size); cli_vcp_tx((const uint8_t*)data, size);
} }
@@ -311,6 +317,7 @@ CliSession cli_vcp = {
cli_vcp_init, cli_vcp_init,
cli_vcp_deinit, cli_vcp_deinit,
cli_vcp_rx, cli_vcp_rx,
cli_vcp_rx_stdin,
cli_vcp_tx, cli_vcp_tx,
cli_vcp_tx_stdout, cli_vcp_tx_stdout,
cli_vcp_is_connected, cli_vcp_is_connected,

View File

@@ -258,6 +258,41 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context)
} }
} }
static void rpc_system_app_button_press_release(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(request->which_content == PB_Main_app_button_press_release_request_tag);
RpcAppSystem* rpc_app = context;
furi_assert(rpc_app);
if(rpc_app->callback) {
FURI_LOG_D(TAG, "ButtonPressRelease");
RpcAppSystemEvent event;
event.type = RpcAppEventTypeButtonPressRelease;
if(strlen(request->content.app_button_press_release_request.args) != 0) {
event.data.type = RpcAppSystemEventDataTypeString;
event.data.string = request->content.app_button_press_release_request.args;
} else {
event.data.type = RpcAppSystemEventDataTypeInt32;
event.data.i32 = request->content.app_button_press_release_request.index;
}
rpc_system_app_error_reset(rpc_app);
rpc_system_app_set_last_command(rpc_app, request->command_id, &event);
rpc_app->callback(&event, rpc_app->callback_context);
} else {
rpc_system_app_send_error_response(
rpc_app,
request->command_id,
PB_CommandStatus_ERROR_APP_NOT_RUNNING,
"ButtonPressRelease");
}
}
static void rpc_system_app_get_error_process(const PB_Main* request, void* context) { static void rpc_system_app_get_error_process(const PB_Main* request, void* context) {
furi_assert(request); furi_assert(request);
furi_assert(request->which_content == PB_Main_app_get_error_request_tag); furi_assert(request->which_content == PB_Main_app_get_error_request_tag);
@@ -332,6 +367,7 @@ void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) {
rpc_app->last_event_type == RpcAppEventTypeLoadFile || rpc_app->last_event_type == RpcAppEventTypeLoadFile ||
rpc_app->last_event_type == RpcAppEventTypeButtonPress || rpc_app->last_event_type == RpcAppEventTypeButtonPress ||
rpc_app->last_event_type == RpcAppEventTypeButtonRelease || rpc_app->last_event_type == RpcAppEventTypeButtonRelease ||
rpc_app->last_event_type == RpcAppEventTypeButtonPressRelease ||
rpc_app->last_event_type == RpcAppEventTypeDataExchange); rpc_app->last_event_type == RpcAppEventTypeDataExchange);
const uint32_t last_command_id = rpc_app->last_command_id; const uint32_t last_command_id = rpc_app->last_command_id;
@@ -432,6 +468,9 @@ void* rpc_system_app_alloc(RpcSession* session) {
rpc_handler.message_handler = rpc_system_app_button_release; rpc_handler.message_handler = rpc_system_app_button_release;
rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler); rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler);
rpc_handler.message_handler = rpc_system_app_button_press_release;
rpc_add_handler(session, PB_Main_app_button_press_release_request_tag, &rpc_handler);
rpc_handler.message_handler = rpc_system_app_get_error_process; rpc_handler.message_handler = rpc_system_app_get_error_process;
rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler); rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler);

View File

@@ -90,6 +90,13 @@ typedef enum {
* all activities to be conducted while a button is being pressed. * all activities to be conducted while a button is being pressed.
*/ */
RpcAppEventTypeButtonRelease, RpcAppEventTypeButtonRelease,
/**
* @brief The client has informed the application that a button has been pressed and released.
*
* This command's meaning is application-specific, e.g. to perform an action
* once without repeating it.
*/
RpcAppEventTypeButtonPressRelease,
/** /**
* @brief The client has sent a byte array of arbitrary size. * @brief The client has sent a byte array of arbitrary size.
* *
@@ -162,6 +169,7 @@ void rpc_system_app_send_exited(RpcAppSystem* rpc_app);
* - RpcAppEventTypeLoadFile * - RpcAppEventTypeLoadFile
* - RpcAppEventTypeButtonPress * - RpcAppEventTypeButtonPress
* - RpcAppEventTypeButtonRelease * - RpcAppEventTypeButtonRelease
* - RpcAppEventTypeButtonPressRelease
* - RpcAppEventTypeDataExchange * - RpcAppEventTypeDataExchange
* *
* Not confirming these events will result in a client-side timeout. * Not confirming these events will result in a client-side timeout.

View File

@@ -62,8 +62,8 @@ packages:
color-name@1.1.4: color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
cross-spawn@7.0.3: cross-spawn@7.0.6:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
eastasianwidth@0.2.0: eastasianwidth@0.2.0:
@@ -240,7 +240,7 @@ snapshots:
color-name@1.1.4: {} color-name@1.1.4: {}
cross-spawn@7.0.3: cross-spawn@7.0.6:
dependencies: dependencies:
path-key: 3.1.1 path-key: 3.1.1
shebang-command: 2.0.0 shebang-command: 2.0.0
@@ -256,7 +256,7 @@ snapshots:
foreground-child@3.3.0: foreground-child@3.3.0:
dependencies: dependencies:
cross-spawn: 7.0.3 cross-spawn: 7.0.6
signal-exit: 4.1.0 signal-exit: 4.1.0
get-caller-file@2.0.5: {} get-caller-file@2.0.5: {}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@next-flip/fz-sdk-mntm", "name": "@next-flip/fz-sdk-mntm",
"version": "0.1.3", "version": "0.1.4",
"description": "Type declarations and documentation for native JS modules available on Momentum Custom Firmware for Flipper Zero", "description": "Type declarations and documentation for native JS modules available on Momentum Custom Firmware for Flipper Zero",
"keywords": [ "keywords": [
"momentum", "momentum",

View File

@@ -8,13 +8,6 @@ importers:
.: .:
dependencies: dependencies:
prompts:
specifier: ^2.4.2
version: 2.4.2
serialport:
specifier: ^12.0.0
version: 12.0.0
devDependencies:
esbuild: esbuild:
specifier: ^0.24.0 specifier: ^0.24.0
version: 0.24.0 version: 0.24.0
@@ -24,6 +17,12 @@ importers:
json5: json5:
specifier: ^2.2.3 specifier: ^2.2.3
version: 2.2.3 version: 2.2.3
prompts:
specifier: ^2.4.2
version: 2.4.2
serialport:
specifier: ^12.0.0
version: 12.0.0
typedoc: typedoc:
specifier: ^0.26.10 specifier: ^0.26.10
version: 0.26.10(typescript@5.6.3) version: 0.26.10(typescript@5.6.3)

View File

@@ -91,9 +91,21 @@ async function build(config) {
async function upload(config) { async function upload(config) {
const appFile = fs.readFileSync(config.input, "utf8"); const appFile = fs.readFileSync(config.input, "utf8");
const flippers = (await SerialPort.list()).filter(x => x.serialNumber?.startsWith("flip_")); const serialPorts = await SerialPort.list();
if (!flippers) { let flippers = serialPorts
.filter(x => x.serialNumber?.startsWith("flip_"))
.map(x => ({ path: x.path, name: x.serialNumber.replace("flip_", "") }));
if (!flippers.length) {
// some Windows installations don't report the serial number correctly;
// filter by STM VCP VID:PID instead
flippers = serialPorts
.filter(x => x?.vendorId === "0483" && x?.productId === "5740")
.map(x => ({ path: x.path, name: x.path }));
}
if (!flippers.length) {
console.error("No Flippers found"); console.error("No Flippers found");
process.exit(1); process.exit(1);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 872 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

View File

@@ -0,0 +1,23 @@
Filetype: Flipper Animation
Version: 1
Width: 128
Height: 64
Passive frames: 10
Active frames: 18
Frames order: 0 1 2 1 0 1 2 1 0 1 2 3 4 5 6 5 4 7 2 8 9 10 11 10 9 10 11 12
Active cycles: 1
Frame rate: 2
Duration: 3600
Active cooldown: 7
Bubble slots: 1
Slot: 0
X: 11
Y: 19
Text: HAPPY\nHOLIDAYS!
AlignH: Right
AlignV: Center
StartFrame: 22
EndFrame: 27

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

View File

@@ -0,0 +1,23 @@
Filetype: Flipper Animation
Version: 1
Width: 128
Height: 64
Passive frames: 18
Active frames: 19
Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
Active cycles: 1
Frame rate: 2
Duration: 3600
Active cooldown: 7
Bubble slots: 1
Slot: 0
X: 21
Y: 25
Text: AAAAaAAAAHHh!!
AlignH: Right
AlignV: Bottom
StartFrame: 30
EndFrame: 32

View File

@@ -8,7 +8,7 @@ Active frames: 0
Frames order: 0 1 2 3 4 5 6 6 Frames order: 0 1 2 3 4 5 6 6
Active cycles: 0 Active cycles: 0
Frame rate: 1 Frame rate: 1
Duration: 3600 Duration: 360
Active cooldown: 0 Active cooldown: 0
Bubble slots: 1 Bubble slots: 1

View File

@@ -231,3 +231,17 @@ Max butthurt: 7
Min level: 11 Min level: 11
Max level: 30 Max level: 30
Weight: 4 Weight: 4
Name: L1_Happy_holidays_128x64
Min butthurt: 0
Max butthurt: 14
Min level: 16
Max level: 30
Weight: 4
Name: L1_Sleigh_ride_128x64
Min butthurt: 0
Max butthurt: 14
Min level: 9
Max level: 30
Weight: 4

View File

@@ -43,7 +43,7 @@ To add unit tests for your protocol, follow these steps:
1. Create a file named `test_<your_protocol_name>.irtest` in the [assets](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) directory. 1. Create a file named `test_<your_protocol_name>.irtest` in the [assets](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) directory.
2. Fill it with the test data (more on it below). 2. Fill it with the test data (more on it below).
3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/infrared/infrared_test.c). 3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/tests/infrared/infrared_test.c).
4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass. 4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass.
##### Test data format ##### Test data format

View File

@@ -106,5 +106,7 @@ void* aligned_malloc(size_t size, size_t alignment) {
} }
void aligned_free(void* p) { void aligned_free(void* p) {
free(((void**)p)[-1]); if(p) {
free(((void**)p)[-1]);
}
} }

View File

@@ -54,6 +54,11 @@ bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigg
pdTRUE; pdTRUE;
} }
size_t furi_stream_get_trigger_level(FuriStreamBuffer* stream_buffer) {
furi_check(stream_buffer);
return ((StaticStreamBuffer_t*)stream_buffer)->xTriggerLevelBytes;
}
size_t furi_stream_buffer_send( size_t furi_stream_buffer_send(
FuriStreamBuffer* stream_buffer, FuriStreamBuffer* stream_buffer,
const void* data, const void* data,

View File

@@ -54,6 +54,17 @@ void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer);
*/ */
bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level); bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level);
/**
* @brief Get trigger level for stream buffer.
* A stream buffer's trigger level is the number of bytes that must be in the
* stream buffer before a task that is blocked on the stream buffer to
* wait for data is moved out of the blocked state.
*
* @param stream_buffer The stream buffer instance
* @return The trigger level for the stream buffer
*/
size_t furi_stream_get_trigger_level(FuriStreamBuffer* stream_buffer);
/** /**
* @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer. * @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer.
* Wakes up task waiting for data to become available if called from ISR. * Wakes up task waiting for data to become available if called from ISR.

View File

@@ -129,12 +129,12 @@ void furi_string_swap(FuriString* string_1, FuriString* string_2);
/** Move string_2 content to string_1. /** Move string_2 content to string_1.
* *
* Set the string to the other one, and destroy the other one. * Copy data from one string to another and destroy the source.
* *
* @param string_1 The FuriString instance 1 * @param destination The destination FuriString
* @param string_2 The FuriString instance 2 * @param source The source FuriString
*/ */
void furi_string_move(FuriString* string_1, FuriString* string_2); void furi_string_move(FuriString* destination, FuriString* source);
/** Compute a hash for the string. /** Compute a hash for the string.
* *

View File

@@ -25,12 +25,17 @@
#define THREAD_MAX_STACK_SIZE (UINT16_MAX * sizeof(StackType_t)) #define THREAD_MAX_STACK_SIZE (UINT16_MAX * sizeof(StackType_t))
typedef struct FuriThreadStdout FuriThreadStdout; typedef struct {
struct FuriThreadStdout {
FuriThreadStdoutWriteCallback write_callback; FuriThreadStdoutWriteCallback write_callback;
FuriString* buffer; FuriString* buffer;
}; void* context;
} FuriThreadStdout;
typedef struct {
FuriThreadStdinReadCallback read_callback;
FuriString* unread_buffer; // <! stores data from `ungetc` and friends
void* context;
} FuriThreadStdin;
struct FuriThread { struct FuriThread {
StaticTask_t container; StaticTask_t container;
@@ -57,6 +62,7 @@ struct FuriThread {
size_t heap_size; size_t heap_size;
FuriThreadStdout output; FuriThreadStdout output;
FuriThreadStdin input;
// Keep all non-alignable byte types in one place, // Keep all non-alignable byte types in one place,
// this ensures that the size of this structure is minimal // this ensures that the size of this structure is minimal
@@ -138,6 +144,7 @@ static void furi_thread_body(void* context) {
static void furi_thread_init_common(FuriThread* thread) { static void furi_thread_init_common(FuriThread* thread) {
thread->output.buffer = furi_string_alloc(); thread->output.buffer = furi_string_alloc();
thread->input.unread_buffer = furi_string_alloc();
FuriThread* parent = NULL; FuriThread* parent = NULL;
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
@@ -247,6 +254,7 @@ void furi_thread_free(FuriThread* thread) {
} }
furi_string_free(thread->output.buffer); furi_string_free(thread->output.buffer);
furi_string_free(thread->input.unread_buffer);
free(thread); free(thread);
} }
@@ -717,13 +725,22 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) {
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) { static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) {
if(thread->output.write_callback != NULL) { if(thread->output.write_callback != NULL) {
thread->output.write_callback(data, size); thread->output.write_callback(data, size, thread->output.context);
} else { } else {
furi_log_tx((const uint8_t*)data, size); furi_log_tx((const uint8_t*)data, size);
} }
return size; return size;
} }
static size_t
__furi_thread_stdin_read(FuriThread* thread, char* data, size_t size, FuriWait timeout) {
if(thread->input.read_callback != NULL) {
return thread->input.read_callback(data, size, timeout, thread->input.context);
} else {
return 0;
}
}
static int32_t __furi_thread_stdout_flush(FuriThread* thread) { static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
FuriString* buffer = thread->output.buffer; FuriString* buffer = thread->output.buffer;
size_t size = furi_string_size(buffer); size_t size = furi_string_size(buffer);
@@ -734,19 +751,33 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
return 0; return 0;
} }
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
__furi_thread_stdout_flush(thread);
thread->output.write_callback = callback;
}
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void) { FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void) {
FuriThread* thread = furi_thread_get_current(); FuriThread* thread = furi_thread_get_current();
furi_check(thread); furi_check(thread);
return thread->output.write_callback; return thread->output.write_callback;
} }
FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
return thread->input.read_callback;
}
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
__furi_thread_stdout_flush(thread);
thread->output.write_callback = callback;
thread->output.context = context;
}
void furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
thread->input.read_callback = callback;
thread->input.context = context;
}
size_t furi_thread_stdout_write(const char* data, size_t size) { size_t furi_thread_stdout_write(const char* data, size_t size) {
FuriThread* thread = furi_thread_get_current(); FuriThread* thread = furi_thread_get_current();
furi_check(thread); furi_check(thread);
@@ -779,6 +810,31 @@ int32_t furi_thread_stdout_flush(void) {
return __furi_thread_stdout_flush(thread); return __furi_thread_stdout_flush(thread);
} }
size_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
size_t from_buffer = MIN(furi_string_size(thread->input.unread_buffer), size);
size_t from_input = size - from_buffer;
size_t from_input_actual =
__furi_thread_stdin_read(thread, buffer + from_buffer, from_input, timeout);
memcpy(buffer, furi_string_get_cstr(thread->input.unread_buffer), from_buffer);
furi_string_right(thread->input.unread_buffer, from_buffer);
return from_buffer + from_input_actual;
}
void furi_thread_stdin_unread(char* buffer, size_t size) {
FuriThread* thread = furi_thread_get_current();
furi_check(thread);
FuriString* new_buf = furi_string_alloc(); // there's no furi_string_alloc_set_strn :(
furi_string_set_strn(new_buf, buffer, size);
furi_string_cat(new_buf, thread->input.unread_buffer);
furi_string_free(thread->input.unread_buffer);
thread->input.unread_buffer = new_buf;
}
void furi_thread_suspend(FuriThreadId thread_id) { void furi_thread_suspend(FuriThreadId thread_id) {
furi_check(thread_id); furi_check(thread_id);

View File

@@ -74,8 +74,23 @@ typedef int32_t (*FuriThreadCallback)(void* context);
* *
* @param[in] data pointer to the data to be written to the standard out * @param[in] data pointer to the data to be written to the standard out
* @param[in] size size of the data in bytes * @param[in] size size of the data in bytes
* @param[in] context optional context
*/ */
typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size, void* context);
/**
* @brief Standard input callback function pointer type
*
* The function to be used as a standard input callback MUST follow this signature.
*
* @param[out] buffer buffer to read data into
* @param[in] size maximum number of bytes to read into the buffer
* @param[in] timeout how long to wait for (in ticks) before giving up
* @param[in] context optional context
* @returns number of bytes that was actually read into the buffer
*/
typedef size_t (
*FuriThreadStdinReadCallback)(char* buffer, size_t size, FuriWait timeout, void* context);
/** /**
* @brief State change callback function pointer type. * @brief State change callback function pointer type.
@@ -468,13 +483,30 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id);
*/ */
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void); FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void);
/**
* @brief Get the standard input callback for the current thead.
*
* @return pointer to the standard in callback function
*/
FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void);
/** Set standard output callback for the current thread. /** Set standard output callback for the current thread.
* *
* @param[in] callback pointer to the callback function or NULL to clear * @param[in] callback pointer to the callback function or NULL to clear
* @param[in] context context to be passed to the callback
*/ */
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context);
/** Set standard input callback for the current thread.
*
* @param[in] callback pointer to the callback function or NULL to clear
* @param[in] context context to be passed to the callback
*/
void furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context);
/** Write data to buffered standard output. /** Write data to buffered standard output.
*
* @note You can also use the standard C `putc`, `puts`, `printf` and friends.
* *
* @param[in] data pointer to the data to be written * @param[in] data pointer to the data to be written
* @param[in] size data size in bytes * @param[in] size data size in bytes
@@ -489,6 +521,29 @@ size_t furi_thread_stdout_write(const char* data, size_t size);
*/ */
int32_t furi_thread_stdout_flush(void); int32_t furi_thread_stdout_flush(void);
/** Read data from the standard input
*
* @note You can also use the standard C `getc`, `gets` and friends.
*
* @param[in] buffer pointer to the buffer to read data into
* @param[in] size how many bytes to read into the buffer
* @param[in] timeout how long to wait for (in ticks) before giving up
* @return number of bytes that was actually read
*/
size_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout);
/** Puts data back into the standard input buffer
*
* `furi_thread_stdin_read` will return the bytes in the same order that they
* were supplied to this function.
*
* @note You can also use the standard C `ungetc`.
*
* @param[in] buffer pointer to the buffer to get data from
* @param[in] size how many bytes to read from the buffer
*/
void furi_thread_stdin_unread(char* buffer, size_t size);
/** /**
* @brief Suspend a thread. * @brief Suspend a thread.
* *

View File

@@ -10,13 +10,13 @@
#define TAG "BleHid" #define TAG "BleHid"
#define BLE_SVC_HID_REPORT_MAP_MAX_LEN (255) #define BLE_SVC_HID_REPORT_MAP_MAX_LEN (255)
#define BLE_SVC_HID_REPORT_MAX_LEN (255) #define BLE_SVC_HID_REPORT_MAX_LEN (255)
#define BLE_SVC_HID_REPORT_REF_LEN (2) #define BLE_SVC_HID_REPORT_REF_LEN (2)
#define BLE_SVC_HID_INFO_LEN (4) #define BLE_SVC_HID_INFO_LEN (4)
#define BLE_SVC_HID_CONTROL_POINT_LEN (1) #define BLE_SVC_HID_CONTROL_POINT_LEN (1)
#define BLE_SVC_HID_INPUT_REPORT_COUNT (3) #define BLE_SVC_HID_INPUT_REPORT_COUNT (3)
#define BLE_SVC_HID_OUTPUT_REPORT_COUNT (0) #define BLE_SVC_HID_OUTPUT_REPORT_COUNT (0)
#define BLE_SVC_HID_FEATURE_REPORT_COUNT (0) #define BLE_SVC_HID_FEATURE_REPORT_COUNT (0)
#define BLE_SVC_HID_REPORT_COUNT \ #define BLE_SVC_HID_REPORT_COUNT \
(BLE_SVC_HID_INPUT_REPORT_COUNT + BLE_SVC_HID_OUTPUT_REPORT_COUNT + \ (BLE_SVC_HID_INPUT_REPORT_COUNT + BLE_SVC_HID_OUTPUT_REPORT_COUNT + \
@@ -157,6 +157,7 @@ static BleEventAckStatus ble_svc_hid_event_handler(void* event, void* context) {
hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data);
evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data;
// aci_gatt_attribute_modified_event_rp0* attribute_modified; // aci_gatt_attribute_modified_event_rp0* attribute_modified;
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
// Process modification events // Process modification events
@@ -274,6 +275,7 @@ bool ble_svc_hid_update_input_report(
.data_ptr = data, .data_ptr = data,
.data_len = len, .data_len = len,
}; };
return ble_gatt_characteristic_update( return ble_gatt_characteristic_update(
hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data);
} }

View File

@@ -830,9 +830,7 @@ void elf_file_free(ELFFile* elf) {
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
ELFSectionDict_next(it)) { ELFSectionDict_next(it)) {
const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);
if(itref->value.data) { aligned_free(itref->value.data);
aligned_free(itref->value.data);
}
if(itref->value.fast_rel) { if(itref->value.fast_rel) {
if(itref->value.fast_rel->data) { if(itref->value.fast_rel->data) {
aligned_free(itref->value.fast_rel->data); aligned_free(itref->value.fast_rel->data);

View File

@@ -403,6 +403,11 @@ bool flipper_format_write_comment_cstr(FlipperFormat* flipper_format, const char
return flipper_format_stream_write_comment_cstr(flipper_format->stream, data); return flipper_format_stream_write_comment_cstr(flipper_format->stream, data);
} }
bool flipper_format_write_empty_line(FlipperFormat* flipper_format) {
furi_check(flipper_format);
return flipper_format_stream_write_eol(flipper_format->stream);
}
bool flipper_format_delete_key(FlipperFormat* flipper_format, const char* key) { bool flipper_format_delete_key(FlipperFormat* flipper_format, const char* key) {
furi_check(flipper_format); furi_check(flipper_format);
FlipperStreamWriteData write_data = { FlipperStreamWriteData write_data = {

View File

@@ -518,6 +518,14 @@ bool flipper_format_write_comment(FlipperFormat* flipper_format, FuriString* dat
*/ */
bool flipper_format_write_comment_cstr(FlipperFormat* flipper_format, const char* data); bool flipper_format_write_comment_cstr(FlipperFormat* flipper_format, const char* data);
/** Write empty line (Improves readability for human based parsing)
*
* @param flipper_format Pointer to a FlipperFormat instance
*
* @return True on success
*/
bool flipper_format_write_empty_line(FlipperFormat* flipper_format);
/** Removes the first matching key and its value. Sets the RW pointer to a /** Removes the first matching key and its value. Sets the RW pointer to a
* position of deleted data. * position of deleted data.
* *

View File

@@ -146,7 +146,7 @@ static void mf_plus_poller_set_callback(
static NfcCommand mf_plus_poller_run(NfcGenericEvent event, void* context) { static NfcCommand mf_plus_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context); furi_assert(context);
furi_assert(event.protocol = NfcProtocolIso14443_4a); furi_assert(event.protocol == NfcProtocolIso14443_4a);
furi_assert(event.event_data); furi_assert(event.event_data);
MfPlusPoller* instance = context; MfPlusPoller* instance = context;
@@ -178,7 +178,7 @@ void mf_plus_poller_free(MfPlusPoller* instance) {
static bool mf_plus_poller_detect(NfcGenericEvent event, void* context) { static bool mf_plus_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context); furi_assert(context);
furi_assert(event.protocol = NfcProtocolIso14443_4a); furi_assert(event.protocol == NfcProtocolIso14443_4a);
furi_assert(event.event_data); furi_assert(event.event_data);
MfPlusPoller* instance = context; MfPlusPoller* instance = context;

View File

@@ -37,7 +37,7 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
furi_check(data); furi_check(data);
uint8_t auth_cmd[5] = {MF_ULTRALIGHT_CMD_PWD_AUTH}; //-V1009 uint8_t auth_cmd[5] = {MF_ULTRALIGHT_CMD_PWD_AUTH}; //-V1009
memccpy(&auth_cmd[1], data->password.data, 0, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE); memcpy(&auth_cmd[1], data->password.data, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE);
bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd)); bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd));
MfUltralightError ret = MfUltralightErrorNone; MfUltralightError ret = MfUltralightErrorNone;

View File

@@ -44,18 +44,24 @@ wrapped_fn_list = [
"vsiprintf", "vsiprintf",
"vsniprintf", "vsniprintf",
# #
# Scanf is not implemented 4 now # standard input
#
"fgetc",
"getc",
"getchar",
"fgets",
"ungetc",
#
# standard input, but unimplemented
#
"gets",
#
# scanf, not implemented for now
# #
# "fscanf", # "fscanf",
# "scanf", # "scanf",
# "sscanf", # "sscanf",
# "vsprintf", # "vsprintf",
# "fgetc",
# "fgets",
# "getc",
# "getchar",
# "gets",
# "ungetc",
# "vfscanf", # "vfscanf",
# "vscanf", # "vscanf",
# "vsscanf", # "vsscanf",

View File

@@ -51,11 +51,54 @@ int __wrap_snprintf(char* str, size_t size, const char* format, ...) {
} }
int __wrap_fflush(FILE* stream) { int __wrap_fflush(FILE* stream) {
UNUSED(stream); if(stream == stdout) furi_thread_stdout_flush();
furi_thread_stdout_flush();
return 0; return 0;
} }
int __wrap_fgetc(FILE* stream) {
if(stream != stdin) return EOF;
char c;
if(furi_thread_stdin_read(&c, 1, FuriWaitForever) == 0) return EOF;
return c;
}
int __wrap_getc(FILE* stream) {
return __wrap_fgetc(stream);
}
int __wrap_getchar(void) {
return __wrap_fgetc(stdin);
}
char* __wrap_fgets(char* str, size_t n, FILE* stream) {
// leave space for the zero terminator
furi_check(n >= 1);
n--;
if(stream != stdin) {
*str = '\0';
return str;
}
// read characters
int c;
do {
c = __wrap_fgetc(stdin);
if(c > 0) *(str++) = c;
} while(c != EOF && c != '\n' && --n);
// place zero terminator
*str = '\0';
return str;
}
int __wrap_ungetc(int ch, FILE* stream) {
char c = ch;
if(stream != stdin) return EOF;
furi_thread_stdin_unread(&c, 1);
return ch;
}
__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) { __attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) {
UNUSED(file); UNUSED(file);
UNUSED(line); UNUSED(line);

View File

@@ -16,6 +16,12 @@ int __wrap_putc(int ch, FILE* stream);
int __wrap_snprintf(char* str, size_t size, const char* format, ...); int __wrap_snprintf(char* str, size_t size, const char* format, ...);
int __wrap_fflush(FILE* stream); int __wrap_fflush(FILE* stream);
int __wrap_fgetc(FILE* stream);
int __wrap_getc(FILE* stream);
int __wrap_getchar(void);
char* __wrap_fgets(char* str, size_t n, FILE* stream);
int __wrap_ungetc(int ch, FILE* stream);
__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e); __attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e);
__attribute__((__noreturn__)) void __attribute__((__noreturn__)) void

View File

@@ -243,6 +243,8 @@ static Iso15693ParserCommand iso15693_parser_parse_1_out_of_256(Iso15693Parser*
instance->parsed_frame, instance->next_byte_part * 4 + j / 2); instance->parsed_frame, instance->next_byte_part * 4 + j / 2);
} }
} }
} else {
instance->zero_found = true;
} }
} }
instance->next_byte_part = (instance->next_byte_part + 1) % 64; instance->next_byte_part = (instance->next_byte_part + 1) % 64;

View File

@@ -316,8 +316,8 @@ SubGhzProtocolStatus
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) { if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) {
break;
res = SubGhzProtocolStatusErrorEncoderGetUpload; res = SubGhzProtocolStatusErrorEncoderGetUpload;
break;
} }
instance->encoder.is_running = true; instance->encoder.is_running = true;

View File

@@ -30,6 +30,7 @@ env.Append(
File("stream/string_stream.h"), File("stream/string_stream.h"),
File("stream/buffered_file_stream.h"), File("stream/buffered_file_stream.h"),
File("strint.h"), File("strint.h"),
File("pipe.h"),
File("protocols/protocol_dict.h"), File("protocols/protocol_dict.h"),
File("pretty_format.h"), File("pretty_format.h"),
File("hex.h"), File("hex.h"),

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