[FL-2627] Flipper applications: SDK, build and debug system (#1387)

* Added support for running applications from SD card (FAPs - Flipper Application Packages)
* Added plugin_dist target for fbt to build FAPs
* All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default
* Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them
* Added debugging support for FAPs with fbt debug & VSCode
* Added public firmware API with automated versioning

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
SG
2022-09-15 02:11:38 +10:00
committed by Aleksandr Kutuzov
parent 0f6f9ad52e
commit b9a766d909
895 changed files with 8862 additions and 1465 deletions

View File

@@ -0,0 +1,25 @@
App(
appid="lfrfid",
name="125 kHz RFID",
apptype=FlipperAppType.APP,
entry_point="lfrfid_app",
cdefines=["APP_LF_RFID"],
requires=[
"gui",
"dialogs",
],
provides=[
"lfrfid_start",
],
icon="A_125khz_14",
stack_size=2 * 1024,
order=20,
)
App(
appid="lfrfid_start",
apptype=FlipperAppType.STARTUP,
entry_point="lfrfid_on_system_start",
requires=["lfrfid"],
order=50,
)

View File

@@ -0,0 +1,314 @@
#include "lfrfid_i.h"
static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
LfRfid* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool lfrfid_debug_back_event_callback(void* context) {
furi_assert(context);
LfRfid* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
furi_assert(context);
LfRfid* app = (LfRfid*)context;
if(rpc_event == RpcAppEventSessionClose) {
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose);
// Detach RPC
rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL);
app->rpc_ctx = NULL;
} else if(rpc_event == RpcAppEventAppExit) {
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit);
} else if(rpc_event == RpcAppEventLoadFile) {
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile);
} else {
rpc_system_app_confirm(app->rpc_ctx, rpc_event, false);
}
}
static LfRfid* lfrfid_alloc() {
LfRfid* lfrfid = malloc(sizeof(LfRfid));
lfrfid->storage = furi_record_open(RECORD_STORAGE);
lfrfid->dialogs = furi_record_open(RECORD_DIALOGS);
string_init(lfrfid->file_name);
string_init(lfrfid->raw_file_name);
string_init_set_str(lfrfid->file_path, LFRFID_APP_FOLDER);
lfrfid->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
size_t size = protocol_dict_get_max_data_size(lfrfid->dict);
lfrfid->new_key_data = (uint8_t*)malloc(size);
lfrfid->old_key_data = (uint8_t*)malloc(size);
lfrfid->lfworker = lfrfid_worker_alloc(lfrfid->dict);
lfrfid->view_dispatcher = view_dispatcher_alloc();
lfrfid->scene_manager = scene_manager_alloc(&lfrfid_scene_handlers, lfrfid);
view_dispatcher_enable_queue(lfrfid->view_dispatcher);
view_dispatcher_set_event_callback_context(lfrfid->view_dispatcher, lfrfid);
view_dispatcher_set_custom_event_callback(
lfrfid->view_dispatcher, lfrfid_debug_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
lfrfid->view_dispatcher, lfrfid_debug_back_event_callback);
// Open GUI record
lfrfid->gui = furi_record_open(RECORD_GUI);
// Open Notification record
lfrfid->notifications = furi_record_open(RECORD_NOTIFICATION);
// Submenu
lfrfid->submenu = submenu_alloc();
view_dispatcher_add_view(
lfrfid->view_dispatcher, LfRfidViewSubmenu, submenu_get_view(lfrfid->submenu));
// Dialog
lfrfid->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
lfrfid->view_dispatcher, LfRfidViewDialogEx, dialog_ex_get_view(lfrfid->dialog_ex));
// Popup
lfrfid->popup = popup_alloc();
view_dispatcher_add_view(
lfrfid->view_dispatcher, LfRfidViewPopup, popup_get_view(lfrfid->popup));
// Widget
lfrfid->widget = widget_alloc();
view_dispatcher_add_view(
lfrfid->view_dispatcher, LfRfidViewWidget, widget_get_view(lfrfid->widget));
// Text Input
lfrfid->text_input = text_input_alloc();
view_dispatcher_add_view(
lfrfid->view_dispatcher, LfRfidViewTextInput, text_input_get_view(lfrfid->text_input));
// Byte Input
lfrfid->byte_input = byte_input_alloc();
view_dispatcher_add_view(
lfrfid->view_dispatcher, LfRfidViewByteInput, byte_input_get_view(lfrfid->byte_input));
// Read custom view
lfrfid->read_view = lfrfid_view_read_alloc();
view_dispatcher_add_view(
lfrfid->view_dispatcher, LfRfidViewRead, lfrfid_view_read_get_view(lfrfid->read_view));
return lfrfid;
}
static void lfrfid_free(LfRfid* lfrfid) {
furi_assert(lfrfid);
string_clear(lfrfid->raw_file_name);
string_clear(lfrfid->file_name);
string_clear(lfrfid->file_path);
protocol_dict_free(lfrfid->dict);
lfrfid_worker_free(lfrfid->lfworker);
if(lfrfid->rpc_ctx) {
rpc_system_app_set_callback(lfrfid->rpc_ctx, NULL, NULL);
rpc_system_app_send_exited(lfrfid->rpc_ctx);
}
free(lfrfid->new_key_data);
free(lfrfid->old_key_data);
// Submenu
view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewSubmenu);
submenu_free(lfrfid->submenu);
// DialogEx
view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewDialogEx);
dialog_ex_free(lfrfid->dialog_ex);
// Popup
view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewPopup);
popup_free(lfrfid->popup);
// Widget
view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewWidget);
widget_free(lfrfid->widget);
// TextInput
view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewTextInput);
text_input_free(lfrfid->text_input);
// ByteInput
view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewByteInput);
byte_input_free(lfrfid->byte_input);
// Read custom view
view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewRead);
lfrfid_view_read_free(lfrfid->read_view);
// View Dispatcher
view_dispatcher_free(lfrfid->view_dispatcher);
// Scene Manager
scene_manager_free(lfrfid->scene_manager);
// GUI
furi_record_close(RECORD_GUI);
lfrfid->gui = NULL;
// Notifications
furi_record_close(RECORD_NOTIFICATION);
lfrfid->notifications = NULL;
furi_record_close(RECORD_STORAGE);
furi_record_close(RECORD_DIALOGS);
free(lfrfid);
}
int32_t lfrfid_app(void* p) {
LfRfid* app = lfrfid_alloc();
char* args = p;
lfrfid_make_app_folder(app);
if(args && strlen(args)) {
uint32_t rpc_ctx_ptr = 0;
if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) {
app->rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr;
rpc_system_app_set_callback(app->rpc_ctx, rpc_command_callback, app);
rpc_system_app_send_started(app->rpc_ctx);
view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop);
scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc);
} else {
string_set_str(app->file_path, args);
lfrfid_load_key_data(app, app->file_path, true);
view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
}
} else {
view_dispatcher_attach_to_gui(
app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
scene_manager_next_scene(app->scene_manager, LfRfidSceneStart);
}
view_dispatcher_run(app->view_dispatcher);
lfrfid_free(app);
return 0;
}
bool lfrfid_save_key(LfRfid* app) {
furi_assert(app);
bool result = false;
lfrfid_make_app_folder(app);
if(string_end_with_str_p(app->file_path, LFRFID_APP_EXTENSION)) {
size_t filename_start = string_search_rchar(app->file_path, '/');
string_left(app->file_path, filename_start);
}
string_cat_printf(
app->file_path, "/%s%s", string_get_cstr(app->file_name), LFRFID_APP_EXTENSION);
result = lfrfid_save_key_data(app, app->file_path);
return result;
}
bool lfrfid_load_key_from_file_select(LfRfid* app) {
furi_assert(app);
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, LFRFID_APP_EXTENSION, &I_125_10px);
// Input events and views are managed by file_browser
bool result =
dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options);
if(result) {
result = lfrfid_load_key_data(app, app->file_path, true);
}
return result;
}
bool lfrfid_delete_key(LfRfid* app) {
furi_assert(app);
return storage_simply_remove(app->storage, string_get_cstr(app->file_path));
}
bool lfrfid_load_key_data(LfRfid* app, string_t path, bool show_dialog) {
bool result = false;
do {
app->protocol_id = lfrfid_dict_file_load(app->dict, string_get_cstr(path));
if(app->protocol_id == PROTOCOL_NO) break;
path_extract_filename(path, app->file_name, true);
result = true;
} while(0);
if((!result) && (show_dialog)) {
dialog_message_show_storage_error(app->dialogs, "Cannot load\nkey file");
}
return result;
}
bool lfrfid_save_key_data(LfRfid* app, string_t path) {
bool result = lfrfid_dict_file_save(app->dict, app->protocol_id, string_get_cstr(path));
if(!result) {
dialog_message_show_storage_error(app->dialogs, "Cannot save\nkey file");
}
return result;
}
void lfrfid_make_app_folder(LfRfid* app) {
furi_assert(app);
if(!storage_simply_mkdir(app->storage, LFRFID_APP_FOLDER)) {
dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
}
}
void lfrfid_text_store_set(LfRfid* app, const char* text, ...) {
furi_assert(app);
va_list args;
va_start(args, text);
vsnprintf(app->text_store, LFRFID_TEXT_STORE_SIZE, text, args);
va_end(args);
}
void lfrfid_text_store_clear(LfRfid* app) {
furi_assert(app);
memset(app->text_store, 0, sizeof(app->text_store));
}
void lfrfid_popup_timeout_callback(void* context) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventPopupClosed);
}
void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context) {
LfRfid* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void lfrfid_text_input_callback(void* context) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventNext);
}

View File

@@ -0,0 +1,575 @@
#include <furi.h>
#include <furi_hal.h>
#include <stdarg.h>
#include <cli/cli.h>
#include <lib/toolbox/args.h>
#include <lib/lfrfid/lfrfid_worker.h>
#include <storage/storage.h>
#include <toolbox/stream/file_stream.h>
#include <toolbox/varint.h>
#include <toolbox/protocols/protocol_dict.h>
#include <lfrfid/protocols/lfrfid_protocols.h>
#include <lfrfid/lfrfid_raw_file.h>
#include <toolbox/pulse_protocols/pulse_glue.h>
static void lfrfid_cli(Cli* cli, string_t args, void* context);
// app cli function
void lfrfid_on_system_start() {
Cli* cli = furi_record_open(RECORD_CLI);
cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
furi_record_close(RECORD_CLI);
}
static void lfrfid_cli_print_usage() {
printf("Usage:\r\n");
printf("rfid read <optional: normal | indala>\r\n");
printf("rfid <write | emulate> <key_type> <key_data>\r\n");
printf("rfid raw_read <ask | psk> <filename>\r\n");
printf("rfid raw_emulate <filename>\r\n");
};
typedef struct {
ProtocolId protocol;
FuriEventFlag* event;
} LFRFIDCliReadContext;
static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto, void* ctx) {
furi_assert(ctx);
LFRFIDCliReadContext* context = ctx;
if(result == LFRFIDWorkerReadDone) {
context->protocol = proto;
FURI_SW_MEMBARRIER();
}
furi_event_flag_set(context->event, 1 << result);
}
static void lfrfid_cli_read(Cli* cli, string_t args) {
string_t type_string;
string_init(type_string);
LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;
if(args_read_string_and_trim(args, type_string)) {
if(string_cmp_str(type_string, "normal") == 0 || string_cmp_str(type_string, "ask") == 0) {
// ask
type = LFRFIDWorkerReadTypeASKOnly;
} else if(
string_cmp_str(type_string, "indala") == 0 ||
string_cmp_str(type_string, "psk") == 0) {
// psk
type = LFRFIDWorkerReadTypePSKOnly;
} else {
lfrfid_cli_print_usage();
string_clear(type_string);
return;
}
}
string_clear(type_string);
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
LFRFIDCliReadContext context;
context.protocol = PROTOCOL_NO;
context.event = furi_event_flag_alloc();
lfrfid_worker_start_thread(worker);
printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n");
const uint32_t available_flags = (1 << LFRFIDWorkerReadDone);
lfrfid_worker_read_start(worker, type, lfrfid_cli_read_callback, &context);
while(true) {
uint32_t flags =
furi_event_flag_wait(context.event, available_flags, FuriFlagWaitAny, 100);
if(flags != FuriFlagErrorTimeout) {
if(FURI_BIT(flags, LFRFIDWorkerReadDone)) {
break;
}
}
if(cli_cmd_interrupt_received(cli)) break;
}
lfrfid_worker_stop(worker);
lfrfid_worker_stop_thread(worker);
lfrfid_worker_free(worker);
if(context.protocol != PROTOCOL_NO) {
printf("%s ", protocol_dict_get_name(dict, context.protocol));
size_t size = protocol_dict_get_data_size(dict, context.protocol);
uint8_t* data = malloc(size);
protocol_dict_get_data(dict, context.protocol, data, size);
for(size_t i = 0; i < size; i++) {
printf("%02X", data[i]);
}
printf("\r\n");
free(data);
string_t info;
string_init(info);
protocol_dict_render_data(dict, info, context.protocol);
if(!string_empty_p(info)) {
printf("%s\r\n", string_get_cstr(info));
}
string_clear(info);
}
printf("Reading stopped\r\n");
protocol_dict_free(dict);
furi_event_flag_free(context.event);
}
static bool lfrfid_cli_parse_args(string_t args, ProtocolDict* dict, ProtocolId* protocol) {
bool result = false;
string_t protocol_name, data_text;
string_init(protocol_name);
string_init(data_text);
size_t data_size = protocol_dict_get_max_data_size(dict);
uint8_t* data = malloc(data_size);
do {
// load args
if(!args_read_string_and_trim(args, protocol_name) ||
!args_read_string_and_trim(args, data_text)) {
lfrfid_cli_print_usage();
break;
}
// check protocol arg
*protocol = protocol_dict_get_protocol_by_name(dict, string_get_cstr(protocol_name));
if(*protocol == PROTOCOL_NO) {
printf(
"Unknown protocol: %s\r\n"
"Available protocols:\r\n",
string_get_cstr(protocol_name));
for(ProtocolId i = 0; i < LFRFIDProtocolMax; i++) {
printf(
"\t%s, %d bytes long\r\n",
protocol_dict_get_name(dict, i),
protocol_dict_get_data_size(dict, i));
}
break;
}
data_size = protocol_dict_get_data_size(dict, *protocol);
// check data arg
if(!args_read_hex_bytes(data_text, data, data_size)) {
printf(
"%s data needs to be %d bytes long\r\n",
protocol_dict_get_name(dict, *protocol),
data_size);
break;
}
// load data to protocol
protocol_dict_set_data(dict, *protocol, data, data_size);
result = true;
} while(false);
free(data);
string_clear(protocol_name);
string_clear(data_text);
return result;
}
static void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx) {
furi_assert(ctx);
FuriEventFlag* events = ctx;
furi_event_flag_set(events, 1 << result);
}
static void lfrfid_cli_write(Cli* cli, string_t args) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
ProtocolId protocol;
if(!lfrfid_cli_parse_args(args, dict, &protocol)) {
protocol_dict_free(dict);
return;
}
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
FuriEventFlag* event = furi_event_flag_alloc();
lfrfid_worker_start_thread(worker);
lfrfid_worker_write_start(worker, protocol, lfrfid_cli_write_callback, event);
printf("Writing RFID...\r\nPress Ctrl+C to abort\r\n");
const uint32_t available_flags = (1 << LFRFIDWorkerWriteOK) |
(1 << LFRFIDWorkerWriteProtocolCannotBeWritten) |
(1 << LFRFIDWorkerWriteFobCannotBeWritten);
while(!cli_cmd_interrupt_received(cli)) {
uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
if(flags != FuriFlagErrorTimeout) {
if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) {
printf("Written!\r\n");
break;
}
if(FURI_BIT(flags, LFRFIDWorkerWriteProtocolCannotBeWritten)) {
printf("This protocol cannot be written.\r\n");
break;
}
if(FURI_BIT(flags, LFRFIDWorkerWriteFobCannotBeWritten)) {
printf("Seems this fob cannot be written.\r\n");
}
}
}
printf("Writing stopped\r\n");
lfrfid_worker_stop(worker);
lfrfid_worker_stop_thread(worker);
lfrfid_worker_free(worker);
protocol_dict_free(dict);
furi_event_flag_free(event);
}
static void lfrfid_cli_emulate(Cli* cli, string_t args) {
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
ProtocolId protocol;
if(!lfrfid_cli_parse_args(args, dict, &protocol)) {
protocol_dict_free(dict);
return;
}
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
lfrfid_worker_start_thread(worker);
lfrfid_worker_emulate_start(worker, protocol);
printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n");
while(!cli_cmd_interrupt_received(cli)) {
furi_delay_ms(100);
}
printf("Emulation stopped\r\n");
lfrfid_worker_stop(worker);
lfrfid_worker_stop_thread(worker);
lfrfid_worker_free(worker);
protocol_dict_free(dict);
}
static void lfrfid_cli_raw_analyze(Cli* cli, string_t args) {
UNUSED(cli);
string_t filepath, info_string;
string_init(filepath);
string_init(info_string);
Storage* storage = furi_record_open(RECORD_STORAGE);
LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);
do {
float frequency = 0;
float duty_cycle = 0;
if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
lfrfid_cli_print_usage();
break;
}
if(!lfrfid_raw_file_open_read(file, string_get_cstr(filepath))) {
printf("Failed to open file\r\n");
break;
}
if(!lfrfid_raw_file_read_header(file, &frequency, &duty_cycle)) {
printf("Invalid header\r\n");
break;
}
bool file_end = false;
uint32_t total_warns = 0;
uint32_t total_duration = 0;
uint32_t total_pulse = 0;
ProtocolId total_protocol = PROTOCOL_NO;
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
protocol_dict_decoders_start(dict);
while(!file_end) {
uint32_t pulse = 0;
uint32_t duration = 0;
if(lfrfid_raw_file_read_pair(file, &duration, &pulse, &file_end)) {
bool warn = false;
if(pulse > duration || pulse <= 0 || duration <= 0) {
total_warns += 1;
warn = true;
}
string_printf(info_string, "[%ld %ld]", pulse, duration);
printf("%-16s", string_get_cstr(info_string));
string_printf(info_string, "[%ld %ld]", pulse, duration - pulse);
printf("%-16s", string_get_cstr(info_string));
if(warn) {
printf(" <<----");
}
if(total_protocol == PROTOCOL_NO) {
total_protocol = protocol_dict_decoders_feed(dict, true, pulse);
if(total_protocol == PROTOCOL_NO) {
total_protocol =
protocol_dict_decoders_feed(dict, false, duration - pulse);
}
if(total_protocol != PROTOCOL_NO) {
printf(" <FOUND %s>", protocol_dict_get_name(dict, total_protocol));
}
}
printf("\r\n");
total_pulse += pulse;
total_duration += duration;
if(total_protocol != PROTOCOL_NO) {
break;
}
} else {
printf("Failed to read pair\r\n");
break;
}
}
printf(" Frequency: %f\r\n", (double)frequency);
printf(" Duty Cycle: %f\r\n", (double)duty_cycle);
printf(" Warns: %ld\r\n", total_warns);
printf(" Pulse sum: %ld\r\n", total_pulse);
printf("Duration sum: %ld\r\n", total_duration);
printf(" Average: %f\r\n", (double)((float)total_pulse / (float)total_duration));
printf(" Protocol: ");
if(total_protocol != PROTOCOL_NO) {
size_t data_size = protocol_dict_get_data_size(dict, total_protocol);
uint8_t* data = malloc(data_size);
protocol_dict_get_data(dict, total_protocol, data, data_size);
printf("%s [", protocol_dict_get_name(dict, total_protocol));
for(size_t i = 0; i < data_size; i++) {
printf("%02X", data[i]);
if(i < data_size - 1) {
printf(" ");
}
}
printf("]\r\n");
protocol_dict_render_data(dict, info_string, total_protocol);
printf("%s\r\n", string_get_cstr(info_string));
free(data);
} else {
printf("not found\r\n");
}
protocol_dict_free(dict);
} while(false);
string_clear(filepath);
string_clear(info_string);
lfrfid_raw_file_free(file);
furi_record_close(RECORD_STORAGE);
}
static void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void* context) {
furi_assert(context);
FuriEventFlag* event = context;
furi_event_flag_set(event, 1 << result);
}
static void lfrfid_cli_raw_read(Cli* cli, string_t args) {
UNUSED(cli);
string_t filepath, type_string;
string_init(filepath);
string_init(type_string);
LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;
do {
if(args_read_string_and_trim(args, type_string)) {
if(string_cmp_str(type_string, "normal") == 0 ||
string_cmp_str(type_string, "ask") == 0) {
// ask
type = LFRFIDWorkerReadTypeASKOnly;
} else if(
string_cmp_str(type_string, "indala") == 0 ||
string_cmp_str(type_string, "psk") == 0) {
// psk
type = LFRFIDWorkerReadTypePSKOnly;
} else {
lfrfid_cli_print_usage();
break;
}
}
if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
lfrfid_cli_print_usage();
break;
}
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
FuriEventFlag* event = furi_event_flag_alloc();
lfrfid_worker_start_thread(worker);
bool overrun = false;
const uint32_t available_flags = (1 << LFRFIDWorkerReadRawFileError) |
(1 << LFRFIDWorkerReadRawOverrun);
lfrfid_worker_read_raw_start(
worker, string_get_cstr(filepath), type, lfrfid_cli_raw_read_callback, event);
while(true) {
uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
if(flags != FuriFlagErrorTimeout) {
if(FURI_BIT(flags, LFRFIDWorkerReadRawFileError)) {
printf("File is not RFID raw file\r\n");
break;
}
if(FURI_BIT(flags, LFRFIDWorkerReadRawOverrun)) {
if(!overrun) {
printf("Overrun\r\n");
overrun = true;
}
}
}
if(cli_cmd_interrupt_received(cli)) break;
}
if(overrun) {
printf("An overrun occurred during read\r\n");
}
lfrfid_worker_stop(worker);
lfrfid_worker_stop_thread(worker);
lfrfid_worker_free(worker);
protocol_dict_free(dict);
furi_event_flag_free(event);
} while(false);
string_clear(filepath);
string_clear(type_string);
}
static void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) {
furi_assert(context);
FuriEventFlag* event = context;
furi_event_flag_set(event, 1 << result);
}
static void lfrfid_cli_raw_emulate(Cli* cli, string_t args) {
UNUSED(cli);
string_t filepath;
string_init(filepath);
Storage* storage = furi_record_open(RECORD_STORAGE);
do {
if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
lfrfid_cli_print_usage();
break;
}
if(!storage_file_exists(storage, string_get_cstr(filepath))) {
printf("File not found: \"%s\"\r\n", string_get_cstr(filepath));
break;
}
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
FuriEventFlag* event = furi_event_flag_alloc();
lfrfid_worker_start_thread(worker);
bool overrun = false;
const uint32_t available_flags = (1 << LFRFIDWorkerEmulateRawFileError) |
(1 << LFRFIDWorkerEmulateRawOverrun);
lfrfid_worker_emulate_raw_start(
worker, string_get_cstr(filepath), lfrfid_cli_raw_emulate_callback, event);
while(true) {
uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
if(flags != FuriFlagErrorTimeout) {
if(FURI_BIT(flags, LFRFIDWorkerEmulateRawFileError)) {
printf("File is not RFID raw file\r\n");
break;
}
if(FURI_BIT(flags, LFRFIDWorkerEmulateRawOverrun)) {
if(!overrun) {
printf("Overrun\r\n");
overrun = true;
}
}
}
if(cli_cmd_interrupt_received(cli)) break;
}
if(overrun) {
printf("An overrun occurred during emulation\r\n");
}
lfrfid_worker_stop(worker);
lfrfid_worker_stop_thread(worker);
lfrfid_worker_free(worker);
protocol_dict_free(dict);
furi_event_flag_free(event);
} while(false);
furi_record_close(RECORD_STORAGE);
string_clear(filepath);
}
static void lfrfid_cli(Cli* cli, string_t args, void* context) {
UNUSED(context);
string_t cmd;
string_init(cmd);
if(!args_read_string_and_trim(args, cmd)) {
string_clear(cmd);
lfrfid_cli_print_usage();
return;
}
if(string_cmp_str(cmd, "read") == 0) {
lfrfid_cli_read(cli, args);
} else if(string_cmp_str(cmd, "write") == 0) {
lfrfid_cli_write(cli, args);
} else if(string_cmp_str(cmd, "emulate") == 0) {
lfrfid_cli_emulate(cli, args);
} else if(string_cmp_str(cmd, "raw_read") == 0) {
lfrfid_cli_raw_read(cli, args);
} else if(string_cmp_str(cmd, "raw_emulate") == 0) {
lfrfid_cli_raw_emulate(cli, args);
} else if(string_cmp_str(cmd, "raw_analyze") == 0) {
lfrfid_cli_raw_analyze(cli, args);
} else {
lfrfid_cli_print_usage();
}
string_clear(cmd);
}

View File

@@ -0,0 +1,145 @@
#pragma once
#include "m-string.h"
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <cli/cli.h>
#include <notification/notification_messages.h>
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#include <gui/modules/widget.h>
#include <lfrfid/views/lfrfid_view_read.h>
#include <notification/notification_messages.h>
#include <dialogs/dialogs.h>
#include <storage/storage.h>
#include <flipper_format/flipper_format.h>
#include <rpc/rpc_app.h>
#include <toolbox/protocols/protocol_dict.h>
#include <toolbox/path.h>
#include <lfrfid/lfrfid_dict_file.h>
#include <lfrfid/protocols/lfrfid_protocols.h>
#include <lfrfid/lfrfid_worker.h>
#include <lfrfid/scenes/lfrfid_scene.h>
#define LFRFID_KEY_NAME_SIZE 22
#define LFRFID_TEXT_STORE_SIZE 40
#define LFRFID_APP_FOLDER ANY_PATH("lfrfid")
#define LFRFID_SD_FOLDER EXT_PATH("lfrfid")
#define LFRFID_APP_EXTENSION ".rfid"
#define LFRFID_APP_SHADOW_EXTENSION ".shd"
#define LFRFID_APP_RAW_ASK_EXTENSION ".ask.raw"
#define LFRFID_APP_RAW_PSK_EXTENSION ".psk.raw"
enum LfRfidCustomEvent {
LfRfidEventNext = 100,
LfRfidEventExit,
LfRfidEventPopupClosed,
LfRfidEventReadSenseStart,
LfRfidEventReadSenseEnd,
LfRfidEventReadSenseCardStart,
LfRfidEventReadSenseCardEnd,
LfRfidEventReadStartASK,
LfRfidEventReadStartPSK,
LfRfidEventReadDone,
LfRfidEventReadOverrun,
LfRfidEventReadError,
LfRfidEventWriteOK,
LfRfidEventWriteProtocolCannotBeWritten,
LfRfidEventWriteFobCannotBeWritten,
LfRfidEventWriteTooLongToWrite,
LfRfidEventRpcLoadFile,
LfRfidEventRpcSessionClose,
};
typedef enum {
LfRfidRpcStateIdle,
LfRfidRpcStateEmulating,
} LfRfidRpcState;
typedef struct LfRfid LfRfid;
struct LfRfid {
LFRFIDWorker* lfworker;
ViewDispatcher* view_dispatcher;
Gui* gui;
NotificationApp* notifications;
SceneManager* scene_manager;
Storage* storage;
DialogsApp* dialogs;
Widget* widget;
char text_store[LFRFID_TEXT_STORE_SIZE + 1];
string_t file_path;
string_t file_name;
string_t raw_file_name;
ProtocolDict* dict;
ProtocolId protocol_id;
ProtocolId protocol_id_next;
LFRFIDWorkerReadType read_type;
uint8_t* old_key_data;
uint8_t* new_key_data;
RpcAppSystem* rpc_ctx;
LfRfidRpcState rpc_state;
// Common Views
Submenu* submenu;
DialogEx* dialog_ex;
Popup* popup;
TextInput* text_input;
ByteInput* byte_input;
// Custom views
LfRfidReadView* read_view;
};
typedef enum {
LfRfidViewSubmenu,
LfRfidViewDialogEx,
LfRfidViewPopup,
LfRfidViewWidget,
LfRfidViewTextInput,
LfRfidViewByteInput,
LfRfidViewRead,
} LfRfidView;
bool lfrfid_save_key(LfRfid* app);
bool lfrfid_load_key_from_file_select(LfRfid* app);
bool lfrfid_delete_key(LfRfid* app);
bool lfrfid_load_key_data(LfRfid* app, string_t path, bool show_dialog);
bool lfrfid_save_key_data(LfRfid* app, string_t path);
void lfrfid_make_app_folder(LfRfid* app);
void lfrfid_text_store_set(LfRfid* app, const char* text, ...);
void lfrfid_text_store_clear(LfRfid* app);
void lfrfid_popup_timeout_callback(void* context);
void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context);
void lfrfid_text_input_callback(void* context);

View File

@@ -0,0 +1,30 @@
#include "lfrfid_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const lfrfid_on_enter_handlers[])(void*) = {
#include "lfrfid_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const lfrfid_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "lfrfid_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const lfrfid_on_exit_handlers[])(void* context) = {
#include "lfrfid_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers lfrfid_scene_handlers = {
.on_enter_handlers = lfrfid_on_enter_handlers,
.on_event_handlers = lfrfid_on_event_handlers,
.on_exit_handlers = lfrfid_on_exit_handlers,
.scene_num = LfRfidSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) LfRfidScene##id,
typedef enum {
#include "lfrfid_scene_config.h"
LfRfidSceneNum,
} LfRfidScene;
#undef ADD_SCENE
extern const SceneManagerHandlers lfrfid_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "lfrfid_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "lfrfid_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "lfrfid_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,24 @@
ADD_SCENE(lfrfid, start, Start)
ADD_SCENE(lfrfid, read, Read)
ADD_SCENE(lfrfid, read_success, ReadSuccess)
ADD_SCENE(lfrfid, retry_confirm, RetryConfirm)
ADD_SCENE(lfrfid, exit_confirm, ExitConfirm)
ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm)
ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu)
ADD_SCENE(lfrfid, write, Write)
ADD_SCENE(lfrfid, write_success, WriteSuccess)
ADD_SCENE(lfrfid, emulate, Emulate)
ADD_SCENE(lfrfid, save_name, SaveName)
ADD_SCENE(lfrfid, save_success, SaveSuccess)
ADD_SCENE(lfrfid, select_key, SelectKey)
ADD_SCENE(lfrfid, saved_key_menu, SavedKeyMenu)
ADD_SCENE(lfrfid, save_data, SaveData)
ADD_SCENE(lfrfid, save_type, SaveType)
ADD_SCENE(lfrfid, saved_info, SavedInfo)
ADD_SCENE(lfrfid, delete_success, DeleteSuccess)
ADD_SCENE(lfrfid, extra_actions, ExtraActions)
ADD_SCENE(lfrfid, raw_info, RawInfo)
ADD_SCENE(lfrfid, raw_name, RawName)
ADD_SCENE(lfrfid, raw_read, RawRead)
ADD_SCENE(lfrfid, raw_success, RawSuccess)
ADD_SCENE(lfrfid, rpc, Rpc)

View File

@@ -0,0 +1,68 @@
#include "../lfrfid_i.h"
void lfrfid_scene_delete_confirm_on_enter(void* context) {
LfRfid* app = context;
Widget* widget = app->widget;
string_t tmp_string;
string_init(tmp_string);
widget_add_button_element(widget, GuiButtonTypeLeft, "Back", lfrfid_widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "Delete", lfrfid_widget_callback, app);
string_printf(tmp_string, "Delete %s?", string_get_cstr(app->file_name));
widget_add_string_element(
widget, 64, 0, AlignCenter, AlignTop, FontPrimary, string_get_cstr(tmp_string));
string_reset(tmp_string);
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
uint8_t* data = (uint8_t*)malloc(size);
protocol_dict_get_data(app->dict, app->protocol_id, data, size);
for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) {
if(i != 0) {
string_cat_printf(tmp_string, " ");
}
string_cat_printf(tmp_string, "%02X", data[i]);
}
free(data);
widget_add_string_element(
widget, 64, 19, AlignCenter, AlignTop, FontSecondary, string_get_cstr(tmp_string));
widget_add_string_element(
widget,
64,
49,
AlignCenter,
AlignBottom,
FontSecondary,
protocol_dict_get_name(app->dict, app->protocol_id));
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
string_clear(tmp_string);
}
bool lfrfid_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
consumed = true; // Ignore Back button presses
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == GuiButtonTypeLeft) {
scene_manager_previous_scene(scene_manager);
} else if(event.event == GuiButtonTypeRight) {
lfrfid_delete_key(app);
scene_manager_next_scene(scene_manager, LfRfidSceneDeleteSuccess);
}
}
return consumed;
}
void lfrfid_scene_delete_confirm_on_exit(void* context) {
LfRfid* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,35 @@
#include "../lfrfid_i.h"
void lfrfid_scene_delete_success_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
}
bool lfrfid_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
if((event.type == SceneManagerEventTypeBack) ||
((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, LfRfidSceneSelectKey);
consumed = true;
}
return consumed;
}
void lfrfid_scene_delete_success_on_exit(void* context) {
LfRfid* app = context;
popup_reset(app->popup);
}

View File

@@ -0,0 +1,44 @@
#include "../lfrfid_i.h"
#include <dolphin/dolphin.h>
void lfrfid_scene_emulate_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
DOLPHIN_DEED(DolphinDeedRfidEmulate);
popup_set_header(popup, "Emulating", 89, 30, AlignCenter, AlignTop);
if(!string_empty_p(app->file_name)) {
popup_set_text(popup, string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
} else {
popup_set_text(
popup,
protocol_dict_get_name(app->dict, app->protocol_id),
89,
43,
AlignCenter,
AlignTop);
}
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
lfrfid_worker_start_thread(app->lfworker);
lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);
notification_message(app->notifications, &sequence_blink_start_magenta);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
}
bool lfrfid_scene_emulate_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
bool consumed = false;
return consumed;
}
void lfrfid_scene_emulate_on_exit(void* context) {
LfRfid* app = context;
notification_message(app->notifications, &sequence_blink_stop);
popup_reset(app->popup);
lfrfid_worker_stop(app->lfworker);
lfrfid_worker_stop_thread(app->lfworker);
}

View File

@@ -0,0 +1,39 @@
#include "../lfrfid_i.h"
void lfrfid_scene_exit_confirm_on_enter(void* context) {
LfRfid* app = context;
Widget* widget = app->widget;
widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app);
widget_add_string_element(
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to RFID Menu?");
widget_add_string_element(
widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
}
bool lfrfid_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
consumed = true; // Ignore Back button presses
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == GuiButtonTypeLeft) {
scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneStart);
} else if(event.event == GuiButtonTypeRight) {
scene_manager_previous_scene(scene_manager);
}
}
return consumed;
}
void lfrfid_scene_exit_confirm_on_exit(void* context) {
LfRfid* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,79 @@
#include "../lfrfid_i.h"
typedef enum {
SubmenuIndexASK,
SubmenuIndexPSK,
SubmenuIndexRAW,
} SubmenuIndex;
static void lfrfid_scene_extra_actions_submenu_callback(void* context, uint32_t index) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void lfrfid_scene_extra_actions_on_enter(void* context) {
LfRfid* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Read ASK (Animal, Ordinary Card)",
SubmenuIndexASK,
lfrfid_scene_extra_actions_submenu_callback,
app);
submenu_add_item(
submenu,
"Read PSK (Indala)",
SubmenuIndexPSK,
lfrfid_scene_extra_actions_submenu_callback,
app);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
submenu_add_item(
submenu,
"Read RAW RFID data",
SubmenuIndexRAW,
lfrfid_scene_extra_actions_submenu_callback,
app);
}
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneExtraActions));
// clear key
string_reset(app->file_name);
app->protocol_id = PROTOCOL_NO;
app->read_type = LFRFIDWorkerReadTypeAuto;
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);
}
bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexASK) {
app->read_type = LFRFIDWorkerReadTypeASKOnly;
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
consumed = true;
} else if(event.event == SubmenuIndexPSK) {
app->read_type = LFRFIDWorkerReadTypePSKOnly;
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
consumed = true;
} else if(event.event == SubmenuIndexRAW) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, event.event);
}
return consumed;
}
void lfrfid_scene_extra_actions_on_exit(void* context) {
LfRfid* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,64 @@
#include "../lfrfid_i.h"
void lfrfid_scene_raw_info_on_enter(void* context) {
LfRfid* app = context;
Widget* widget = app->widget;
// string_t tmp_string;
// string_init(tmp_string);
bool sd_exist = storage_sd_status(app->storage) == FSE_OK;
if(!sd_exist) {
widget_add_icon_element(widget, 0, 0, &I_SDQuestion_35x43);
widget_add_string_multiline_element(
widget,
81,
4,
AlignCenter,
AlignTop,
FontSecondary,
"No SD card found.\nThis function will not\nwork without\nSD card.");
widget_add_button_element(widget, GuiButtonTypeLeft, "Back", lfrfid_widget_callback, app);
} else {
widget_add_string_multiline_element(
widget,
0,
1,
AlignLeft,
AlignTop,
FontSecondary,
"RAW RFID data reader\n1) Put the Flipper on your card\n2) Press OK\n3) Wait until data is read");
widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app);
}
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
//string_clear(tmp_string);
}
bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
consumed = true;
scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneExtraActions);
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == GuiButtonTypeCenter) {
scene_manager_next_scene(scene_manager, LfRfidSceneRawRead);
} else if(event.event == GuiButtonTypeLeft) {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, LfRfidSceneExtraActions);
}
}
return consumed;
}
void lfrfid_scene_raw_info_on_exit(void* context) {
LfRfid* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,58 @@
#include "../lfrfid_i.h"
void lfrfid_scene_raw_name_on_enter(void* context) {
LfRfid* app = context;
TextInput* text_input = app->text_input;
const char* key_name = string_get_cstr(app->raw_file_name);
bool key_name_is_empty = string_empty_p(app->file_name);
if(key_name_is_empty) {
lfrfid_text_store_set(app, "RfidRecord");
} else {
lfrfid_text_store_set(app, "%s", key_name);
}
text_input_set_header_text(text_input, "Name the raw file");
text_input_set_result_callback(
text_input,
lfrfid_text_input_callback,
app,
app->text_store,
LFRFID_KEY_NAME_SIZE,
key_name_is_empty);
ValidatorIsFile* validator_is_file =
validator_is_file_alloc_init(LFRFID_SD_FOLDER, LFRFID_APP_RAW_ASK_EXTENSION, NULL);
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewTextInput);
}
bool lfrfid_scene_raw_name_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == LfRfidEventNext) {
consumed = true;
string_set_str(app->raw_file_name, app->text_store);
scene_manager_next_scene(scene_manager, LfRfidSceneRawInfo);
}
}
return consumed;
}
void lfrfid_scene_raw_name_on_exit(void* context) {
LfRfid* app = context;
TextInput* text_input = app->text_input;
void* validator_context = text_input_get_validator_callback_context(text_input);
text_input_set_validator(text_input, NULL, NULL);
validator_is_file_free((ValidatorIsFile*)validator_context);
text_input_reset(text_input);
}

View File

@@ -0,0 +1,126 @@
#include "../lfrfid_i.h"
#define RAW_READ_TIME 5000
typedef struct {
string_t string_file_name;
FuriTimer* timer;
bool is_psk;
bool error;
} LfRfidReadRawState;
static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* context) {
LfRfid* app = context;
if(result == LFRFIDWorkerReadRawFileError) {
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadError);
} else if(result == LFRFIDWorkerReadRawOverrun) {
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadOverrun);
}
}
static void timer_callback(void* context) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadDone);
}
void lfrfid_scene_raw_read_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
LfRfidReadRawState* state = malloc(sizeof(LfRfidReadRawState));
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawRead, (uint32_t)state);
string_init(state->string_file_name);
popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
lfrfid_worker_start_thread(app->lfworker);
lfrfid_make_app_folder(app);
state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app);
furi_timer_start(state->timer, RAW_READ_TIME);
string_printf(
state->string_file_name,
"%s/%s%s",
LFRFID_SD_FOLDER,
string_get_cstr(app->raw_file_name),
LFRFID_APP_RAW_ASK_EXTENSION);
popup_set_header(popup, "Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop);
lfrfid_worker_read_raw_start(
app->lfworker,
string_get_cstr(state->string_file_name),
LFRFIDWorkerReadTypeASKOnly,
lfrfid_read_callback,
app);
notification_message(app->notifications, &sequence_blink_start_cyan);
state->is_psk = false;
state->error = false;
}
bool lfrfid_scene_raw_read_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
Popup* popup = app->popup;
LfRfidReadRawState* state =
(LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead);
bool consumed = false;
furi_assert(state);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == LfRfidEventReadError) {
consumed = true;
state->error = true;
popup_set_header(
popup, "Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop);
notification_message(app->notifications, &sequence_blink_start_red);
furi_timer_stop(state->timer);
} else if(event.event == LfRfidEventReadDone) {
consumed = true;
if(!state->error) {
if(state->is_psk) {
notification_message(app->notifications, &sequence_success);
scene_manager_next_scene(app->scene_manager, LfRfidSceneRawSuccess);
} else {
popup_set_header(
popup, "Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop);
notification_message(app->notifications, &sequence_blink_start_yellow);
lfrfid_worker_stop(app->lfworker);
string_printf(
state->string_file_name,
"%s/%s%s",
LFRFID_SD_FOLDER,
string_get_cstr(app->raw_file_name),
LFRFID_APP_RAW_PSK_EXTENSION);
lfrfid_worker_read_raw_start(
app->lfworker,
string_get_cstr(state->string_file_name),
LFRFIDWorkerReadTypePSKOnly,
lfrfid_read_callback,
app);
furi_timer_start(state->timer, RAW_READ_TIME);
state->is_psk = true;
}
}
}
}
return consumed;
}
void lfrfid_scene_raw_read_on_exit(void* context) {
LfRfid* app = context;
LfRfidReadRawState* state =
(LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead);
notification_message(app->notifications, &sequence_blink_stop);
popup_reset(app->popup);
lfrfid_worker_stop(app->lfworker);
lfrfid_worker_stop_thread(app->lfworker);
furi_timer_free(state->timer);
string_clear(state->string_file_name);
free(state);
}

View File

@@ -0,0 +1,39 @@
#include "../lfrfid_i.h"
void lfrfid_scene_raw_success_on_enter(void* context) {
LfRfid* app = context;
Widget* widget = app->widget;
widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app);
widget_add_string_multiline_element(
widget,
0,
1,
AlignLeft,
AlignTop,
FontSecondary,
"RAW RFID read success!\nNow you can analyze files\nOr send them to developers");
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
}
bool lfrfid_scene_raw_success_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == GuiButtonTypeCenter) {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, LfRfidSceneExtraActions);
}
}
return consumed;
}
void lfrfid_scene_raw_success_on_exit(void* context) {
LfRfid* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,109 @@
#include "../lfrfid_i.h"
#include <dolphin/dolphin.h>
static const NotificationSequence sequence_blink_set_yellow = {
&message_blink_set_color_yellow,
NULL,
};
static const NotificationSequence sequence_blink_set_green = {
&message_blink_set_color_green,
NULL,
};
static const NotificationSequence sequence_blink_set_cyan = {
&message_blink_set_color_cyan,
NULL,
};
static void
lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context) {
LfRfid* app = context;
uint32_t event = 0;
if(result == LFRFIDWorkerReadSenseStart) {
event = LfRfidEventReadSenseStart;
} else if(result == LFRFIDWorkerReadSenseEnd) {
event = LfRfidEventReadSenseEnd;
} else if(result == LFRFIDWorkerReadSenseCardStart) {
event = LfRfidEventReadSenseCardStart;
} else if(result == LFRFIDWorkerReadSenseCardEnd) {
event = LfRfidEventReadSenseCardEnd;
} else if(result == LFRFIDWorkerReadDone) {
event = LfRfidEventReadDone;
app->protocol_id_next = protocol;
} else if(result == LFRFIDWorkerReadStartASK) {
event = LfRfidEventReadStartASK;
} else if(result == LFRFIDWorkerReadStartPSK) {
event = LfRfidEventReadStartPSK;
} else {
return;
}
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void lfrfid_scene_read_on_enter(void* context) {
LfRfid* app = context;
DOLPHIN_DEED(DolphinDeedRfidRead);
if(app->read_type == LFRFIDWorkerReadTypePSKOnly) {
lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly);
} else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) {
lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAskOnly);
}
lfrfid_worker_start_thread(app->lfworker);
lfrfid_worker_read_start(app->lfworker, app->read_type, lfrfid_read_callback, app);
notification_message(app->notifications, &sequence_blink_start_cyan);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewRead);
}
bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == LfRfidEventReadSenseStart) {
notification_message(app->notifications, &sequence_blink_set_yellow);
consumed = true;
} else if(event.event == LfRfidEventReadSenseCardStart) {
notification_message(app->notifications, &sequence_blink_set_green);
consumed = true;
} else if(
(event.event == LfRfidEventReadSenseEnd) ||
(event.event == LfRfidEventReadSenseCardEnd)) {
notification_message(app->notifications, &sequence_blink_set_cyan);
consumed = true;
} else if(event.event == LfRfidEventReadDone) {
app->protocol_id = app->protocol_id_next;
DOLPHIN_DEED(DolphinDeedRfidReadSuccess);
notification_message(app->notifications, &sequence_success);
string_reset(app->file_name);
scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess);
consumed = true;
} else if(event.event == LfRfidEventReadStartPSK) {
if(app->read_type == LFRFIDWorkerReadTypeAuto) {
lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPsk);
}
consumed = true;
} else if(event.event == LfRfidEventReadStartASK) {
if(app->read_type == LFRFIDWorkerReadTypeAuto) {
lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAsk);
}
consumed = true;
}
}
return consumed;
}
void lfrfid_scene_read_on_exit(void* context) {
LfRfid* app = context;
notification_message(app->notifications, &sequence_blink_stop);
popup_reset(app->popup);
lfrfid_worker_stop(app->lfworker);
lfrfid_worker_stop_thread(app->lfworker);
}

View File

@@ -0,0 +1,58 @@
#include "../lfrfid_i.h"
typedef enum {
SubmenuIndexSave,
SubmenuIndexEmulate,
SubmenuIndexWrite,
} SubmenuIndex;
void lfrfid_scene_read_key_menu_submenu_callback(void* context, uint32_t index) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void lfrfid_scene_read_key_menu_on_enter(void* context) {
LfRfid* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu, "Save", SubmenuIndexSave, lfrfid_scene_read_key_menu_submenu_callback, app);
submenu_add_item(
submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_read_key_menu_submenu_callback, app);
submenu_add_item(
submenu, "Write", SubmenuIndexWrite, lfrfid_scene_read_key_menu_submenu_callback, app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu));
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);
}
bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite);
consumed = true;
} else if(event.event == SubmenuIndexSave) {
string_reset(app->file_name);
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveName);
consumed = true;
} else if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event);
}
return consumed;
}
void lfrfid_scene_read_key_menu_on_exit(void* context) {
LfRfid* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,79 @@
#include "../lfrfid_i.h"
void lfrfid_scene_read_success_on_enter(void* context) {
LfRfid* app = context;
Widget* widget = app->widget;
string_t tmp_string;
string_init(tmp_string);
widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app);
string_printf(
tmp_string,
"%s[%s]",
protocol_dict_get_name(app->dict, app->protocol_id),
protocol_dict_get_manufacturer(app->dict, app->protocol_id));
widget_add_string_element(
widget, 0, 2, AlignLeft, AlignTop, FontPrimary, string_get_cstr(tmp_string));
string_reset(tmp_string);
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
uint8_t* data = (uint8_t*)malloc(size);
protocol_dict_get_data(app->dict, app->protocol_id, data, size);
for(uint8_t i = 0; i < size; i++) {
if(i != 0) {
string_cat_printf(tmp_string, " ");
}
if(i >= 9) {
string_cat_printf(tmp_string, "...");
break;
} else {
string_cat_printf(tmp_string, "%02X", data[i]);
}
}
free(data);
string_t render_data;
string_init(render_data);
protocol_dict_render_brief_data(app->dict, render_data, app->protocol_id);
string_cat_printf(tmp_string, "\r\n%s", string_get_cstr(render_data));
string_clear(render_data);
widget_add_string_element(
widget, 0, 16, AlignLeft, AlignTop, FontSecondary, string_get_cstr(tmp_string));
notification_message_block(app->notifications, &sequence_set_green_255);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
string_clear(tmp_string);
}
bool lfrfid_scene_read_success_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
scene_manager_next_scene(scene_manager, LfRfidSceneExitConfirm);
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == GuiButtonTypeLeft) {
scene_manager_next_scene(scene_manager, LfRfidSceneRetryConfirm);
} else if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(scene_manager, LfRfidSceneReadKeyMenu);
}
}
return consumed;
}
void lfrfid_scene_read_success_on_exit(void* context) {
LfRfid* app = context;
notification_message_block(app->notifications, &sequence_reset_green);
widget_reset(app->widget);
}

View File

@@ -0,0 +1,39 @@
#include "../lfrfid_i.h"
void lfrfid_scene_retry_confirm_on_enter(void* context) {
LfRfid* app = context;
Widget* widget = app->widget;
widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app);
widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app);
widget_add_string_element(
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
widget_add_string_element(
widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
}
bool lfrfid_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
consumed = true; // Ignore Back button presses
} else if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == GuiButtonTypeLeft) {
scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneRead);
} else if(event.event == GuiButtonTypeRight) {
scene_manager_previous_scene(scene_manager);
}
}
return consumed;
}
void lfrfid_scene_retry_confirm_on_exit(void* context) {
LfRfid* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,67 @@
#include "../lfrfid_i.h"
void lfrfid_scene_rpc_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
popup_set_header(popup, "LF RFID", 89, 42, AlignCenter, AlignBottom);
popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
notification_message(app->notifications, &sequence_display_backlight_on);
app->rpc_state = LfRfidRpcStateIdle;
}
bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
Popup* popup = app->popup;
UNUSED(event);
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == LfRfidEventExit) {
rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true);
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
} else if(event.event == LfRfidEventRpcSessionClose) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
} else if(event.event == LfRfidEventRpcLoadFile) {
const char* arg = rpc_system_app_get_data(app->rpc_ctx);
bool result = false;
if(arg && (app->rpc_state == LfRfidRpcStateIdle)) {
string_set_str(app->file_path, arg);
if(lfrfid_load_key_data(app, app->file_path, false)) {
lfrfid_worker_start_thread(app->lfworker);
lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);
app->rpc_state = LfRfidRpcStateEmulating;
lfrfid_text_store_set(app, "emulating\n%s", string_get_cstr(app->file_name));
popup_set_text(popup, app->text_store, 89, 44, AlignCenter, AlignTop);
notification_message(app->notifications, &sequence_blink_start_magenta);
result = true;
}
}
rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result);
}
}
return consumed;
}
void lfrfid_scene_rpc_on_exit(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
if(app->rpc_state == LfRfidRpcStateEmulating) {
lfrfid_worker_stop(app->lfworker);
lfrfid_worker_stop_thread(app->lfworker);
notification_message(app->notifications, &sequence_blink_stop);
}
popup_reset(popup);
}

View File

@@ -0,0 +1,51 @@
#include "../lfrfid_i.h"
#include <dolphin/dolphin.h>
void lfrfid_scene_save_data_on_enter(void* context) {
LfRfid* app = context;
ByteInput* byte_input = app->byte_input;
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
bool need_restore = scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveData);
if(need_restore) {
protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
} else {
protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);
}
protocol_dict_get_data(app->dict, app->protocol_id, app->new_key_data, size);
byte_input_set_header_text(byte_input, "Enter the data in hex");
byte_input_set_result_callback(
byte_input, lfrfid_text_input_callback, NULL, app, app->new_key_data, size);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewByteInput);
}
bool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == LfRfidEventNext) {
consumed = true;
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size);
DOLPHIN_DEED(DolphinDeedRfidAdd);
scene_manager_next_scene(scene_manager, LfRfidSceneSaveName);
scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 1);
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 0);
}
return consumed;
}
void lfrfid_scene_save_data_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,76 @@
#include "m-string.h"
#include <lib/toolbox/random_name.h>
#include "../lfrfid_i.h"
void lfrfid_scene_save_name_on_enter(void* context) {
LfRfid* app = context;
TextInput* text_input = app->text_input;
string_t folder_path;
string_init(folder_path);
bool key_name_is_empty = string_empty_p(app->file_name);
if(key_name_is_empty) {
string_set_str(app->file_path, LFRFID_APP_FOLDER);
set_random_name(app->text_store, LFRFID_TEXT_STORE_SIZE);
string_set_str(folder_path, LFRFID_APP_FOLDER);
} else {
lfrfid_text_store_set(app, "%s", string_get_cstr(app->file_name));
path_extract_dirname(string_get_cstr(app->file_path), folder_path);
}
text_input_set_header_text(text_input, "Name the card");
text_input_set_result_callback(
text_input,
lfrfid_text_input_callback,
app,
app->text_store,
LFRFID_KEY_NAME_SIZE,
key_name_is_empty);
FURI_LOG_I("", "%s %s", string_get_cstr(folder_path), app->text_store);
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
string_get_cstr(folder_path), LFRFID_APP_EXTENSION, string_get_cstr(app->file_name));
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
string_clear(folder_path);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewTextInput);
}
bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == LfRfidEventNext) {
consumed = true;
if(!string_empty_p(app->file_name)) {
lfrfid_delete_key(app);
}
string_set_str(app->file_name, app->text_store);
if(lfrfid_save_key(app)) {
scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess);
} else {
scene_manager_search_and_switch_to_previous_scene(
scene_manager, LfRfidSceneReadKeyMenu);
}
}
}
return consumed;
}
void lfrfid_scene_save_name_on_exit(void* context) {
LfRfid* app = context;
TextInput* text_input = app->text_input;
void* validator_context = text_input_get_validator_callback_context(text_input);
text_input_set_validator(text_input, NULL, NULL);
validator_is_file_free((ValidatorIsFile*)validator_context);
text_input_reset(text_input);
}

View File

@@ -0,0 +1,43 @@
#include "../lfrfid_i.h"
#include <dolphin/dolphin.h>
void lfrfid_scene_save_success_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
DOLPHIN_DEED(DolphinDeedRfidSave);
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
}
bool lfrfid_scene_save_success_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
const uint32_t prev_scenes[] = {LfRfidSceneReadKeyMenu, LfRfidSceneSelectKey};
if((event.type == SceneManagerEventTypeBack) ||
((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) {
bool result = scene_manager_search_and_switch_to_previous_scene_one_of(
app->scene_manager, prev_scenes, COUNT_OF(prev_scenes));
if(!result) {
scene_manager_search_and_switch_to_another_scene(
app->scene_manager, LfRfidSceneSelectKey);
}
consumed = true;
}
return consumed;
}
void lfrfid_scene_save_success_on_exit(void* context) {
LfRfid* app = context;
popup_reset(app->popup);
}

View File

@@ -0,0 +1,86 @@
#include "../lfrfid_i.h"
typedef struct {
string_t menu_item_name[LFRFIDProtocolMax];
uint32_t line_sel;
} SaveTypeCtx;
static void lfrfid_scene_save_type_submenu_callback(void* context, uint32_t index) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void lfrfid_scene_save_type_on_enter(void* context) {
LfRfid* app = context;
Submenu* submenu = app->submenu;
SaveTypeCtx* state = malloc(sizeof(SaveTypeCtx));
for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) {
if(strcmp(
protocol_dict_get_manufacturer(app->dict, i),
protocol_dict_get_name(app->dict, i)) != 0) {
string_init_printf(
state->menu_item_name[i],
"%s %s",
protocol_dict_get_manufacturer(app->dict, i),
protocol_dict_get_name(app->dict, i));
} else {
string_init_printf(
state->menu_item_name[i], "%s", protocol_dict_get_name(app->dict, i));
}
submenu_add_item(
submenu,
string_get_cstr(state->menu_item_name[i]),
i,
lfrfid_scene_save_type_submenu_callback,
app);
}
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType));
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, (uint32_t)state);
// clear key name
string_reset(app->file_name);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);
}
bool lfrfid_scene_save_type_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
SaveTypeCtx* state =
(SaveTypeCtx*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType);
furi_check(state);
if(event.type == SceneManagerEventTypeCustom) {
app->protocol_id = event.event;
state->line_sel = event.event;
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData);
consumed = true;
}
return consumed;
}
void lfrfid_scene_save_type_on_exit(void* context) {
LfRfid* app = context;
SaveTypeCtx* state =
(SaveTypeCtx*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType);
furi_check(state);
submenu_reset(app->submenu);
for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) {
string_clear(state->menu_item_name[i]);
}
uint32_t line_sel = state->line_sel;
free(state);
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, line_sel);
}

View File

@@ -0,0 +1,51 @@
#include "../lfrfid_i.h"
void lfrfid_scene_saved_info_on_enter(void* context) {
LfRfid* app = context;
Widget* widget = app->widget;
string_t tmp_string;
string_init(tmp_string);
string_printf(
tmp_string,
"%s [%s]\r\n",
string_get_cstr(app->file_name),
protocol_dict_get_name(app->dict, app->protocol_id));
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
uint8_t* data = (uint8_t*)malloc(size);
protocol_dict_get_data(app->dict, app->protocol_id, data, size);
for(uint8_t i = 0; i < size; i++) {
if(i != 0) {
string_cat_printf(tmp_string, " ");
}
string_cat_printf(tmp_string, "%02X", data[i]);
}
free(data);
string_t render_data;
string_init(render_data);
protocol_dict_render_data(app->dict, render_data, app->protocol_id);
string_cat_printf(tmp_string, "\r\n%s", string_get_cstr(render_data));
string_clear(render_data);
widget_add_string_multiline_element(
widget, 0, 1, AlignLeft, AlignTop, FontSecondary, string_get_cstr(tmp_string));
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
string_clear(tmp_string);
}
bool lfrfid_scene_saved_info_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
bool consumed = false;
return consumed;
}
void lfrfid_scene_saved_info_on_exit(void* context) {
LfRfid* app = context;
widget_reset(app->widget);
}

View File

@@ -0,0 +1,69 @@
#include "../lfrfid_i.h"
typedef enum {
SubmenuIndexEmulate,
SubmenuIndexWrite,
SubmenuIndexEdit,
SubmenuIndexDelete,
SubmenuIndexInfo,
} SubmenuIndex;
static void lfrfid_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void lfrfid_scene_saved_key_menu_on_enter(void* context) {
LfRfid* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item(
submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item(
submenu, "Edit", SubmenuIndexEdit, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item(
submenu, "Delete", SubmenuIndexDelete, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_add_item(
submenu, "Info", SubmenuIndexInfo, lfrfid_scene_saved_key_menu_submenu_callback, app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu));
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);
}
bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexEmulate) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate);
consumed = true;
} else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite);
consumed = true;
} else if(event.event == SubmenuIndexEdit) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData);
consumed = true;
} else if(event.event == SubmenuIndexDelete) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneDeleteConfirm);
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedInfo);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu, event.event);
}
return consumed;
}
void lfrfid_scene_saved_key_menu_on_exit(void* context) {
LfRfid* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,22 @@
#include "../lfrfid_i.h"
void lfrfid_scene_select_key_on_enter(void* context) {
LfRfid* app = context;
if(lfrfid_load_key_from_file_select(app)) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedKeyMenu);
} else {
scene_manager_previous_scene(app->scene_manager);
}
}
bool lfrfid_scene_select_key_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
bool consumed = false;
return consumed;
}
void lfrfid_scene_select_key_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,72 @@
#include "../lfrfid_i.h"
typedef enum {
SubmenuIndexRead,
SubmenuIndexSaved,
SubmenuIndexAddManually,
SubmenuIndexExtraActions,
} SubmenuIndex;
static void lfrfid_scene_start_submenu_callback(void* context, uint32_t index) {
LfRfid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void lfrfid_scene_start_on_enter(void* context) {
LfRfid* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(submenu, "Read", SubmenuIndexRead, lfrfid_scene_start_submenu_callback, app);
submenu_add_item(
submenu, "Saved", SubmenuIndexSaved, lfrfid_scene_start_submenu_callback, app);
submenu_add_item(
submenu, "Add Manually", SubmenuIndexAddManually, lfrfid_scene_start_submenu_callback, app);
submenu_add_item(
submenu,
"Extra Actions",
SubmenuIndexExtraActions,
lfrfid_scene_start_submenu_callback,
app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneStart));
// clear key
string_reset(app->file_name);
app->protocol_id = PROTOCOL_NO;
app->read_type = LFRFIDWorkerReadTypeAuto;
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);
}
bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneRead);
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
string_set_str(app->file_path, LFRFID_APP_FOLDER);
scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey);
consumed = true;
} else if(event.event == SubmenuIndexAddManually) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType);
consumed = true;
} else if(event.event == SubmenuIndexExtraActions) {
scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event);
}
return consumed;
}
void lfrfid_scene_start_on_exit(void* context) {
LfRfid* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,96 @@
#include "../lfrfid_i.h"
static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* context) {
LfRfid* app = context;
uint32_t event = 0;
if(result == LFRFIDWorkerWriteOK) {
event = LfRfidEventWriteOK;
} else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) {
event = LfRfidEventWriteProtocolCannotBeWritten;
} else if(result == LFRFIDWorkerWriteFobCannotBeWritten) {
event = LfRfidEventWriteFobCannotBeWritten;
} else if(result == LFRFIDWorkerWriteTooLongToWrite) {
event = LfRfidEventWriteTooLongToWrite;
}
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void lfrfid_scene_write_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop);
if(!string_empty_p(app->file_name)) {
popup_set_text(popup, string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
} else {
popup_set_text(
popup,
protocol_dict_get_name(app->dict, app->protocol_id),
89,
43,
AlignCenter,
AlignTop);
}
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
app->old_key_data = (uint8_t*)malloc(size);
protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);
lfrfid_worker_start_thread(app->lfworker);
lfrfid_worker_write_start(
app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app);
notification_message(app->notifications, &sequence_blink_start_magenta);
}
bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
Popup* popup = app->popup;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == LfRfidEventWriteOK) {
notification_message(app->notifications, &sequence_success);
scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);
consumed = true;
} else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48);
popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop);
popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
notification_message(app->notifications, &sequence_blink_start_red);
consumed = true;
} else if(
(event.event == LfRfidEventWriteFobCannotBeWritten) ||
(event.event == LfRfidEventWriteTooLongToWrite)) {
popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48);
popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop);
popup_set_text(
popup,
"Make sure this\ncard is writable\nand not\nprotected.",
3,
17,
AlignLeft,
AlignTop);
notification_message(app->notifications, &sequence_blink_start_yellow);
consumed = true;
}
}
return consumed;
}
void lfrfid_scene_write_on_exit(void* context) {
LfRfid* app = context;
notification_message(app->notifications, &sequence_blink_stop);
popup_reset(app->popup);
lfrfid_worker_stop(app->lfworker);
lfrfid_worker_stop_thread(app->lfworker);
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
free(app->old_key_data);
}

View File

@@ -0,0 +1,38 @@
#include "../lfrfid_i.h"
void lfrfid_scene_write_success_on_enter(void* context) {
LfRfid* app = context;
Popup* popup = app->popup;
popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
popup_set_context(popup, app);
popup_set_callback(popup, lfrfid_popup_timeout_callback);
popup_set_timeout(popup, 1500);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
notification_message_block(app->notifications, &sequence_set_green_255);
}
bool lfrfid_scene_write_success_on_event(void* context, SceneManagerEvent event) {
LfRfid* app = context;
bool consumed = false;
const uint32_t prev_scenes[] = {LfRfidSceneReadKeyMenu, LfRfidSceneSelectKey};
if((event.type == SceneManagerEventTypeBack) ||
((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) {
scene_manager_search_and_switch_to_previous_scene_one_of(
app->scene_manager, prev_scenes, COUNT_OF(prev_scenes));
consumed = true;
}
return consumed;
}
void lfrfid_scene_write_success_on_exit(void* context) {
LfRfid* app = context;
notification_message_block(app->notifications, &sequence_reset_green);
popup_reset(app->popup);
}

View File

@@ -0,0 +1,117 @@
#include "lfrfid_view_read.h"
#include <gui/elements.h>
#define TEMP_STR_LEN 128
struct LfRfidReadView {
View* view;
};
typedef struct {
IconAnimation* icon;
LfRfidReadViewMode read_mode;
} LfRfidReadViewModel;
static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) {
LfRfidReadViewModel* model = _model;
canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, 0, 8, &I_NFC_manual);
canvas_set_font(canvas, FontPrimary);
if(model->read_mode == LfRfidReadAsk) {
canvas_draw_str(canvas, 70, 16, "Reading 1/2");
canvas_draw_str(canvas, 77, 29, "ASK");
canvas_draw_icon(canvas, 70, 22, &I_ButtonRight_4x7);
canvas_draw_icon_animation(canvas, 102, 21, model->icon);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 77, 43, "PSK");
} else if(model->read_mode == LfRfidReadPsk) {
canvas_draw_str(canvas, 70, 16, "Reading 2/2");
canvas_draw_str(canvas, 77, 43, "PSK");
canvas_draw_icon(canvas, 70, 36, &I_ButtonRight_4x7);
canvas_draw_icon_animation(canvas, 102, 35, model->icon);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 77, 29, "ASK");
} else {
canvas_draw_str(canvas, 72, 16, "Reading");
if(model->read_mode == LfRfidReadAskOnly) {
canvas_draw_str(canvas, 77, 35, "ASK");
} else {
canvas_draw_str(canvas, 77, 35, "PSK");
}
canvas_draw_icon_animation(canvas, 102, 27, model->icon);
}
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 61, 56, "Don't move card");
}
void lfrfid_view_read_enter(void* context) {
LfRfidReadView* read_view = context;
with_view_model(
read_view->view, (LfRfidReadViewModel * model) {
icon_animation_start(model->icon);
return true;
});
}
void lfrfid_view_read_exit(void* context) {
LfRfidReadView* read_view = context;
with_view_model(
read_view->view, (LfRfidReadViewModel * model) {
icon_animation_stop(model->icon);
return false;
});
}
LfRfidReadView* lfrfid_view_read_alloc() {
LfRfidReadView* read_view = malloc(sizeof(LfRfidReadView));
read_view->view = view_alloc();
view_set_context(read_view->view, read_view);
view_allocate_model(read_view->view, ViewModelTypeLocking, sizeof(LfRfidReadViewModel));
with_view_model(
read_view->view, (LfRfidReadViewModel * model) {
model->icon = icon_animation_alloc(&A_Round_loader_8x8);
view_tie_icon_animation(read_view->view, model->icon);
return false;
});
view_set_draw_callback(read_view->view, lfrfid_view_read_draw_callback);
view_set_enter_callback(read_view->view, lfrfid_view_read_enter);
view_set_exit_callback(read_view->view, lfrfid_view_read_exit);
return read_view;
}
void lfrfid_view_read_free(LfRfidReadView* read_view) {
with_view_model(
read_view->view, (LfRfidReadViewModel * model) {
icon_animation_free(model->icon);
return false;
});
view_free(read_view->view);
free(read_view);
}
View* lfrfid_view_read_get_view(LfRfidReadView* read_view) {
return read_view->view;
}
void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode) {
with_view_model(
read_view->view, (LfRfidReadViewModel * model) {
icon_animation_stop(model->icon);
icon_animation_start(model->icon);
model->read_mode = mode;
return true;
});
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <gui/view.h>
typedef enum {
LfRfidReadAsk,
LfRfidReadPsk,
LfRfidReadAskOnly,
LfRfidReadPskOnly
} LfRfidReadViewMode;
typedef struct LfRfidReadView LfRfidReadView;
LfRfidReadView* lfrfid_view_read_alloc();
void lfrfid_view_read_free(LfRfidReadView* read_view);
View* lfrfid_view_read_get_view(LfRfidReadView* read_view);
void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode);