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

NFC Magic: New functions for Gen4 cards (DW to block 0/shadow mode) (#98)

Leptopt1los 1 год назад
Родитель
Сommit
e001e65c0b

+ 8 - 0
.catalog/changelog.md

@@ -1,3 +1,11 @@
+## 1.9
+ - Get revision, get config moved into main Gen4 menu
+ - New function: Set Gen4 card shadow mode
+ - New function: Set Gen4 card direct write to block 0 mode
+ - Fixed: back button did not allow to exit from some scenes while the card is next to the Flipper HF RFID antenna
+ - Gen4 protocol refactor
+ - Gen4 UI refactor
+
 ## 1.8
  - Ultralight Various fixes and improvements
 

BIN
.catalog/screenshots/1.png


+ 18 - 3
magic/nfc_magic_scanner.c

@@ -1,5 +1,7 @@
 #include "nfc_magic_scanner.h"
 
+#include "core/check.h"
+#include "protocols/gen4/gen4.h"
 #include "protocols/gen1a/gen1a_poller.h"
 #include "protocols/gen2/gen2_poller.h"
 #include "protocols/gen4/gen4_poller.h"
@@ -18,7 +20,8 @@ struct NfcMagicScanner {
     NfcMagicScannerSessionState session_state;
     NfcMagicProtocol current_protocol;
 
-    uint32_t gen4_password;
+    Gen4Password gen4_password;
+    Gen4* gen4_data;
     bool magic_protocol_detected;
 
     NfcMagicScannerCallback callback;
@@ -43,6 +46,7 @@ NfcMagicScanner* nfc_magic_scanner_alloc(Nfc* nfc) {
 
     NfcMagicScanner* instance = malloc(sizeof(NfcMagicScanner));
     instance->nfc = nfc;
+    instance->gen4_data = gen4_alloc();
 
     return instance;
 }
@@ -50,10 +54,11 @@ NfcMagicScanner* nfc_magic_scanner_alloc(Nfc* nfc) {
 void nfc_magic_scanner_free(NfcMagicScanner* instance) {
     furi_assert(instance);
 
+    gen4_free(instance->gen4_data);
     free(instance);
 }
 
-void nfc_magic_scanner_set_gen4_password(NfcMagicScanner* instance, uint32_t password) {
+void nfc_magic_scanner_set_gen4_password(NfcMagicScanner* instance, Gen4Password password) {
     furi_assert(instance);
 
     instance->gen4_password = password;
@@ -73,9 +78,13 @@ static int32_t nfc_magic_scanner_worker(void* context) {
                     break;
                 }
             } else if(instance->current_protocol == NfcMagicProtocolGen4) {
-                Gen4PollerError error = gen4_poller_detect(instance->nfc, instance->gen4_password);
+                gen4_reset(instance->gen4_data);
+                Gen4 gen4_data;
+                Gen4PollerError error =
+                    gen4_poller_detect(instance->nfc, instance->gen4_password, &gen4_data);
                 instance->magic_protocol_detected = (error == Gen4PollerErrorNone);
                 if(instance->magic_protocol_detected) {
+                    gen4_copy(instance->gen4_data, &gen4_data);
                     break;
                 }
             } else if(instance->current_protocol == NfcMagicProtocolGen2) {
@@ -162,3 +171,9 @@ void nfc_magic_scanner_stop(NfcMagicScanner* instance) {
     instance->callback = NULL;
     instance->context = NULL;
 }
+
+const Gen4* nfc_magic_scanner_get_gen4_data(NfcMagicScanner* instance) {
+    furi_assert(instance);
+
+    return instance->gen4_data;
+}

+ 4 - 1
magic/nfc_magic_scanner.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include "protocols/gen4/gen4.h"
 #include <nfc/nfc.h>
 #include "protocols/nfc_magic_protocols.h"
 
@@ -30,7 +31,7 @@ NfcMagicScanner* nfc_magic_scanner_alloc(Nfc* nfc);
 
 void nfc_magic_scanner_free(NfcMagicScanner* instance);
 
-void nfc_magic_scanner_set_gen4_password(NfcMagicScanner* instance, uint32_t password);
+void nfc_magic_scanner_set_gen4_password(NfcMagicScanner* instance, Gen4Password password);
 
 void nfc_magic_scanner_start(
     NfcMagicScanner* instance,
@@ -39,6 +40,8 @@ void nfc_magic_scanner_start(
 
 void nfc_magic_scanner_stop(NfcMagicScanner* instance);
 
+const Gen4* nfc_magic_scanner_get_gen4_data(NfcMagicScanner* instance);
+
 #ifdef __cplusplus
 }
 #endif

+ 120 - 0
magic/protocols/gen4/gen4.c

@@ -0,0 +1,120 @@
+#include "gen4.h"
+#include "core/check.h"
+
+Gen4* gen4_alloc() {
+    Gen4* instance = malloc(sizeof(Gen4));
+
+    return instance;
+}
+
+void gen4_free(Gen4* instance) {
+    furi_check(instance);
+
+    free(instance);
+}
+
+void gen4_reset(Gen4* instance) {
+    furi_check(instance);
+
+    memset(&instance->config, 0, sizeof(Gen4Config));
+    memset(&instance->revision, 0, sizeof(Gen4Revision));
+}
+
+void gen4_copy(Gen4* dest, const Gen4* source) {
+    furi_check(dest);
+    furi_check(source);
+
+    memcpy(dest, source, sizeof(Gen4));
+}
+
+bool gen4_password_is_set(const Gen4Password* instance) {
+    furi_check(instance);
+
+    return (instance->bytes[0] || instance->bytes[1] || instance->bytes[2] || instance->bytes[3]);
+}
+
+void gen4_password_reset(Gen4Password* instance) {
+    furi_check(instance);
+
+    memset(instance->bytes, 0, GEN4_PASSWORD_LEN);
+}
+
+void gen4_password_copy(Gen4Password* dest, const Gen4Password* source) {
+    furi_check(dest);
+    furi_check(source);
+
+    memcpy(dest->bytes, source->bytes, GEN4_PASSWORD_LEN);
+}
+
+const char* gen4_get_shadow_mode_name(Gen4ShadowMode mode) {
+    switch(mode) {
+    case Gen4ShadowModePreWrite:
+        return "Pre-Write";
+    case Gen4ShadowModeRestore:
+        return "Restore";
+    case Gen4ShadowModeDisabled:
+        return "Disabled";
+    case Gen4ShadowModeHighSpeedDisabled:
+        return "Disabled (High-speed)";
+    case Gen4ShadowModeSplit:
+        return "Split";
+    default:
+        return "Unknown";
+    }
+}
+
+const char* gen4_get_direct_write_mode_name(Gen4DirectWriteBlock0Mode mode) {
+    switch(mode) {
+    case Gen4DirectWriteBlock0ModeEnabled:
+        return "Enabled";
+    case Gen4DirectWriteBlock0ModeDisabled:
+        return "Disabled";
+    case Gen4DirectWriteBlock0ModeDefault:
+        return "Default";
+    default:
+        return "Unknown";
+    }
+}
+
+const char* gen4_get_uid_len_num(Gen4UIDLength code) {
+    switch(code) {
+    case Gen4UIDLengthSingle:
+        return "4";
+    case Gen4UIDLengthDouble:
+        return "7";
+    case Gen4UIDLengthTriple:
+        return "10";
+    default:
+        return "Unknown";
+    }
+}
+
+const char* gen4_get_configuration_name(const Gen4Config* config) {
+    switch(config->data_parsed.protocol) {
+    case Gen4ProtocolMfClassic: {
+        switch(config->data_parsed.total_blocks) {
+        case 255:
+            return "MIFARE Classic 4K";
+        case 63:
+            return "MIFARE Classic 1K";
+        case 19:
+            return "MIFARE Classic Mini (0.3K)";
+        default:
+            return "Unknown";
+        }
+    } break;
+    case Gen4ProtocolMfUltralight: {
+        switch(config->data_parsed.total_blocks) {
+        case 63:
+            return "MIFARE Ultralight";
+        case 127:
+            return "NTAG 2XX";
+        default:
+            return "Unknown";
+        }
+    } break;
+    default:
+        return "Unknown";
+        break;
+    };
+}

+ 105 - 0
magic/protocols/gen4/gen4.h

@@ -0,0 +1,105 @@
+#pragma once
+
+#include "core/common_defines.h"
+#include <stdint.h>
+
+#define GEN4_CONFIG_SIZE (32)
+#define GEN4_REVISION_SIZE (5)
+
+#define GEN4_PASSWORD_LEN (4)
+#define GEN4_ATS_MAX_LEN (16)
+#define GEN4_ATQA_LEN (2)
+#define GEN4_CRC_LEN (2)
+
+typedef enum {
+    Gen4ProtocolMfClassic = 0x00,
+    Gen4ProtocolMfUltralight = 0x01,
+} Gen4Protocol;
+
+typedef struct {
+    uint8_t bytes[GEN4_PASSWORD_LEN];
+} Gen4Password;
+
+typedef enum {
+    Gen4UIDLengthSingle = 0x00,
+    Gen4UIDLengthDouble = 0x01,
+    Gen4UIDLengthTriple = 0x02
+} Gen4UIDLength;
+
+typedef enum {
+    Gen4UltralightModeUL_EV1 = 0x00,
+    Gen4UltralightModeNTAG = 0x01,
+    Gen4UltralightModeUL_C = 0x02,
+    Gen4UltralightModeUL = 0x03
+} Gen4UltralightMode;
+
+typedef enum {
+    // for writing original (shadow) data
+    Gen4ShadowModePreWrite = 0x00,
+    // written data can be read once before restored to original
+    Gen4ShadowModeRestore = 0x01,
+    // shadow mode disabled
+    Gen4ShadowModeDisabled = 0x02,
+    // apparently for UL?
+    Gen4ShadowModeHighSpeedDisabled = 0x03,
+    // work with new UMC. With old UMC is untested
+    Gen4ShadowModeSplit = 0x04,
+} Gen4ShadowMode;
+
+typedef enum {
+    // gen2 card behavour
+    Gen4DirectWriteBlock0ModeEnabled = 0x00,
+    // common card behavour
+    Gen4DirectWriteBlock0ModeDisabled = 0x01,
+    // default mode. same behavour as Gen4DirectWriteBlock0ModeActivate
+    Gen4DirectWriteBlock0ModeDefault = 0x02,
+} Gen4DirectWriteBlock0Mode;
+
+typedef union {
+    uint8_t data_raw[GEN4_CONFIG_SIZE];
+    struct {
+        Gen4Protocol protocol;
+        Gen4UIDLength uid_len_code;
+        Gen4Password password;
+        Gen4ShadowMode gtu_mode;
+        uint8_t ats_len;
+        uint8_t ats[GEN4_ATS_MAX_LEN]; // mb another class?
+        uint8_t atqa[GEN4_ATQA_LEN];
+        uint8_t sak;
+        Gen4UltralightMode mfu_mode;
+        uint8_t total_blocks;
+        Gen4DirectWriteBlock0Mode direct_write_mode;
+        uint8_t crc[GEN4_CRC_LEN];
+    } FURI_PACKED data_parsed;
+} Gen4Config;
+
+typedef struct {
+    uint8_t data[GEN4_REVISION_SIZE];
+} Gen4Revision;
+
+typedef struct {
+    Gen4Config config;
+    Gen4Revision revision;
+} Gen4;
+
+Gen4* gen4_alloc();
+
+void gen4_free(Gen4* instance);
+
+void gen4_reset(Gen4* instance);
+
+void gen4_copy(Gen4* dest, const Gen4* source);
+
+bool gen4_password_is_set(const Gen4Password* instance);
+
+void gen4_password_reset(Gen4Password* instance);
+
+void gen4_password_copy(Gen4Password* dest, const Gen4Password* source);
+
+const char* gen4_get_shadow_mode_name(Gen4ShadowMode mode);
+
+const char* gen4_get_direct_write_mode_name(Gen4DirectWriteBlock0Mode mode);
+
+const char* gen4_get_uid_len_num(Gen4UIDLength code);
+
+const char* gen4_get_configuration_name(const Gen4Config* config);

+ 211 - 67
magic/protocols/gen4/gen4_poller.c

@@ -1,30 +1,34 @@
-#include "core/log.h"
+#include "bit_buffer.h"
+#include "core/check.h"
 #include "gen4_poller_i.h"
+#include "magic/protocols/gen4/gen4.h"
+#include "magic/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 <bit_lib/bit_lib.h>
 #include <nfc/nfc_poller.h>
-
-#include <furi/furi.h>
+#include <bit_lib.h>
+#include <string.h>
 
 #define GEN4_POLLER_THREAD_FLAG_DETECTED (1U << 0)
+#define GEN4_POLLER_DEFAULT_CONFIG_SIZE (28)
 
 typedef NfcCommand (*Gen4PollerStateHandler)(Gen4Poller* instance);
 
 typedef struct {
     NfcPoller* poller;
-    uint32_t password;
+    Gen4Password password;
+    Gen4 gen4_data;
     BitBuffer* tx_buffer;
     BitBuffer* rx_buffer;
     FuriThreadId thread_id;
     Gen4PollerError error;
 } Gen4PollerDetectContext;
 
-static const uint8_t gen4_poller_default_config[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-                                                     0x00, 0x09, 0x78, 0x00, 0x91, 0x02, 0xDA,
-                                                     0xBC, 0x19, 0x10, 0x10, 0x11, 0x12, 0x13,
-                                                     0x14, 0x15, 0x16, 0x04, 0x00, 0x08, 0x00};
+static const Gen4Config gen4_poller_default_config = {
+    .data_raw = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x78,
+                 0x00, 0x91, 0x02, 0xDA, 0xBC, 0x19, 0x10, 0x10, 0x11, 0x12,
+                 0x13, 0x14, 0x15, 0x16, 0x04, 0x00, 0x08, 0x00}};
+
 static const uint8_t gen4_poller_default_block_0[GEN4_POLLER_BLOCK_SIZE] =
     {0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
@@ -56,6 +60,8 @@ Gen4Poller* gen4_poller_alloc(Nfc* nfc) {
     instance->tx_buffer = bit_buffer_alloc(GEN4_POLLER_MAX_BUFFER_SIZE);
     instance->rx_buffer = bit_buffer_alloc(GEN4_POLLER_MAX_BUFFER_SIZE);
 
+    instance->gen4_data = gen4_alloc();
+
     return instance;
 }
 
@@ -67,10 +73,12 @@ void gen4_poller_free(Gen4Poller* instance) {
     bit_buffer_free(instance->tx_buffer);
     bit_buffer_free(instance->rx_buffer);
 
+    gen4_free(instance->gen4_data);
+
     free(instance);
 }
 
-void gen4_poller_set_password(Gen4Poller* instance, uint32_t password) {
+void gen4_poller_set_password(Gen4Poller* instance, Gen4Password password) {
     furi_assert(instance);
 
     instance->password = password;
@@ -90,10 +98,12 @@ NfcCommand gen4_poller_detect_callback(NfcGenericEvent event, void* context) {
 
     if(iso3_event->type == Iso14443_3aPollerEventTypeReady) {
         do {
+            // check config
             bit_buffer_append_byte(gen4_poller_detect_ctx->tx_buffer, GEN4_CMD_PREFIX);
-            uint8_t pwd_arr[4] = {};
-            bit_lib_num_to_bytes_be(gen4_poller_detect_ctx->password, COUNT_OF(pwd_arr), pwd_arr);
-            bit_buffer_append_bytes(gen4_poller_detect_ctx->tx_buffer, pwd_arr, COUNT_OF(pwd_arr));
+            bit_buffer_append_bytes(
+                gen4_poller_detect_ctx->tx_buffer,
+                gen4_poller_detect_ctx->password.bytes,
+                GEN4_PASSWORD_LEN);
             bit_buffer_append_byte(gen4_poller_detect_ctx->tx_buffer, GEN4_CMD_GET_CFG);
 
             Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
@@ -107,11 +117,48 @@ NfcCommand gen4_poller_detect_callback(NfcGenericEvent event, void* context) {
                 break;
             }
             size_t rx_bytes = bit_buffer_get_size_bytes(gen4_poller_detect_ctx->rx_buffer);
-            if((rx_bytes != 30) && (rx_bytes != 32)) {
+            if(rx_bytes != GEN4_CONFIG_SIZE) {
+                gen4_poller_detect_ctx->error = Gen4PollerErrorProtocol;
+                break;
+            }
+
+            memcpy(
+                gen4_poller_detect_ctx->gen4_data.config.data_raw,
+                bit_buffer_get_data(gen4_poller_detect_ctx->rx_buffer),
+                GEN4_CONFIG_SIZE);
+
+            // check revision
+            bit_buffer_reset(gen4_poller_detect_ctx->tx_buffer);
+            bit_buffer_reset(gen4_poller_detect_ctx->rx_buffer);
+
+            bit_buffer_append_byte(gen4_poller_detect_ctx->tx_buffer, GEN4_CMD_PREFIX);
+            bit_buffer_append_bytes(
+                gen4_poller_detect_ctx->tx_buffer,
+                gen4_poller_detect_ctx->password.bytes,
+                GEN4_PASSWORD_LEN);
+            bit_buffer_append_byte(gen4_poller_detect_ctx->tx_buffer, GEN4_CMD_GET_REVISION);
+
+            error = iso14443_3a_poller_send_standard_frame(
+                iso3_poller,
+                gen4_poller_detect_ctx->tx_buffer,
+                gen4_poller_detect_ctx->rx_buffer,
+                GEN4_POLLER_MAX_FWT);
+
+            if(error != Iso14443_3aErrorNone) {
+                gen4_poller_detect_ctx->error = Gen4PollerErrorProtocol;
+                break;
+            }
+            rx_bytes = bit_buffer_get_size_bytes(gen4_poller_detect_ctx->rx_buffer);
+            if(rx_bytes != GEN4_REVISION_SIZE) {
                 gen4_poller_detect_ctx->error = Gen4PollerErrorProtocol;
                 break;
             }
 
+            memcpy(
+                gen4_poller_detect_ctx->gen4_data.revision.data,
+                bit_buffer_get_data(gen4_poller_detect_ctx->rx_buffer),
+                GEN4_REVISION_SIZE);
+
             gen4_poller_detect_ctx->error = Gen4PollerErrorNone;
         } while(false);
     } else if(iso3_event->type == Iso14443_3aPollerEventTypeError) {
@@ -122,7 +169,7 @@ NfcCommand gen4_poller_detect_callback(NfcGenericEvent event, void* context) {
     return command;
 }
 
-Gen4PollerError gen4_poller_detect(Nfc* nfc, uint32_t password) {
+Gen4PollerError gen4_poller_detect(Nfc* nfc, Gen4Password password, Gen4* gen4_data) {
     furi_assert(nfc);
 
     Gen4PollerDetectContext gen4_poller_detect_ctx = {};
@@ -146,6 +193,10 @@ Gen4PollerError gen4_poller_detect(Nfc* nfc, uint32_t password) {
     bit_buffer_free(gen4_poller_detect_ctx.tx_buffer);
     bit_buffer_free(gen4_poller_detect_ctx.rx_buffer);
 
+    if(gen4_poller_detect_ctx.error == Gen4PollerErrorNone) {
+        gen4_copy(gen4_data, &gen4_poller_detect_ctx.gen4_data);
+    }
+
     return gen4_poller_detect_ctx.error;
 }
 
@@ -153,7 +204,7 @@ NfcCommand gen4_poller_idle_handler(Gen4Poller* instance) {
     NfcCommand command = NfcCommandContinue;
 
     instance->current_block = 0;
-    memset(instance->config, 0, sizeof(instance->config));
+
     instance->gen4_event.type = Gen4PollerEventTypeCardDetected;
     command = instance->callback(instance->gen4_event, instance->context);
     instance->state = Gen4PollerStateRequestMode;
@@ -172,12 +223,14 @@ 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 == Gen4PollerModeGetInfo) {
+        instance->state = Gen4PollerStateGetInfo;
+    } else if(instance->gen4_event_data.request_mode.mode == Gen4PollerModeSetDefaultCfg) {
         instance->state = Gen4PollerStateSetDefaultConfig;
-    } 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;
     }
@@ -194,15 +247,15 @@ NfcCommand gen4_poller_wipe_handler(Gen4Poller* instance) {
             error = gen4_poller_set_config(
                 instance,
                 instance->password,
-                gen4_poller_default_config,
-                sizeof(gen4_poller_default_config),
+                &gen4_poller_default_config,
+                GEN4_POLLER_DEFAULT_CONFIG_SIZE,
                 false);
             if(error != Gen4PollerErrorNone) {
                 FURI_LOG_D(TAG, "Failed to set default config: %d", error);
                 instance->state = Gen4PollerStateFail;
                 break;
             }
-            instance->password = 0;
+            gen4_password_reset(&instance->password);
             error = gen4_poller_write_block(
                 instance, instance->password, instance->current_block, gen4_poller_default_block_0);
             if(error != Gen4PollerErrorNone) {
@@ -257,29 +310,29 @@ static NfcCommand gen4_poller_write_mf_classic(Gen4Poller* instance) {
         const MfClassicData* mfc_data = instance->data;
         const Iso14443_3aData* iso3_data = mfc_data->iso14443_3a_data;
         if(instance->current_block == 0) {
-            instance->config[0] = 0x00;
+            instance->config.data_parsed.protocol = Gen4ProtocolMfClassic;
             instance->total_blocks = mf_classic_get_total_block_num(mfc_data->type);
 
             if(iso3_data->uid_len == 4) {
-                instance->config[1] = Gen4PollerUIDLengthSingle;
+                instance->config.data_parsed.uid_len_code = Gen4UIDLengthSingle;
             } else if(iso3_data->uid_len == 7) {
-                instance->config[1] = Gen4PollerUIDLengthDouble;
+                instance->config.data_parsed.uid_len_code = Gen4UIDLengthDouble;
             } else {
                 FURI_LOG_E(TAG, "Unsupported UID len: %d", iso3_data->uid_len);
                 instance->state = Gen4PollerStateFail;
                 break;
             }
 
-            instance->config[6] = Gen4PollerShadowModeIgnore;
-            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.data_parsed.gtu_mode = Gen4ShadowModeDisabled;
+            instance->config.data_parsed.atqa[0] = iso3_data->atqa[0];
+            instance->config.data_parsed.atqa[1] = iso3_data->atqa[1];
+            instance->config.data_parsed.sak = iso3_data->sak;
+            instance->config.data_parsed.mfu_mode = Gen4UltralightModeUL_EV1;
+            instance->config.data_parsed.total_blocks = instance->total_blocks - 1;
+            instance->config.data_parsed.direct_write_mode = Gen4DirectWriteBlock0ModeDisabled;
 
             Gen4PollerError error = gen4_poller_set_config(
-                instance, instance->password, instance->config, sizeof(instance->config), false);
+                instance, instance->password, &instance->config, GEN4_CONFIG_SIZE, false);
             if(error != Gen4PollerErrorNone) {
                 FURI_LOG_D(TAG, "Failed to write config: %d", error);
                 instance->state = Gen4PollerStateFail;
@@ -316,7 +369,7 @@ static NfcCommand gen4_poller_write_mf_ultralight(Gen4Poller* instance) {
         const Iso14443_3aData* iso3_data = mfu_data->iso14443_3a_data;
         if(instance->current_block == 0) {
             instance->total_blocks = 64;
-            instance->config[0] = 0x01;
+            instance->config.data_parsed.protocol = Gen4ProtocolMfUltralight;
             switch(mfu_data->type) {
             case MfUltralightTypeNTAG203:
             case MfUltralightTypeNTAG213:
@@ -327,47 +380,47 @@ static NfcCommand gen4_poller_write_mf_ultralight(Gen4Poller* instance) {
             case MfUltralightTypeNTAGI2CPlus1K:
             case MfUltralightTypeNTAGI2CPlus2K:
                 FURI_LOG_D(TAG, "NTAG type");
-                instance->config[27] = Gen4PollerUltralightModeNTAG;
+                instance->config.data_parsed.mfu_mode = Gen4UltralightModeNTAG;
                 instance->total_blocks = 64 * 2;
                 break;
 
             case MfUltralightTypeUnknown:
                 FURI_LOG_D(TAG, "Ultralight type");
-                instance->config[27] = Gen4PollerUltralightModeUL;
+                instance->config.data_parsed.mfu_mode = Gen4UltralightModeUL;
                 break;
 
             case MfUltralightTypeMfulC:
                 FURI_LOG_D(TAG, "MfulC type");
-                instance->config[27] = Gen4PollerUltralightModeUL_C;
+                instance->config.data_parsed.mfu_mode = Gen4UltralightModeUL_C;
                 break;
 
             case MfUltralightTypeUL11:
             case MfUltralightTypeUL21:
             default:
                 FURI_LOG_D(TAG, "EV1 type");
-                instance->config[27] = Gen4PollerUltralightModeUL_EV1;
+                instance->config.data_parsed.mfu_mode = Gen4UltralightModeUL_EV1;
                 break;
             }
 
             if(iso3_data->uid_len == 4) {
-                instance->config[1] = Gen4PollerUIDLengthSingle;
+                instance->config.data_parsed.uid_len_code = Gen4UIDLengthSingle;
             } else if(iso3_data->uid_len == 7) {
-                instance->config[1] = Gen4PollerUIDLengthDouble;
+                instance->config.data_parsed.uid_len_code = Gen4UIDLengthDouble;
             } else {
                 FURI_LOG_E(TAG, "Unsupported UID len: %d", iso3_data->uid_len);
                 instance->state = Gen4PollerStateFail;
                 break;
             }
 
-            instance->config[6] = Gen4PollerShadowModeHighSpeedIgnore;
-            instance->config[24] = iso3_data->atqa[0];
-            instance->config[25] = iso3_data->atqa[1];
-            instance->config[26] = iso3_data->sak;
-            instance->config[28] = instance->total_blocks - 1;
-            instance->config[29] = 0x01;
+            instance->config.data_parsed.gtu_mode = Gen4ShadowModeHighSpeedDisabled;
+            instance->config.data_parsed.atqa[0] = iso3_data->atqa[0];
+            instance->config.data_parsed.atqa[1] = iso3_data->atqa[1];
+            instance->config.data_parsed.sak = iso3_data->sak;
+            instance->config.data_parsed.total_blocks = instance->total_blocks - 1;
+            instance->config.data_parsed.direct_write_mode = Gen4DirectWriteBlock0ModeDisabled;
 
             Gen4PollerError error = gen4_poller_set_config(
-                instance, instance->password, instance->config, sizeof(instance->config), false);
+                instance, instance->password, &instance->config, GEN4_CONFIG_SIZE, false);
             if(error != Gen4PollerErrorNone) {
                 FURI_LOG_D(TAG, "Failed to write config: %d", error);
                 instance->state = Gen4PollerStateFail;
@@ -502,11 +555,14 @@ static NfcCommand gen4_poller_write_mf_ultralight(Gen4Poller* instance) {
 NfcCommand gen4_poller_write_handler(Gen4Poller* instance) {
     NfcCommand command = NfcCommandContinue;
 
-    memcpy(instance->config, gen4_poller_default_config, sizeof(gen4_poller_default_config));
-    uint8_t password_arr[4] = {};
-    bit_lib_num_to_bytes_be(instance->password, sizeof(password_arr), password_arr);
-    memcpy(&instance->config[2], password_arr, sizeof(password_arr));
-    memset(&instance->config[7], 0, 17);
+    memcpy(
+        instance->config.data_raw,
+        gen4_poller_default_config.data_raw,
+        GEN4_POLLER_DEFAULT_CONFIG_SIZE);
+
+    memcpy(
+        instance->config.data_parsed.password.bytes, instance->password.bytes, GEN4_PASSWORD_LEN);
+    memset(&instance->config.data_raw[7], 0, 17);
     if(instance->protocol == NfcProtocolMfClassic) {
         command = gen4_poller_write_mf_classic(instance);
     } else if(instance->protocol == NfcProtocolMfUltralight) {
@@ -526,7 +582,7 @@ NfcCommand gen4_poller_change_password_handler(Gen4Poller* instance) {
         command = instance->callback(instance->gen4_event, instance->context);
         if(command != NfcCommandContinue) break;
 
-        uint32_t new_password = instance->gen4_event_data.request_password.password;
+        Gen4Password new_password = instance->gen4_event_data.request_password.password;
         Gen4PollerError error =
             gen4_poller_change_password(instance, instance->password, new_password);
         if(error != Gen4PollerErrorNone) {
@@ -549,8 +605,8 @@ NfcCommand gen4_poller_set_default_cfg_handler(Gen4Poller* instance) {
         Gen4PollerError error = gen4_poller_set_config(
             instance,
             instance->password,
-            gen4_poller_default_config,
-            sizeof(gen4_poller_default_config),
+            &gen4_poller_default_config,
+            GEN4_POLLER_DEFAULT_CONFIG_SIZE,
             false);
         if(error != Gen4PollerErrorNone) {
             FURI_LOG_E(TAG, "Failed to set default config: %d", error);
@@ -568,16 +624,16 @@ NfcCommand gen4_poller_get_current_cfg_handler(Gen4Poller* instance) {
     NfcCommand command = NfcCommandContinue;
 
     do {
-        uint8_t the_config[32] = {};
+        Gen4Config config = {};
 
-        Gen4PollerError error = gen4_poller_get_config(instance, instance->password, the_config);
+        Gen4PollerError error = gen4_poller_get_config(instance, instance->password, &config);
         if(error != Gen4PollerErrorNone) {
             FURI_LOG_E(TAG, "Failed to get current config: %d", error);
             instance->state = Gen4PollerStateFail;
             break;
         }
         // Copy config data to event data buffer
-        memcpy(instance->gen4_event_data.display_config, the_config, sizeof(the_config));
+        memcpy(instance->gen4_data->config.data_raw, config.data_raw, sizeof(config));
 
         instance->state = Gen4PollerStateSuccess;
     } while(false);
@@ -589,16 +645,83 @@ NfcCommand gen4_poller_get_revision_handler(Gen4Poller* instance) {
     NfcCommand command = NfcCommandContinue;
 
     do {
-        uint8_t the_revision[5] = {0};
-        Gen4PollerError error =
-            gen4_poller_get_revision(instance, instance->password, the_revision);
+        Gen4Revision revision = {};
+        Gen4PollerError error = gen4_poller_get_revision(instance, instance->password, &revision);
         if(error != Gen4PollerErrorNone) {
             FURI_LOG_E(TAG, "Failed to get revision: %d", error);
             instance->state = Gen4PollerStateFail;
             break;
         }
         // Copy revision data to event data buffer
-        memcpy(instance->gen4_event_data.revision_data, the_revision, sizeof(the_revision));
+        memcpy(instance->gen4_data->revision.data, revision.data, sizeof(revision));
+
+        instance->state = Gen4PollerStateSuccess;
+    } while(false);
+
+    return command;
+}
+
+NfcCommand gen4_poller_get_info_handler(Gen4Poller* instance) {
+    NfcCommand command = NfcCommandContinue;
+
+    do {
+        Gen4 gen4_data;
+
+        Gen4PollerError error =
+            gen4_poller_get_revision(instance, instance->password, &gen4_data.revision);
+        if(error != Gen4PollerErrorNone) {
+            FURI_LOG_E(TAG, "Failed to get revision: %d", error);
+            instance->state = Gen4PollerStateFail;
+            break;
+        }
+
+        error = gen4_poller_get_config(instance, instance->password, &gen4_data.config);
+        if(error != Gen4PollerErrorNone) {
+            FURI_LOG_E(TAG, "Failed to get current config: %d", error);
+            instance->state = Gen4PollerStateFail;
+            break;
+        }
+
+        // Copy config&&revision data to event data buffer
+        gen4_copy(instance->gen4_data, &gen4_data);
+
+        instance->state = Gen4PollerStateSuccess;
+    } while(false);
+
+    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);
@@ -637,9 +760,10 @@ static const Gen4PollerStateHandler gen4_poller_state_handlers[Gen4PollerStateNu
     [Gen4PollerStateWrite] = gen4_poller_write_handler,
     [Gen4PollerStateWipe] = gen4_poller_wipe_handler,
     [Gen4PollerStateChangePassword] = gen4_poller_change_password_handler,
+    [Gen4PollerStateGetInfo] = gen4_poller_get_info_handler,
     [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,
 
@@ -678,3 +802,23 @@ void gen4_poller_stop(Gen4Poller* instance) {
 
     nfc_poller_stop(instance->poller);
 }
+
+const Gen4* gen4_poller_get_gen4_data(const Gen4Poller* instance) {
+    furi_assert(instance);
+
+    return instance->gen4_data;
+}
+
+void gen4_poller_struct_set_direct_write_block_0_mode(
+    Gen4Poller* instance,
+    Gen4DirectWriteBlock0Mode mode) {
+    furi_assert(instance);
+
+    instance->direct_write_block_0_mode = mode;
+}
+
+void gen4_poller_struct_set_shadow_mode(Gen4Poller* instance, Gen4ShadowMode mode) {
+    furi_assert(instance);
+
+    instance->shadow_mode = mode;
+}

+ 19 - 9
magic/protocols/gen4/gen4_poller.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include "gen4.h"
 #include <nfc/nfc.h>
 #include <nfc/protocols/nfc_protocol.h>
 #include <nfc/protocols/mf_classic/mf_classic.h>
@@ -9,8 +10,10 @@
 extern "C" {
 #endif
 
+// TODO: cleanup, check gen4_poller_i.c defines
 #define GEN4_CMD_PREFIX (0xCF)
 #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_CFG (0xF0)
@@ -38,9 +41,11 @@ typedef enum {
     Gen4PollerModeWrite,
     Gen4PollerModeSetPassword,
 
-    Gen4PollerModeSetDefaultCFG,
-    Gen4PollerModeGetCFG,
-    Gen4PollerModeGetRevision,
+    Gen4PollerModeGetInfo,
+
+    Gen4PollerModeSetDefaultCfg,
+    Gen4PollerModeSetShadowMode,
+    Gen4PollerModeSetDirectWriteBlock0Mode
 } Gen4PollerMode;
 
 typedef struct {
@@ -53,16 +58,13 @@ typedef struct {
 } Gen4PollerEventDataRequestDataToWrite;
 
 typedef struct {
-    uint32_t password;
+    Gen4Password password;
 } Gen4PollerEventDataRequestNewPassword;
 
 typedef union {
     Gen4PollerEventDataRequestMode request_mode;
     Gen4PollerEventDataRequestDataToWrite request_data;
     Gen4PollerEventDataRequestNewPassword request_password;
-
-    uint8_t display_config[32];
-    uint8_t revision_data[5];
 } Gen4PollerEventData;
 
 typedef struct {
@@ -74,18 +76,26 @@ typedef NfcCommand (*Gen4PollerCallback)(Gen4PollerEvent event, void* context);
 
 typedef struct Gen4Poller Gen4Poller;
 
-Gen4PollerError gen4_poller_detect(Nfc* nfc, uint32_t password);
+Gen4PollerError gen4_poller_detect(Nfc* nfc, Gen4Password password, Gen4* gen4_data);
 
 Gen4Poller* gen4_poller_alloc(Nfc* nfc);
 
 void gen4_poller_free(Gen4Poller* instance);
 
-void gen4_poller_set_password(Gen4Poller* instance, uint32_t password);
+void gen4_poller_set_password(Gen4Poller* instance, Gen4Password password);
 
 void gen4_poller_start(Gen4Poller* instance, Gen4PollerCallback callback, void* context);
 
 void gen4_poller_stop(Gen4Poller* instance);
 
+const Gen4* gen4_poller_get_gen4_data(const Gen4Poller* instance);
+
+void gen4_poller_struct_set_direct_write_block_0_mode(
+    Gen4Poller* instance,
+    Gen4DirectWriteBlock0Mode mode);
+
+void gen4_poller_struct_set_shadow_mode(Gen4Poller* instance, Gen4ShadowMode mode);
+
 #ifdef __cplusplus
 }
 #endif

+ 117 - 37
magic/protocols/gen4/gen4_poller_i.c

@@ -1,21 +1,22 @@
 #include "gen4_poller_i.h"
 
 #include "bit_buffer.h"
-#include "core/log.h"
+#include "magic/protocols/gen4/gen4_poller.h"
 #include <nfc/protocols/iso14443_3a/iso14443_3a_poller.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 REVISION_SIZE (5)
+#define GEN4_RESPONSE_SUCCESS (0x02)
 
 static Gen4PollerError gen4_poller_process_error(Iso14443_3aError error) {
     Gen4PollerError ret = Gen4PollerErrorNone;
@@ -30,15 +31,81 @@ static Gen4PollerError gen4_poller_process_error(Iso14443_3aError error) {
 }
 
 Gen4PollerError
-    gen4_poller_get_config(Gen4Poller* instance, uint32_t password, uint8_t* config_result) {
+    gen4_poller_set_shadow_mode(Gen4Poller* instance, Gen4Password password, Gen4ShadowMode mode) {
     Gen4PollerError ret = Gen4PollerErrorNone;
     bit_buffer_reset(instance->tx_buffer);
 
     do {
-        uint8_t password_arr[4] = {};
-        bit_lib_num_to_bytes_be(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_bytes(instance->tx_buffer, password.bytes, GEN4_PASSWORD_LEN);
+        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;
+        }
+
+        size_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 != GEN4_RESPONSE_SUCCESS) {
+            ret = Gen4PollerErrorProtocol;
+            break;
+        }
+
+    } while(false);
+
+    return ret;
+}
+
+Gen4PollerError gen4_poller_set_direct_write_block_0_mode(
+    Gen4Poller* instance,
+    Gen4Password password,
+    Gen4DirectWriteBlock0Mode mode) {
+    Gen4PollerError ret = Gen4PollerErrorNone;
+    bit_buffer_reset(instance->tx_buffer);
+
+    do {
+        bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_PREFIX);
+        bit_buffer_append_bytes(instance->tx_buffer, password.bytes, GEN4_PASSWORD_LEN);
+        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;
+        }
+        size_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 != GEN4_RESPONSE_SUCCESS) {
+            ret = Gen4PollerErrorProtocol;
+            break;
+        }
+
+    } while(false);
+
+    return ret;
+}
+
+Gen4PollerError
+    gen4_poller_get_config(Gen4Poller* instance, Gen4Password password, Gen4Config* config_result) {
+    Gen4PollerError ret = Gen4PollerErrorNone;
+    bit_buffer_reset(instance->tx_buffer);
+
+    do {
+        bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_PREFIX);
+        bit_buffer_append_bytes(instance->tx_buffer, password.bytes, GEN4_PASSWORD_LEN);
         bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_GET_CFG);
 
         Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
@@ -51,26 +118,26 @@ Gen4PollerError
 
         size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
 
-        if(rx_bytes != CONFIG_SIZE) {
+        if(rx_bytes != GEN4_CONFIG_SIZE) {
             ret = Gen4PollerErrorProtocol;
             break;
         }
-        bit_buffer_write_bytes(instance->rx_buffer, config_result, CONFIG_SIZE);
+        bit_buffer_write_bytes(instance->rx_buffer, config_result->data_raw, GEN4_CONFIG_SIZE);
     } while(false);
 
     return ret;
 }
 
-Gen4PollerError
-    gen4_poller_get_revision(Gen4Poller* instance, uint32_t password, uint8_t* revision_result) {
+Gen4PollerError gen4_poller_get_revision(
+    Gen4Poller* instance,
+    Gen4Password password,
+    Gen4Revision* revision_result) {
     Gen4PollerError ret = Gen4PollerErrorNone;
     bit_buffer_reset(instance->tx_buffer);
 
     do {
-        uint8_t password_arr[4] = {};
-        bit_lib_num_to_bytes_be(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_bytes(instance->tx_buffer, password.bytes, GEN4_PASSWORD_LEN);
         bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_GET_REVISION);
 
         Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
@@ -82,11 +149,11 @@ Gen4PollerError
         }
 
         size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
-        if(rx_bytes != 5) {
+        if(rx_bytes != GEN4_REVISION_SIZE) {
             ret = Gen4PollerErrorProtocol;
             break;
         }
-        bit_buffer_write_bytes(instance->rx_buffer, revision_result, REVISION_SIZE);
+        bit_buffer_write_bytes(instance->rx_buffer, revision_result->data, GEN4_REVISION_SIZE);
     } while(false);
 
     return ret;
@@ -94,21 +161,19 @@ Gen4PollerError
 
 Gen4PollerError gen4_poller_set_config(
     Gen4Poller* instance,
-    uint32_t password,
-    const uint8_t* config,
+    Gen4Password password,
+    const Gen4Config* config,
     size_t config_size,
     bool fuse) {
     Gen4PollerError ret = Gen4PollerErrorNone;
     bit_buffer_reset(instance->tx_buffer);
 
     do {
-        uint8_t password_arr[4] = {};
-        bit_lib_num_to_bytes_be(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_bytes(instance->tx_buffer, password.bytes, GEN4_PASSWORD_LEN);
         uint8_t fuse_config = fuse ? GEN4_CMD_FUSE_CFG : GEN4_CMD_SET_CFG;
         bit_buffer_append_byte(instance->tx_buffer, fuse_config);
-        bit_buffer_append_bytes(instance->tx_buffer, config, config_size);
+        bit_buffer_append_bytes(instance->tx_buffer, config->data_raw, config_size);
 
         Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
             instance->iso3_poller, instance->tx_buffer, instance->rx_buffer, GEN4_POLLER_MAX_FWT);
@@ -118,8 +183,11 @@ Gen4PollerError gen4_poller_set_config(
             break;
         }
 
-        size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
-        if(rx_bytes != 2) {
+        size_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 != GEN4_RESPONSE_SUCCESS) {
             ret = Gen4PollerErrorProtocol;
             break;
         }
@@ -130,17 +198,15 @@ Gen4PollerError gen4_poller_set_config(
 
 Gen4PollerError gen4_poller_write_block(
     Gen4Poller* instance,
-    uint32_t password,
+    Gen4Password password,
     uint8_t block_num,
     const uint8_t* data) {
     Gen4PollerError ret = Gen4PollerErrorNone;
     bit_buffer_reset(instance->tx_buffer);
 
     do {
-        uint8_t password_arr[4] = {};
-        bit_lib_num_to_bytes_be(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_bytes(instance->tx_buffer, password.bytes, GEN4_PASSWORD_LEN);
         bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_WRITE);
         bit_buffer_append_byte(instance->tx_buffer, block_num);
         bit_buffer_append_bytes(instance->tx_buffer, data, GEN4_POLLER_BLOCK_SIZE);
@@ -163,20 +229,19 @@ Gen4PollerError gen4_poller_write_block(
     return ret;
 }
 
-Gen4PollerError
-    gen4_poller_change_password(Gen4Poller* instance, uint32_t pwd_current, uint32_t pwd_new) {
+Gen4PollerError gen4_poller_change_password(
+    Gen4Poller* instance,
+    Gen4Password pwd_current,
+    Gen4Password pwd_new) {
     Gen4PollerError ret = Gen4PollerErrorNone;
     bit_buffer_reset(instance->tx_buffer);
 
     do {
-        uint8_t password_arr[4] = {};
-        bit_lib_num_to_bytes_be(pwd_current, 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_bytes(instance->tx_buffer, pwd_current.bytes, GEN4_PASSWORD_LEN);
 
         bit_buffer_append_byte(instance->tx_buffer, GEN4_CMD_SET_PWD);
-        bit_lib_num_to_bytes_be(pwd_new, COUNT_OF(password_arr), password_arr);
-        bit_buffer_append_bytes(instance->tx_buffer, password_arr, COUNT_OF(password_arr));
+        bit_buffer_append_bytes(instance->tx_buffer, pwd_new.bytes, GEN4_PASSWORD_LEN);
 
         Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
             instance->iso3_poller, instance->tx_buffer, instance->rx_buffer, GEN4_POLLER_MAX_FWT);
@@ -186,8 +251,23 @@ Gen4PollerError
             break;
         }
 
-        size_t rx_bytes = bit_buffer_get_size_bytes(instance->rx_buffer);
-        if(rx_bytes != 2) {
+        size_t response = bit_buffer_get_size_bytes(instance->rx_buffer);
+
+        FURI_LOG_D(
+            TAG,
+            "Trying to change password from 0x%02X %02X %02X %02X to "
+            "0x%02X %02X %02X %02X. Card response: 0x%02X",
+            pwd_current.bytes[0],
+            pwd_current.bytes[1],
+            pwd_current.bytes[2],
+            pwd_current.bytes[3],
+            pwd_new.bytes[0],
+            pwd_new.bytes[1],
+            pwd_new.bytes[2],
+            pwd_new.bytes[3],
+            response);
+
+        if(response != GEN4_RESPONSE_SUCCESS) {
             ret = Gen4PollerErrorProtocol;
             break;
         }

+ 31 - 39
magic/protocols/gen4/gen4_poller_i.h

@@ -17,32 +17,6 @@ extern "C" {
 #define GEN4_POLLER_BLOCK_SIZE (16)
 #define GEN4_POLLER_BLOCKS_TOTAL (256)
 
-#define GEN4_POLLER_CONFIG_SIZE_MAX (30)
-
-typedef enum {
-    Gen4PollerUIDLengthSingle = 0x00,
-    Gen4PollerUIDLengthDouble = 0x01,
-    Gen4PollerUIDLengthTriple = 0x02
-} Gen4PollerUIDLength;
-
-typedef enum {
-    Gen4PollerUltralightModeUL_EV1 = 0x00,
-    Gen4PollerUltralightModeNTAG = 0x01,
-    Gen4PollerUltralightModeUL_C = 0x02,
-    Gen4PollerUltralightModeUL = 0x03
-} Gen4PollerUltralightMode;
-
-typedef enum {
-    // for writing original (shadow) data
-    Gen4PollerShadowModePreWrite = 0x00,
-    // written data can be read once before restored to original
-    Gen4PollerShadowModeRestore = 0x01,
-    // written data is discarded
-    Gen4PollerShadowModeIgnore = 0x02,
-    // apparently for UL?
-    Gen4PollerShadowModeHighSpeedIgnore = 0x03
-} Gen4PollerShadowMode;
-
 typedef enum {
     Gen4PollerStateIdle,
     Gen4PollerStateRequestMode,
@@ -51,9 +25,10 @@ typedef enum {
     Gen4PollerStateWipe,
     Gen4PollerStateChangePassword,
 
+    Gen4PollerStateGetInfo,
     Gen4PollerStateSetDefaultConfig,
-    Gen4PollerStateGetCurrentConfig,
-    Gen4PollerStateGetRevision,
+    Gen4PollerStateSetShadowMode,
+    Gen4PollerStateSetDirectWriteBlock0,
 
     Gen4PollerStateSuccess,
     Gen4PollerStateFail,
@@ -65,7 +40,15 @@ struct Gen4Poller {
     NfcPoller* poller;
     Iso14443_3aPoller* iso3_poller;
     Gen4PollerState state;
-    uint32_t password;
+
+    Gen4* gen4_data;
+
+    Gen4Password password;
+
+    Gen4Password new_password;
+    Gen4Config config;
+    Gen4ShadowMode shadow_mode;
+    Gen4DirectWriteBlock0Mode direct_write_block_0_mode;
 
     BitBuffer* tx_buffer;
     BitBuffer* rx_buffer;
@@ -75,9 +58,6 @@ struct Gen4Poller {
 
     NfcProtocol protocol;
     const NfcDeviceData* data;
-    uint32_t new_password;
-
-    uint8_t config[GEN4_POLLER_CONFIG_SIZE_MAX];
 
     Gen4PollerEvent gen4_event;
     Gen4PollerEventData gen4_event_data;
@@ -88,25 +68,37 @@ struct Gen4Poller {
 
 Gen4PollerError gen4_poller_set_config(
     Gen4Poller* instance,
-    uint32_t password,
-    const uint8_t* config,
+    Gen4Password password,
+    const Gen4Config* config,
     size_t config_size,
     bool fuse);
 
 Gen4PollerError gen4_poller_write_block(
     Gen4Poller* instance,
-    uint32_t password,
+    Gen4Password password,
     uint8_t block_num,
     const uint8_t* data);
 
-Gen4PollerError
-    gen4_poller_change_password(Gen4Poller* instance, uint32_t pwd_current, uint32_t pwd_new);
+Gen4PollerError gen4_poller_change_password(
+    Gen4Poller* instance,
+    Gen4Password pwd_current,
+    Gen4Password pwd_new);
+
+Gen4PollerError gen4_poller_get_revision(
+    Gen4Poller* instance,
+    Gen4Password password,
+    Gen4Revision* revision_result);
 
 Gen4PollerError
-    gen4_poller_get_revision(Gen4Poller* instance, uint32_t password, uint8_t* revision_result);
+    gen4_poller_get_config(Gen4Poller* instance, Gen4Password password, Gen4Config* config_result);
 
 Gen4PollerError
-    gen4_poller_get_config(Gen4Poller* instance, uint32_t password, uint8_t* config_result);
+    gen4_poller_set_shadow_mode(Gen4Poller* instance, Gen4Password password, Gen4ShadowMode mode);
+
+Gen4PollerError gen4_poller_set_direct_write_block_0_mode(
+    Gen4Poller* instance,
+    Gen4Password password,
+    Gen4DirectWriteBlock0Mode mode);
 
 #ifdef __cplusplus
 }

+ 5 - 0
nfc_magic_app.c

@@ -1,4 +1,5 @@
 #include "nfc_magic_app_i.h"
+#include "magic/protocols/gen4/gen4.h"
 
 bool nfc_magic_app_custom_event_callback(void* context, uint32_t event) {
     furi_assert(context);
@@ -106,6 +107,8 @@ NfcMagicApp* nfc_magic_app_alloc() {
     view_dispatcher_add_view(
         instance->view_dispatcher, NfcMagicAppViewWidget, widget_get_view(instance->widget));
 
+    instance->gen4_data = gen4_alloc();
+
     // Dict attack
     instance->dict_attack = dict_attack_alloc();
     view_dispatcher_add_view(
@@ -191,6 +194,8 @@ void nfc_magic_app_free(NfcMagicApp* instance) {
     furi_record_close(RECORD_STORAGE);
     instance->storage = NULL;
 
+    gen4_free(instance->gen4_data);
+
     nfc_magic_scanner_free(instance->scanner);
     nfc_free(instance->nfc);
 

+ 5 - 6
nfc_magic_app_i.h

@@ -109,17 +109,16 @@ struct NfcMagicApp {
 
     Gen4Poller* gen4_poller;
 
+    Gen4* gen4_data;
+
+    Gen4Password gen4_password;
+    Gen4Password gen4_password_new;
+
     NfcMagicAppMfClassicDictAttackContext nfc_dict_context;
     DictAttack* dict_attack;
     NfcMagicAppWriteProblemsContext write_problems_context;
     WriteProblems* write_problems;
 
-    uint32_t gen4_password;
-    uint32_t gen4_password_new;
-
-    uint8_t gen4_config_display[32];
-    uint8_t gen4_revision_display[5];
-
     FuriString* text_box_store;
     uint8_t byte_input_store[NFC_MAGIC_APP_BYTE_INPUT_STORE_SIZE];
 

+ 7 - 5
scenes/nfc_magic_scene_config.h

@@ -5,11 +5,13 @@ 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_revision, Gen4Revision)
-ADD_SCENE(nfc_magic, gen4_show_rev, Gen4ShowRev)
-ADD_SCENE(nfc_magic, gen4_show_cfg, Gen4ShowCFG)
+ADD_SCENE(nfc_magic, gen4_set_default_cfg, Gen4SetDefaultCfg)
+ADD_SCENE(nfc_magic, gen4_get_info, Gen4GetInfo)
+ADD_SCENE(nfc_magic, gen4_show_cfg, Gen4ShowCfg)
+ADD_SCENE(nfc_magic, gen4_show_info, Gen4ShowInfo)
+ADD_SCENE(nfc_magic, gen4_select_shd_mode, Gen4SelectShdMode)
+ADD_SCENE(nfc_magic, gen4_set_shd_mode, Gen4SetShdMode)
+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)

+ 6 - 28
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,
-    SubmenuIndexSetStandardConfig,
-    SubmenuIndexGetConfig,
-    SubmenuIndexGetRevision
+    SubmenuIndexSetDefaultConfig,
 };
 
 void nfc_magic_scene_gen4_actions_menu_submenu_callback(void* context, uint32_t index) {
@@ -26,24 +23,10 @@ void nfc_magic_scene_gen4_actions_menu_on_enter(void* context) {
         instance);
     submenu_add_item(
         submenu,
-        "Get Revision",
-        SubmenuIndexGetRevision,
+        "Set Default Config",
+        SubmenuIndexSetDefaultConfig,
         nfc_magic_scene_gen4_actions_menu_submenu_callback,
         instance);
-    submenu_add_item(
-        submenu,
-        "Set Standard Config",
-        SubmenuIndexSetStandardConfig,
-        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,
@@ -59,16 +42,11 @@ bool nfc_magic_scene_gen4_actions_menu_on_event(void* context, SceneManagerEvent
         if(event.event == SubmenuIndexAuthenticate) {
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneKeyInput);
             consumed = true;
-        } else if(event.event == SubmenuIndexSetStandardConfig) {
-            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);
+        } else if(event.event == SubmenuIndexSetDefaultConfig) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetDefaultCfg);
             consumed = true;
         }
+
         scene_manager_set_scene_state(
             instance->scene_manager, NfcMagicSceneGen4ActionsMenu, event.event);
     } else if(event.type == SceneManagerEventTypeBack) {

+ 28 - 28
scenes/nfc_magic_scene_gen4_revision.c → scenes/nfc_magic_scene_gen4_get_info.c

@@ -1,11 +1,11 @@
 #include "../nfc_magic_app_i.h"
 
 enum {
-    NfcMagicSceneGen4RevisionStateCardSearch,
-    NfcMagicSceneGen4RevisionStateCardFound,
+    NfcMagicSceneGen4GetInfoStateCardSearch,
+    NfcMagicSceneGen4GetInfoStateCardFound,
 };
 
-NfcCommand nfc_mafic_scene_gen4_revision_poller_callback(Gen4PollerEvent event, void* context) {
+NfcCommand nfc_mafic_scene_gen4_get_info_poller_callback(Gen4PollerEvent event, void* context) {
     NfcMagicApp* instance = context;
     furi_assert(event.data);
 
@@ -15,13 +15,10 @@ NfcCommand nfc_mafic_scene_gen4_revision_poller_callback(Gen4PollerEvent event,
         view_dispatcher_send_custom_event(
             instance->view_dispatcher, NfcMagicCustomEventCardDetected);
     } else if(event.type == Gen4PollerEventTypeRequestMode) {
-        event.data->request_mode.mode = Gen4PollerModeGetRevision;
+        event.data->request_mode.mode = Gen4PollerModeGetInfo;
     } else if(event.type == Gen4PollerEventTypeSuccess) {
-        // Copy revision from event to main instance to display it on success scene
-        memcpy(
-            instance->gen4_revision_display,
-            event.data->revision_data,
-            sizeof(event.data->revision_data));
+        // Copy data from event to main instance
+        gen4_copy(instance->gen4_data, gen4_poller_get_gen4_data(instance->gen4_poller));
 
         view_dispatcher_send_custom_event(
             instance->view_dispatcher, NfcMagicCustomEventWorkerSuccess);
@@ -35,13 +32,13 @@ NfcCommand nfc_mafic_scene_gen4_revision_poller_callback(Gen4PollerEvent event,
     return command;
 }
 
-static void nfc_magic_scene_gen4_revision_setup_view(NfcMagicApp* instance) {
+static void nfc_magic_scene_gen4_get_info_setup_view(NfcMagicApp* instance) {
     Popup* popup = instance->popup;
     popup_reset(popup);
     uint32_t state =
-        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4Revision);
+        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4GetInfo);
 
-    if(state == NfcMagicSceneGen4RevisionStateCardSearch) {
+    if(state == NfcMagicSceneGen4GetInfoStateCardSearch) {
         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);
@@ -53,24 +50,24 @@ static void nfc_magic_scene_gen4_revision_setup_view(NfcMagicApp* instance) {
     view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewPopup);
 }
 
-void nfc_magic_scene_gen4_revision_on_enter(void* context) {
+void nfc_magic_scene_gen4_get_info_on_enter(void* context) {
     NfcMagicApp* instance = context;
 
     scene_manager_set_scene_state(
         instance->scene_manager,
-        NfcMagicSceneGen4Revision,
-        NfcMagicSceneGen4RevisionStateCardSearch);
-    nfc_magic_scene_gen4_revision_setup_view(instance);
+        NfcMagicSceneGen4GetInfo,
+        NfcMagicSceneGen4GetInfoStateCardSearch);
+    nfc_magic_scene_gen4_get_info_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);
     gen4_poller_start(
-        instance->gen4_poller, nfc_mafic_scene_gen4_revision_poller_callback, instance);
+        instance->gen4_poller, nfc_mafic_scene_gen4_get_info_poller_callback, instance);
 }
 
-bool nfc_magic_scene_gen4_revision_on_event(void* context, SceneManagerEvent event) {
+bool nfc_magic_scene_gen4_get_info_on_event(void* context, SceneManagerEvent event) {
     NfcMagicApp* instance = context;
     bool consumed = false;
 
@@ -78,19 +75,22 @@ bool nfc_magic_scene_gen4_revision_on_event(void* context, SceneManagerEvent eve
         if(event.event == NfcMagicCustomEventCardDetected) {
             scene_manager_set_scene_state(
                 instance->scene_manager,
-                NfcMagicSceneGen4Revision,
-                NfcMagicSceneGen4RevisionStateCardFound);
-            nfc_magic_scene_gen4_revision_setup_view(instance);
+                NfcMagicSceneGen4GetInfo,
+                NfcMagicSceneGen4GetInfoStateCardFound);
+            nfc_magic_scene_gen4_get_info_setup_view(instance);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventCardLost) {
             scene_manager_set_scene_state(
                 instance->scene_manager,
-                NfcMagicSceneGen4Revision,
-                NfcMagicSceneGen4RevisionStateCardSearch);
-            nfc_magic_scene_gen4_revision_setup_view(instance);
+                NfcMagicSceneGen4GetInfo,
+                NfcMagicSceneGen4GetInfoStateCardSearch);
+            nfc_magic_scene_gen4_get_info_setup_view(instance);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventWorkerSuccess) {
-            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4ShowRev);
+            // for notification message
+            scene_manager_set_scene_state(
+                instance->scene_manager, NfcMagicSceneGen4ShowInfo, true);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4ShowInfo);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventWorkerFail) {
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4Fail);
@@ -101,15 +101,15 @@ bool nfc_magic_scene_gen4_revision_on_event(void* context, SceneManagerEvent eve
     return consumed;
 }
 
-void nfc_magic_scene_gen4_revision_on_exit(void* context) {
+void nfc_magic_scene_gen4_get_info_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,
-        NfcMagicSceneGen4Revision,
-        NfcMagicSceneGen4RevisionStateCardSearch);
+        NfcMagicSceneGen4GetInfo,
+        NfcMagicSceneGen4GetInfoStateCardSearch);
     // Clear view
     popup_reset(instance->popup);
 

+ 48 - 1
scenes/nfc_magic_scene_gen4_menu.c

@@ -3,6 +3,9 @@
 enum SubmenuIndex {
     SubmenuIndexWrite,
     SubmenuIndexChangePassword,
+    SubmenuIndexSetShadowMode,
+    SubmenuIndexSetDirectWriteBlock0Mode,
+    SubmenuIndexInfo,
     SubmenuIndexWipe,
 };
 
@@ -24,8 +27,41 @@ 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);
+    if(instance->gen4_data->config.data_parsed.direct_write_mode ==
+       Gen4DirectWriteBlock0ModeEnabled) {
+        submenu_add_item(
+            submenu,
+            "Disable Direct Write Mode",
+            SubmenuIndexSetDirectWriteBlock0Mode,
+            nfc_magic_scene_gen4_menu_submenu_callback,
+            instance);
+        scene_manager_set_scene_state(
+            instance->scene_manager,
+            NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+            Gen4DirectWriteBlock0ModeDisabled);
+    } else {
+        submenu_add_item(
+            submenu,
+            "Enable Direct Write Mode",
+            SubmenuIndexSetDirectWriteBlock0Mode,
+            nfc_magic_scene_gen4_menu_submenu_callback,
+            instance);
+        scene_manager_set_scene_state(
+            instance->scene_manager,
+            NfcMagicSceneGen4SetDirectWriteBlock0Mode,
+            Gen4DirectWriteBlock0ModeEnabled);
+    }
+
     submenu_add_item(
         submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_gen4_menu_submenu_callback, instance);
+    submenu_add_item(
+        submenu, "Info", SubmenuIndexInfo, nfc_magic_scene_gen4_menu_submenu_callback, instance);
 
     submenu_set_selected_item(
         submenu, scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4Menu));
@@ -46,10 +82,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 == SubmenuIndexInfo) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4GetInfo);
+            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, NfcMagicSceneGen4SetDirectWriteBlock0Mode);
+            consumed = true;
         }
+
         scene_manager_set_scene_state(instance->scene_manager, NfcMagicSceneGen4Menu, event.event);
     } else if(event.type == SceneManagerEventTypeBack) {
-        if(instance->gen4_password != 0) {
+        if(gen4_password_is_set(&instance->gen4_password)) {
             consumed = scene_manager_search_and_switch_to_previous_scene(
                 instance->scene_manager, NfcMagicSceneGen4ActionsMenu);
         } else {

+ 105 - 0
scenes/nfc_magic_scene_gen4_select_shd_mode.c

@@ -0,0 +1,105 @@
+#include "../nfc_magic_app_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, Gen4ShadowModePreWrite);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexRestoreMode) {
+            scene_manager_set_scene_state(
+                instance->scene_manager, NfcMagicSceneGen4SetShdMode, Gen4ShadowModeRestore);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexDisable) {
+            scene_manager_set_scene_state(
+                instance->scene_manager, NfcMagicSceneGen4SetShdMode, Gen4ShadowModeDisabled);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexDisableHighSpeed) {
+            scene_manager_set_scene_state(
+                instance->scene_manager,
+                NfcMagicSceneGen4SetShdMode,
+                Gen4ShadowModeHighSpeedDisabled);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
+            consumed = true;
+        } else if(event.event == SubmenuIndexSplitMode) {
+            scene_manager_set_scene_state(
+                instance->scene_manager, NfcMagicSceneGen4SetShdMode, Gen4ShadowModeSplit);
+            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);
+}

+ 24 - 23
scenes/nfc_magic_scene_gen4_set_cfg.c → scenes/nfc_magic_scene_gen4_set_default_cfg.c

@@ -1,11 +1,12 @@
 #include "../nfc_magic_app_i.h"
 
 enum {
-    NfcMagicSceneGen4SetDefCFGStateCardSearch,
-    NfcMagicSceneGen4SetDefCFGStateCardFound,
+    NfcMagicSceneGen4SetDefCfgStateCardSearch,
+    NfcMagicSceneGen4SetDefCfgStateCardFound,
 };
 
-NfcCommand nfc_mafic_scene_gen4_set_cfg_poller_callback(Gen4PollerEvent event, void* context) {
+NfcCommand
+    nfc_mafic_scene_gen4_set_default_cfg_poller_callback(Gen4PollerEvent event, void* context) {
     NfcMagicApp* instance = context;
     furi_assert(event.data);
 
@@ -15,7 +16,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);
@@ -29,42 +30,42 @@ NfcCommand nfc_mafic_scene_gen4_set_cfg_poller_callback(Gen4PollerEvent event, v
     return command;
 }
 
-static void nfc_magic_scene_gen4_set_cfg_setup_view(NfcMagicApp* instance) {
+static void nfc_magic_scene_gen4_set_default_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, NfcMagicSceneGen4SetDefaultCfg);
 
-    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);
     } else {
         popup_set_icon(popup, 12, 23, &I_Loading_24);
-        popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
+        popup_set_header(popup, "Configuring\nDon't move...", 52, 32, AlignLeft, AlignCenter);
     }
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewPopup);
 }
 
-void nfc_magic_scene_gen4_set_cfg_on_enter(void* context) {
+void nfc_magic_scene_gen4_set_default_cfg_on_enter(void* context) {
     NfcMagicApp* instance = context;
 
     scene_manager_set_scene_state(
         instance->scene_manager,
-        NfcMagicSceneGen4SetCFG,
-        NfcMagicSceneGen4SetDefCFGStateCardSearch);
-    nfc_magic_scene_gen4_set_cfg_setup_view(instance);
+        NfcMagicSceneGen4SetDefaultCfg,
+        NfcMagicSceneGen4SetDefCfgStateCardSearch);
+    nfc_magic_scene_gen4_set_default_cfg_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);
     gen4_poller_start(
-        instance->gen4_poller, nfc_mafic_scene_gen4_set_cfg_poller_callback, instance);
+        instance->gen4_poller, nfc_mafic_scene_gen4_set_default_cfg_poller_callback, instance);
 }
 
-bool nfc_magic_scene_gen4_set_cfg_on_event(void* context, SceneManagerEvent event) {
+bool nfc_magic_scene_gen4_set_default_cfg_on_event(void* context, SceneManagerEvent event) {
     NfcMagicApp* instance = context;
     bool consumed = false;
 
@@ -72,16 +73,16 @@ 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);
-            nfc_magic_scene_gen4_set_cfg_setup_view(instance);
+                NfcMagicSceneGen4SetDefaultCfg,
+                NfcMagicSceneGen4SetDefCfgStateCardFound);
+            nfc_magic_scene_gen4_set_default_cfg_setup_view(instance);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventCardLost) {
             scene_manager_set_scene_state(
                 instance->scene_manager,
-                NfcMagicSceneGen4SetCFG,
-                NfcMagicSceneGen4SetDefCFGStateCardSearch);
-            nfc_magic_scene_gen4_set_cfg_setup_view(instance);
+                NfcMagicSceneGen4SetDefaultCfg,
+                NfcMagicSceneGen4SetDefCfgStateCardSearch);
+            nfc_magic_scene_gen4_set_default_cfg_setup_view(instance);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventWorkerSuccess) {
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneSuccess);
@@ -95,15 +96,15 @@ bool nfc_magic_scene_gen4_set_cfg_on_event(void* context, SceneManagerEvent even
     return consumed;
 }
 
-void nfc_magic_scene_gen4_set_cfg_on_exit(void* context) {
+void nfc_magic_scene_gen4_set_default_cfg_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,
-        NfcMagicSceneGen4SetCFG,
-        NfcMagicSceneGen4SetDefCFGStateCardSearch);
+        NfcMagicSceneGen4SetDefaultCfg,
+        NfcMagicSceneGen4SetDefCfgStateCardSearch);
     // Clear view
     popup_reset(instance->popup);
 

+ 123 - 0
scenes/nfc_magic_scene_gen4_set_direct_write_block_0_mode.c

@@ -0,0 +1,123 @@
+#include "../nfc_magic_app_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, "Configuring\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);
+
+    gen4_poller_struct_set_direct_write_block_0_mode(
+        instance->gen4_poller, 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);
+}

+ 32 - 29
scenes/nfc_magic_scene_gen4_get_cfg.c → scenes/nfc_magic_scene_gen4_set_shd_mode.c

@@ -1,28 +1,22 @@
 #include "../nfc_magic_app_i.h"
 
 enum {
-    NfcMagicSceneGen4GetCFGStateCardSearch,
-    NfcMagicSceneGen4GetCFGStateCardFound,
+    NfcMagicSceneGen4SetShadowModeStateCardSearch,
+    NfcMagicSceneGen4SetShadowModeStateCardFound,
 };
 
-NfcCommand nfc_mafic_scene_gen4_get_cfg_poller_callback(Gen4PollerEvent event, void* context) {
+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 = Gen4PollerModeGetCFG;
+        event.data->request_mode.mode = Gen4PollerModeSetShadowMode;
     } else if(event.type == Gen4PollerEventTypeSuccess) {
-        // Copy config from event to main instance to display it on success scene
-        memcpy(
-            instance->gen4_config_display,
-            event.data->display_config,
-            sizeof(event.data->display_config));
-
         view_dispatcher_send_custom_event(
             instance->view_dispatcher, NfcMagicCustomEventWorkerSuccess);
         command = NfcCommandStop;
@@ -35,40 +29,47 @@ NfcCommand nfc_mafic_scene_gen4_get_cfg_poller_callback(Gen4PollerEvent event, v
     return command;
 }
 
-static void nfc_magic_scene_gen4_get_cfg_setup_view(NfcMagicApp* instance) {
+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, NfcMagicSceneGen4GetCFG);
+        scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4SetShdMode);
 
-    if(state == NfcMagicSceneGen4GetCFGStateCardSearch) {
+    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, "Reading\nDon't move...", 52, 32, AlignLeft, AlignCenter);
+        popup_set_header(popup, "Configuring\nDon't move...", 52, 32, AlignLeft, AlignCenter);
     }
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewPopup);
 }
 
-void nfc_magic_scene_gen4_get_cfg_on_enter(void* context) {
+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, NfcMagicSceneGen4GetCFG, NfcMagicSceneGen4GetCFGStateCardSearch);
-    nfc_magic_scene_gen4_get_cfg_setup_view(instance);
+        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);
+    gen4_poller_struct_set_shadow_mode(instance->gen4_poller, shadow_mode);
+
     gen4_poller_start(
-        instance->gen4_poller, nfc_mafic_scene_gen4_get_cfg_poller_callback, instance);
+        instance->gen4_poller, nfc_magic_scene_gen4_set_shd_mode_poller_callback, instance);
 }
 
-bool nfc_magic_scene_gen4_get_cfg_on_event(void* context, SceneManagerEvent event) {
+bool nfc_magic_scene_gen4_set_shd_mode_on_event(void* context, SceneManagerEvent event) {
     NfcMagicApp* instance = context;
     bool consumed = false;
 
@@ -76,19 +77,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);
-            nfc_magic_scene_gen4_get_cfg_setup_view(instance);
+                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,
-                NfcMagicSceneGen4GetCFG,
-                NfcMagicSceneGen4GetCFGStateCardSearch);
-            nfc_magic_scene_gen4_get_cfg_setup_view(instance);
+                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, NfcMagicSceneGen4ShowCFG);
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneSuccess);
             consumed = true;
         } else if(event.event == NfcMagicCustomEventWorkerFail) {
             scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4Fail);
@@ -99,13 +100,15 @@ bool nfc_magic_scene_gen4_get_cfg_on_event(void* context, SceneManagerEvent even
     return consumed;
 }
 
-void nfc_magic_scene_gen4_get_cfg_on_exit(void* context) {
+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, NfcMagicSceneGen4GetCFG, NfcMagicSceneGen4GetCFGStateCardSearch);
+        instance->scene_manager,
+        NfcMagicSceneGen4SetShdMode,
+        NfcMagicSceneGen4SetShadowModeStateCardSearch);
     // Clear view
     popup_reset(instance->popup);
 

+ 11 - 20
scenes/nfc_magic_scene_gen4_show_cfg.c

@@ -1,7 +1,5 @@
 #include "../nfc_magic_app_i.h"
 
-#define CONFIG_SIZE (32)
-
 void nfc_magic_scene_gen4_show_cfg_widget_callback(
     GuiButtonType result,
     InputType type,
@@ -17,25 +15,20 @@ void nfc_magic_scene_gen4_show_cfg_on_enter(void* context) {
     NfcMagicApp* instance = context;
     Widget* widget = instance->widget;
 
-    notification_message(instance->notifications, &sequence_success);
+    FuriString* output = furi_string_alloc();
+
+    Gen4Config* config = &instance->gen4_data->config;
 
-    FuriString* temp_config = furi_string_alloc();
-    for(size_t i = 0; i < CONFIG_SIZE; i++) {
-        if((i != 0) && (i % 8 == 0)) {
-            furi_string_cat_printf(temp_config, "\n");
-        }
-        furi_string_cat_printf(temp_config, "%02X ", instance->gen4_config_display[i]);
+    for(size_t i = 0; i < GEN4_CONFIG_SIZE; i += 2) {
+        if(i && !(i % 8)) furi_string_cat_printf(output, "\n");
+        furi_string_cat_printf(output, "%02X%02X ", config->data_raw[i], config->data_raw[i + 1]);
     }
-    furi_string_cat_printf(temp_config, "\n");
 
     widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Gen4 Config");
 
-    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_add_text_scroll_element(widget, 3, 17, 124, 50, furi_string_get_cstr(output));
 
-    furi_string_free(temp_config);
+    furi_string_free(output);
     view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewWidget);
 }
 
@@ -43,11 +36,9 @@ bool nfc_magic_scene_gen4_show_cfg_on_event(void* context, SceneManagerEvent eve
     NfcMagicApp* instance = context;
     bool consumed = false;
 
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == GuiButtonTypeLeft) {
-            consumed = scene_manager_search_and_switch_to_previous_scene(
-                instance->scene_manager, NfcMagicSceneGen4ActionsMenu);
-        }
+    if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneGen4ShowInfo);
     }
     return consumed;
 }

+ 121 - 0
scenes/nfc_magic_scene_gen4_show_info.c

@@ -0,0 +1,121 @@
+#include "../nfc_magic_app_i.h"
+
+void nfc_magic_scene_gen4_show_info_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    NfcMagicApp* instance = context;
+
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(instance->view_dispatcher, result);
+    }
+}
+
+void nfc_magic_scene_gen4_show_info_on_enter(void* context) {
+    NfcMagicApp* instance = context;
+    Widget* widget = instance->widget;
+
+    if(scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneGen4ShowInfo))
+        notification_message(instance->notifications, &sequence_success);
+    scene_manager_set_scene_state(instance->scene_manager, NfcMagicSceneGen4ShowInfo, false);
+
+    Gen4* gen4 = instance->gen4_data;
+
+    Gen4DirectWriteBlock0Mode dw_mode = gen4->config.data_parsed.direct_write_mode;
+    Gen4ShadowMode s_mode = gen4->config.data_parsed.gtu_mode;
+    uint8_t ats_len = gen4->config.data_parsed.ats_len;
+
+    FuriString* output = furi_string_alloc();
+
+    furi_string_printf(output, "\e#Gen4\n");
+
+    // Revision
+    furi_string_cat_printf(
+        output, "Revision: %02X %02X\n", gen4->revision.data[3], gen4->revision.data[4]);
+
+    // Password
+    furi_string_cat_printf(
+        output,
+        "Password: %02X %02X %02X %02X\n",
+        gen4->config.data_parsed.password.bytes[0],
+        gen4->config.data_parsed.password.bytes[1],
+        gen4->config.data_parsed.password.bytes[2],
+        gen4->config.data_parsed.password.bytes[3]);
+
+    // Shadow mode
+    furi_string_cat_printf(output, "Shadow Mode: %s\n", gen4_get_shadow_mode_name(s_mode));
+
+    // Direct write to block 0 mode
+    furi_string_cat_printf(
+        output, "Direct Write Mode: %s\n", gen4_get_direct_write_mode_name(dw_mode));
+
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+    furi_string_cat_printf(output, ":::::::::::::[Configured As]::::::::::::::\n");
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    // Configuration type:
+    furi_string_cat_printf(output, "%s\n", gen4_get_configuration_name(&gen4->config));
+
+    // UID len
+    furi_string_cat_printf(
+        output, "UID Length: %s\n", gen4_get_uid_len_num(gen4->config.data_parsed.uid_len_code));
+
+    // ATS
+    furi_string_cat_printf(output, "ATS:");
+    if(ats_len)
+        for(uint8_t i = 0; i < ats_len; i++)
+            furi_string_cat_printf(output, " %02X", gen4->config.data_parsed.ats[i]);
+    else
+        furi_string_cat_printf(output, " No");
+    furi_string_cat_printf(output, "\n");
+
+    // ATQA
+    furi_string_cat_printf(
+        output,
+        "ATQA: %02X %02X\n",
+        gen4->config.data_parsed.atqa[0],
+        gen4->config.data_parsed.atqa[1]);
+
+    // SAK
+    furi_string_cat_printf(output, "SAK: %02X\n", gen4->config.data_parsed.sak);
+
+    // Blocks
+    furi_string_cat_printf(
+        output,
+        "Total blocks: %u",
+        gen4->config.data_parsed.total_blocks + 1); // config stores the number of the last block
+
+    widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(output));
+
+    widget_add_button_element(
+        widget,
+        GuiButtonTypeRight,
+        "More",
+        nfc_magic_scene_gen4_show_info_widget_callback,
+        instance);
+
+    furi_string_free(output);
+    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewWidget);
+}
+
+bool nfc_magic_scene_gen4_show_info_on_event(void* context, SceneManagerEvent event) {
+    NfcMagicApp* instance = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeRight) {
+            scene_manager_next_scene(instance->scene_manager, NfcMagicSceneGen4ShowCfg);
+            consumed = true;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            instance->scene_manager, NfcMagicSceneGen4Menu);
+    }
+    return consumed;
+}
+
+void nfc_magic_scene_gen4_show_info_on_exit(void* context) {
+    NfcMagicApp* instance = context;
+
+    widget_reset(instance->widget);
+}

+ 0 - 55
scenes/nfc_magic_scene_gen4_show_rev.c

@@ -1,55 +0,0 @@
-#include "../nfc_magic_app_i.h"
-
-#define REVISION_SIZE (5)
-
-void nfc_magic_scene_gen4_show_rev_widget_callback(
-    GuiButtonType result,
-    InputType type,
-    void* context) {
-    NfcMagicApp* instance = context;
-
-    if(type == InputTypeShort) {
-        view_dispatcher_send_custom_event(instance->view_dispatcher, result);
-    }
-}
-
-void nfc_magic_scene_gen4_show_rev_on_enter(void* context) {
-    NfcMagicApp* instance = context;
-    Widget* widget = instance->widget;
-
-    notification_message(instance->notifications, &sequence_success);
-
-    FuriString* temp_revision = furi_string_alloc();
-    for(size_t i = REVISION_SIZE - 2; i < REVISION_SIZE; i++) {
-        furi_string_cat_printf(temp_revision, "%02X ", instance->gen4_revision_display[i]);
-    }
-
-    widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
-    widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Gen4 Revision");
-    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);
-
-    furi_string_free(temp_revision);
-    view_dispatcher_switch_to_view(instance->view_dispatcher, NfcMagicAppViewWidget);
-}
-
-bool nfc_magic_scene_gen4_show_rev_on_event(void* context, SceneManagerEvent event) {
-    NfcMagicApp* instance = context;
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == GuiButtonTypeLeft) {
-            consumed = scene_manager_search_and_switch_to_previous_scene(
-                instance->scene_manager, NfcMagicSceneGen4ActionsMenu);
-        }
-    }
-    return consumed;
-}
-
-void nfc_magic_scene_gen4_show_rev_on_exit(void* context) {
-    NfcMagicApp* instance = context;
-
-    widget_reset(instance->widget);
-}

+ 7 - 6
scenes/nfc_magic_scene_key_input.c

@@ -1,7 +1,5 @@
 #include "../nfc_magic_app_i.h"
 
-#include <bit_lib/bit_lib.h>
-
 void nfc_magic_scene_key_input_byte_input_callback(void* context) {
     NfcMagicApp* instance = context;
 
@@ -31,13 +29,16 @@ bool nfc_magic_scene_key_input_on_event(void* context, SceneManagerEvent event)
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == NfcMagicAppCustomEventByteInputDone) {
+            // TODO: NEED TEST
             if(scene_manager_has_previous_scene(instance->scene_manager, NfcMagicSceneGen4Menu)) {
-                instance->gen4_password_new = bit_lib_bytes_to_num_be(
-                    instance->byte_input_store, NFC_MAGIC_APP_BYTE_INPUT_STORE_SIZE);
+                memcpy(
+                    instance->gen4_password_new.bytes,
+                    instance->byte_input_store,
+                    GEN4_PASSWORD_LEN);
                 scene_manager_next_scene(instance->scene_manager, NfcMagicSceneChangeKey);
             } else {
-                instance->gen4_password = bit_lib_bytes_to_num_be(
-                    instance->byte_input_store, NFC_MAGIC_APP_BYTE_INPUT_STORE_SIZE);
+                memcpy(
+                    instance->gen4_password.bytes, instance->byte_input_store, GEN4_PASSWORD_LEN);
                 scene_manager_next_scene(instance->scene_manager, NfcMagicSceneCheck);
             }
             consumed = true;

+ 26 - 0
scenes/nfc_magic_scene_magic_info.c

@@ -1,4 +1,5 @@
 #include "../nfc_magic_app_i.h"
+#include "magic/nfc_magic_scanner.h"
 
 void nfc_magic_scene_magic_info_widget_callback(
     GuiButtonType result,
@@ -31,6 +32,28 @@ void nfc_magic_scene_magic_info_on_enter(void* context) {
         message, "Magic Type: %s", nfc_magic_protocols_get_name(instance->protocol));
     widget_add_text_box_element(
         widget, 0, 10, 128, 54, AlignLeft, AlignTop, furi_string_get_cstr(message), false);
+
+    if(instance->protocol == NfcMagicProtocolGen4) {
+        gen4_copy(instance->gen4_data, nfc_magic_scanner_get_gen4_data(instance->scanner));
+
+        furi_string_printf(
+            message,
+            "Revision: %02X %02X\n",
+            instance->gen4_data->revision.data[3],
+            instance->gen4_data->revision.data[4]);
+
+        widget_add_string_element(
+            widget, 0, 20, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(message));
+
+        furi_string_printf(
+            message,
+            "Configured As %s",
+            gen4_get_configuration_name(&instance->gen4_data->config));
+
+        widget_add_string_multiline_element(
+            widget, 0, 30, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(message));
+    }
+
     widget_add_button_element(
         widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_magic_info_widget_callback, instance);
     widget_add_button_element(
@@ -63,6 +86,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
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;
 }

+ 1 - 1
scenes/nfc_magic_scene_start.c

@@ -27,7 +27,7 @@ void nfc_magic_scene_start_on_enter(void* context) {
         nfc_magic_scene_start_submenu_callback,
         instance);
 
-    instance->gen4_password = 0;
+    gen4_password_reset(&instance->gen4_password);
 
     submenu_set_selected_item(
         submenu, scene_manager_get_scene_state(instance->scene_manager, NfcMagicSceneStart));

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