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

NFC Magic: gui fixes, code cleanup

Methodius 2 лет назад
Родитель
Сommit
6ee37bd121

+ 26 - 4
lib/magic/protocols/gen4/gen4_poller.c

@@ -181,6 +181,8 @@ NfcCommand gen4_poller_request_mode_handler(Gen4Poller* instance) {
         instance->state = Gen4PollerStateGetRevision;
         instance->state = Gen4PollerStateGetRevision;
     } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetShadowMode) {
     } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetShadowMode) {
         instance->state = Gen4PollerStateSetShadowMode;
         instance->state = Gen4PollerStateSetShadowMode;
+    } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetDirectWriteBlock0Mode) {
+        instance->state = Gen4PollerStateSetDirectWriteBlock0;
     } else {
     } else {
         instance->state = Gen4PollerStateFail;
         instance->state = Gen4PollerStateFail;
     }
     }
@@ -273,13 +275,13 @@ static NfcCommand gen4_poller_write_mf_classic(Gen4Poller* instance) {
                 break;
                 break;
             }
             }
 
 
-            instance->config[6] = Gen4PollerShadowModeIgnore;
+            instance->config[6] = Gen4PollerShadowModeDisabled;
             instance->config[24] = iso3_data->atqa[0];
             instance->config[24] = iso3_data->atqa[0];
             instance->config[25] = iso3_data->atqa[1];
             instance->config[25] = iso3_data->atqa[1];
             instance->config[26] = iso3_data->sak;
             instance->config[26] = iso3_data->sak;
             instance->config[27] = 0x00;
             instance->config[27] = 0x00;
             instance->config[28] = instance->total_blocks;
             instance->config[28] = instance->total_blocks;
-            instance->config[29] = 0x01;
+            instance->config[29] = Gen4PollerDirectWriteBlock0ModeDisabled;
 
 
             Gen4PollerError error = gen4_poller_set_config(
             Gen4PollerError error = gen4_poller_set_config(
                 instance, instance->password, instance->config, sizeof(instance->config), false);
                 instance, instance->password, instance->config, sizeof(instance->config), false);
@@ -352,13 +354,13 @@ static NfcCommand gen4_poller_write_mf_ultralight(Gen4Poller* instance) {
                 break;
                 break;
             }
             }
 
 
-            instance->config[6] = Gen4PollerShadowModeHighSpeedIgnore;
+            instance->config[6] = Gen4PollerShadowModeHighSpeedDisabled;
             instance->config[24] = iso3_data->atqa[0];
             instance->config[24] = iso3_data->atqa[0];
             instance->config[25] = iso3_data->atqa[1];
             instance->config[25] = iso3_data->atqa[1];
             instance->config[26] = iso3_data->sak;
             instance->config[26] = iso3_data->sak;
             instance->config[27] = 0x00;
             instance->config[27] = 0x00;
             instance->config[28] = instance->total_blocks;
             instance->config[28] = instance->total_blocks;
-            instance->config[29] = 0x01;
+            instance->config[29] = Gen4PollerDirectWriteBlock0ModeDisabled;
 
 
             Gen4PollerError error = gen4_poller_set_config(
             Gen4PollerError error = gen4_poller_set_config(
                 instance, instance->password, instance->config, sizeof(instance->config), false);
                 instance, instance->password, instance->config, sizeof(instance->config), false);
@@ -557,6 +559,25 @@ NfcCommand gen4_poller_set_shadow_mode_handler(Gen4Poller* instance) {
     return command;
     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 gen4_poller_success_handler(Gen4Poller* instance) {
     NfcCommand command = NfcCommandContinue;
     NfcCommand command = NfcCommandContinue;
 
 
@@ -592,6 +613,7 @@ static const Gen4PollerStateHandler gen4_poller_state_handlers[Gen4PollerStateNu
     [Gen4PollerStateGetCurrentConfig] = gen4_poller_get_current_cfg_handler,
     [Gen4PollerStateGetCurrentConfig] = gen4_poller_get_current_cfg_handler,
     [Gen4PollerStateGetRevision] = gen4_poller_get_revision_handler,
     [Gen4PollerStateGetRevision] = gen4_poller_get_revision_handler,
     [Gen4PollerStateSetShadowMode] = gen4_poller_set_shadow_mode_handler,
     [Gen4PollerStateSetShadowMode] = gen4_poller_set_shadow_mode_handler,
+    [Gen4PollerStateSetDirectWriteBlock0] = gen4_poller_set_direct_write_block_0_mode_handler,
     [Gen4PollerStateSuccess] = gen4_poller_success_handler,
     [Gen4PollerStateSuccess] = gen4_poller_success_handler,
     [Gen4PollerStateFail] = gen4_poller_fail_handler,
     [Gen4PollerStateFail] = gen4_poller_fail_handler,
 
 

+ 1 - 0
lib/magic/protocols/gen4/gen4_poller.h

@@ -42,6 +42,7 @@ typedef enum {
     Gen4PollerModeGetCfg,
     Gen4PollerModeGetCfg,
     Gen4PollerModeGetRevision,
     Gen4PollerModeGetRevision,
     Gen4PollerModeSetShadowMode,
     Gen4PollerModeSetShadowMode,
+    Gen4PollerModeSetDirectWriteBlock0Mode
 } Gen4PollerMode;
 } Gen4PollerMode;
 
 
 typedef struct {
 typedef struct {

+ 67 - 15
lib/magic/protocols/gen4/gen4_poller_i.c

@@ -1,10 +1,9 @@
 #include "gen4_poller_i.h"
 #include "gen4_poller_i.h"
 
 
 #include "bit_buffer.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/protocols/iso14443_3a/iso14443_3a_poller.h>
 #include <nfc/helpers/nfc_util.h>
 #include <nfc/helpers/nfc_util.h>
-#include <stdint.h>
 
 
 #define GEN4_CMD_PREFIX (0xCF)
 #define GEN4_CMD_PREFIX (0xCF)
 
 
@@ -13,13 +12,16 @@
 #define GEN4_CMD_GET_REVISION (0xCC)
 #define GEN4_CMD_GET_REVISION (0xCC)
 #define GEN4_CMD_WRITE (0xCD)
 #define GEN4_CMD_WRITE (0xCD)
 #define GEN4_CMD_READ (0xCE)
 #define GEN4_CMD_READ (0xCE)
+#define GEN4_CMD_SET_DW_BLOCK_0 (0xCF)
 #define GEN4_CMD_SET_CFG (0xF0)
 #define GEN4_CMD_SET_CFG (0xF0)
 #define GEN4_CMD_FUSE_CFG (0xF1)
 #define GEN4_CMD_FUSE_CFG (0xF1)
 #define GEN4_CMD_SET_PWD (0xFE)
 #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)
 #define REVISION_SIZE (5)
-#define SHD_MODE_RESPONSE_SIZE (2)
 
 
 static Gen4PollerError gen4_poller_process_error(Iso14443_3aError error) {
 static Gen4PollerError gen4_poller_process_error(Iso14443_3aError error) {
     Gen4PollerError ret = Gen4PollerErrorNone;
     Gen4PollerError ret = Gen4PollerErrorNone;
@@ -33,8 +35,10 @@ static Gen4PollerError gen4_poller_process_error(Iso14443_3aError error) {
     return ret;
     return ret;
 }
 }
 
 
-Gen4PollerError
-    gen4_poller_set_shadow_mode(Gen4Poller* instance, uint32_t password, uint8_t mode) {
+Gen4PollerError gen4_poller_set_shadow_mode(
+    Gen4Poller* instance,
+    uint32_t password,
+    Gen4PollerShadowMode mode) {
     Gen4PollerError ret = Gen4PollerErrorNone;
     Gen4PollerError ret = Gen4PollerErrorNone;
     bit_buffer_reset(instance->tx_buffer);
     bit_buffer_reset(instance->tx_buffer);
 
 
@@ -54,14 +58,51 @@ Gen4PollerError
             break;
             break;
         }
         }
 
 
-        size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
-        if(rx_bytes != SHD_MODE_RESPONSE_SIZE) {
+        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;
             ret = Gen4PollerErrorProtocol;
             break;
             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);
         uint16_t response = bit_buffer_get_size_bytes(instance->rx_buffer);
 
 
-        FURI_LOG_D(TAG, "Card response: %X, Shadow mode set: %u", response, mode);
+        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);
     } while(false);
 
 
@@ -90,11 +131,11 @@ Gen4PollerError
 
 
         size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
         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;
             ret = Gen4PollerErrorProtocol;
             break;
             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);
     } while(false);
 
 
     return ret;
     return ret;
@@ -157,8 +198,11 @@ Gen4PollerError gen4_poller_set_config(
             break;
             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;
             ret = Gen4PollerErrorProtocol;
             break;
             break;
         }
         }
@@ -225,8 +269,16 @@ Gen4PollerError
             break;
             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;
             ret = Gen4PollerErrorProtocol;
             break;
             break;
         }
         }

+ 24 - 5
lib/magic/protocols/gen4/gen4_poller_i.h

@@ -37,12 +37,23 @@ typedef enum {
     Gen4PollerShadowModePreWrite = 0x00,
     Gen4PollerShadowModePreWrite = 0x00,
     // written data can be read once before restored to original
     // written data can be read once before restored to original
     Gen4PollerShadowModeRestore = 0x01,
     Gen4PollerShadowModeRestore = 0x01,
-    // written data is discarded
-    Gen4PollerShadowModeIgnore = 0x02,
+    // shadow mode disabled
+    Gen4PollerShadowModeDisabled = 0x02,
     // apparently for UL?
     // apparently for UL?
-    Gen4PollerShadowModeHighSpeedIgnore = 0x03
+    Gen4PollerShadowModeHighSpeedDisabled = 0x03,
+    // work with new UMC. With old UMC is untested
+    Gen4PollerShadowModeSplit = 0x04,
 } Gen4PollerShadowMode;
 } Gen4PollerShadowMode;
 
 
+typedef enum {
+    // gen2 card behavour
+    Gen4PollerDirectWriteBlock0ModeEnabled = 0x00,
+    // common card behavour
+    Gen4PollerDirectWriteBlock0ModeDisabled = 0x01,
+    // default mode. same behavour as Gen4PollerDirectWriteBlock0ModeActivate
+    Gen4PollerDirectWriteBlock0ModeDefault = 0x02,
+} Gen4PollerDirectWriteBlock0Mode;
+
 typedef enum {
 typedef enum {
     Gen4PollerStateIdle,
     Gen4PollerStateIdle,
     Gen4PollerStateRequestMode,
     Gen4PollerStateRequestMode,
@@ -55,6 +66,7 @@ typedef enum {
     Gen4PollerStateGetCurrentConfig,
     Gen4PollerStateGetCurrentConfig,
     Gen4PollerStateGetRevision,
     Gen4PollerStateGetRevision,
     Gen4PollerStateSetShadowMode,
     Gen4PollerStateSetShadowMode,
+    Gen4PollerStateSetDirectWriteBlock0,
 
 
     Gen4PollerStateSuccess,
     Gen4PollerStateSuccess,
     Gen4PollerStateFail,
     Gen4PollerStateFail,
@@ -80,7 +92,8 @@ struct Gen4Poller {
 
 
     uint8_t config[GEN4_POLLER_CONFIG_SIZE_MAX];
     uint8_t config[GEN4_POLLER_CONFIG_SIZE_MAX];
 
 
-    uint8_t shadow_mode;
+    Gen4PollerShadowMode shadow_mode;
+    Gen4PollerDirectWriteBlock0Mode direct_write_block_0_mode;
 
 
     Gen4PollerEvent gen4_event;
     Gen4PollerEvent gen4_event;
     Gen4PollerEventData gen4_event_data;
     Gen4PollerEventData gen4_event_data;
@@ -111,7 +124,13 @@ Gen4PollerError
 Gen4PollerError
 Gen4PollerError
     gen4_poller_get_config(Gen4Poller* instance, uint32_t password, uint8_t* config_result);
     gen4_poller_get_config(Gen4Poller* instance, uint32_t password, uint8_t* config_result);
 
 
-Gen4PollerError gen4_poller_set_shadow_mode(Gen4Poller* instance, uint32_t password, uint8_t mode);
+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
 #ifdef __cplusplus
 }
 }

+ 2 - 0
scenes/nfc_magic_scene_config.h

@@ -12,6 +12,8 @@ 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_select_shd_mode, Gen4SelectShdMode)
 ADD_SCENE(nfc_magic, gen4_set_shd_mode, Gen4SetShdMode)
 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, gen4_fail, Gen4Fail)
 ADD_SCENE(nfc_magic, wipe, Wipe)
 ADD_SCENE(nfc_magic, wipe, Wipe)
 ADD_SCENE(nfc_magic, wipe_fail, WipeFail)
 ADD_SCENE(nfc_magic, wipe_fail, WipeFail)

+ 11 - 0
scenes/nfc_magic_scene_gen4_menu.c

@@ -5,6 +5,7 @@ enum SubmenuIndex {
     SubmenuIndexWrite,
     SubmenuIndexWrite,
     SubmenuIndexChangePassword,
     SubmenuIndexChangePassword,
     SubmenuIndexSetShadowMode,
     SubmenuIndexSetShadowMode,
+    SubmenuIndexSetDirectWriteBlock0Mode,
     SubmenuIndexGetRevision,
     SubmenuIndexGetRevision,
     SubmenuIndexGetConfig,
     SubmenuIndexGetConfig,
     SubmenuIndexWipe,
     SubmenuIndexWipe,
@@ -34,6 +35,12 @@ void nfc_magic_scene_gen4_menu_on_enter(void* context) {
         SubmenuIndexSetShadowMode,
         SubmenuIndexSetShadowMode,
         nfc_magic_scene_gen4_menu_submenu_callback,
         nfc_magic_scene_gen4_menu_submenu_callback,
         instance);
         instance);
+    submenu_add_item(
+        submenu,
+        "Set Gen2 Mode",
+        SubmenuIndexSetDirectWriteBlock0Mode,
+        nfc_magic_scene_gen4_menu_submenu_callback,
+        instance);
     submenu_add_item(
     submenu_add_item(
         submenu,
         submenu,
         "Get Revision",
         "Get Revision",
@@ -79,6 +86,10 @@ bool nfc_magic_scene_gen4_menu_on_event(void* context, SceneManagerEvent event)
         } else if(event.event == SubmenuIndexSetShadowMode) {
         } else if(event.event == SubmenuIndexSetShadowMode) {
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SelectShdMode);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SelectShdMode);
             consumed = true;
             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);
         scene_manager_set_scene_state(instance->scene_manager, NfcMagicSceneGen4Menu, event.event);

+ 96 - 0
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);
+}

+ 10 - 13
scenes/nfc_magic_scene_gen4_select_shd_mode.c

@@ -1,13 +1,6 @@
 #include "../nfc_magic_app_i.h"
 #include "../nfc_magic_app_i.h"
+#include "protocols/gen4/gen4_poller_i.h"
 
 
-/* SHADOW MODES DESCRIPTION:
-    00: pre-write, shadow data can be written
-    01: restore mode
-        WARNING: new UMC (06a0) cards return garbage data when using 01
-    02: disabled
-    03: disabled, high speed R/W mode for Ultralight?
-    04: split mode, work with new UMC. With old UMC is untested.
-*/
 enum SubmenuIndex {
 enum SubmenuIndex {
     SubmenuIndexPreWriteMode,
     SubmenuIndexPreWriteMode,
     SubmenuIndexRestoreMode,
     SubmenuIndexRestoreMode,
@@ -70,29 +63,33 @@ bool nfc_magic_scene_gen4_select_shd_mode_on_event(void* context, SceneManagerEv
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubmenuIndexPreWriteMode) {
         if(event.event == SubmenuIndexPreWriteMode) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
-                instance->scene_manager, NfcMagicSceneGen4SetShdMode, SubmenuIndexPreWriteMode);
+                instance->scene_manager,
+                NfcMagicSceneGen4SetShdMode,
+                Gen4PollerShadowModePreWrite);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexRestoreMode) {
         } else if(event.event == SubmenuIndexRestoreMode) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
-                instance->scene_manager, NfcMagicSceneGen4SetShdMode, SubmenuIndexRestoreMode);
+                instance->scene_manager, NfcMagicSceneGen4SetShdMode, Gen4PollerShadowModeRestore);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexDisable) {
         } else if(event.event == SubmenuIndexDisable) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
-                instance->scene_manager, NfcMagicSceneGen4SetShdMode, SubmenuIndexDisable);
+                instance->scene_manager,
+                NfcMagicSceneGen4SetShdMode,
+                Gen4PollerShadowModeDisabled);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexDisableHighSpeed) {
         } else if(event.event == SubmenuIndexDisableHighSpeed) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
                 instance->scene_manager,
                 instance->scene_manager,
                 NfcMagicSceneGen4SetShdMode,
                 NfcMagicSceneGen4SetShdMode,
-                SubmenuIndexDisableHighSpeed);
+                Gen4PollerShadowModeHighSpeedDisabled);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexSplitMode) {
         } else if(event.event == SubmenuIndexSplitMode) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
-                instance->scene_manager, NfcMagicSceneGen4SetShdMode, SubmenuIndexSplitMode);
+                instance->scene_manager, NfcMagicSceneGen4SetShdMode, Gen4PollerShadowModeSplit);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
             consumed = true;
             consumed = true;
         }
         }

+ 122 - 0
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);
+}

+ 0 - 4
scenes/nfc_magic_scene_gen4_set_shd_mode.c

@@ -1,9 +1,5 @@
 #include "../nfc_magic_app_i.h"
 #include "../nfc_magic_app_i.h"
-#include "applications_user/nfc_magic/lib/magic/protocols/gen4/gen4_poller.h"
 #include "applications_user/nfc_magic/lib/magic/protocols/gen4/gen4_poller_i.h"
 #include "applications_user/nfc_magic/lib/magic/protocols/gen4/gen4_poller_i.h"
-#include "core/log.h"
-#include <stdbool.h>
-#include <stdint.h>
 
 
 enum {
 enum {
     NfcMagicSceneGen4SetShadowModeStateCardSearch,
     NfcMagicSceneGen4SetShadowModeStateCardSearch,

+ 0 - 1
scenes/nfc_magic_scene_gen4_show_cfg.c

@@ -1,5 +1,4 @@
 #include "../nfc_magic_app_i.h"
 #include "../nfc_magic_app_i.h"
-#include "gui/scene_manager.h"
 
 
 #define CONFIG_SIZE (32)
 #define CONFIG_SIZE (32)