|
@@ -0,0 +1,339 @@
|
|
|
|
|
+#include "../mfc_editor_app_i.h"
|
|
|
|
|
+
|
|
|
|
|
+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) {
|
|
|
|
|
+ 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 =
|
|
|
|
|
+ scene_manager_get_scene_state(instance->scene_manager, MfcEditorSceneDataView);
|
|
|
|
|
+
|
|
|
|
|
+ MfClassicData* mf_classic_data = instance->mf_classic_data;
|
|
|
|
|
+ Iso14443_3aData* iso14443_3a_data = mf_classic_data->iso14443_3a_data;
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_reset(instance->data_view_text);
|
|
|
|
|
+
|
|
|
|
|
+ if(block_view == MfcEditorBlockViewUID) {
|
|
|
|
|
+ dialog_ex_set_header(dialog_ex, "UID", 63, 3, AlignCenter, AlignTop);
|
|
|
|
|
+
|
|
|
|
|
+ mfc_editor_furi_string_render_bytes(
|
|
|
|
|
+ instance->data_view_text, iso14443_3a_data->uid, iso14443_3a_data->uid_len);
|
|
|
|
|
+
|
|
|
|
|
+ if(memcmp(
|
|
|
|
|
+ iso14443_3a_data->uid, mf_classic_data->block[0].data, iso14443_3a_data->uid_len)) {
|
|
|
|
|
+ // ISO-14443 UID does not match first bytes of block 0
|
|
|
|
|
+ furi_string_cat_printf(instance->data_view_text, "\nBlock 0 does not match UID!\n(");
|
|
|
|
|
+ mfc_editor_furi_string_render_bytes(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ mf_classic_data->block[0].data,
|
|
|
|
|
+ iso14443_3a_data->uid_len);
|
|
|
|
|
+ furi_string_push_back(instance->data_view_text, ')');
|
|
|
|
|
+ dialog_ex_set_center_button_text(dialog_ex, "Fix");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(mf_classic_is_block_read(mf_classic_data, 0)) {
|
|
|
|
|
+ dialog_ex_set_right_button_text(dialog_ex, "Edit");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if(block_view == MfcEditorBlockViewBCC) {
|
|
|
|
|
+ dialog_ex_set_header(dialog_ex, "Block Check Character", 63, 3, AlignCenter, AlignTop);
|
|
|
|
|
+
|
|
|
|
|
+ uint8_t stored_bcc = mf_classic_data->block[0].data[4];
|
|
|
|
|
+ uint8_t calculated_bcc =
|
|
|
|
|
+ mfc_editor_calculate_uid_bcc(iso14443_3a_data->uid, iso14443_3a_data->uid_len);
|
|
|
|
|
+
|
|
|
|
|
+ if(mf_classic_is_block_read(mf_classic_data, 0)) {
|
|
|
|
|
+ furi_string_printf(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ "Stored BCC: %02X\nActual BCC: %02X",
|
|
|
|
|
+ stored_bcc,
|
|
|
|
|
+ calculated_bcc);
|
|
|
|
|
+
|
|
|
|
|
+ if(stored_bcc != calculated_bcc) {
|
|
|
|
|
+ furi_string_cat(instance->data_view_text, "\n(Mismatch!)");
|
|
|
|
|
+ dialog_ex_set_center_button_text(dialog_ex, "Fix");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_string_printf(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ "Actual BCC: %02X\nStored BCC is unavailable\nas Block 0 has not been read.",
|
|
|
|
|
+ calculated_bcc);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if(block_view == MfcEditorBlockViewManufacturerBytes) {
|
|
|
|
|
+ dialog_ex_set_header(dialog_ex, "Manufacturer Bytes", 63, 3, AlignCenter, AlignTop);
|
|
|
|
|
+
|
|
|
|
|
+ if(mf_classic_is_block_read(mf_classic_data, 0)) {
|
|
|
|
|
+ // Skip BCC byte (not present on 7B UID cards)
|
|
|
|
|
+ bool skip_byte = iso14443_3a_data->uid_len == 4;
|
|
|
|
|
+ uint8_t start_index = iso14443_3a_data->uid_len + skip_byte;
|
|
|
|
|
+ uint8_t byte_num = MF_CLASSIC_BLOCK_SIZE - iso14443_3a_data->uid_len - skip_byte;
|
|
|
|
|
+ uint8_t line_len = byte_num / 2;
|
|
|
|
|
+
|
|
|
|
|
+ mfc_editor_furi_string_render_bytes(
|
|
|
|
|
+ instance->data_view_text, mf_classic_data->block[0].data + start_index, line_len);
|
|
|
|
|
+ furi_string_push_back(instance->data_view_text, '\n');
|
|
|
|
|
+ mfc_editor_furi_string_render_bytes(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ mf_classic_data->block[0].data + start_index + line_len,
|
|
|
|
|
+ byte_num - line_len);
|
|
|
|
|
+
|
|
|
|
|
+ dialog_ex_set_right_button_text(dialog_ex, "Edit");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_string_set(
|
|
|
|
|
+ instance->data_view_text, "Data unavailable.\nBlock 0 has not been read.");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if(block_view == MfcEditorBlockViewKeyA) {
|
|
|
|
|
+ dialog_ex_set_header(dialog_ex, "Key A", 63, 3, AlignCenter, AlignTop);
|
|
|
|
|
+
|
|
|
|
|
+ if(mf_classic_is_key_found(mf_classic_data, instance->current_sector, MfClassicKeyTypeA)) {
|
|
|
|
|
+ MfClassicSectorTrailer* sector_trailer =
|
|
|
|
|
+ mf_classic_get_sector_trailer_by_sector(mf_classic_data, instance->current_sector);
|
|
|
|
|
+ mfc_editor_furi_string_render_bytes(
|
|
|
|
|
+ instance->data_view_text, sector_trailer->key_a.data, MF_CLASSIC_KEY_SIZE);
|
|
|
|
|
+ dialog_ex_set_right_button_text(dialog_ex, "Edit");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_string_set(
|
|
|
|
|
+ instance->data_view_text, "Key A has not been found\nfor this sector.");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if(block_view == MfcEditorBlockViewKeyB) {
|
|
|
|
|
+ dialog_ex_set_header(dialog_ex, "Key B", 63, 3, AlignCenter, AlignTop);
|
|
|
|
|
+
|
|
|
|
|
+ if(mf_classic_is_key_found(mf_classic_data, instance->current_sector, MfClassicKeyTypeB)) {
|
|
|
|
|
+ MfClassicSectorTrailer* sector_trailer =
|
|
|
|
|
+ mf_classic_get_sector_trailer_by_sector(mf_classic_data, instance->current_sector);
|
|
|
|
|
+ mfc_editor_furi_string_render_bytes(
|
|
|
|
|
+ instance->data_view_text, sector_trailer->key_b.data, MF_CLASSIC_KEY_SIZE);
|
|
|
|
|
+ dialog_ex_set_right_button_text(dialog_ex, "Edit");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_string_set(
|
|
|
|
|
+ instance->data_view_text, "Key B has not been found\nfor this sector.");
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if(block_view == MfcEditorBlockViewAccessBits) {
|
|
|
|
|
+ uint8_t sector_trailer_num =
|
|
|
|
|
+ mf_classic_get_sector_trailer_num_by_sector(instance->current_sector);
|
|
|
|
|
+
|
|
|
|
|
+ if(mf_classic_is_block_read(mf_classic_data, sector_trailer_num)) {
|
|
|
|
|
+ furi_string_printf(
|
|
|
|
|
+ instance->data_view_header, "Access Bits (Block %u)", instance->current_block);
|
|
|
|
|
+ dialog_ex_set_header(
|
|
|
|
|
+ dialog_ex,
|
|
|
|
|
+ furi_string_get_cstr(instance->data_view_header),
|
|
|
|
|
+ 63,
|
|
|
|
|
+ 3,
|
|
|
|
|
+ AlignCenter,
|
|
|
|
|
+ AlignTop);
|
|
|
|
|
+
|
|
|
|
|
+ MfcEditorAccessBits access_bits =
|
|
|
|
|
+ mfc_editor_get_block_access_bits(mf_classic_data, instance->current_block);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_printf(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ "C1: %i(%i), C2: %i(%i), C3: %i(%i)\n",
|
|
|
|
|
+ FURI_BIT(access_bits.bits, 0),
|
|
|
|
|
+ FURI_BIT(access_bits.check_bits, 0),
|
|
|
|
|
+ FURI_BIT(access_bits.bits, 1),
|
|
|
|
|
+ FURI_BIT(access_bits.check_bits, 1),
|
|
|
|
|
+ FURI_BIT(access_bits.bits, 2),
|
|
|
|
|
+ FURI_BIT(access_bits.check_bits, 2));
|
|
|
|
|
+
|
|
|
|
|
+ if(access_bits.bits != access_bits.check_bits) {
|
|
|
|
|
+ furi_string_cat(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ "Access Bits are invalid.\nEntire sector inaccessible.");
|
|
|
|
|
+ } else if(instance->current_block == sector_trailer_num) {
|
|
|
|
|
+ furi_string_cat(
|
|
|
|
|
+ instance->data_view_text, access_sector_trailer_labels[access_bits.bits]);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_string_cat(
|
|
|
|
|
+ instance->data_view_text, access_data_block_labels[access_bits.bits]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ dialog_ex_set_center_button_text(dialog_ex, "Next");
|
|
|
|
|
+ dialog_ex_set_left_button_text(dialog_ex, "Prev");
|
|
|
|
|
+ dialog_ex_set_right_button_text(dialog_ex, "Edit");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ dialog_ex_set_header(dialog_ex, "Access Bits", 63, 3, AlignCenter, AlignTop);
|
|
|
|
|
+ furi_string_printf(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ "Access Bits unavailable.\nBlock %u has not been read.",
|
|
|
|
|
+ sector_trailer_num);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if(block_view == MfcEditorBlockViewUserByte) {
|
|
|
|
|
+ dialog_ex_set_header(dialog_ex, "User Byte", 63, 3, AlignCenter, AlignTop);
|
|
|
|
|
+
|
|
|
|
|
+ uint8_t sector_trailer_num =
|
|
|
|
|
+ mf_classic_get_sector_trailer_num_by_sector(instance->current_sector);
|
|
|
|
|
+
|
|
|
|
|
+ if(mf_classic_is_block_read(mf_classic_data, sector_trailer_num)) {
|
|
|
|
|
+ furi_string_printf(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ "Free byte between\nAccess Bits and Key B:\n%02X",
|
|
|
|
|
+ mf_classic_data->block[sector_trailer_num].data[9]);
|
|
|
|
|
+ dialog_ex_set_right_button_text(dialog_ex, "Edit");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_string_printf(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ "Data unavailable.\nBlock %u has not been read.",
|
|
|
|
|
+ sector_trailer_num);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uint8_t current_block = instance->current_block;
|
|
|
|
|
+ furi_string_printf(instance->data_view_header, "Block %u Data", current_block);
|
|
|
|
|
+ dialog_ex_set_header(
|
|
|
|
|
+ dialog_ex,
|
|
|
|
|
+ furi_string_get_cstr(instance->data_view_header),
|
|
|
|
|
+ 63,
|
|
|
|
|
+ 3,
|
|
|
|
|
+ AlignCenter,
|
|
|
|
|
+ AlignTop);
|
|
|
|
|
+
|
|
|
|
|
+ // Only display a block if it is fully read, and, if it is a sector trailer, both keys are found
|
|
|
|
|
+ if(mf_classic_is_block_read(mf_classic_data, current_block) &&
|
|
|
|
|
+ (!mf_classic_is_sector_trailer(current_block) ||
|
|
|
|
|
+ (mf_classic_is_key_found(
|
|
|
|
|
+ mf_classic_data, instance->current_sector, MfClassicKeyTypeA) &&
|
|
|
|
|
+ mf_classic_is_key_found(
|
|
|
|
|
+ mf_classic_data, instance->current_sector, MfClassicKeyTypeB)))) {
|
|
|
|
|
+ // Split block data across 2 even lines
|
|
|
|
|
+ const uint8_t* block_data = mf_classic_data->block[current_block].data;
|
|
|
|
|
+ mfc_editor_furi_string_render_bytes(
|
|
|
|
|
+ instance->data_view_text, block_data, MF_CLASSIC_BLOCK_SIZE / 2);
|
|
|
|
|
+ furi_string_push_back(instance->data_view_text, '\n');
|
|
|
|
|
+ mfc_editor_furi_string_render_bytes(
|
|
|
|
|
+ instance->data_view_text,
|
|
|
|
|
+ block_data + MF_CLASSIC_BLOCK_SIZE / 2,
|
|
|
|
|
+ MF_CLASSIC_BLOCK_SIZE / 2);
|
|
|
|
|
+ dialog_ex_set_right_button_text(dialog_ex, "Edit");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ furi_string_set(
|
|
|
|
|
+ instance->data_view_text, "Data unavailable.\nBlock has not been fully read.");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ dialog_ex_set_text(
|
|
|
|
|
+ dialog_ex,
|
|
|
|
|
+ furi_string_get_cstr(instance->data_view_text),
|
|
|
|
|
+ 63,
|
|
|
|
|
+ 31,
|
|
|
|
|
+ AlignCenter,
|
|
|
|
|
+ AlignCenter);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void mfc_editor_scene_data_view_on_enter(void* context) {
|
|
|
|
|
+ MfcEditorApp* instance = context;
|
|
|
|
|
+
|
|
|
|
|
+ mfc_editor_scene_data_view_update_display(instance);
|
|
|
|
|
+
|
|
|
|
|
+ view_dispatcher_switch_to_view(instance->view_dispatcher, MfcEditorAppViewDialogEx);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool mfc_editor_scene_data_view_on_event(void* context, SceneManagerEvent event) {
|
|
|
|
|
+ MfcEditorApp* instance = context;
|
|
|
|
|
+ bool consumed = false;
|
|
|
|
|
+
|
|
|
|
|
+ if(event.type == SceneManagerEventTypeCustom) {
|
|
|
|
|
+ MfcEditorBlockView block_view =
|
|
|
|
|
+ scene_manager_get_scene_state(instance->scene_manager, MfcEditorSceneDataView);
|
|
|
|
|
+ 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) {
|
|
|
|
|
+ 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(event.event == DialogExResultCenter) {
|
|
|
|
|
+ if(mfc_editor_warn_risky_operation(instance)) {
|
|
|
|
|
+ memcpy(
|
|
|
|
|
+ instance->mf_classic_data->block[0].data,
|
|
|
|
|
+ instance->mf_classic_data->iso14443_3a_data->uid,
|
|
|
|
|
+ instance->mf_classic_data->iso14443_3a_data->uid_len);
|
|
|
|
|
+ instance->is_unsaved_changes = true;
|
|
|
|
|
+ mfc_editor_scene_data_view_update_display(instance);
|
|
|
|
|
+ }
|
|
|
|
|
+ consumed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if(
|
|
|
|
|
+ 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) {
|
|
|
|
|
+ if(mfc_editor_warn_risky_operation(instance)) {
|
|
|
|
|
+ // 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);
|
|
|
|
|
+ instance->is_unsaved_changes = true;
|
|
|
|
|
+ mfc_editor_scene_data_view_update_display(instance);
|
|
|
|
|
+ }
|
|
|
|
|
+ consumed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if(block_view == MfcEditorBlockViewAccessBits) {
|
|
|
|
|
+ if(event.event == DialogExResultLeft) {
|
|
|
|
|
+ uint8_t new_sector = mf_classic_get_sector_by_block(--instance->current_block);
|
|
|
|
|
+ if(new_sector != instance->current_sector) {
|
|
|
|
|
+ instance->current_block =
|
|
|
|
|
+ mf_classic_get_sector_trailer_num_by_sector(instance->current_sector);
|
|
|
|
|
+ }
|
|
|
|
|
+ mfc_editor_scene_data_view_update_display(instance);
|
|
|
|
|
+ consumed = true;
|
|
|
|
|
+ } else if(event.event == DialogExResultCenter) {
|
|
|
|
|
+ uint8_t new_sector = mf_classic_get_sector_by_block(++instance->current_block);
|
|
|
|
|
+ if(new_sector != instance->current_sector) {
|
|
|
|
|
+ instance->current_block =
|
|
|
|
|
+ mf_classic_get_first_block_num_of_sector(instance->current_sector);
|
|
|
|
|
+ }
|
|
|
|
|
+ mfc_editor_scene_data_view_update_display(instance);
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return consumed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void mfc_editor_scene_data_view_on_exit(void* context) {
|
|
|
|
|
+ MfcEditorApp* instance = context;
|
|
|
|
|
+
|
|
|
|
|
+ dialog_ex_reset(instance->dialog_ex);
|
|
|
|
|
+}
|