Zachary Weiss 3 лет назад
Сommit
3556b956ce

+ 2 - 0
.gitattributes

@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto

+ 19 - 0
application.fam

@@ -0,0 +1,19 @@
+App(
+    appid="mag",
+    name="MagSpoof WIP",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="mag_app",
+    cdefines=["APP_MAG"],
+    requires=[
+        "gui", 
+        "storage",
+        "notification",
+        "dialogs",
+    ],
+    provides=[],
+    stack_size=2 * 1024,
+    order=20,
+    fap_icon="mag_10px.png",
+    fap_category="Tools",
+    fap_icon_assets="imgs",
+)

BIN
imgs/mag_10px.png


+ 242 - 0
mag.c

@@ -0,0 +1,242 @@
+#include "mag_i.h"
+
+static bool mag_debug_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    Mag* mag = context;
+    return scene_manager_handle_custom_event(mag->scene_manager, event);
+}
+
+static bool mag_debug_back_event_callback(void* context) {
+    furi_assert(context);
+    Mag* mag = context;
+    return scene_manager_handle_back_event(mag->scene_manager);
+}
+
+static Mag* mag_alloc() {
+    Mag* mag = malloc(sizeof(Mag));
+
+    mag->storage = furi_record_open(RECORD_STORAGE);
+    mag->dialogs = furi_record_open(RECORD_DIALOGS);
+
+    mag->file_name = furi_string_alloc();
+    mag->file_path = furi_string_alloc_set(MAG_APP_FOLDER);
+
+    mag->view_dispatcher = view_dispatcher_alloc();
+    mag->scene_manager = scene_manager_alloc(&mag_scene_handlers, mag);
+    view_dispatcher_enable_queue(mag->view_dispatcher);
+    view_dispatcher_set_event_callback_context(mag->view_dispatcher, mag);
+    view_dispatcher_set_custom_event_callback(
+        mag->view_dispatcher, mag_debug_custom_event_callback);
+    view_dispatcher_set_navigation_event_callback(
+        mag->view_dispatcher, mag_debug_back_event_callback);
+
+    // Open GUI record
+    mag->gui = furi_record_open(RECORD_GUI);
+
+    // Open Notification record
+    mag->notifications = furi_record_open(RECORD_NOTIFICATION);
+
+    // Submenu
+    mag->submenu = submenu_alloc();
+    view_dispatcher_add_view(mag->view_dispatcher, MagViewSubmenu, submenu_get_view(mag->submenu));
+
+    // Dialog
+    mag->dialog_ex = dialog_ex_alloc();
+    view_dispatcher_add_view(
+        mag->view_dispatcher, MagViewDialogEx, dialog_ex_get_view(mag->dialog_ex));
+
+    // Popup
+    mag->popup = popup_alloc();
+    view_dispatcher_add_view(mag->view_dispatcher, MagViewPopup, popup_get_view(mag->popup));
+
+    // Widget
+    mag->widget = widget_alloc();
+    view_dispatcher_add_view(mag->view_dispatcher, MagViewWidget, widget_get_view(mag->widget));
+
+    // Text Input
+    mag->text_input = text_input_alloc();
+    view_dispatcher_add_view(
+        mag->view_dispatcher, MagViewTextInput, text_input_get_view(mag->text_input));
+
+    // Byte Input
+    mag->byte_input = byte_input_alloc();
+    view_dispatcher_add_view(
+        mag->view_dispatcher, MagViewByteInput, byte_input_get_view(mag->byte_input));
+
+    return mag;
+}
+
+static void mag_free(Mag* mag) {
+    furi_assert(mag);
+
+    furi_string_free(mag->file_name);
+    furi_string_free(mag->file_path);
+
+    // Submenu
+    view_dispatcher_remove_view(mag->view_dispatcher, MagViewSubmenu);
+    submenu_free(mag->submenu);
+
+    // DialogEx
+    view_dispatcher_remove_view(mag->view_dispatcher, MagViewDialogEx);
+    dialog_ex_free(mag->dialog_ex);
+
+    // Popup
+    view_dispatcher_remove_view(mag->view_dispatcher, MagViewPopup);
+    popup_free(mag->popup);
+
+    // Widget
+    view_dispatcher_remove_view(mag->view_dispatcher, MagViewWidget);
+    widget_free(mag->widget);
+
+    // TextInput
+    view_dispatcher_remove_view(mag->view_dispatcher, MagViewTextInput);
+    text_input_free(mag->text_input);
+
+    // ByteInput
+    view_dispatcher_remove_view(mag->view_dispatcher, MagViewByteInput);
+    byte_input_free(mag->byte_input);
+
+    // View Dispatcher
+    view_dispatcher_free(mag->view_dispatcher);
+
+    // Scene Manager
+    scene_manager_free(mag->scene_manager);
+
+    // GUI
+    furi_record_close(RECORD_GUI);
+    mag->gui = NULL;
+
+    // Notifications
+    furi_record_close(RECORD_NOTIFICATION);
+    mag->notifications = NULL;
+
+    furi_record_close(RECORD_STORAGE);
+    furi_record_close(RECORD_DIALOGS);
+
+    free(mag);
+}
+
+// entry point for app
+int32_t mag_app(void* p) {
+    Mag* mag = mag_alloc();
+    char* args = p;
+    UNUSED(args);
+
+    mag_make_app_folder(mag);
+
+    view_dispatcher_attach_to_gui(mag->view_dispatcher, mag->gui, ViewDispatcherTypeFullscreen);
+    scene_manager_next_scene(mag->scene_manager, MagSceneStart);
+
+    view_dispatcher_run(mag->view_dispatcher);
+
+    mag_free(mag);
+
+    return 0;
+}
+
+bool mag_save_key(Mag* mag) {
+    furi_assert(mag);
+
+    bool result = false;
+
+    mag_make_app_folder(mag);
+
+    if(furi_string_end_with(mag->file_path, MAG_APP_EXTENSION)) {
+        size_t filename_start = furi_string_search_rchar(mag->file_path, '/');
+        furi_string_left(mag->file_path, filename_start);
+    }
+
+    furi_string_cat_printf(
+        mag->file_path, "/%s%s", furi_string_get_cstr(mag->file_name), MAG_APP_EXTENSION);
+
+    result = mag_save_key_data(mag, mag->file_path);
+    return result;
+}
+
+bool mag_load_key_from_file_select(Mag* mag) {
+    furi_assert(mag);
+
+    DialogsFileBrowserOptions browser_options;
+    // TODO: Fix icon reference / definition! Temporarily importing asset_icons.h in mag_i.h to let it compile. Remove when fixed!
+    dialog_file_browser_set_basic_options(&browser_options, MAG_APP_EXTENSION, &I_125_10px);
+    browser_options.base_path = MAG_APP_FOLDER;
+
+    // Input events and views are managed by file_browser
+    bool result =
+        dialog_file_browser_show(mag->dialogs, mag->file_path, mag->file_path, &browser_options);
+
+    if(result) {
+        result = mag_load_key_data(mag, mag->file_path, true);
+    }
+
+    return result;
+}
+
+bool mag_delete_key(Mag* mag) {
+    furi_assert(mag);
+
+    return storage_simply_remove(mag->storage, furi_string_get_cstr(mag->file_path));
+}
+
+bool mag_load_key_data(Mag* mag, FuriString* path, bool show_dialog) {
+    bool result = false;
+    UNUSED(mag);
+    UNUSED(path);
+    UNUSED(show_dialog);
+
+    // TODO: Needs reworking from LFRFID version, as that goes through some custom protocol by key type.
+
+    return result;
+}
+
+bool mag_save_key_data(Mag* mag, FuriString* path) {
+    bool result = false;
+    UNUSED(path);
+    //bool result = lfrfid_dict_file_save(app->dict, app->protocol_id, furi_string_get_cstr(path));
+    // TODO: needs reworking from LFRFID version
+    if(!result) {
+        dialog_message_show_storage_error(mag->dialogs, "Cannot save\nkey file");
+    }
+
+    return result;
+}
+
+void mag_make_app_folder(Mag* mag) {
+    furi_assert(mag);
+
+    if(!storage_simply_mkdir(mag->storage, MAG_APP_FOLDER)) {
+        dialog_message_show_storage_error(mag->dialogs, "Cannot create\napp folder");
+    }
+}
+
+void mag_text_store_set(Mag* mag, const char* text, ...) {
+    furi_assert(mag);
+    va_list args;
+    va_start(args, text);
+
+    vsnprintf(mag->text_store, MAG_TEXT_STORE_SIZE, text, args);
+
+    va_end(args);
+}
+
+void mag_text_store_clear(Mag* mag) {
+    furi_assert(mag);
+    memset(mag->text_store, 0, sizeof(mag->text_store));
+}
+
+void mag_popup_timeout_callback(void* context) {
+    Mag* mag = context;
+    view_dispatcher_send_custom_event(mag->view_dispatcher, MagEventPopupClosed);
+}
+
+void mag_widget_callback(GuiButtonType result, InputType type, void* context) {
+    Mag* mag = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(mag->view_dispatcher, result);
+    }
+}
+
+void mag_text_input_callback(void* context) {
+    Mag* mag = context;
+    view_dispatcher_send_custom_event(mag->view_dispatcher, MagEventNext);
+}



+ 97 - 0
mag_i.h

@@ -0,0 +1,97 @@
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+
+#include <gui/gui.h>
+#include <gui/view.h>
+#include <assets_icons.h>
+#include <gui/view_dispatcher.h>
+#include <gui/scene_manager.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 <notification/notification_messages.h>
+#include <dialogs/dialogs.h>
+#include <storage/storage.h>
+#include <flipper_format/flipper_format.h>
+
+#include <toolbox/path.h>
+
+#include "scenes/mag_scene.h"
+
+#define MAG_KEY_NAME_SIZE 22
+#define MAG_TEXT_STORE_SIZE 40
+
+#define MAG_APP_FOLDER ANY_PATH("mag")
+#define MAG_SD_FOLDER EXT_PATH("mag")
+#define MAG_APP_EXTENSION ".mag"
+#define MAG_APP_SHADOW_EXTENSION ".shd"
+
+enum MagCustomEvent {
+    MagEventNext = 100,
+    MagEventExit,
+    MagEventPopupClosed,
+};
+
+typedef struct Mag Mag;
+
+struct Mag {
+    ViewDispatcher* view_dispatcher;
+    Gui* gui;
+    NotificationApp* notifications;
+    SceneManager* scene_manager;
+    Storage* storage;
+    DialogsApp* dialogs;
+    Widget* widget;
+
+    char text_store[MAG_TEXT_STORE_SIZE + 1];
+    FuriString* file_path;
+    FuriString* file_name;
+
+    // Common views
+    Submenu* submenu;
+    DialogEx* dialog_ex;
+    Popup* popup;
+    TextInput* text_input;
+    ByteInput* byte_input;
+
+    // Custom views?
+};
+
+typedef enum {
+    MagViewSubmenu,
+    MagViewDialogEx,
+    MagViewPopup,
+    MagViewWidget,
+    MagViewTextInput,
+    MagViewByteInput,
+} MagView;
+
+bool mag_save_key(Mag* mag);
+
+bool mag_load_key_from_file_select(Mag* mag);
+
+bool mag_delete_key(Mag* mag);
+
+bool mag_load_key_data(Mag* mag, FuriString* path, bool show_dialog);
+
+bool mag_save_key_data(Mag* mag, FuriString* path);
+
+void mag_make_app_folder(Mag* mag);
+
+void mag_text_store_set(Mag* mag, const char* text, ...);
+
+void mag_text_store_clear(Mag* mag);
+
+void mag_popup_timeout_callback(void* context);
+
+void mag_widget_callback(GuiButtonType result, InputType type, void* context);
+
+void mag_text_input_callback(void* context);

+ 30 - 0
scenes/mag_scene.c

@@ -0,0 +1,30 @@
+#include "mag_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const mag_on_enter_handlers[])(void*) = {
+#include "mag_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 mag_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "mag_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 mag_on_exit_handlers[])(void* context) = {
+#include "mag_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers mag_scene_handlers = {
+    .on_enter_handlers = mag_on_enter_handlers,
+    .on_event_handlers = mag_on_event_handlers,
+    .on_exit_handlers = mag_on_exit_handlers,
+    .scene_num = MagSceneNum,
+};

+ 29 - 0
scenes/mag_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) MagScene##id,
+typedef enum {
+#include "mag_scene_config.h"
+    MagSceneNum,
+} MagScene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers mag_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "mag_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 "mag_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 "mag_scene_config.h"
+#undef ADD_SCENE

+ 11 - 0
scenes/mag_scene_config.h

@@ -0,0 +1,11 @@
+ADD_SCENE(mag, start, Start)
+ADD_SCENE(mag, emulate_test, EmulateTest)
+ADD_SCENE(mag, select_key, SelectKey)
+ADD_SCENE(mag, saved_key_menu, SavedKeyMenu)
+ADD_SCENE(mag, saved_info, SavedInfo)
+ADD_SCENE(mag, save_data, SaveData)
+ADD_SCENE(mag, save_name, SaveName)
+ADD_SCENE(mag, save_success, SaveSuccess)
+ADD_SCENE(mag, delete_success, DeleteSuccess)
+ADD_SCENE(mag, delete_confirm, DeleteConfirm)
+ADD_SCENE(mag, exit_confirm, ExitConfirm)

+ 20 - 0
scenes/mag_scene_delete_confirm.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_delete_confirm_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_delete_confirm_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_delete_success.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_delete_success_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_delete_success_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_emulate_test.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_emulate_test_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_emulate_test_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_emulate_test_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_exit_confirm.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_exit_confirm_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_exit_confirm_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_save_data.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_save_data_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_save_data_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_save_data_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_save_name.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_save_name_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_save_name_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_save_name_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_save_success.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_save_success_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_save_success_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_save_success_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_saved_info.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_saved_info_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_saved_info_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_saved_info_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_saved_key_menu.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_saved_key_menu_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_saved_key_menu_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 20 - 0
scenes/mag_scene_select_key.c

@@ -0,0 +1,20 @@
+#include "../mag_i.h"
+
+void mag_scene_select_key_on_enter(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}
+
+bool mag_scene_select_key_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    UNUSED(mag);
+    UNUSED(event);
+    bool consumed = false;
+
+    return consumed;
+}
+
+void mag_scene_select_key_on_exit(void* context) {
+    Mag* mag = context;
+    UNUSED(mag);
+}

+ 64 - 0
scenes/mag_scene_start.c

@@ -0,0 +1,64 @@
+#include "../mag_i.h"
+
+typedef enum {
+    SubmenuIndexEmulateTest,
+    SubmenuIndexSaved,
+    SubmenuIndexAddManually,
+} SubmenuIndex;
+
+static void mag_scene_start_submenu_callback(void* context, uint32_t index) {
+    Mag* mag = context;
+
+    view_dispatcher_send_custom_event(mag->view_dispatcher, index);
+}
+
+void mag_scene_start_on_enter(void* context) {
+    Mag* mag = context;
+    Submenu* submenu = mag->submenu;
+
+    submenu_add_item(
+        submenu,
+        "Emulate (Hardcoded)",
+        SubmenuIndexEmulateTest,
+        mag_scene_start_submenu_callback,
+        mag);
+    submenu_add_item(submenu, "Saved", SubmenuIndexSaved, mag_scene_start_submenu_callback, mag);
+    submenu_add_item(
+        submenu, "Add Manually", SubmenuIndexAddManually, mag_scene_start_submenu_callback, mag);
+
+    submenu_set_selected_item(
+        submenu, scene_manager_get_scene_state(mag->scene_manager, MagSceneStart));
+
+    // clear key
+    furi_string_reset(mag->file_name);
+
+    view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewSubmenu);
+}
+
+bool mag_scene_start_on_event(void* context, SceneManagerEvent event) {
+    Mag* mag = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexEmulateTest) {
+            scene_manager_next_scene(mag->scene_manager, MagSceneEmulateTest);
+            consumed = true;
+        } else if(event.event == SubmenuIndexSaved) {
+            furi_string_set(mag->file_path, MAG_APP_FOLDER);
+            scene_manager_next_scene(mag->scene_manager, MagSceneSelectKey);
+            consumed = true;
+        } else if(event.event == SubmenuIndexAddManually) {
+            scene_manager_next_scene(mag->scene_manager, MagSceneSaveData);
+            consumed = true;
+        }
+        scene_manager_set_scene_state(mag->scene_manager, MagSceneStart, event.event);
+    }
+
+    return consumed;
+}
+
+void mag_scene_start_on_exit(void* context) {
+    Mag* mag = context;
+
+    submenu_reset(mag->submenu);
+}