Просмотр исходного кода

Merge nfc_magic from https://github.com/xMasterX/all-the-plugins

# Conflicts:
#	nfc_magic/application.fam
#	nfc_magic/lib/magic/protocols/gen4/gen4_poller.c
Willy-JL 2 лет назад
Родитель
Сommit
ccc753c5e4

+ 51 - 9
nfc_magic/lib/magic/protocols/gen4/gen4_poller.c

@@ -1,12 +1,10 @@
-#include "core/log.h"
 #include "gen4_poller_i.h"
+#include "protocols/gen4/gen4_poller.h"
 #include <nfc/protocols/iso14443_3a/iso14443_3a.h>
 #include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
 #include <nfc/helpers/nfc_util.h>
 #include <nfc/nfc_poller.h>
 
-#include <furi/furi.h>
-
 #define GEN4_POLLER_THREAD_FLAG_DETECTED (1U << 0)
 
 typedef NfcCommand (*Gen4PollerStateHandler)(Gen4Poller* instance);
@@ -171,12 +169,16 @@ NfcCommand gen4_poller_request_mode_handler(Gen4Poller* instance) {
         instance->state = Gen4PollerStateRequestWriteData;
     } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetPassword) {
         instance->state = Gen4PollerStateChangePassword;
-    } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetDefaultCFG) {
+    } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetDefaultCfg) {
         instance->state = Gen4PollerStateSetDefaultConfig;
-    } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeGetCFG) {
+    } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeGetCfg) {
         instance->state = Gen4PollerStateGetCurrentConfig;
     } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeGetRevision) {
         instance->state = Gen4PollerStateGetRevision;
+    } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetShadowMode) {
+        instance->state = Gen4PollerStateSetShadowMode;
+    } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetDirectWriteBlock0Mode) {
+        instance->state = Gen4PollerStateSetDirectWriteBlock0;
     } else {
         instance->state = Gen4PollerStateFail;
     }
@@ -269,13 +271,13 @@ static NfcCommand gen4_poller_write_mf_classic(Gen4Poller* instance) {
                 break;
             }
 
-            instance->config[6] = Gen4PollerShadowModeIgnore;
+            instance->config[6] = Gen4PollerShadowModeDisabled;
             instance->config[24] = iso3_data->atqa[0];
             instance->config[25] = iso3_data->atqa[1];
             instance->config[26] = iso3_data->sak;
             instance->config[27] = 0x00;
             instance->config[28] = instance->total_blocks - 1;
-            instance->config[29] = 0x01;
+            instance->config[29] = Gen4PollerDirectWriteBlock0ModeDisabled;
 
             Gen4PollerError error = gen4_poller_set_config(
                 instance, instance->password, instance->config, sizeof(instance->config), false);
@@ -348,13 +350,13 @@ static NfcCommand gen4_poller_write_mf_ultralight(Gen4Poller* instance) {
                 break;
             }
 
-            instance->config[6] = Gen4PollerShadowModeHighSpeedIgnore;
+            instance->config[6] = Gen4PollerShadowModeHighSpeedDisabled;
             instance->config[24] = iso3_data->atqa[0];
             instance->config[25] = iso3_data->atqa[1];
             instance->config[26] = iso3_data->sak;
             instance->config[27] = 0x00;
             instance->config[28] = instance->total_blocks - 1;
-            instance->config[29] = 0x01;
+            instance->config[29] = Gen4PollerDirectWriteBlock0ModeDisabled;
 
             Gen4PollerError error = gen4_poller_set_config(
                 instance, instance->password, instance->config, sizeof(instance->config), false);
@@ -534,6 +536,44 @@ NfcCommand gen4_poller_get_revision_handler(Gen4Poller* instance) {
     return command;
 }
 
+NfcCommand gen4_poller_set_shadow_mode_handler(Gen4Poller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    do {
+        Gen4PollerError error =
+            gen4_poller_set_shadow_mode(instance, instance->password, instance->shadow_mode);
+
+        if(error != Gen4PollerErrorNone) {
+            FURI_LOG_E(TAG, "Failed to set shadow mode: %d", error);
+            instance->state = Gen4PollerStateFail;
+            break;
+        }
+
+        instance->state = Gen4PollerStateSuccess;
+    } while(false);
+
+    return command;
+}
+
+NfcCommand gen4_poller_set_direct_write_block_0_mode_handler(Gen4Poller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    do {
+        Gen4PollerError error = gen4_poller_set_direct_write_block_0_mode(
+            instance, instance->password, instance->direct_write_block_0_mode);
+
+        if(error != Gen4PollerErrorNone) {
+            FURI_LOG_E(TAG, "Failed to set direct write to block 0 mode: %d", error);
+            instance->state = Gen4PollerStateFail;
+            break;
+        }
+
+        instance->state = Gen4PollerStateSuccess;
+    } while(false);
+
+    return command;
+}
+
 NfcCommand gen4_poller_success_handler(Gen4Poller* instance) {
     NfcCommand command = NfcCommandContinue;
 
@@ -568,6 +608,8 @@ static const Gen4PollerStateHandler gen4_poller_state_handlers[Gen4PollerStateNu
     [Gen4PollerStateSetDefaultConfig] = gen4_poller_set_default_cfg_handler,
     [Gen4PollerStateGetCurrentConfig] = gen4_poller_get_current_cfg_handler,
     [Gen4PollerStateGetRevision] = gen4_poller_get_revision_handler,
+    [Gen4PollerStateSetShadowMode] = gen4_poller_set_shadow_mode_handler,
+    [Gen4PollerStateSetDirectWriteBlock0] = gen4_poller_set_direct_write_block_0_mode_handler,
     [Gen4PollerStateSuccess] = gen4_poller_success_handler,
     [Gen4PollerStateFail] = gen4_poller_fail_handler,
 

+ 4 - 2
nfc_magic/lib/magic/protocols/gen4/gen4_poller.h

@@ -38,9 +38,11 @@ typedef enum {
     Gen4PollerModeWrite,
     Gen4PollerModeSetPassword,
 
-    Gen4PollerModeSetDefaultCFG,
-    Gen4PollerModeGetCFG,
+    Gen4PollerModeSetDefaultCfg,
+    Gen4PollerModeGetCfg,
     Gen4PollerModeGetRevision,
+    Gen4PollerModeSetShadowMode,
+    Gen4PollerModeSetDirectWriteBlock0Mode
 } Gen4PollerMode;
 
 typedef struct {

+ 99 - 9
nfc_magic/lib/magic/protocols/gen4/gen4_poller_i.c

@@ -1,21 +1,26 @@
 #include "gen4_poller_i.h"
 
 #include "bit_buffer.h"
-#include "core/log.h"
+#include "protocols/gen4/gen4_poller.h"
 #include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
 #include <nfc/helpers/nfc_util.h>
 
 #define GEN4_CMD_PREFIX (0xCF)
 
+#define GEN4_CMD_SET_SHD_MODE (0x32)
 #define GEN4_CMD_GET_CFG (0xC6)
 #define GEN4_CMD_GET_REVISION (0xCC)
 #define GEN4_CMD_WRITE (0xCD)
 #define GEN4_CMD_READ (0xCE)
+#define GEN4_CMD_SET_DW_BLOCK_0 (0xCF)
 #define GEN4_CMD_SET_CFG (0xF0)
 #define GEN4_CMD_FUSE_CFG (0xF1)
 #define GEN4_CMD_SET_PWD (0xFE)
 
-#define CONFIG_SIZE (32)
+#define GEM4_RESPONSE_SUCCESS (0x02)
+
+#define CONFIG_SIZE_MAX (32)
+#define CONFIG_SIZE_MIN (30)
 #define REVISION_SIZE (5)
 
 static Gen4PollerError gen4_poller_process_error(Iso14443_3aError error) {
@@ -30,6 +35,80 @@ static Gen4PollerError gen4_poller_process_error(Iso14443_3aError error) {
     return ret;
 }
 
+Gen4PollerError gen4_poller_set_shadow_mode(
+    Gen4Poller* instance,
+    uint32_t password,
+    Gen4PollerShadowMode mode) {
+    Gen4PollerError ret = Gen4PollerErrorNone;
+    bit_buffer_reset(instance->tx_buffer);
+
+    do {
+        uint8_t password_arr[4] = {};
+        nfc_util_num2bytes(password, COUNT_OF(password_arr), password_arr);
+        bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_PREFIX);
+        bit_buffer_append_bytes(instance->tx_buffer, password_arr, COUNT_OF(password_arr));
+        bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_SET_SHD_MODE);
+        bit_buffer_append_byte(instance->tx_buffer, mode);
+
+        Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
+            instance->iso3_poller, instance->tx_buffer, instance->rx_buffer, GEN4_POLLER_MAX_FWT);
+
+        if(error != Iso14443_3aErrorNone) {
+            ret = gen4_poller_process_error(error);
+            break;
+        }
+
+        uint16_t response = bit_buffer_get_size_bytes(instance->rx_buffer);
+
+        FURI_LOG_D(TAG, "Card response: 0x%02X, Shadow mode set: 0x%02X", response, mode);
+
+        if(response != GEM4_RESPONSE_SUCCESS) {
+            ret = Gen4PollerErrorProtocol;
+            break;
+        }
+
+    } while(false);
+
+    return ret;
+}
+
+Gen4PollerError gen4_poller_set_direct_write_block_0_mode(
+    Gen4Poller* instance,
+    uint32_t password,
+    Gen4PollerDirectWriteBlock0Mode mode) {
+    Gen4PollerError ret = Gen4PollerErrorNone;
+    bit_buffer_reset(instance->tx_buffer);
+
+    do {
+        uint8_t password_arr[4] = {};
+        nfc_util_num2bytes(password, COUNT_OF(password_arr), password_arr);
+        bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_PREFIX);
+        bit_buffer_append_bytes(instance->tx_buffer, password_arr, COUNT_OF(password_arr));
+        bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_SET_DW_BLOCK_0);
+        bit_buffer_append_byte(instance->tx_buffer, mode);
+
+        Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
+            instance->iso3_poller, instance->tx_buffer, instance->rx_buffer, GEN4_POLLER_MAX_FWT);
+
+        if(error != Iso14443_3aErrorNone) {
+            ret = gen4_poller_process_error(error);
+            break;
+        }
+        uint16_t response = bit_buffer_get_size_bytes(instance->rx_buffer);
+
+        FURI_LOG_D(
+            TAG, "Card response: 0x%02X, Direct write to block 0 mode set: 0x%02X", response, mode);
+
+        if(response != GEM4_RESPONSE_SUCCESS) {
+            ret = Gen4PollerErrorProtocol;
+            break;
+        }
+
+    } while(false);
+
+    return ret;
+}
+
 Gen4PollerError
     gen4_poller_get_config(Gen4Poller* instance, uint32_t password, uint8_t* config_result) {
     Gen4PollerError ret = Gen4PollerErrorNone;
@@ -52,11 +131,11 @@ Gen4PollerError
 
         size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
 
-        if(rx_bytes != CONFIG_SIZE) {
+        if(rx_bytes != CONFIG_SIZE_MAX || rx_bytes != CONFIG_SIZE_MIN) {
             ret = Gen4PollerErrorProtocol;
             break;
         }
-        bit_buffer_write_bytes(instance->rx_buffer, config_result, CONFIG_SIZE);
+        bit_buffer_write_bytes(instance->rx_buffer, config_result, CONFIG_SIZE_MAX);
     } while(false);
 
     return ret;
@@ -83,7 +162,7 @@ Gen4PollerError
         }
 
         size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
-        if(rx_bytes != 5) {
+        if(rx_bytes != REVISION_SIZE) {
             ret = Gen4PollerErrorProtocol;
             break;
         }
@@ -119,8 +198,11 @@ Gen4PollerError gen4_poller_set_config(
             break;
         }
 
-        size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
-        if(rx_bytes != 2) {
+        uint16_t response = bit_buffer_get_size_bytes(instance->rx_buffer);
+
+        FURI_LOG_D(TAG, "Card response to set default config command: 0x%02X", response);
+
+        if(response != GEM4_RESPONSE_SUCCESS) {
             ret = Gen4PollerErrorProtocol;
             break;
         }
@@ -187,8 +269,16 @@ Gen4PollerError
             break;
         }
 
-        size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
-        if(rx_bytes != 2) {
+        uint16_t response = bit_buffer_get_size_bytes(instance->rx_buffer);
+
+        FURI_LOG_D(
+            TAG,
+            "Trying to change password from 0x%08lX to 0x%08lX. Card response: 0x%02X",
+            pwd_current,
+            pwd_new,
+            response);
+
+        if(response != GEM4_RESPONSE_SUCCESS) {
             ret = Gen4PollerErrorProtocol;
             break;
         }

+ 27 - 3
nfc_magic/lib/magic/protocols/gen4/gen4_poller_i.h

@@ -36,12 +36,23 @@ typedef enum {
     Gen4PollerShadowModePreWrite = 0x00,
     // written data can be read once before restored to original
     Gen4PollerShadowModeRestore = 0x01,
-    // written data is discarded
-    Gen4PollerShadowModeIgnore = 0x02,
+    // shadow mode disabled
+    Gen4PollerShadowModeDisabled = 0x02,
     // apparently for UL?
-    Gen4PollerShadowModeHighSpeedIgnore = 0x03
+    Gen4PollerShadowModeHighSpeedDisabled = 0x03,
+    // work with new UMC. With old UMC is untested
+    Gen4PollerShadowModeSplit = 0x04,
 } Gen4PollerShadowMode;
 
+typedef enum {
+    // gen2 card behavour
+    Gen4PollerDirectWriteBlock0ModeEnabled = 0x00,
+    // common card behavour
+    Gen4PollerDirectWriteBlock0ModeDisabled = 0x01,
+    // default mode. same behavour as Gen4PollerDirectWriteBlock0ModeActivate
+    Gen4PollerDirectWriteBlock0ModeDefault = 0x02,
+} Gen4PollerDirectWriteBlock0Mode;
+
 typedef enum {
     Gen4PollerStateIdle,
     Gen4PollerStateRequestMode,
@@ -53,6 +64,8 @@ typedef enum {
     Gen4PollerStateSetDefaultConfig,
     Gen4PollerStateGetCurrentConfig,
     Gen4PollerStateGetRevision,
+    Gen4PollerStateSetShadowMode,
+    Gen4PollerStateSetDirectWriteBlock0,
 
     Gen4PollerStateSuccess,
     Gen4PollerStateFail,
@@ -78,6 +91,9 @@ struct Gen4Poller {
 
     uint8_t config[GEN4_POLLER_CONFIG_SIZE_MAX];
 
+    Gen4PollerShadowMode shadow_mode;
+    Gen4PollerDirectWriteBlock0Mode direct_write_block_0_mode;
+
     Gen4PollerEvent gen4_event;
     Gen4PollerEventData gen4_event_data;
 
@@ -107,6 +123,14 @@ Gen4PollerError
 Gen4PollerError
     gen4_poller_get_config(Gen4Poller* instance, uint32_t password, uint8_t* config_result);
 
+Gen4PollerError
+    gen4_poller_set_shadow_mode(Gen4Poller* instance, uint32_t password, Gen4PollerShadowMode mode);
+
+Gen4PollerError gen4_poller_set_direct_write_block_0_mode(
+    Gen4Poller* instance,
+    uint32_t password,
+    Gen4PollerDirectWriteBlock0Mode mode);
+
 #ifdef __cplusplus
 }
 #endif

+ 7 - 3
nfc_magic/scenes/nfc_magic_scene_config.h

@@ -5,11 +5,15 @@ ADD_SCENE(nfc_magic, magic_info, MagicInfo)
 ADD_SCENE(nfc_magic, gen1_menu, Gen1Menu)
 ADD_SCENE(nfc_magic, gen4_menu, Gen4Menu)
 ADD_SCENE(nfc_magic, gen4_actions_menu, Gen4ActionsMenu)
-ADD_SCENE(nfc_magic, gen4_get_cfg, Gen4GetCFG)
-ADD_SCENE(nfc_magic, gen4_set_cfg, Gen4SetCFG)
+ADD_SCENE(nfc_magic, gen4_get_cfg, Gen4GetCfg)
+ADD_SCENE(nfc_magic, gen4_set_cfg, Gen4SetCfg)
 ADD_SCENE(nfc_magic, gen4_revision, Gen4Revision)
 ADD_SCENE(nfc_magic, gen4_show_rev, Gen4ShowRev)
-ADD_SCENE(nfc_magic, gen4_show_cfg, Gen4ShowCFG)
+ADD_SCENE(nfc_magic, gen4_show_cfg, Gen4ShowCfg)
+ADD_SCENE(nfc_magic, gen4_select_shd_mode, Gen4SelectShdMode)
+ADD_SCENE(nfc_magic, gen4_set_shd_mode, Gen4SetShdMode)
+ADD_SCENE(nfc_magic, gen4_select_direct_write_block_0_mode, Gen4SelectDirectWriteBlock0Mode)
+ADD_SCENE(nfc_magic, gen4_set_direct_write_block_0_mode, Gen4SetDirectWriteBlock0Mode)
 ADD_SCENE(nfc_magic, gen4_fail, Gen4Fail)
 ADD_SCENE(nfc_magic, wipe, Wipe)
 ADD_SCENE(nfc_magic, wipe_fail, WipeFail)

+ 2 - 24
nfc_magic/scenes/nfc_magic_scene_gen4_actions_menu.c

@@ -1,11 +1,8 @@
 #include "../nfc_magic_app_i.h"
-#include "furi_hal_rtc.h"
 
 enum SubmenuIndex {
     SubmenuIndexAuthenticate,
     SubmenuIndexSetStandartConfig,
-    SubmenuIndexGetConfig,
-    SubmenuIndexGetRevision
 };
 
 void nfc_magic_scene_gen4_actions_menu_submenu_callback(void* context, uint32_t index) {
@@ -24,26 +21,12 @@ void nfc_magic_scene_gen4_actions_menu_on_enter(void* context) {
         SubmenuIndexAuthenticate,
         nfc_magic_scene_gen4_actions_menu_submenu_callback,
         instance);
-    submenu_add_item(
-        submenu,
-        "Get Revision",
-        SubmenuIndexGetRevision,
-        nfc_magic_scene_gen4_actions_menu_submenu_callback,
-        instance);
     submenu_add_item(
         submenu,
         "Set Standart Config",
         SubmenuIndexSetStandartConfig,
         nfc_magic_scene_gen4_actions_menu_submenu_callback,
         instance);
-    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
-        submenu_add_item(
-            submenu,
-            "Get Config",
-            SubmenuIndexGetConfig,
-            nfc_magic_scene_gen4_actions_menu_submenu_callback,
-            instance);
-    }
 
     submenu_set_selected_item(
         submenu,
@@ -60,15 +43,10 @@ bool nfc_magic_scene_gen4_actions_menu_on_event(void* context, SceneManagerEvent
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneKeyInput);
             consumed = true;
         } else if(event.event == SubmenuIndexSetStandartConfig) {
-            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetCFG);
-            consumed = true;
-        } else if(event.event == SubmenuIndexGetConfig) {
-            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4GetCFG);
-            consumed = true;
-        } else if(event.event == SubmenuIndexGetRevision) {
-            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4Revision);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetCfg);
             consumed = true;
         }
+
         scene_manager_set_scene_state(
             instance->scene_manager, NfcMagicSceneGen4ActionsMenu, event.event);
     } else if(event.type == SceneManagerEventTypeBack) {

+ 12 - 12
nfc_magic/scenes/nfc_magic_scene_gen4_get_cfg.c

@@ -1,8 +1,8 @@
 #include "../nfc_magic_app_i.h"
 
 enum {
-    NfcMagicSceneGen4GetCFGStateCardSearch,
-    NfcMagicSceneGen4GetCFGStateCardFound,
+    NfcMagicSceneGen4GetCfgStateCardSearch,
+    NfcMagicSceneGen4GetCfgStateCardFound,
 };
 
 NfcCommand nfc_mafic_scene_gen4_get_cfg_poller_callback(Gen4PollerEvent event, void* context) {
@@ -15,7 +15,7 @@ NfcCommand nfc_mafic_scene_gen4_get_cfg_poller_callback(Gen4PollerEvent event, v
         view_dispatcher_send_custom_event(
             instance->view_dispatcher, NfcMagicCustomEventCardDetected);
     } else if(event.type == Gen4PollerEventTypeRequestMode) {
-        event.data->request_mode.mode = Gen4PollerModeGetCFG;
+        event.data->request_mode.mode = Gen4PollerModeGetCfg;
     } else if(event.type == Gen4PollerEventTypeSuccess) {
         // Copy config from event to main instance to display it on success scene
         memcpy(
@@ -39,9 +39,9 @@ static void nfc_magic_scene_gen4_get_cfg_setup_view(NfcMagicApp* instance) {
     Popup* popup = instance->popup;
     popup_reset(popup);
     uint32_t state =
-        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4GetCFG);
+        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4GetCfg);
 
-    if(state == NfcMagicSceneGen4GetCFGStateCardSearch) {
+    if(state == NfcMagicSceneGen4GetCfgStateCardSearch) {
         popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
         popup_set_text(
             instance->popup, "Apply the\ncard\nto the back", 128, 32, AlignRight, AlignCenter);
@@ -57,7 +57,7 @@ void nfc_magic_scene_gen4_get_cfg_on_enter(void* context) {
     NfcMagicApp* instance = context;
 
     scene_manager_set_scene_state(
-        instance->scene_manager, NfcMagicSceneGen4GetCFG, NfcMagicSceneGen4GetCFGStateCardSearch);
+        instance->scene_manager, NfcMagicSceneGen4GetCfg, NfcMagicSceneGen4GetCfgStateCardSearch);
     nfc_magic_scene_gen4_get_cfg_setup_view(instance);
 
     nfc_magic_app_blink_start(instance);
@@ -76,19 +76,19 @@ bool nfc_magic_scene_gen4_get_cfg_on_event(void* context, SceneManagerEvent even
         if(event.event == NfcMagicCustomEventCardDetected) {
             scene_manager_set_scene_state(
                 instance->scene_manager,
-                NfcMagicSceneGen4GetCFG,
-                NfcMagicSceneGen4GetCFGStateCardFound);
+                NfcMagicSceneGen4GetCfg,
+                NfcMagicSceneGen4GetCfgStateCardFound);
             nfc_magic_scene_gen4_get_cfg_setup_view(instance);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventCardLost) {
             scene_manager_set_scene_state(
                 instance->scene_manager,
-                NfcMagicSceneGen4GetCFG,
-                NfcMagicSceneGen4GetCFGStateCardSearch);
+                NfcMagicSceneGen4GetCfg,
+                NfcMagicSceneGen4GetCfgStateCardSearch);
             nfc_magic_scene_gen4_get_cfg_setup_view(instance);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventWorkerSuccess) {
-            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4ShowCFG);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4ShowCfg);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventWorkerFail) {
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4Fail);
@@ -105,7 +105,7 @@ void nfc_magic_scene_gen4_get_cfg_on_exit(void* context) {
     gen4_poller_stop(instance->gen4_poller);
     gen4_poller_free(instance->gen4_poller);
     scene_manager_set_scene_state(
-        instance->scene_manager, NfcMagicSceneGen4GetCFG, NfcMagicSceneGen4GetCFGStateCardSearch);
+        instance->scene_manager, NfcMagicSceneGen4GetCfg, NfcMagicSceneGen4GetCfgStateCardSearch);
     // Clear view
     popup_reset(instance->popup);
 

+ 45 - 0
nfc_magic/scenes/nfc_magic_scene_gen4_menu.c

@@ -1,8 +1,13 @@
 #include "../nfc_magic_app_i.h"
+#include "furi_hal_rtc.h"
 
 enum SubmenuIndex {
     SubmenuIndexWrite,
     SubmenuIndexChangePassword,
+    SubmenuIndexSetShadowMode,
+    SubmenuIndexSetDirectWriteBlock0Mode,
+    SubmenuIndexGetRevision,
+    SubmenuIndexGetConfig,
     SubmenuIndexWipe,
 };
 
@@ -24,8 +29,34 @@ void nfc_magic_scene_gen4_menu_on_enter(void* context) {
         SubmenuIndexChangePassword,
         nfc_magic_scene_gen4_menu_submenu_callback,
         instance);
+    submenu_add_item(
+        submenu,
+        "Set Shadow Mode",
+        SubmenuIndexSetShadowMode,
+        nfc_magic_scene_gen4_menu_submenu_callback,
+        instance);
+    submenu_add_item(
+        submenu,
+        "Set Gen2 Mode",
+        SubmenuIndexSetDirectWriteBlock0Mode,
+        nfc_magic_scene_gen4_menu_submenu_callback,
+        instance);
+    submenu_add_item(
+        submenu,
+        "Get Revision",
+        SubmenuIndexGetRevision,
+        nfc_magic_scene_gen4_menu_submenu_callback,
+        instance);
     submenu_add_item(
         submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_gen4_menu_submenu_callback, instance);
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        submenu_add_item(
+            submenu,
+            "Get Config",
+            SubmenuIndexGetConfig,
+            nfc_magic_scene_gen4_menu_submenu_callback,
+            instance);
+    }
 
     submenu_set_selected_item(
         submenu, scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4Menu));
@@ -46,7 +77,21 @@ bool nfc_magic_scene_gen4_menu_on_event(void* context, SceneManagerEvent event)
         } else if(event.event == SubmenuIndexWipe) {
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneWipe);
             consumed = true;
+        } else if(event.event == SubmenuIndexGetConfig) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4GetCfg);
+            consumed = true;
+        } else if(event.event == SubmenuIndexGetRevision) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4Revision);
+            consumed = true;
+        } else if(event.event == SubmenuIndexSetShadowMode) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SelectShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexSetDirectWriteBlock0Mode) {
+            scene_manager_next_scene(
+                instance->scene_manager, NfcMagicSceneGen4SelectDirectWriteBlock0Mode);
+            consumed = true;
         }
+
         scene_manager_set_scene_state(instance->scene_manager, NfcMagicSceneGen4Menu, event.event);
     } else if(event.type == SceneManagerEventTypeBack) {
         if(instance->gen4_password != 0) {

+ 96 - 0
nfc_magic/scenes/nfc_magic_scene_gen4_select_direct_write_block_0_mode.c

@@ -0,0 +1,96 @@
+#include "../nfc_magic_app_i.h"
+#include "furi_hal_rtc.h"
+#include "protocols/gen4/gen4_poller_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexEnable,
+    SubmenuIndexDisable,
+    SubmenuIndexDefault,
+};
+
+void nfc_magic_scene_gen4_select_direct_write_block_0_mode_submenu_callback(
+    void* context,
+    uint32_t index) {
+    NfcMagicApp* instance = context;
+
+    view_dispatcher_send_custom_event(instance->view_dispatcher, index);
+}
+
+void nfc_magic_scene_gen4_select_direct_write_block_0_mode_on_enter(void* context) {
+    NfcMagicApp* instance = context;
+
+    Submenu* submenu = instance->submenu;
+    submenu_add_item(
+        submenu,
+        "Enable",
+        SubmenuIndexEnable,
+        nfc_magic_scene_gen4_select_direct_write_block_0_mode_submenu_callback,
+        instance);
+    submenu_add_item(
+        submenu,
+        "Disable",
+        SubmenuIndexDisable,
+        nfc_magic_scene_gen4_select_direct_write_block_0_mode_submenu_callback,
+        instance);
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        submenu_add_item(
+            submenu,
+            "Default",
+            SubmenuIndexDefault,
+            nfc_magic_scene_gen4_select_direct_write_block_0_mode_submenu_callback,
+            instance);
+    }
+    submenu_set_selected_item(
+        submenu,
+        scene_manager_get_scene_state(
+            instance->scene_manager, NfcMagicSceneGen4SelectDirectWriteBlock0Mode));
+    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewMenu);
+}
+
+bool nfc_magic_scene_gen4_select_direct_write_block_0_mode_on_event(
+    void* context,
+    SceneManagerEvent event) {
+    NfcMagicApp* instance = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexEnable) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+                Gen4PollerDirectWriteBlock0ModeEnabled);
+            scene_manager_next_scene(
+                instance->scene_manager, NfcMagicSceneGen4SetDirectWriteBlock0Mode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexDisable) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+                Gen4PollerDirectWriteBlock0ModeDisabled);
+            scene_manager_next_scene(
+                instance->scene_manager, NfcMagicSceneGen4SetDirectWriteBlock0Mode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexDefault) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+                Gen4PollerDirectWriteBlock0ModeDefault);
+            scene_manager_next_scene(
+                instance->scene_manager, NfcMagicSceneGen4SetDirectWriteBlock0Mode);
+            consumed = true;
+        }
+        scene_manager_set_scene_state(
+            instance->scene_manager, NfcMagicSceneGen4SelectDirectWriteBlock0Mode, event.event);
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneGen4ActionsMenu);
+    }
+
+    return consumed;
+}
+
+void nfc_magic_scene_gen4_select_direct_write_block_0_mode_on_exit(void* context) {
+    NfcMagicApp* instance = context;
+
+    submenu_reset(instance->submenu);
+}

+ 110 - 0
nfc_magic/scenes/nfc_magic_scene_gen4_select_shd_mode.c

@@ -0,0 +1,110 @@
+#include "../nfc_magic_app_i.h"
+#include "protocols/gen4/gen4_poller_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexPreWriteMode,
+    SubmenuIndexRestoreMode,
+    SubmenuIndexDisable,
+    SubmenuIndexDisableHighSpeed,
+    SubmenuIndexSplitMode,
+};
+
+void nfc_magic_scene_gen4_select_shd_mode_submenu_callback(void* context, uint32_t index) {
+    NfcMagicApp* instance = context;
+
+    view_dispatcher_send_custom_event(instance->view_dispatcher, index);
+}
+
+void nfc_magic_scene_gen4_select_shd_mode_on_enter(void* context) {
+    NfcMagicApp* instance = context;
+
+    Submenu* submenu = instance->submenu;
+    submenu_add_item(
+        submenu,
+        "Pre-Write",
+        SubmenuIndexPreWriteMode,
+        nfc_magic_scene_gen4_select_shd_mode_submenu_callback,
+        instance);
+    submenu_add_item(
+        submenu,
+        "Restore",
+        SubmenuIndexRestoreMode,
+        nfc_magic_scene_gen4_select_shd_mode_submenu_callback,
+        instance);
+    submenu_add_item(
+        submenu,
+        "Disable",
+        SubmenuIndexDisable,
+        nfc_magic_scene_gen4_select_shd_mode_submenu_callback,
+        instance);
+    submenu_add_item(
+        submenu,
+        "Disable (High-Speed)",
+        SubmenuIndexDisableHighSpeed,
+        nfc_magic_scene_gen4_select_shd_mode_submenu_callback,
+        instance);
+    submenu_add_item(
+        submenu,
+        "Split",
+        SubmenuIndexSplitMode,
+        nfc_magic_scene_gen4_select_shd_mode_submenu_callback,
+        instance);
+
+    submenu_set_selected_item(
+        submenu,
+        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4SelectShdMode));
+    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewMenu);
+}
+
+bool nfc_magic_scene_gen4_select_shd_mode_on_event(void* context, SceneManagerEvent event) {
+    NfcMagicApp* instance = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexPreWriteMode) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetShdMode,
+                Gen4PollerShadowModePreWrite);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexRestoreMode) {
+            scene_manager_set_scene_state(
+                instance->scene_manager, NfcMagicSceneGen4SetShdMode, Gen4PollerShadowModeRestore);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexDisable) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetShdMode,
+                Gen4PollerShadowModeDisabled);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexDisableHighSpeed) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetShdMode,
+                Gen4PollerShadowModeHighSpeedDisabled);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexSplitMode) {
+            scene_manager_set_scene_state(
+                instance->scene_manager, NfcMagicSceneGen4SetShdMode, Gen4PollerShadowModeSplit);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        }
+        scene_manager_set_scene_state(
+            instance->scene_manager, NfcMagicSceneGen4SelectShdMode, event.event);
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneGen4ActionsMenu);
+    }
+
+    return consumed;
+}
+
+void nfc_magic_scene_gen4_select_shd_mode_on_exit(void* context) {
+    NfcMagicApp* instance = context;
+
+    submenu_reset(instance->submenu);
+}

+ 13 - 13
nfc_magic/scenes/nfc_magic_scene_gen4_set_cfg.c

@@ -1,8 +1,8 @@
 #include "../nfc_magic_app_i.h"
 
 enum {
-    NfcMagicSceneGen4SetDefCFGStateCardSearch,
-    NfcMagicSceneGen4SetDefCFGStateCardFound,
+    NfcMagicSceneGen4SetDefCfgStateCardSearch,
+    NfcMagicSceneGen4SetDefCfgStateCardFound,
 };
 
 NfcCommand nfc_mafic_scene_gen4_set_cfg_poller_callback(Gen4PollerEvent event, void* context) {
@@ -15,7 +15,7 @@ NfcCommand nfc_mafic_scene_gen4_set_cfg_poller_callback(Gen4PollerEvent event, v
         view_dispatcher_send_custom_event(
             instance->view_dispatcher, NfcMagicCustomEventCardDetected);
     } else if(event.type == Gen4PollerEventTypeRequestMode) {
-        event.data->request_mode.mode = Gen4PollerModeSetDefaultCFG;
+        event.data->request_mode.mode = Gen4PollerModeSetDefaultCfg;
     } else if(event.type == Gen4PollerEventTypeSuccess) {
         view_dispatcher_send_custom_event(
             instance->view_dispatcher, NfcMagicCustomEventWorkerSuccess);
@@ -33,9 +33,9 @@ static void nfc_magic_scene_gen4_set_cfg_setup_view(NfcMagicApp* instance) {
     Popup* popup = instance->popup;
     popup_reset(popup);
     uint32_t state =
-        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4SetCFG);
+        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4SetCfg);
 
-    if(state == NfcMagicSceneGen4SetDefCFGStateCardSearch) {
+    if(state == NfcMagicSceneGen4SetDefCfgStateCardSearch) {
         popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
         popup_set_text(
             instance->popup, "Apply the\ncard\nto the back", 128, 32, AlignRight, AlignCenter);
@@ -52,8 +52,8 @@ void nfc_magic_scene_gen4_set_cfg_on_enter(void* context) {
 
     scene_manager_set_scene_state(
         instance->scene_manager,
-        NfcMagicSceneGen4SetCFG,
-        NfcMagicSceneGen4SetDefCFGStateCardSearch);
+        NfcMagicSceneGen4SetCfg,
+        NfcMagicSceneGen4SetDefCfgStateCardSearch);
     nfc_magic_scene_gen4_set_cfg_setup_view(instance);
 
     nfc_magic_app_blink_start(instance);
@@ -72,15 +72,15 @@ bool nfc_magic_scene_gen4_set_cfg_on_event(void* context, SceneManagerEvent even
         if(event.event == NfcMagicCustomEventCardDetected) {
             scene_manager_set_scene_state(
                 instance->scene_manager,
-                NfcMagicSceneGen4SetCFG,
-                NfcMagicSceneGen4SetDefCFGStateCardFound);
+                NfcMagicSceneGen4SetCfg,
+                NfcMagicSceneGen4SetDefCfgStateCardFound);
             nfc_magic_scene_gen4_set_cfg_setup_view(instance);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventCardLost) {
             scene_manager_set_scene_state(
                 instance->scene_manager,
-                NfcMagicSceneGen4SetCFG,
-                NfcMagicSceneGen4SetDefCFGStateCardSearch);
+                NfcMagicSceneGen4SetCfg,
+                NfcMagicSceneGen4SetDefCfgStateCardSearch);
             nfc_magic_scene_gen4_set_cfg_setup_view(instance);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventWorkerSuccess) {
@@ -102,8 +102,8 @@ void nfc_magic_scene_gen4_set_cfg_on_exit(void* context) {
     gen4_poller_free(instance->gen4_poller);
     scene_manager_set_scene_state(
         instance->scene_manager,
-        NfcMagicSceneGen4SetCFG,
-        NfcMagicSceneGen4SetDefCFGStateCardSearch);
+        NfcMagicSceneGen4SetCfg,
+        NfcMagicSceneGen4SetDefCfgStateCardSearch);
     // Clear view
     popup_reset(instance->popup);
 

+ 122 - 0
nfc_magic/scenes/nfc_magic_scene_gen4_set_direct_write_block_0_mode.c

@@ -0,0 +1,122 @@
+#include "../nfc_magic_app_i.h"
+#include "applications_user/nfc_magic/lib/magic/protocols/gen4/gen4_poller_i.h"
+
+enum {
+    NfcMagicSceneGen4SetDirectWriteBlock0ModeStateCardSearch,
+    NfcMagicSceneGen4SetDirectWriteBlock0ModeStateCardFound,
+};
+
+NfcCommand nfc_magic_scene_gen4_set_direct_write_block_0_mode_poller_callback(
+    Gen4PollerEvent event,
+    void* context) {
+    NfcMagicApp* instance = context;
+    furi_assert(event.data);
+
+    NfcCommand command = NfcCommandContinue;
+    if(event.type == Gen4PollerEventTypeCardDetected) {
+        view_dispatcher_send_custom_event(
+            instance->view_dispatcher, NfcMagicCustomEventCardDetected);
+    } else if(event.type == Gen4PollerEventTypeRequestMode) {
+        event.data->request_mode.mode = Gen4PollerModeSetDirectWriteBlock0Mode;
+    } else if(event.type == Gen4PollerEventTypeSuccess) {
+        view_dispatcher_send_custom_event(
+            instance->view_dispatcher, NfcMagicCustomEventWorkerSuccess);
+        command = NfcCommandStop;
+    } else if(event.type == Gen4PollerEventTypeFail) {
+        view_dispatcher_send_custom_event(
+            instance->view_dispatcher, NfcMagicCustomEventWorkerFail);
+        command = NfcCommandStop;
+    }
+
+    return command;
+}
+
+static void nfc_magic_scene_gen4_set_direct_write_block_0_mode_setup_view(NfcMagicApp* instance) {
+    Popup* popup = instance->popup;
+    popup_reset(popup);
+    uint32_t state = scene_manager_get_scene_state(
+        instance->scene_manager, NfcMagicSceneGen4SetDirectWriteBlock0Mode);
+
+    if(state == NfcMagicSceneGen4SetDirectWriteBlock0ModeStateCardSearch) {
+        popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
+        popup_set_text(
+            instance->popup, "Apply the\ncard\nto the back", 128, 32, AlignRight, AlignCenter);
+    } else {
+        popup_set_icon(popup, 12, 23, &I_Loading_24);
+        popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
+    }
+
+    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewPopup);
+}
+
+void nfc_magic_scene_gen4_set_direct_write_block_0_mode_on_enter(void* context) {
+    NfcMagicApp* instance = context;
+
+    uint8_t direct_write_block_0_mode = scene_manager_get_scene_state(
+        instance->scene_manager, NfcMagicSceneGen4SetDirectWriteBlock0Mode);
+
+    scene_manager_set_scene_state(
+        instance->scene_manager,
+        NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+        NfcMagicSceneGen4SetDirectWriteBlock0ModeStateCardSearch);
+    nfc_magic_scene_gen4_set_direct_write_block_0_mode_setup_view(instance);
+
+    nfc_magic_app_blink_start(instance);
+
+    instance->gen4_poller = gen4_poller_alloc(instance->nfc);
+    gen4_poller_set_password(instance->gen4_poller, instance->gen4_password);
+    instance->gen4_poller->direct_write_block_0_mode = direct_write_block_0_mode;
+
+    gen4_poller_start(
+        instance->gen4_poller,
+        nfc_magic_scene_gen4_set_direct_write_block_0_mode_poller_callback,
+        instance);
+}
+
+bool nfc_magic_scene_gen4_set_direct_write_block_0_mode_on_event(
+    void* context,
+    SceneManagerEvent event) {
+    NfcMagicApp* instance = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == NfcMagicCustomEventCardDetected) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+                NfcMagicSceneGen4SetDirectWriteBlock0ModeStateCardFound);
+            nfc_magic_scene_gen4_set_direct_write_block_0_mode_setup_view(instance);
+            consumed = true;
+        } else if(event.event == NfcMagicCustomEventCardLost) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+                NfcMagicSceneGen4SetDirectWriteBlock0ModeStateCardSearch);
+            nfc_magic_scene_gen4_set_direct_write_block_0_mode_setup_view(instance);
+            consumed = true;
+        } else if(event.event == NfcMagicCustomEventWorkerSuccess) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneSuccess);
+            consumed = true;
+        } else if(event.event == NfcMagicCustomEventWorkerFail) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4Fail);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void nfc_magic_scene_gen4_set_direct_write_block_0_mode_on_exit(void* context) {
+    NfcMagicApp* instance = context;
+
+    gen4_poller_stop(instance->gen4_poller);
+    gen4_poller_free(instance->gen4_poller);
+    scene_manager_set_scene_state(
+        instance->scene_manager,
+        NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+        NfcMagicSceneGen4SetDirectWriteBlock0ModeStateCardSearch);
+    // Clear view
+    popup_reset(instance->popup);
+
+    nfc_magic_app_blink_stop(instance);
+}

+ 117 - 0
nfc_magic/scenes/nfc_magic_scene_gen4_set_shd_mode.c

@@ -0,0 +1,117 @@
+#include "../nfc_magic_app_i.h"
+#include "applications_user/nfc_magic/lib/magic/protocols/gen4/gen4_poller_i.h"
+
+enum {
+    NfcMagicSceneGen4SetShadowModeStateCardSearch,
+    NfcMagicSceneGen4SetShadowModeStateCardFound,
+};
+
+NfcCommand
+    nfc_magic_scene_gen4_set_shd_mode_poller_callback(Gen4PollerEvent event, void* context) {
+    NfcMagicApp* instance = context;
+    furi_assert(event.data);
+
+    NfcCommand command = NfcCommandContinue;
+    if(event.type == Gen4PollerEventTypeCardDetected) {
+        view_dispatcher_send_custom_event(
+            instance->view_dispatcher, NfcMagicCustomEventCardDetected);
+    } else if(event.type == Gen4PollerEventTypeRequestMode) {
+        event.data->request_mode.mode = Gen4PollerModeSetShadowMode;
+    } else if(event.type == Gen4PollerEventTypeSuccess) {
+        view_dispatcher_send_custom_event(
+            instance->view_dispatcher, NfcMagicCustomEventWorkerSuccess);
+        command = NfcCommandStop;
+    } else if(event.type == Gen4PollerEventTypeFail) {
+        view_dispatcher_send_custom_event(
+            instance->view_dispatcher, NfcMagicCustomEventWorkerFail);
+        command = NfcCommandStop;
+    }
+
+    return command;
+}
+
+static void nfc_magic_scene_gen4_set_shd_mode_setup_view(NfcMagicApp* instance) {
+    Popup* popup = instance->popup;
+    popup_reset(popup);
+    uint32_t state =
+        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+
+    if(state == NfcMagicSceneGen4SetShadowModeStateCardSearch) {
+        popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
+        popup_set_text(
+            instance->popup, "Apply the\ncard\nto the back", 128, 32, AlignRight, AlignCenter);
+    } else {
+        popup_set_icon(popup, 12, 23, &I_Loading_24);
+        popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
+    }
+
+    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewPopup);
+}
+
+void nfc_magic_scene_gen4_set_shd_mode_on_enter(void* context) {
+    NfcMagicApp* instance = context;
+
+    uint8_t shadow_mode =
+        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+
+    scene_manager_set_scene_state(
+        instance->scene_manager,
+        NfcMagicSceneGen4SetShdMode,
+        NfcMagicSceneGen4SetShadowModeStateCardSearch);
+    nfc_magic_scene_gen4_set_shd_mode_setup_view(instance);
+
+    nfc_magic_app_blink_start(instance);
+
+    instance->gen4_poller = gen4_poller_alloc(instance->nfc);
+    gen4_poller_set_password(instance->gen4_poller, instance->gen4_password);
+    instance->gen4_poller->shadow_mode = shadow_mode;
+
+    gen4_poller_start(
+        instance->gen4_poller, nfc_magic_scene_gen4_set_shd_mode_poller_callback, instance);
+}
+
+bool nfc_magic_scene_gen4_set_shd_mode_on_event(void* context, SceneManagerEvent event) {
+    NfcMagicApp* instance = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == NfcMagicCustomEventCardDetected) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetShdMode,
+                NfcMagicSceneGen4SetShadowModeStateCardFound);
+            nfc_magic_scene_gen4_set_shd_mode_setup_view(instance);
+            consumed = true;
+        } else if(event.event == NfcMagicCustomEventCardLost) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetShdMode,
+                NfcMagicSceneGen4SetShadowModeStateCardSearch);
+            nfc_magic_scene_gen4_set_shd_mode_setup_view(instance);
+            consumed = true;
+        } else if(event.event == NfcMagicCustomEventWorkerSuccess) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneSuccess);
+            consumed = true;
+        } else if(event.event == NfcMagicCustomEventWorkerFail) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4Fail);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void nfc_magic_scene_gen4_set_shd_mode_on_exit(void* context) {
+    NfcMagicApp* instance = context;
+
+    gen4_poller_stop(instance->gen4_poller);
+    gen4_poller_free(instance->gen4_poller);
+    scene_manager_set_scene_state(
+        instance->scene_manager,
+        NfcMagicSceneGen4SetShdMode,
+        NfcMagicSceneGen4SetShadowModeStateCardSearch);
+    // Clear view
+    popup_reset(instance->popup);
+
+    nfc_magic_app_blink_stop(instance);
+}

+ 9 - 3
nfc_magic/scenes/nfc_magic_scene_gen4_show_cfg.c

@@ -33,7 +33,11 @@ void nfc_magic_scene_gen4_show_cfg_on_enter(void* context) {
     widget_add_text_scroll_element(widget, 3, 17, 124, 50, furi_string_get_cstr(temp_config));
 
     widget_add_button_element(
-        widget, GuiButtonTypeLeft, "Exit", nfc_magic_scene_gen4_show_cfg_widget_callback, instance);
+        widget,
+        GuiButtonTypeLeft,
+        "Retry",
+        nfc_magic_scene_gen4_show_cfg_widget_callback,
+        instance);
 
     furi_string_free(temp_config);
     view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewWidget);
@@ -45,9 +49,11 @@ bool nfc_magic_scene_gen4_show_cfg_on_event(void* context, SceneManagerEvent eve
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == GuiButtonTypeLeft) {
-            consumed = scene_manager_search_and_switch_to_previous_scene(
-                instance->scene_manager, NfcMagicSceneGen4ActionsMenu);
+            consumed = scene_manager_previous_scene(instance->scene_manager);
         }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneGen4Menu);
     }
     return consumed;
 }

+ 9 - 3
nfc_magic/scenes/nfc_magic_scene_gen4_show_rev.c

@@ -29,7 +29,11 @@ void nfc_magic_scene_gen4_show_rev_on_enter(void* context) {
     widget_add_string_multiline_element(
         widget, 3, 17, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_revision));
     widget_add_button_element(
-        widget, GuiButtonTypeLeft, "Exit", nfc_magic_scene_gen4_show_rev_widget_callback, instance);
+        widget,
+        GuiButtonTypeLeft,
+        "Retry",
+        nfc_magic_scene_gen4_show_rev_widget_callback,
+        instance);
 
     furi_string_free(temp_revision);
     view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewWidget);
@@ -41,9 +45,11 @@ bool nfc_magic_scene_gen4_show_rev_on_event(void* context, SceneManagerEvent eve
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == GuiButtonTypeLeft) {
-            consumed = scene_manager_search_and_switch_to_previous_scene(
-                instance->scene_manager, NfcMagicSceneGen4ActionsMenu);
+            consumed = scene_manager_previous_scene(instance->scene_manager);
         }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneGen4Menu);
     }
     return consumed;
 }

+ 3 - 0
nfc_magic/scenes/nfc_magic_scene_magic_info.c

@@ -52,6 +52,9 @@ bool nfc_magic_scene_magic_info_on_event(void* context, SceneManagerEvent event)
                 consumed = true;
             }
         }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneStart);
     }
     return consumed;
 }

+ 3 - 0
nfc_magic/scenes/nfc_magic_scene_not_magic.c

@@ -38,6 +38,9 @@ bool nfc_magic_scene_not_magic_on_event(void* context, SceneManagerEvent event)
         if(event.event == GuiButtonTypeLeft) {
             consumed = scene_manager_previous_scene(instance->scene_manager);
         }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneStart);
     }
     return consumed;
 }

+ 3 - 0
nfc_magic/scenes/nfc_magic_scene_wrong_card.c

@@ -42,6 +42,9 @@ bool nfc_magic_scene_wrong_card_on_event(void* context, SceneManagerEvent event)
         if(event.event == GuiButtonTypeLeft) {
             consumed = scene_manager_previous_scene(instance->scene_manager);
         }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneStart);
     }
     return consumed;
 }