TollyH пре 2 година
родитељ
комит
1d8db4adbf

+ 54 - 2
mfc_editor_app.c

@@ -1,5 +1,29 @@
 #include "mfc_editor_app_i.h"
 #include "mfc_editor_app_i.h"
 
 
+const char* access_data_block_labels[8] = {
+    // C3, C2, C1
+    "Key A: Read, Write, Inc, Dec\nKey B: Read, Write, Inc, Dec", // 000
+    "Key A: Read\nKey B: Read, Write", // 001
+    "Key A: Read\nKey B: Read", // 010
+    "Key A: Read, Dec\nKey B: Read, Write, Inc, Dec", // 011
+    "Key A: Read, Dec\nKey B: Read, Dec", // 100
+    "Key A: No Access\nKey B: Read", // 101
+    "Key A: No Access\nKey B: Read, Write", // 110
+    "Key A: No Access\nKey B: No Access", // 111
+};
+
+const char* access_sector_trailer_labels[8] = {
+    // C3, C2, C1
+    "Key A: KA-W, AB-R, KB-RW\nKey B: No Access", // 000
+    "Key A: AB-R\nKey B: KA+KB-W, AB-R", // 001
+    "Key A: AB+KB-R\nKey B: No Access", // 010
+    "Key A: AB-R\nKey B: AB-R", // 011
+    "Key A: KA-W, AB+KB-RW\nKey B: No Access", // 100
+    "Key A: AB-R\nKey B: AB-RW", // 101
+    "Key A: AB-R\nKey B: KA+KB-W, AB-RW", // 110
+    "Key A: AB-R\nKey B: AB-R", // 111
+};
+
 bool mfc_editor_app_custom_event_callback(void* context, uint32_t event) {
 bool mfc_editor_app_custom_event_callback(void* context, uint32_t event) {
     furi_assert(context);
     furi_assert(context);
     MfcEditorApp* instance = context;
     MfcEditorApp* instance = context;
@@ -60,6 +84,12 @@ MfcEditorApp* mfc_editor_app_alloc() {
         MfcEditorAppViewDialogEx,
         MfcEditorAppViewDialogEx,
         dialog_ex_get_view(instance->dialog_ex));
         dialog_ex_get_view(instance->dialog_ex));
 
 
+    instance->byte_input = byte_input_alloc();
+    view_dispatcher_add_view(
+        instance->view_dispatcher,
+        MfcEditorAppViewByteInput,
+        byte_input_get_view(instance->byte_input));
+
     return instance;
     return instance;
 }
 }
 
 
@@ -75,6 +105,9 @@ void mfc_editor_app_free(MfcEditorApp* instance) {
     view_dispatcher_remove_view(instance->view_dispatcher, MfcEditorAppViewDialogEx);
     view_dispatcher_remove_view(instance->view_dispatcher, MfcEditorAppViewDialogEx);
     dialog_ex_free(instance->dialog_ex);
     dialog_ex_free(instance->dialog_ex);
 
 
+    view_dispatcher_remove_view(instance->view_dispatcher, MfcEditorAppViewByteInput);
+    byte_input_free(instance->byte_input);
+
     view_dispatcher_free(instance->view_dispatcher);
     view_dispatcher_free(instance->view_dispatcher);
     scene_manager_free(instance->scene_manager);
     scene_manager_free(instance->scene_manager);
 
 
@@ -124,12 +157,12 @@ MfcEditorPromptResponse mfc_editor_load_file(MfcEditorApp* instance, FuriString*
 
 
 static DialogMessageButton mfc_editor_prompt_should_load_shadow(MfcEditorApp* instance) {
 static DialogMessageButton mfc_editor_prompt_should_load_shadow(MfcEditorApp* instance) {
     DialogMessage* message = dialog_message_alloc();
     DialogMessage* message = dialog_message_alloc();
-    dialog_message_set_header(message, "File has modifications", 63, 0, AlignCenter, AlignTop);
+    dialog_message_set_header(message, "File has modifications", 63, 3, AlignCenter, AlignTop);
     dialog_message_set_text(
     dialog_message_set_text(
         message,
         message,
         "Would you like to load the\nmodified file (recommended)\nor the original file?",
         "Would you like to load the\nmodified file (recommended)\nor the original file?",
         63,
         63,
-        30,
+        31,
         AlignCenter,
         AlignCenter,
         AlignCenter);
         AlignCenter);
     dialog_message_set_buttons(message, "Original", NULL, "Modified");
     dialog_message_set_buttons(message, "Original", NULL, "Modified");
@@ -200,6 +233,25 @@ MfcEditorPromptResponse mfc_editor_prompt_load_file(MfcEditorApp* instance) {
     return result;
     return result;
 }
 }
 
 
+bool mfc_editor_warn_risky_operation(MfcEditorApp* instance) {
+    DialogMessage* message = dialog_message_alloc();
+    dialog_message_set_header(message, "Risky operation", 63, 3, AlignCenter, AlignTop);
+    dialog_message_set_text(
+        message,
+        "Changing this data may\ninhibit writing to the card\nor could brick the card.",
+        63,
+        31,
+        AlignCenter,
+        AlignCenter);
+    dialog_message_set_buttons(message, "Back", "Continue", NULL);
+
+    DialogMessageButton message_button = dialog_message_show(instance->dialogs, message);
+
+    dialog_message_free(message);
+
+    return message_button == DialogMessageButtonCenter;
+}
+
 int32_t mfc_editor_app(void* p) {
 int32_t mfc_editor_app(void* p) {
     UNUSED(p);
     UNUSED(p);
 
 

+ 27 - 5
mfc_editor_app_i.h

@@ -8,6 +8,7 @@
 #include <gui/scene_manager.h>
 #include <gui/scene_manager.h>
 #include <gui/view_dispatcher.h>
 #include <gui/view_dispatcher.h>
 
 
+#include <gui/modules/byte_input.h>
 #include <gui/modules/dialog_ex.h>
 #include <gui/modules/dialog_ex.h>
 #include <gui/modules/popup.h>
 #include <gui/modules/popup.h>
 #include <gui/modules/submenu.h>
 #include <gui/modules/submenu.h>
@@ -33,8 +34,14 @@ enum MfcEditorCustomEvent {
     MfcEditorCustomEventReserved = 100,
     MfcEditorCustomEventReserved = 100,
 
 
     MfcEditorCustomEventViewExit,
     MfcEditorCustomEventViewExit,
+    MfcEditorCustomEventSave,
 };
 };
 
 
+typedef struct {
+    uint8_t bits : 3;
+    uint8_t check_bits : 3;
+} MfcEditorAccessBits;
+
 struct MfcEditorApp {
 struct MfcEditorApp {
     ViewDispatcher* view_dispatcher;
     ViewDispatcher* view_dispatcher;
     SceneManager* scene_manager;
     SceneManager* scene_manager;
@@ -46,6 +53,7 @@ struct MfcEditorApp {
     Submenu* submenu;
     Submenu* submenu;
     Popup* popup;
     Popup* popup;
     DialogEx* dialog_ex;
     DialogEx* dialog_ex;
+    ByteInput* byte_input;
 
 
     MfClassicData* mf_classic_data;
     MfClassicData* mf_classic_data;
 
 
@@ -57,17 +65,16 @@ struct MfcEditorApp {
     // DialogEx doesn't copy the strings given to it, so we need these
     // DialogEx doesn't copy the strings given to it, so we need these
     FuriString* data_view_header;
     FuriString* data_view_header;
     FuriString* data_view_text;
     FuriString* data_view_text;
-};
 
 
-typedef struct {
-    uint8_t bits : 3;
-    uint8_t check_bits : 3;
-} MfcEditorAccessBits;
+    uint8_t* edit_buffer;
+    MfcEditorAccessBits access_bits_edit;
+};
 
 
 typedef enum {
 typedef enum {
     MfcEditorAppViewSubmenu,
     MfcEditorAppViewSubmenu,
     MfcEditorAppViewPopup,
     MfcEditorAppViewPopup,
     MfcEditorAppViewDialogEx,
     MfcEditorAppViewDialogEx,
+    MfcEditorAppViewByteInput,
 } MfcEditorAppView;
 } MfcEditorAppView;
 
 
 typedef enum {
 typedef enum {
@@ -103,8 +110,23 @@ MfcEditorPromptResponse mfc_editor_prompt_load_file(MfcEditorApp* instance);
 
 
 MfcEditorPromptResponse mfc_editor_load_file(MfcEditorApp* instance, FuriString* file_path);
 MfcEditorPromptResponse mfc_editor_load_file(MfcEditorApp* instance, FuriString* file_path);
 
 
+// Warning dialog
+
+bool mfc_editor_warn_risky_operation(MfcEditorApp* instance);
+
 // Helper methods
 // Helper methods
 
 
 uint8_t mfc_editor_calculate_uid_bcc(uint8_t* uid, uint8_t uid_len);
 uint8_t mfc_editor_calculate_uid_bcc(uint8_t* uid, uint8_t uid_len);
 
 
 MfcEditorAccessBits mfc_editor_get_block_access_bits(const MfClassicData* data, uint8_t block_num);
 MfcEditorAccessBits mfc_editor_get_block_access_bits(const MfClassicData* data, uint8_t block_num);
+
+void mfc_editor_set_block_access_bits(
+    MfClassicData* data,
+    uint8_t block_num,
+    const MfcEditorAccessBits* access_bits);
+
+// Strings
+
+extern const char* access_data_block_labels[8];
+
+extern const char* access_sector_trailer_labels[8];

+ 54 - 0
mfc_editor_helpers.c

@@ -38,3 +38,57 @@ MfcEditorAccessBits
 
 
     return result;
     return result;
 }
 }
+
+void mfc_editor_set_block_access_bits(
+    MfClassicData* data,
+    uint8_t block_num,
+    const MfcEditorAccessBits* access_bits) {
+    uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
+    uint8_t trailer_block_num = mf_classic_get_sector_trailer_num_by_sector(sector_num);
+    uint8_t relative_block_num = block_num - mf_classic_get_first_block_num_of_sector(sector_num);
+    if(sector_num >= 32) {
+        // 4K large sector - access bits affect range of blocks
+        relative_block_num /= 5;
+    }
+
+    uint8_t* access_byte_1 = &data->block[trailer_block_num].data[6];
+    uint8_t* access_byte_2 = &data->block[trailer_block_num].data[7];
+    uint8_t* access_byte_3 = &data->block[trailer_block_num].data[8];
+
+    if(FURI_BIT(access_bits->bits, 0)) {
+        FURI_BIT_SET(*access_byte_2, 4 + relative_block_num);
+    } else {
+        FURI_BIT_CLEAR(*access_byte_2, 4 + relative_block_num);
+    }
+
+    if(FURI_BIT(access_bits->bits, 1)) {
+        FURI_BIT_SET(*access_byte_3, relative_block_num);
+    } else {
+        FURI_BIT_CLEAR(*access_byte_3, relative_block_num);
+    }
+
+    if(FURI_BIT(access_bits->bits, 2)) {
+        FURI_BIT_SET(*access_byte_3, 4 + relative_block_num);
+    } else {
+        FURI_BIT_CLEAR(*access_byte_3, 4 + relative_block_num);
+    }
+
+    // Check bits are the inverse of the the actual bits
+    if(FURI_BIT(access_bits->check_bits, 0)) {
+        FURI_BIT_CLEAR(*access_byte_1, relative_block_num);
+    } else {
+        FURI_BIT_SET(*access_byte_1, relative_block_num);
+    }
+
+    if(FURI_BIT(access_bits->check_bits, 1)) {
+        FURI_BIT_CLEAR(*access_byte_1, 4 + relative_block_num);
+    } else {
+        FURI_BIT_SET(*access_byte_1, 4 + relative_block_num);
+    }
+
+    if(FURI_BIT(access_bits->check_bits, 2)) {
+        FURI_BIT_CLEAR(*access_byte_2, relative_block_num);
+    } else {
+        FURI_BIT_SET(*access_byte_2, relative_block_num);
+    }
+}

+ 2 - 0
scenes/mfc_editor_scene_config.h

@@ -4,3 +4,5 @@ ADD_SCENE(mfc_editor, invalid_file, InvalidFile)
 ADD_SCENE(mfc_editor, sector_select, SectorSelect)
 ADD_SCENE(mfc_editor, sector_select, SectorSelect)
 ADD_SCENE(mfc_editor, block_select, BlockSelect)
 ADD_SCENE(mfc_editor, block_select, BlockSelect)
 ADD_SCENE(mfc_editor, data_view, DataView)
 ADD_SCENE(mfc_editor, data_view, DataView)
+ADD_SCENE(mfc_editor, data_edit, DataEdit)
+ADD_SCENE(mfc_editor, data_edit_access_bits, DataEditAccessBits)

+ 204 - 0
scenes/mfc_editor_scene_data_edit.c

@@ -0,0 +1,204 @@
+#include "../mfc_editor_app_i.h"
+
+void mfc_editor_scene_data_edit_block_view_save_callback(void* context) {
+    MfcEditorApp* instance = context;
+    view_dispatcher_send_custom_event(instance->view_dispatcher, MfcEditorCustomEventSave);
+}
+
+void mfc_editor_scene_data_edit_block_view_changed_callback(void* context) {
+    UNUSED(context);
+}
+
+void mfc_editor_scene_data_edit_on_enter(void* context) {
+    MfcEditorApp* instance = context;
+
+    ByteInput* byte_input = instance->byte_input;
+
+    MfClassicData* mf_classic_data = instance->mf_classic_data;
+    Iso14443_3aData* iso14443_3a_data = mf_classic_data->iso14443_3a_data;
+
+    MfcEditorBlockView block_view =
+        scene_manager_get_scene_state(instance->scene_manager, MfcEditorSceneDataView);
+
+    if(block_view == MfcEditorBlockViewUID) {
+        byte_input_set_header_text(byte_input, "Enter new UID");
+
+        instance->edit_buffer = malloc(iso14443_3a_data->uid_len);
+        memcpy(instance->edit_buffer, iso14443_3a_data->uid, iso14443_3a_data->uid_len);
+
+        byte_input_set_result_callback(
+            byte_input,
+            mfc_editor_scene_data_edit_block_view_save_callback,
+            mfc_editor_scene_data_edit_block_view_changed_callback,
+            instance,
+            instance->edit_buffer,
+            iso14443_3a_data->uid_len);
+    } else if(block_view == MfcEditorBlockViewManufacturerBytes) {
+        byte_input_set_header_text(byte_input, "Enter new Manufacturer Bytes");
+
+        // Skip BCC byte (not present on 7B UID cards)
+        bool skip_byte = iso14443_3a_data->uid_len == 4;
+        uint8_t byte_num = MF_CLASSIC_BLOCK_SIZE - iso14443_3a_data->uid_len - skip_byte;
+
+        instance->edit_buffer = malloc(byte_num);
+        memcpy(
+            instance->edit_buffer,
+            mf_classic_data->block[0].data + iso14443_3a_data->uid_len + skip_byte,
+            byte_num);
+
+        byte_input_set_result_callback(
+            byte_input,
+            mfc_editor_scene_data_edit_block_view_save_callback,
+            mfc_editor_scene_data_edit_block_view_changed_callback,
+            instance,
+            instance->edit_buffer,
+            byte_num);
+    } else if(block_view == MfcEditorBlockViewKeyA) {
+        byte_input_set_header_text(byte_input, "Enter new Key A");
+
+        instance->edit_buffer = malloc(MF_CLASSIC_KEY_SIZE);
+        memcpy(
+            instance->edit_buffer,
+            mf_classic_get_sector_trailer_by_sector(mf_classic_data, instance->current_sector)
+                ->key_a.data,
+            MF_CLASSIC_KEY_SIZE);
+
+        byte_input_set_result_callback(
+            byte_input,
+            mfc_editor_scene_data_edit_block_view_save_callback,
+            mfc_editor_scene_data_edit_block_view_changed_callback,
+            instance,
+            instance->edit_buffer,
+            MF_CLASSIC_KEY_SIZE);
+    } else if(block_view == MfcEditorBlockViewKeyB) {
+        byte_input_set_header_text(byte_input, "Enter new Key B");
+
+        instance->edit_buffer = malloc(MF_CLASSIC_KEY_SIZE);
+        memcpy(
+            instance->edit_buffer,
+            mf_classic_get_sector_trailer_by_sector(mf_classic_data, instance->current_sector)
+                ->key_b.data,
+            MF_CLASSIC_KEY_SIZE);
+
+        byte_input_set_result_callback(
+            byte_input,
+            mfc_editor_scene_data_edit_block_view_save_callback,
+            mfc_editor_scene_data_edit_block_view_changed_callback,
+            instance,
+            instance->edit_buffer,
+            MF_CLASSIC_KEY_SIZE);
+    } else if(block_view == MfcEditorBlockViewUserByte) {
+        byte_input_set_header_text(byte_input, "Enter new User Byte");
+
+        instance->edit_buffer = malloc(1);
+        instance->edit_buffer[0] =
+            mf_classic_get_sector_trailer_by_sector(mf_classic_data, instance->current_sector)
+                ->access_bits.data[3];
+
+        byte_input_set_result_callback(
+            byte_input,
+            mfc_editor_scene_data_edit_block_view_save_callback,
+            mfc_editor_scene_data_edit_block_view_changed_callback,
+            instance,
+            instance->edit_buffer,
+            1);
+    } else {
+        byte_input_set_header_text(byte_input, "Enter new block content");
+
+        instance->edit_buffer = malloc(MF_CLASSIC_BLOCK_SIZE);
+        memcpy(
+            instance->edit_buffer,
+            mf_classic_data->block[instance->current_block].data,
+            MF_CLASSIC_BLOCK_SIZE);
+
+        byte_input_set_result_callback(
+            byte_input,
+            mfc_editor_scene_data_edit_block_view_save_callback,
+            mfc_editor_scene_data_edit_block_view_changed_callback,
+            instance,
+            instance->edit_buffer,
+            MF_CLASSIC_BLOCK_SIZE);
+    }
+
+    view_dispatcher_switch_to_view(instance->view_dispatcher, MfcEditorAppViewByteInput);
+}
+
+bool mfc_editor_scene_data_edit_on_event(void* context, SceneManagerEvent event) {
+    MfcEditorApp* instance = context;
+    bool consumed = false;
+
+    MfClassicData* mf_classic_data = instance->mf_classic_data;
+    Iso14443_3aData* iso14443_3a_data = mf_classic_data->iso14443_3a_data;
+
+    MfcEditorBlockView block_view =
+        scene_manager_get_scene_state(instance->scene_manager, MfcEditorSceneDataView);
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == MfcEditorCustomEventSave) {
+            if(block_view == MfcEditorBlockViewNormal) {
+                memcpy(
+                    mf_classic_data->block[instance->current_block].data,
+                    instance->edit_buffer,
+                    MF_CLASSIC_BLOCK_SIZE);
+                if(instance->current_block == 0) {
+                    // UID needs to be equal to the first bytes in block 0
+                    memcpy(
+                        iso14443_3a_data->uid,
+                        mf_classic_data->block[0].data,
+                        iso14443_3a_data->uid_len);
+                }
+            } else if(block_view == MfcEditorBlockViewUID) {
+                memcpy(iso14443_3a_data->uid, instance->edit_buffer, iso14443_3a_data->uid_len);
+                // UID is also the first bytes in block 0
+                memcpy(
+                    mf_classic_data->block[0].data,
+                    iso14443_3a_data->uid,
+                    iso14443_3a_data->uid_len);
+                // 4-byte UID cards need the BCC byte set to correct value
+                if(iso14443_3a_data->uid_len == 4) {
+                    mf_classic_data->block[0].data[4] = mfc_editor_calculate_uid_bcc(
+                        iso14443_3a_data->uid, iso14443_3a_data->uid_len);
+                }
+            } else if(block_view == MfcEditorBlockViewManufacturerBytes) {
+                // Skip BCC byte (not present on 7B UID cards)
+                bool skip_byte = iso14443_3a_data->uid_len == 4;
+                uint8_t byte_num = MF_CLASSIC_BLOCK_SIZE - iso14443_3a_data->uid_len - skip_byte;
+
+                memcpy(
+                    mf_classic_data->block[0].data + iso14443_3a_data->uid_len + skip_byte,
+                    instance->edit_buffer,
+                    byte_num);
+            } else if(block_view == MfcEditorBlockViewKeyA) {
+                memcpy(
+                    mf_classic_get_sector_trailer_by_sector(
+                        mf_classic_data, instance->current_sector)
+                        ->key_a.data,
+                    instance->edit_buffer,
+                    MF_CLASSIC_KEY_SIZE);
+            } else if(block_view == MfcEditorBlockViewKeyB) {
+                memcpy(
+                    mf_classic_get_sector_trailer_by_sector(
+                        mf_classic_data, instance->current_sector)
+                        ->key_b.data,
+                    instance->edit_buffer,
+                    MF_CLASSIC_KEY_SIZE);
+            } else if(block_view == MfcEditorBlockViewUserByte) {
+                mf_classic_get_sector_trailer_by_sector(mf_classic_data, instance->current_sector)
+                    ->access_bits.data[3] = instance->edit_buffer[0];
+            }
+        }
+        scene_manager_previous_scene(instance->scene_manager);
+        consumed = true;
+    }
+
+    return consumed;
+}
+
+void mfc_editor_scene_data_edit_on_exit(void* context) {
+    MfcEditorApp* instance = context;
+
+    if(instance->edit_buffer != NULL) {
+        free(instance->edit_buffer);
+        instance->edit_buffer = NULL;
+    }
+}

+ 102 - 0
scenes/mfc_editor_scene_data_edit_access_bits.c

@@ -0,0 +1,102 @@
+#include "../mfc_editor_app_i.h"
+
+void mfc_editor_scene_data_edit_access_bits_dialog_ex_callback(
+    DialogExResult result,
+    void* context) {
+    MfcEditorApp* instance = context;
+    view_dispatcher_send_custom_event(instance->view_dispatcher, result);
+}
+
+void mfc_editor_scene_data_edit_access_bits_update_display(MfcEditorApp* instance) {
+    DialogEx* dialog_ex = instance->dialog_ex;
+
+    uint8_t sector_trailer_num =
+        mf_classic_get_sector_trailer_num_by_sector(instance->current_sector);
+
+    furi_string_printf(
+        instance->data_view_text,
+        "C1: %i, C2: %i, C3: %i\n",
+        FURI_BIT(instance->access_bits_edit.bits, 0),
+        FURI_BIT(instance->access_bits_edit.bits, 1),
+        FURI_BIT(instance->access_bits_edit.bits, 2));
+
+    if(instance->current_block == sector_trailer_num) {
+        furi_string_cat(
+            instance->data_view_text,
+            access_sector_trailer_labels[instance->access_bits_edit.bits]);
+    } else {
+        furi_string_cat(
+            instance->data_view_text, access_data_block_labels[instance->access_bits_edit.bits]);
+    }
+
+    dialog_ex_set_text(
+        dialog_ex,
+        furi_string_get_cstr(instance->data_view_text),
+        63,
+        31,
+        AlignCenter,
+        AlignCenter);
+}
+
+void mfc_editor_scene_data_edit_access_bits_on_enter(void* context) {
+    MfcEditorApp* instance = context;
+
+    DialogEx* dialog_ex = instance->dialog_ex;
+
+    dialog_ex_set_context(instance->dialog_ex, instance);
+
+    furi_string_printf(
+        instance->data_view_header, "Edit Block %u Access", instance->current_block);
+    dialog_ex_set_header(
+        dialog_ex, furi_string_get_cstr(instance->data_view_header), 63, 3, AlignCenter, AlignTop);
+
+    instance->access_bits_edit =
+        mfc_editor_get_block_access_bits(instance->mf_classic_data, instance->current_block);
+
+    if(instance->access_bits_edit.bits != instance->access_bits_edit.check_bits) {
+        // If access check bits don't match, make them match now
+        instance->access_bits_edit.check_bits = instance->access_bits_edit.bits;
+    }
+
+    mfc_editor_scene_data_edit_access_bits_update_display(instance);
+
+    dialog_ex_set_center_button_text(dialog_ex, "Save");
+    dialog_ex_set_left_button_text(dialog_ex, "Prev");
+    dialog_ex_set_right_button_text(dialog_ex, "Next");
+
+    dialog_ex_set_result_callback(
+        dialog_ex, mfc_editor_scene_data_edit_access_bits_dialog_ex_callback);
+    view_dispatcher_switch_to_view(instance->view_dispatcher, MfcEditorAppViewDialogEx);
+}
+
+bool mfc_editor_scene_data_edit_access_bits_on_event(void* context, SceneManagerEvent event) {
+    MfcEditorApp* instance = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == DialogExResultLeft) {
+            instance->access_bits_edit.bits--;
+            instance->access_bits_edit.check_bits = instance->access_bits_edit.bits;
+            mfc_editor_scene_data_edit_access_bits_update_display(instance);
+            consumed = true;
+        } else if(event.event == DialogExResultRight) {
+            instance->access_bits_edit.bits++;
+            instance->access_bits_edit.check_bits = instance->access_bits_edit.bits;
+            mfc_editor_scene_data_edit_access_bits_update_display(instance);
+            consumed = true;
+        } else if(event.event == DialogExResultCenter) {
+            mfc_editor_set_block_access_bits(
+                instance->mf_classic_data, instance->current_block, &instance->access_bits_edit);
+            scene_manager_previous_scene(instance->scene_manager);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void mfc_editor_scene_data_edit_access_bits_on_exit(void* context) {
+    MfcEditorApp* instance = context;
+
+    dialog_ex_reset(instance->dialog_ex);
+}

+ 68 - 33
scenes/mfc_editor_scene_data_view.c

@@ -1,32 +1,18 @@
 #include "../mfc_editor_app_i.h"
 #include "../mfc_editor_app_i.h"
 
 
-const char* access_data_block_labels[8] = {
-    // C3, C2, C1
-    "Key A: Read, Write, Inc, Dec\nKey B: Read, Write, Inc, Dec", // 000
-    "Key A: Read\nKey B: Read, Write", // 001
-    "Key A: Read\nKey B: Read", // 010
-    "Key A: Read, Dec\nKey B: Read, Write, Inc, Dec", // 011
-    "Key A: Read, Dec\nKey B: Read, Dec", // 100
-    "Key A: No Access\nKey B: Read", // 101
-    "Key A: No Access\nKey B: Read, Write", // 110
-    "Key A: No Access\nKey B: No Access", // 111
-};
-
-const char* access_sector_trailer_labels[8] = {
-    // C3, C2, C1
-    "Key A: KA-W, AB-R, KB-RW\nKey B: No Access", // 000
-    "Key A: AB-R\nKey B: KA+KB-W, AB-R", // 001
-    "Key A: AB+KB-R\nKey B: No Access", // 010
-    "Key A: AB-R\nKey B: AB-R", // 011
-    "Key A: KA-W, AB+KB-RW\nKey B: No Access", // 100
-    "Key A: AB-R\nKey B: AB-RW", // 101
-    "Key A: AB-R\nKey B: KA+KB-W, AB-RW", // 110
-    "Key A: AB-R\nKey B: AB-R", // 111
-};
+void mfc_editor_scene_data_view_dialog_ex_callback(DialogExResult result, void* context) {
+    MfcEditorApp* instance = context;
+    view_dispatcher_send_custom_event(instance->view_dispatcher, result);
+}
 
 
 void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
 void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
     DialogEx* dialog_ex = instance->dialog_ex;
     DialogEx* dialog_ex = instance->dialog_ex;
 
 
+    dialog_ex_reset(instance->dialog_ex);
+    dialog_ex_set_context(instance->dialog_ex, instance);
+    dialog_ex_set_result_callback(
+        instance->dialog_ex, mfc_editor_scene_data_view_dialog_ex_callback);
+
     MfcEditorBlockView block_view =
     MfcEditorBlockView block_view =
         scene_manager_get_scene_state(instance->scene_manager, MfcEditorSceneDataView);
         scene_manager_get_scene_state(instance->scene_manager, MfcEditorSceneDataView);
 
 
@@ -43,6 +29,10 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
         }
         }
         // Remove trailing space
         // Remove trailing space
         furi_string_trim(instance->data_view_text);
         furi_string_trim(instance->data_view_text);
+
+        if(mf_classic_is_block_read(mf_classic_data, 0)) {
+            dialog_ex_set_right_button_text(dialog_ex, "Edit");
+        }
     } else if(block_view == MfcEditorBlockViewBCC) {
     } else if(block_view == MfcEditorBlockViewBCC) {
         dialog_ex_set_header(dialog_ex, "Block Check Character", 63, 3, AlignCenter, AlignTop);
         dialog_ex_set_header(dialog_ex, "Block Check Character", 63, 3, AlignCenter, AlignTop);
 
 
@@ -59,6 +49,7 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
 
 
             if(stored_bcc != calculated_bcc) {
             if(stored_bcc != calculated_bcc) {
                 furi_string_cat(instance->data_view_text, "\n(Mismatch!)");
                 furi_string_cat(instance->data_view_text, "\n(Mismatch!)");
+                dialog_ex_set_center_button_text(dialog_ex, "Fix");
             }
             }
         } else {
         } else {
             furi_string_printf(
             furi_string_printf(
@@ -85,6 +76,8 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
             }
             }
             // Remove trailing space
             // Remove trailing space
             furi_string_trim(instance->data_view_text);
             furi_string_trim(instance->data_view_text);
+
+            dialog_ex_set_right_button_text(dialog_ex, "Edit");
         } else {
         } else {
             furi_string_set(
             furi_string_set(
                 instance->data_view_text, "Data unavailable.\nBlock 0 has not been read.");
                 instance->data_view_text, "Data unavailable.\nBlock 0 has not been read.");
@@ -105,6 +98,7 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
                 key_a.data[3],
                 key_a.data[3],
                 key_a.data[4],
                 key_a.data[4],
                 key_a.data[5]);
                 key_a.data[5]);
+            dialog_ex_set_right_button_text(dialog_ex, "Edit");
         } else {
         } else {
             furi_string_set(
             furi_string_set(
                 instance->data_view_text, "Key A has not been found\nfor this sector.");
                 instance->data_view_text, "Key A has not been found\nfor this sector.");
@@ -125,6 +119,7 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
                 key_b.data[3],
                 key_b.data[3],
                 key_b.data[4],
                 key_b.data[4],
                 key_b.data[5]);
                 key_b.data[5]);
+            dialog_ex_set_right_button_text(dialog_ex, "Edit");
         } else {
         } else {
             furi_string_set(
             furi_string_set(
                 instance->data_view_text, "Key B has not been found\nfor this sector.");
                 instance->data_view_text, "Key B has not been found\nfor this sector.");
@@ -171,6 +166,7 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
 
 
             dialog_ex_set_center_button_text(dialog_ex, "Next");
             dialog_ex_set_center_button_text(dialog_ex, "Next");
             dialog_ex_set_left_button_text(dialog_ex, "Prev");
             dialog_ex_set_left_button_text(dialog_ex, "Prev");
+            dialog_ex_set_right_button_text(dialog_ex, "Edit");
         } else {
         } else {
             dialog_ex_set_header(dialog_ex, "Access Bits", 63, 3, AlignCenter, AlignTop);
             dialog_ex_set_header(dialog_ex, "Access Bits", 63, 3, AlignCenter, AlignTop);
             furi_string_printf(
             furi_string_printf(
@@ -189,6 +185,7 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
                 instance->data_view_text,
                 instance->data_view_text,
                 "Free byte between\nAccess Bits and Key B:\n%02X",
                 "Free byte between\nAccess Bits and Key B:\n%02X",
                 mf_classic_data->block[sector_trailer_num].data[9]);
                 mf_classic_data->block[sector_trailer_num].data[9]);
+            dialog_ex_set_right_button_text(dialog_ex, "Edit");
         } else {
         } else {
             furi_string_printf(
             furi_string_printf(
                 instance->data_view_text,
                 instance->data_view_text,
@@ -226,6 +223,7 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
             }
             }
             // Remove trailing space
             // Remove trailing space
             furi_string_trim(instance->data_view_text);
             furi_string_trim(instance->data_view_text);
+            dialog_ex_set_right_button_text(dialog_ex, "Edit");
         } else {
         } else {
             furi_string_set(
             furi_string_set(
                 instance->data_view_text, "Data unavailable.\nBlock has not been fully read.");
                 instance->data_view_text, "Data unavailable.\nBlock has not been fully read.");
@@ -241,20 +239,11 @@ void mfc_editor_scene_data_view_update_display(MfcEditorApp* instance) {
         AlignCenter);
         AlignCenter);
 }
 }
 
 
-void mfc_editor_scene_data_view_dialog_ex_callback(DialogExResult result, void* context) {
-    MfcEditorApp* instance = context;
-    view_dispatcher_send_custom_event(instance->view_dispatcher, result);
-}
-
 void mfc_editor_scene_data_view_on_enter(void* context) {
 void mfc_editor_scene_data_view_on_enter(void* context) {
     MfcEditorApp* instance = context;
     MfcEditorApp* instance = context;
 
 
-    dialog_ex_set_context(instance->dialog_ex, instance);
-
     mfc_editor_scene_data_view_update_display(instance);
     mfc_editor_scene_data_view_update_display(instance);
 
 
-    dialog_ex_set_result_callback(
-        instance->dialog_ex, mfc_editor_scene_data_view_dialog_ex_callback);
     view_dispatcher_switch_to_view(instance->view_dispatcher, MfcEditorAppViewDialogEx);
     view_dispatcher_switch_to_view(instance->view_dispatcher, MfcEditorAppViewDialogEx);
 }
 }
 
 
@@ -265,7 +254,42 @@ bool mfc_editor_scene_data_view_on_event(void* context, SceneManagerEvent event)
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         MfcEditorBlockView block_view =
         MfcEditorBlockView block_view =
             scene_manager_get_scene_state(instance->scene_manager, MfcEditorSceneDataView);
             scene_manager_get_scene_state(instance->scene_manager, MfcEditorSceneDataView);
-        if(block_view == MfcEditorBlockViewAccessBits) {
+        if(block_view == MfcEditorBlockViewNormal) {
+            if(event.event == DialogExResultRight) {
+                // Block 0 and sector trailer blocks are risky edits
+                bool risky_block =
+                    instance->current_block == 0 ||
+                    instance->current_block ==
+                        mf_classic_get_sector_trailer_num_by_block(instance->current_block);
+                if(!risky_block || mfc_editor_warn_risky_operation(instance)) {
+                    scene_manager_set_scene_state(
+                        instance->scene_manager, MfcEditorSceneDataEdit, block_view);
+                    scene_manager_next_scene(instance->scene_manager, MfcEditorSceneDataEdit);
+                }
+                consumed = true;
+            }
+        } else if(
+            block_view == MfcEditorBlockViewUID ||
+            block_view == MfcEditorBlockViewManufacturerBytes ||
+            block_view == MfcEditorBlockViewKeyA || block_view == MfcEditorBlockViewKeyB) {
+            if(event.event == DialogExResultRight) {
+                if(mfc_editor_warn_risky_operation(instance)) {
+                    scene_manager_set_scene_state(
+                        instance->scene_manager, MfcEditorSceneDataEdit, block_view);
+                    scene_manager_next_scene(instance->scene_manager, MfcEditorSceneDataEdit);
+                }
+                consumed = true;
+            }
+        } else if(block_view == MfcEditorBlockViewBCC) {
+            if(event.event == DialogExResultCenter) {
+                // Fix BCC byte by setting it to calculated one
+                instance->mf_classic_data->block[0].data[4] = mfc_editor_calculate_uid_bcc(
+                    instance->mf_classic_data->iso14443_3a_data->uid,
+                    instance->mf_classic_data->iso14443_3a_data->uid_len);
+                mfc_editor_scene_data_view_update_display(instance);
+                consumed = true;
+            }
+        } else if(block_view == MfcEditorBlockViewAccessBits) {
             if(event.event == DialogExResultLeft) {
             if(event.event == DialogExResultLeft) {
                 uint8_t new_sector = mf_classic_get_sector_by_block(--instance->current_block);
                 uint8_t new_sector = mf_classic_get_sector_by_block(--instance->current_block);
                 if(new_sector != instance->current_sector) {
                 if(new_sector != instance->current_sector) {
@@ -282,7 +306,18 @@ bool mfc_editor_scene_data_view_on_event(void* context, SceneManagerEvent event)
                 }
                 }
                 mfc_editor_scene_data_view_update_display(instance);
                 mfc_editor_scene_data_view_update_display(instance);
                 consumed = true;
                 consumed = true;
+            } else if(event.event == DialogExResultRight) {
+                if(mfc_editor_warn_risky_operation(instance)) {
+                    scene_manager_next_scene(
+                        instance->scene_manager, MfcEditorSceneDataEditAccessBits);
+                }
+                consumed = true;
             }
             }
+        } else if(block_view == MfcEditorBlockViewUserByte) {
+            scene_manager_set_scene_state(
+                instance->scene_manager, MfcEditorSceneDataEdit, block_view);
+            scene_manager_next_scene(instance->scene_manager, MfcEditorSceneDataEdit);
+            consumed = true;
         }
         }
     }
     }