Merge branch 'fz-dev' into dev
@ -48,7 +48,7 @@ FileBrowserApp* file_browser_app_alloc(char* arg) {
|
||||
|
||||
app->file_path = furi_string_alloc();
|
||||
app->file_browser = file_browser_alloc(app->file_path);
|
||||
file_browser_configure(app->file_browser, "*", true, &I_badusb_10px, true);
|
||||
file_browser_configure(app->file_browser, "*", NULL, true, &I_badusb_10px, true);
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget));
|
||||
|
@ -215,26 +215,26 @@ static UartEchoApp* uart_echo_app_alloc() {
|
||||
view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
||||
|
||||
app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app);
|
||||
furi_thread_start(app->worker_thread);
|
||||
|
||||
// Enable uart listener
|
||||
furi_hal_console_disable();
|
||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
||||
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
|
||||
|
||||
app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app);
|
||||
furi_thread_start(app->worker_thread);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void uart_echo_app_free(UartEchoApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
furi_hal_console_enable(); // this will also clear IRQ callback so thread is no longer referenced
|
||||
|
||||
furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop);
|
||||
furi_thread_join(app->worker_thread);
|
||||
furi_thread_free(app->worker_thread);
|
||||
|
||||
furi_hal_console_enable();
|
||||
|
||||
// Free views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, 0);
|
||||
|
||||
|
116
applications/debug/unit_tests/furi_hal/furi_hal_tests.c
Normal file
@ -0,0 +1,116 @@
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <lp5562_reg.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
#define DATA_SIZE 4
|
||||
|
||||
static void furi_hal_i2c_int_setup() {
|
||||
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
|
||||
}
|
||||
|
||||
static void furi_hal_i2c_int_teardown() {
|
||||
furi_hal_i2c_release(&furi_hal_i2c_handle_power);
|
||||
}
|
||||
|
||||
MU_TEST(furi_hal_i2c_int_1b) {
|
||||
bool ret = false;
|
||||
uint8_t data_one = 0;
|
||||
|
||||
// 1 byte: read, write, read
|
||||
ret = furi_hal_i2c_read_reg_8(
|
||||
&furi_hal_i2c_handle_power,
|
||||
LP5562_ADDRESS,
|
||||
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||
&data_one,
|
||||
LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "0 read_reg_8 failed");
|
||||
mu_assert(data_one != 0, "0 invalid data");
|
||||
ret = furi_hal_i2c_write_reg_8(
|
||||
&furi_hal_i2c_handle_power,
|
||||
LP5562_ADDRESS,
|
||||
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||
data_one,
|
||||
LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "1 write_reg_8 failed");
|
||||
ret = furi_hal_i2c_read_reg_8(
|
||||
&furi_hal_i2c_handle_power,
|
||||
LP5562_ADDRESS,
|
||||
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||
&data_one,
|
||||
LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "2 read_reg_8 failed");
|
||||
mu_assert(data_one != 0, "2 invalid data");
|
||||
}
|
||||
|
||||
MU_TEST(furi_hal_i2c_int_3b) {
|
||||
bool ret = false;
|
||||
uint8_t data_many[DATA_SIZE] = {0};
|
||||
|
||||
// 3 byte: read, write, read
|
||||
data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;
|
||||
ret = furi_hal_i2c_tx(
|
||||
&furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "3 tx failed");
|
||||
ret = furi_hal_i2c_rx(
|
||||
&furi_hal_i2c_handle_power,
|
||||
LP5562_ADDRESS,
|
||||
data_many + 1,
|
||||
DATA_SIZE - 1,
|
||||
LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "4 rx failed");
|
||||
for(size_t i = 0; i < DATA_SIZE; i++) mu_assert(data_many[i] != 0, "4 invalid data_many");
|
||||
|
||||
ret = furi_hal_i2c_tx(
|
||||
&furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, DATA_SIZE, LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "5 tx failed");
|
||||
|
||||
ret = furi_hal_i2c_tx(
|
||||
&furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "6 tx failed");
|
||||
ret = furi_hal_i2c_rx(
|
||||
&furi_hal_i2c_handle_power,
|
||||
LP5562_ADDRESS,
|
||||
data_many + 1,
|
||||
DATA_SIZE - 1,
|
||||
LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "7 rx failed");
|
||||
for(size_t i = 0; i < DATA_SIZE; i++) mu_assert(data_many[i] != 0, "7 invalid data_many");
|
||||
}
|
||||
|
||||
MU_TEST(furi_hal_i2c_int_1b_fail) {
|
||||
bool ret = false;
|
||||
uint8_t data_one = 0;
|
||||
|
||||
// 1 byte: fail, read, fail, write, fail, read
|
||||
data_one = 0;
|
||||
ret = furi_hal_i2c_read_reg_8(
|
||||
&furi_hal_i2c_handle_power,
|
||||
LP5562_ADDRESS + 0x10,
|
||||
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||
&data_one,
|
||||
LP5562_I2C_TIMEOUT);
|
||||
mu_assert(!ret, "8 read_reg_8 failed");
|
||||
mu_assert(data_one == 0, "8 invalid data");
|
||||
ret = furi_hal_i2c_read_reg_8(
|
||||
&furi_hal_i2c_handle_power,
|
||||
LP5562_ADDRESS,
|
||||
LP5562_CHANNEL_BLUE_CURRENT_REGISTER,
|
||||
&data_one,
|
||||
LP5562_I2C_TIMEOUT);
|
||||
mu_assert(ret, "9 read_reg_8 failed");
|
||||
mu_assert(data_one != 0, "9 invalid data");
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(furi_hal_i2c_int_suite) {
|
||||
MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown);
|
||||
MU_RUN_TEST(furi_hal_i2c_int_1b);
|
||||
MU_RUN_TEST(furi_hal_i2c_int_3b);
|
||||
MU_RUN_TEST(furi_hal_i2c_int_1b_fail);
|
||||
}
|
||||
|
||||
int run_minunit_test_furi_hal() {
|
||||
MU_RUN_SUITE(furi_hal_i2c_int_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
@ -393,7 +393,7 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) {
|
||||
"nfc_device_save == true assert failed\r\n");
|
||||
// Verify that key cache is saved
|
||||
FuriString* key_cache_name = furi_string_alloc();
|
||||
furi_string_set_str(key_cache_name, "/ext/nfc/cache/");
|
||||
furi_string_set_str(key_cache_name, "/ext/nfc/.cache/");
|
||||
for(size_t i = 0; i < uid_len; i++) {
|
||||
furi_string_cat_printf(key_cache_name, "%02X", uid[i]);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define TAG "UnitTests"
|
||||
|
||||
int run_minunit_test_furi();
|
||||
int run_minunit_test_furi_hal();
|
||||
int run_minunit_test_furi_string();
|
||||
int run_minunit_test_infrared();
|
||||
int run_minunit_test_rpc();
|
||||
@ -32,6 +33,7 @@ typedef struct {
|
||||
|
||||
const UnitTest unit_tests[] = {
|
||||
{.name = "furi", .entry = run_minunit_test_furi},
|
||||
{.name = "furi_hal", .entry = run_minunit_test_furi_hal},
|
||||
{.name = "furi_string", .entry = run_minunit_test_furi_string},
|
||||
{.name = "storage", .entry = run_minunit_test_storage},
|
||||
{.name = "stream", .entry = run_minunit_test_stream},
|
||||
|
@ -80,10 +80,12 @@ static void archive_file_browser_set_path(
|
||||
ArchiveBrowserView* browser,
|
||||
FuriString* path,
|
||||
const char* filter_ext,
|
||||
bool skip_assets) {
|
||||
bool skip_assets,
|
||||
bool hide_dot_files) {
|
||||
furi_assert(browser);
|
||||
if(!browser->worker_running) {
|
||||
browser->worker = file_browser_worker_alloc(path, filter_ext, skip_assets);
|
||||
browser->worker =
|
||||
file_browser_worker_alloc(path, NULL, filter_ext, skip_assets, hide_dot_files);
|
||||
file_browser_worker_set_callback_context(browser->worker, browser);
|
||||
file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb);
|
||||
file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb);
|
||||
@ -92,7 +94,8 @@ static void archive_file_browser_set_path(
|
||||
browser->worker_running = true;
|
||||
} else {
|
||||
furi_assert(browser->worker);
|
||||
file_browser_worker_set_config(browser->worker, path, filter_ext, skip_assets);
|
||||
file_browser_worker_set_config(
|
||||
browser->worker, path, filter_ext, skip_assets, hide_dot_files);
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,8 +476,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
|
||||
tab = archive_get_tab(browser);
|
||||
if(archive_is_dir_exists(browser->path)) {
|
||||
bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
|
||||
// Hide dot files everywhere except Browser
|
||||
bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
|
||||
archive_file_browser_set_path(
|
||||
browser, browser->path, archive_get_tab_ext(tab), skip_assets);
|
||||
browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files);
|
||||
tab_empty = false; // Empty check will be performed later
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
#include "../bad_usb_app_i.h"
|
||||
#include "furi_hal_power.h"
|
||||
#include "furi_hal_usb.h"
|
||||
#include <storage/storage.h>
|
||||
|
||||
static bool bad_usb_file_select(BadUsbApp* bad_usb) {
|
||||
furi_assert(bad_usb);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px);
|
||||
dialog_file_browser_set_basic_options(&browser_options, BAD_USB_APP_EXTENSION, &I_badusb_10px);
|
||||
browser_options.base_path = BAD_USB_APP_PATH_FOLDER;
|
||||
browser_options.skip_assets = true;
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
|
@ -148,6 +148,7 @@ static bool fap_loader_select_app(FapLoader* loader) {
|
||||
.hide_ext = true,
|
||||
.item_loader_callback = fap_loader_item_callback,
|
||||
.item_loader_context = loader,
|
||||
.base_path = EXT_PATH("apps"),
|
||||
};
|
||||
|
||||
return dialog_file_browser_show(
|
||||
|
@ -218,6 +218,7 @@ void ibutton_free(iButton* ibutton) {
|
||||
bool ibutton_file_select(iButton* ibutton) {
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
|
||||
browser_options.base_path = IBUTTON_APP_FOLDER;
|
||||
|
||||
bool success = dialog_file_browser_show(
|
||||
ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options);
|
||||
|
@ -7,6 +7,7 @@ void infrared_scene_remote_list_on_enter(void* context) {
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px);
|
||||
browser_options.base_path = INFRARED_APP_FOLDER;
|
||||
|
||||
bool success = dialog_file_browser_show(
|
||||
infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options);
|
||||
|
@ -230,6 +230,7 @@ bool lfrfid_load_key_from_file_select(LfRfid* app) {
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, LFRFID_APP_EXTENSION, &I_125_10px);
|
||||
browser_options.base_path = LFRFID_APP_FOLDER;
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
bool result =
|
||||
|
@ -37,8 +37,8 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) {
|
||||
FuriString* info_str;
|
||||
info_str = furi_string_alloc();
|
||||
|
||||
widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating UID");
|
||||
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
widget_add_string_element(widget, 57, 13, AlignLeft, AlignTop, FontPrimary, "Emulating UID");
|
||||
if(strcmp(nfc->dev->dev_name, "")) {
|
||||
furi_string_printf(info_str, "%s", nfc->dev->dev_name);
|
||||
} else {
|
||||
@ -48,7 +48,7 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) {
|
||||
}
|
||||
furi_string_trim(info_str);
|
||||
widget_add_text_box_element(
|
||||
widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true);
|
||||
widget, 57, 28, 67, 25, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true);
|
||||
furi_string_free(info_str);
|
||||
if(data_received) {
|
||||
widget_add_button_element(
|
||||
|
@ -17,13 +17,14 @@ void nfc_scene_mf_classic_emulate_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
Popup* popup = nfc->popup;
|
||||
popup_set_header(popup, "Emulating", 67, 13, AlignLeft, AlignTop);
|
||||
if(strcmp(nfc->dev->dev_name, "")) {
|
||||
nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name);
|
||||
nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
|
||||
} else {
|
||||
nfc_text_store_set(nfc, "Emulating\nMf Classic", nfc->dev->dev_name);
|
||||
nfc_text_store_set(nfc, "MIFARE\nClassic");
|
||||
}
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop);
|
||||
popup_set_icon(popup, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
popup_set_text(popup, nfc->text_store, 90, 28, AlignCenter, AlignTop);
|
||||
|
||||
// Setup and start worker
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
|
@ -16,14 +16,20 @@ void nfc_scene_mf_ultralight_emulate_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Setup view
|
||||
MfUltralightType type = nfc->dev->dev_data.mf_ul_data.type;
|
||||
bool is_ultralight = (type == MfUltralightTypeUL11) || (type == MfUltralightTypeUL21) ||
|
||||
(type == MfUltralightTypeUnknown);
|
||||
Popup* popup = nfc->popup;
|
||||
popup_set_header(popup, "Emulating", 67, 13, AlignLeft, AlignTop);
|
||||
if(strcmp(nfc->dev->dev_name, "")) {
|
||||
nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name);
|
||||
nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
|
||||
} else if(is_ultralight) {
|
||||
nfc_text_store_set(nfc, "MIFARE\nUltralight");
|
||||
} else {
|
||||
nfc_text_store_set(nfc, "Emulating\nMf Ultralight", nfc->dev->dev_name);
|
||||
nfc_text_store_set(nfc, "MIFARE\nNTAG");
|
||||
}
|
||||
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||
popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop);
|
||||
popup_set_icon(popup, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
popup_set_text(popup, nfc->text_store, 90, 28, AlignCenter, AlignTop);
|
||||
|
||||
// Setup and start worker
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
|
@ -7,7 +7,7 @@ void nfc_scene_rpc_on_enter(void* context) {
|
||||
popup_set_header(popup, "NFC", 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_icon(popup, 0, 12, &I_NFC_dolphin_emulation_47x61);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
|
||||
|
13
applications/main/subghz/helpers/subghz_error_type.h
Normal file
@ -0,0 +1,13 @@
|
||||
#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 */
|
||||
} SubGhzErrorType;
|
@ -45,6 +45,12 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
result = subghz_tx_start(subghz, subghz->txrx->fff_data);
|
||||
if(result) subghz_blink_start(subghz);
|
||||
}
|
||||
if(!result) {
|
||||
rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeOnlyRX);
|
||||
rpc_system_app_set_error_text(
|
||||
subghz->rpc_ctx,
|
||||
"Transmission on this frequency is restricted in your region");
|
||||
}
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result);
|
||||
} else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) {
|
||||
bool result = false;
|
||||
@ -76,6 +82,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) {
|
||||
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_text(subghz->rpc_ctx, "Cannot parse file");
|
||||
}
|
||||
}
|
||||
rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result);
|
||||
|
@ -461,6 +461,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px);
|
||||
browser_options.base_path = SUBGHZ_APP_FOLDER;
|
||||
|
||||
// Input events and views are managed by file_select
|
||||
bool res = dialog_file_browser_show(
|
||||
|
@ -1,6 +1,7 @@
|
||||
#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"
|
||||
|
@ -313,6 +313,7 @@ int32_t music_player_app(void* p) {
|
||||
dialog_file_browser_set_basic_options(
|
||||
&browser_options, MUSIC_PLAYER_APP_EXTENSION, &I_music_10px);
|
||||
browser_options.hide_ext = false;
|
||||
browser_options.base_path = MUSIC_PLAYER_APP_PATH_FOLDER;
|
||||
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
|
||||
|
@ -231,6 +231,7 @@ bool picopass_file_select(PicopassDevice* dev) {
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, PICOPASS_APP_EXTENSION, &I_Nfc_10px);
|
||||
browser_options.base_path = PICOPASS_APP_FOLDER;
|
||||
|
||||
bool res = dialog_file_browser_show(
|
||||
dev->dialogs, dev->load_path, picopass_app_folder, &browser_options);
|
||||
|
@ -14,6 +14,7 @@ void dialog_file_browser_set_basic_options(
|
||||
options->hide_ext = true;
|
||||
options->item_loader_callback = NULL;
|
||||
options->item_loader_context = NULL;
|
||||
options->base_path = NULL;
|
||||
}
|
||||
|
||||
static DialogsApp* dialogs_app_alloc() {
|
||||
|
@ -18,7 +18,9 @@ typedef struct DialogsApp DialogsApp;
|
||||
/**
|
||||
* File browser dialog extra options
|
||||
* @param extension file extension to be offered for selection
|
||||
* @param base_path root folder path for navigation with back key
|
||||
* @param skip_assets true - do not show assets folders
|
||||
* @param hide_dot_files true - hide dot files
|
||||
* @param icon file icon pointer, NULL for default icon
|
||||
* @param hide_ext true - hide extensions for files
|
||||
* @param item_loader_callback callback function for providing custom icon & entry name
|
||||
@ -26,7 +28,9 @@ typedef struct DialogsApp DialogsApp;
|
||||
*/
|
||||
typedef struct {
|
||||
const char* extension;
|
||||
const char* base_path;
|
||||
bool skip_assets;
|
||||
bool hide_dot_files;
|
||||
const Icon* icon;
|
||||
bool hide_ext;
|
||||
FileBrowserLoadItemCallback item_loader_callback;
|
||||
|
@ -20,9 +20,11 @@ bool dialog_file_browser_show(
|
||||
.file_icon = options ? options->icon : NULL,
|
||||
.hide_ext = options ? options->hide_ext : true,
|
||||
.skip_assets = options ? options->skip_assets : true,
|
||||
.hide_dot_files = options ? options->hide_dot_files : true,
|
||||
.preselected_filename = path,
|
||||
.item_callback = options ? options->item_loader_callback : NULL,
|
||||
.item_callback_context = options ? options->item_loader_context : NULL,
|
||||
.base_path = options ? options->base_path : NULL,
|
||||
}};
|
||||
|
||||
DialogsAppReturn return_data;
|
||||
|
@ -11,11 +11,13 @@ typedef struct {
|
||||
const char* extension;
|
||||
bool skip_assets;
|
||||
bool hide_ext;
|
||||
bool hide_dot_files;
|
||||
const Icon* file_icon;
|
||||
FuriString* result_path;
|
||||
FuriString* preselected_filename;
|
||||
FileBrowserLoadItemCallback item_callback;
|
||||
void* item_callback_context;
|
||||
const char* base_path;
|
||||
} DialogsAppMessageDataFileBrowser;
|
||||
|
||||
typedef struct {
|
||||
|
@ -38,7 +38,13 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
|
||||
file_browser_set_callback(
|
||||
file_browser, dialogs_app_file_browser_callback, file_browser_context);
|
||||
file_browser_configure(
|
||||
file_browser, data->extension, data->skip_assets, data->file_icon, data->hide_ext);
|
||||
file_browser,
|
||||
data->extension,
|
||||
data->base_path,
|
||||
data->skip_assets,
|
||||
data->hide_dot_files,
|
||||
data->file_icon,
|
||||
data->hide_ext);
|
||||
file_browser_set_item_callback(file_browser, data->item_callback, data->item_callback_context);
|
||||
file_browser_start(file_browser, data->preselected_filename);
|
||||
|
||||
|
@ -83,7 +83,9 @@ struct FileBrowser {
|
||||
View* view;
|
||||
BrowserWorker* worker;
|
||||
const char* ext_filter;
|
||||
const char* base_path;
|
||||
bool skip_assets;
|
||||
bool hide_dot_files;
|
||||
bool hide_ext;
|
||||
|
||||
FileBrowserCallback callback;
|
||||
@ -162,7 +164,9 @@ View* file_browser_get_view(FileBrowser* browser) {
|
||||
void file_browser_configure(
|
||||
FileBrowser* browser,
|
||||
const char* extension,
|
||||
const char* base_path,
|
||||
bool skip_assets,
|
||||
bool hide_dot_files,
|
||||
const Icon* file_icon,
|
||||
bool hide_ext) {
|
||||
furi_assert(browser);
|
||||
@ -170,6 +174,8 @@ void file_browser_configure(
|
||||
browser->ext_filter = extension;
|
||||
browser->skip_assets = skip_assets;
|
||||
browser->hide_ext = hide_ext;
|
||||
browser->base_path = base_path;
|
||||
browser->hide_dot_files = hide_dot_files;
|
||||
|
||||
with_view_model(
|
||||
browser->view,
|
||||
@ -183,7 +189,12 @@ void file_browser_configure(
|
||||
|
||||
void file_browser_start(FileBrowser* browser, FuriString* path) {
|
||||
furi_assert(browser);
|
||||
browser->worker = file_browser_worker_alloc(path, browser->ext_filter, browser->skip_assets);
|
||||
browser->worker = file_browser_worker_alloc(
|
||||
path,
|
||||
browser->base_path,
|
||||
browser->ext_filter,
|
||||
browser->skip_assets,
|
||||
browser->hide_dot_files);
|
||||
file_browser_worker_set_callback_context(browser->worker, browser);
|
||||
file_browser_worker_set_folder_callback(browser->worker, browser_folder_open_cb);
|
||||
file_browser_worker_set_list_callback(browser->worker, browser_list_load_cb);
|
||||
|
@ -29,7 +29,9 @@ View* file_browser_get_view(FileBrowser* browser);
|
||||
void file_browser_configure(
|
||||
FileBrowser* browser,
|
||||
const char* extension,
|
||||
const char* base_path,
|
||||
bool skip_assets,
|
||||
bool hide_dot_files,
|
||||
const Icon* file_icon,
|
||||
bool hide_ext);
|
||||
|
||||
|
@ -44,6 +44,7 @@ struct BrowserWorker {
|
||||
uint32_t load_offset;
|
||||
uint32_t load_count;
|
||||
bool skip_assets;
|
||||
bool hide_dot_files;
|
||||
idx_last_array_t idx_last;
|
||||
|
||||
void* cb_ctx;
|
||||
@ -78,6 +79,13 @@ static bool browser_path_trim(FuriString* path) {
|
||||
}
|
||||
|
||||
static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, bool is_folder) {
|
||||
// Skip dot files if enabled
|
||||
if(browser->hide_dot_files) {
|
||||
if(furi_string_start_with_str(name, ".")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_folder) {
|
||||
// Skip assets folders (if enabled)
|
||||
if(browser->skip_assets) {
|
||||
@ -365,20 +373,26 @@ static int32_t browser_worker(void* context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BrowserWorker*
|
||||
file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets) {
|
||||
BrowserWorker* file_browser_worker_alloc(
|
||||
FuriString* path,
|
||||
const char* base_path,
|
||||
const char* filter_ext,
|
||||
bool skip_assets,
|
||||
bool hide_dot_files) {
|
||||
BrowserWorker* browser = malloc(sizeof(BrowserWorker)); //-V773
|
||||
|
||||
idx_last_array_init(browser->idx_last);
|
||||
|
||||
browser->filter_extension = furi_string_alloc_set(filter_ext);
|
||||
browser->skip_assets = skip_assets;
|
||||
browser->path_start = furi_string_alloc_set(path);
|
||||
browser->hide_dot_files = hide_dot_files;
|
||||
|
||||
browser->path_current = furi_string_alloc_set(path);
|
||||
browser->path_next = furi_string_alloc_set(path);
|
||||
|
||||
if(browser_path_is_file(browser->path_start)) {
|
||||
browser_path_trim(browser->path_start);
|
||||
browser->path_start = furi_string_alloc();
|
||||
if(base_path) {
|
||||
furi_string_set_str(browser->path_start, base_path);
|
||||
}
|
||||
|
||||
browser->thread = furi_thread_alloc_ex("BrowserWorker", 2048, browser_worker, browser);
|
||||
@ -441,11 +455,13 @@ void file_browser_worker_set_config(
|
||||
BrowserWorker* browser,
|
||||
FuriString* path,
|
||||
const char* filter_ext,
|
||||
bool skip_assets) {
|
||||
bool skip_assets,
|
||||
bool hide_dot_files) {
|
||||
furi_assert(browser);
|
||||
furi_string_set(browser->path_next, path);
|
||||
furi_string_set(browser->filter_extension, filter_ext);
|
||||
browser->skip_assets = skip_assets;
|
||||
browser->hide_dot_files = hide_dot_files;
|
||||
furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange);
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,12 @@ typedef void (*BrowserWorkerListItemCallback)(
|
||||
bool is_last);
|
||||
typedef void (*BrowserWorkerLongLoadCallback)(void* context);
|
||||
|
||||
BrowserWorker*
|
||||
file_browser_worker_alloc(FuriString* path, const char* filter_ext, bool skip_assets);
|
||||
BrowserWorker* file_browser_worker_alloc(
|
||||
FuriString* path,
|
||||
const char* base_path,
|
||||
const char* filter_ext,
|
||||
bool skip_assets,
|
||||
bool hide_dot_files);
|
||||
|
||||
void file_browser_worker_free(BrowserWorker* browser);
|
||||
|
||||
@ -48,7 +52,8 @@ void file_browser_worker_set_config(
|
||||
BrowserWorker* browser,
|
||||
FuriString* path,
|
||||
const char* filter_ext,
|
||||
bool skip_assets);
|
||||
bool skip_assets,
|
||||
bool hide_dot_files);
|
||||
|
||||
void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx);
|
||||
|
||||
|
@ -32,6 +32,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context)
|
||||
furi_assert(request->which_content == PB_Main_app_start_request_tag);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
RpcSession* session = rpc_app->session;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
furi_assert(session);
|
||||
char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE];
|
||||
|
||||
@ -79,6 +80,7 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_lock_status_request_tag);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
@ -108,6 +110,7 @@ static void rpc_system_app_exit_request(const PB_Main* request, void* context) {
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_exit_request_tag);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
@ -133,6 +136,7 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) {
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_load_file_request_tag);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
@ -158,6 +162,7 @@ static void rpc_system_app_button_press(const PB_Main* request, void* context) {
|
||||
|
||||
furi_assert(request->which_content == PB_Main_app_button_press_request_tag);
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
@ -183,6 +188,7 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context)
|
||||
furi_assert(context);
|
||||
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
@ -222,6 +228,7 @@ static void rpc_system_app_data_exchange_process(const PB_Main* request, void* c
|
||||
furi_assert(context);
|
||||
|
||||
RpcAppSystem* rpc_app = context;
|
||||
rpc_system_app_error_reset(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
|
||||
@ -326,6 +333,13 @@ void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text
|
||||
content->text = error_text ? strdup(error_text) : NULL;
|
||||
}
|
||||
|
||||
void rpc_system_app_error_reset(RpcAppSystem* rpc_app) {
|
||||
furi_assert(rpc_app);
|
||||
|
||||
rpc_system_app_set_error_code(rpc_app, 0);
|
||||
rpc_system_app_set_error_text(rpc_app, NULL);
|
||||
}
|
||||
|
||||
void rpc_system_app_set_data_exchange_callback(
|
||||
RpcAppSystem* rpc_app,
|
||||
RpcAppSystemDataExchangeCallback callback,
|
||||
|
@ -33,6 +33,8 @@ void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code);
|
||||
|
||||
void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text);
|
||||
|
||||
void rpc_system_app_error_reset(RpcAppSystem* rpc_app);
|
||||
|
||||
void rpc_system_app_set_data_exchange_callback(
|
||||
RpcAppSystem* rpc_app,
|
||||
RpcAppSystemDataExchangeCallback callback,
|
||||
|
@ -106,6 +106,7 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
|
||||
.hide_ext = true,
|
||||
.item_loader_callback = favorite_fap_selector_item_callback,
|
||||
.item_loader_context = app,
|
||||
.base_path = EXT_PATH("apps"),
|
||||
};
|
||||
|
||||
if(primary_favorite) { // Select favorite fap in file browser
|
||||
|
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_0.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_1.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_10.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_11.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_12.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_13.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_14.png
vendored
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_15.png
vendored
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_16.png
vendored
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_17.png
vendored
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_18.png
vendored
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_19.png
vendored
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_2.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_20.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_3.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_4.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_5.png
vendored
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_6.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_7.png
vendored
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_8.png
vendored
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/dolphin/external/L2_Wake_up_128x64/frame_9.png
vendored
Normal file
After Width: | Height: | Size: 4.5 KiB |
14
assets/dolphin/external/L2_Wake_up_128x64/meta.txt
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
Filetype: Flipper Animation
|
||||
Version: 1
|
||||
|
||||
Width: 128
|
||||
Height: 64
|
||||
Passive frames: 10
|
||||
Active frames: 18
|
||||
Frames order: 0 1 0 1 0 1 0 2 3 4 0 5 6 7 8 9 10 11 10 12 13 14 15 16 17 18 19 20
|
||||
Active cycles: 1
|
||||
Frame rate: 2
|
||||
Duration: 3600
|
||||
Active cooldown: 7
|
||||
|
||||
Bubble slots: 0
|
69
assets/dolphin/external/manifest.txt
vendored
@ -36,19 +36,12 @@ Min level: 1
|
||||
Max level: 1
|
||||
Weight: 3
|
||||
|
||||
Name: L2_Furippa2_128x64
|
||||
Name: L2_Wake_up_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 6
|
||||
Max butthurt: 12
|
||||
Min level: 2
|
||||
Max level: 2
|
||||
Weight: 3
|
||||
|
||||
Name: L3_Furippa3_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 6
|
||||
Min level: 3
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
Weight: 4
|
||||
|
||||
Name: L1_Read_books_128x64
|
||||
Min butthurt: 0
|
||||
@ -57,13 +50,6 @@ Min level: 1
|
||||
Max level: 1
|
||||
Weight: 3
|
||||
|
||||
Name: L2_Hacking_pc_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 8
|
||||
Min level: 2
|
||||
Max level: 2
|
||||
Weight: 3
|
||||
|
||||
Name: L1_Cry_128x64
|
||||
Min butthurt: 8
|
||||
Max butthurt: 13
|
||||
@ -90,27 +76,34 @@ Min butthurt: 0
|
||||
Max butthurt: 9
|
||||
Min level: 1
|
||||
Max level: 3
|
||||
Weight: 5
|
||||
Weight: 4
|
||||
|
||||
Name: L1_Painting_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 7
|
||||
Min level: 1
|
||||
Max level: 3
|
||||
Weight: 4
|
||||
Weight: 3
|
||||
|
||||
Name: L3_Hijack_radio_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 8
|
||||
Min level: 3
|
||||
Name: L1_Leaving_sad_128x64
|
||||
Min butthurt: 14
|
||||
Max butthurt: 14
|
||||
Min level: 1
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
|
||||
Name: L3_Lab_research_128x54
|
||||
Name: L2_Furippa2_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 10
|
||||
Min level: 3
|
||||
Max level: 3
|
||||
Max butthurt: 6
|
||||
Min level: 2
|
||||
Max level: 2
|
||||
Weight: 3
|
||||
|
||||
Name: L2_Hacking_pc_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 8
|
||||
Min level: 2
|
||||
Max level: 2
|
||||
Weight: 3
|
||||
|
||||
Name: L1_New_year_128x64
|
||||
@ -148,9 +141,23 @@ Min level: 2
|
||||
Max level: 2
|
||||
Weight: 3
|
||||
|
||||
Name: L1_Leaving_sad_128x64
|
||||
Min butthurt: 14
|
||||
Max butthurt: 14
|
||||
Min level: 1
|
||||
Name: L3_Furippa3_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 6
|
||||
Min level: 3
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
|
||||
Name: L3_Hijack_radio_128x64
|
||||
Min butthurt: 0
|
||||
Max butthurt: 8
|
||||
Min level: 3
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
|
||||
Name: L3_Lab_research_128x54
|
||||
Min butthurt: 0
|
||||
Max butthurt: 10
|
||||
Min level: 3
|
||||
Max level: 3
|
||||
Weight: 3
|
||||
|
BIN
assets/icons/NFC/NFC_dolphin_emulation_47x61.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,8.11,,
|
||||
Version,+,10.1,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
@ -825,14 +825,14 @@ Function,-,fgetpos,int,"FILE*, fpos_t*"
|
||||
Function,-,fgets,char*,"char*, int, FILE*"
|
||||
Function,-,fgets_unlocked,char*,"char*, int, FILE*"
|
||||
Function,+,file_browser_alloc,FileBrowser*,FuriString*
|
||||
Function,+,file_browser_configure,void,"FileBrowser*, const char*, _Bool, const Icon*, _Bool"
|
||||
Function,+,file_browser_configure,void,"FileBrowser*, const char*, const char*, _Bool, _Bool, const Icon*, _Bool"
|
||||
Function,+,file_browser_free,void,FileBrowser*
|
||||
Function,+,file_browser_get_view,View*,FileBrowser*
|
||||
Function,+,file_browser_set_callback,void,"FileBrowser*, FileBrowserCallback, void*"
|
||||
Function,+,file_browser_set_item_callback,void,"FileBrowser*, FileBrowserLoadItemCallback, void*"
|
||||
Function,+,file_browser_start,void,"FileBrowser*, FuriString*"
|
||||
Function,+,file_browser_stop,void,FileBrowser*
|
||||
Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, _Bool"
|
||||
Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool"
|
||||
Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t"
|
||||
Function,+,file_browser_worker_folder_exit,void,BrowserWorker*
|
||||
Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t"
|
||||
@ -840,7 +840,7 @@ Function,+,file_browser_worker_free,void,BrowserWorker*
|
||||
Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker*
|
||||
Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t"
|
||||
Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*"
|
||||
Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool"
|
||||
Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool"
|
||||
Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback"
|
||||
Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback"
|
||||
Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback"
|
||||
@ -2307,6 +2307,7 @@ Function,+,rpc_session_set_context,void,"RpcSession*, void*"
|
||||
Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback"
|
||||
Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback"
|
||||
Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool"
|
||||
Function,+,rpc_system_app_error_reset,void,RpcAppSystem*
|
||||
Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t"
|
||||
Function,+,rpc_system_app_get_data,const char*,RpcAppSystem*
|
||||
Function,+,rpc_system_app_send_exited,void,RpcAppSystem*
|
||||
|
|
@ -244,6 +244,9 @@ bool furi_hal_nfc_listen(
|
||||
params.lmConfigPA.SEL_RES = sak;
|
||||
rfalNfcDiscover(¶ms);
|
||||
|
||||
// Disable EMD suppression.
|
||||
st25r3916ModifyRegister(ST25R3916_REG_EMD_SUP_CONF, ST25R3916_REG_EMD_SUP_CONF_emd_emv, 0);
|
||||
|
||||
uint32_t start = DWT->CYCCNT;
|
||||
while(state != RFAL_NFC_STATE_ACTIVATED) {
|
||||
rfalNfcWorker();
|
||||
|
@ -8,11 +8,11 @@
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define TAG "NfcDevice"
|
||||
#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/cache")
|
||||
#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/.cache")
|
||||
#define NFC_DEVICE_KEYS_EXTENSION ".keys"
|
||||
|
||||
static const char* nfc_file_header = "Flipper NFC device";
|
||||
static const uint32_t nfc_file_version = 2;
|
||||
static const uint32_t nfc_file_version = 3;
|
||||
|
||||
static const char* nfc_keys_file_header = "Flipper NFC keys";
|
||||
static const uint32_t nfc_keys_file_version = 1;
|
||||
@ -27,6 +27,11 @@ NfcDevice* nfc_device_alloc() {
|
||||
nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
nfc_dev->load_path = furi_string_alloc();
|
||||
nfc_dev->dev_data.parsed_data = furi_string_alloc();
|
||||
|
||||
// Rename cache folder name for backward compatibility
|
||||
if(storage_common_stat(nfc_dev->storage, "/ext/nfc/cache", NULL) == FSE_OK) {
|
||||
storage_common_rename(nfc_dev->storage, "/ext/nfc/cache", NFC_DEVICE_KEYS_FOLDER);
|
||||
}
|
||||
return nfc_dev;
|
||||
}
|
||||
|
||||
@ -1069,7 +1074,9 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||
if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break;
|
||||
if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break;
|
||||
// Save ATQA in MSB order for correct companion apps display
|
||||
uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
|
||||
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
|
||||
if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break;
|
||||
// Save more data if necessary
|
||||
if(dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||
@ -1119,6 +1126,9 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
|
||||
temp_str = furi_string_alloc();
|
||||
bool deprecated_version = false;
|
||||
|
||||
// Version 2 of file format had ATQA bytes swapped
|
||||
uint32_t version_with_lsb_atqa = 2;
|
||||
|
||||
if(dev->loading_cb) {
|
||||
dev->loading_cb(dev->loading_cb_ctx, true);
|
||||
}
|
||||
@ -1137,9 +1147,12 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
|
||||
// Read and verify file header
|
||||
uint32_t version = 0;
|
||||
if(!flipper_format_read_header(file, temp_str, &version)) break;
|
||||
if(furi_string_cmp_str(temp_str, nfc_file_header) || (version != nfc_file_version)) {
|
||||
deprecated_version = true;
|
||||
break;
|
||||
if(furi_string_cmp_str(temp_str, nfc_file_header)) break;
|
||||
if(version != nfc_file_version) {
|
||||
if(version < version_with_lsb_atqa) {
|
||||
deprecated_version = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Read Nfc device type
|
||||
if(!flipper_format_read_string(file, "Device type", temp_str)) break;
|
||||
@ -1149,7 +1162,14 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
|
||||
if(!(data_cnt == 4 || data_cnt == 7)) break;
|
||||
data->uid_len = data_cnt;
|
||||
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
|
||||
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
|
||||
if(version == version_with_lsb_atqa) {
|
||||
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
|
||||
} else {
|
||||
uint8_t atqa[2] = {};
|
||||
if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break;
|
||||
data->atqa[0] = atqa[1];
|
||||
data->atqa[1] = atqa[0];
|
||||
}
|
||||
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
|
||||
// Load CUID
|
||||
uint8_t* cuid_start = data->uid;
|
||||
@ -1217,10 +1237,12 @@ bool nfc_file_select(NfcDevice* dev) {
|
||||
const DialogsFileBrowserOptions browser_options = {
|
||||
.extension = NFC_APP_EXTENSION,
|
||||
.skip_assets = true,
|
||||
.hide_dot_files = true,
|
||||
.icon = &I_Nfc_10px,
|
||||
.hide_ext = true,
|
||||
.item_loader_callback = NULL,
|
||||
.item_loader_context = NULL,
|
||||
.base_path = NFC_APP_FOLDER,
|
||||
};
|
||||
|
||||
bool res =
|
||||
|
@ -712,46 +712,16 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(data);
|
||||
|
||||
uint8_t sectors_read = 0;
|
||||
Crypto1 crypto = {};
|
||||
uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type);
|
||||
uint64_t key_a = 0;
|
||||
uint64_t key_b = 0;
|
||||
MfClassicSectorReader sec_reader = {};
|
||||
MfClassicSector temp_sector = {};
|
||||
|
||||
for(size_t i = 0; i < total_sectors; i++) {
|
||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i);
|
||||
// Load key A
|
||||
if(mf_classic_is_key_found(data, i, MfClassicKeyA)) {
|
||||
sec_reader.key_a = nfc_util_bytes2num(sec_tr->key_a, 6);
|
||||
} else {
|
||||
sec_reader.key_a = MF_CLASSIC_NO_KEY;
|
||||
}
|
||||
// Load key B
|
||||
if(mf_classic_is_key_found(data, i, MfClassicKeyB)) {
|
||||
sec_reader.key_b = nfc_util_bytes2num(sec_tr->key_b, 6);
|
||||
} else {
|
||||
sec_reader.key_b = MF_CLASSIC_NO_KEY;
|
||||
}
|
||||
if((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)) {
|
||||
sec_reader.sector_num = i;
|
||||
if(mf_classic_read_sector_with_reader(tx_rx, &crypto, &sec_reader, &temp_sector)) {
|
||||
uint8_t first_block = mf_classic_get_first_block_num_of_sector(i);
|
||||
for(uint8_t j = 0; j < temp_sector.total_blocks; j++) {
|
||||
mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
|
||||
}
|
||||
sectors_read++;
|
||||
} else {
|
||||
// Invalid key, set it to not found
|
||||
if(key_a != MF_CLASSIC_NO_KEY) {
|
||||
mf_classic_set_key_not_found(data, i, MfClassicKeyA);
|
||||
} else {
|
||||
mf_classic_set_key_not_found(data, i, MfClassicKeyB);
|
||||
}
|
||||
}
|
||||
}
|
||||
mf_classic_read_sector(tx_rx, data, i);
|
||||
}
|
||||
uint8_t sectors_read = 0;
|
||||
uint8_t keys_found = 0;
|
||||
mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found);
|
||||
FURI_LOG_D(TAG, "Read %d sectors and %d keys", sectors_read, keys_found);
|
||||
|
||||
return sectors_read;
|
||||
}
|
||||
|
||||
|