Skip to content

Commit

Permalink
add mfc_editor app
Browse files Browse the repository at this point in the history
  • Loading branch information
xMasterX committed Jan 30, 2024
1 parent 37833bb commit f44ede8
Show file tree
Hide file tree
Showing 20 changed files with 2,304 additions and 0 deletions.
674 changes: 674 additions & 0 deletions base_pack/mfc_editor/LICENSE

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions base_pack/mfc_editor/application.fam
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
App(
appid="mfc_editor",
name="MIFARE Classic Editor",
apptype=FlipperAppType.EXTERNAL,
targets=["f7"],
entry_point="mfc_editor_app",
requires=[
"storage",
"gui",
],
stack_size=4 * 1024,
fap_description="Application for viewing and editing MIFARE Classic .nfc files",
fap_version="1.0",
fap_icon="assets/app_icon_10px.png",
fap_category="NFC",
fap_icon_assets="assets",
fap_author="TollyH",
fap_weburl="https://github.com/TollyH/flipper-apps/tree/main/mfc-editor",
)
Binary file added base_pack/mfc_editor/assets/DolphinSaved_92x58.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added base_pack/mfc_editor/assets/Nfc_10px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added base_pack/mfc_editor/assets/app_icon_10px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
312 changes: 312 additions & 0 deletions base_pack/mfc_editor/mfc_editor_app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
#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) {
furi_assert(context);
MfcEditorApp* instance = context;
return scene_manager_handle_custom_event(instance->scene_manager, event);
}

bool mfc_editor_app_back_event_callback(void* context) {
furi_assert(context);
MfcEditorApp* instance = context;
return scene_manager_handle_back_event(instance->scene_manager);
}

void mfc_editor_app_tick_event_callback(void* context) {
furi_assert(context);
MfcEditorApp* instance = context;
scene_manager_handle_tick_event(instance->scene_manager);
}

MfcEditorApp* mfc_editor_app_alloc() {
MfcEditorApp* instance = malloc(sizeof(MfcEditorApp));

instance->view_dispatcher = view_dispatcher_alloc();
instance->scene_manager = scene_manager_alloc(&mfc_editor_scene_handlers, instance);
view_dispatcher_enable_queue(instance->view_dispatcher);
view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);
view_dispatcher_set_custom_event_callback(
instance->view_dispatcher, mfc_editor_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
instance->view_dispatcher, mfc_editor_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
instance->view_dispatcher, mfc_editor_app_tick_event_callback, 100);

instance->gui = furi_record_open(RECORD_GUI);
view_dispatcher_attach_to_gui(
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);

instance->storage = furi_record_open(RECORD_STORAGE);

instance->dialogs = furi_record_open(RECORD_DIALOGS);

instance->mf_classic_data = mf_classic_alloc();
instance->file_path = furi_string_alloc_set(NFC_APP_FOLDER);

instance->data_view_header = furi_string_alloc();
instance->data_view_text = furi_string_alloc();

instance->submenu = submenu_alloc();
view_dispatcher_add_view(
instance->view_dispatcher, MfcEditorAppViewSubmenu, submenu_get_view(instance->submenu));

instance->popup = popup_alloc();
view_dispatcher_add_view(
instance->view_dispatcher, MfcEditorAppViewPopup, popup_get_view(instance->popup));

instance->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
instance->view_dispatcher,
MfcEditorAppViewDialogEx,
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;
}

void mfc_editor_app_free(MfcEditorApp* instance) {
furi_assert(instance);

view_dispatcher_remove_view(instance->view_dispatcher, MfcEditorAppViewSubmenu);
submenu_free(instance->submenu);

view_dispatcher_remove_view(instance->view_dispatcher, MfcEditorAppViewPopup);
popup_free(instance->popup);

view_dispatcher_remove_view(instance->view_dispatcher, MfcEditorAppViewDialogEx);
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);
scene_manager_free(instance->scene_manager);

furi_record_close(RECORD_GUI);
instance->gui = NULL;

furi_record_close(RECORD_STORAGE);
instance->storage = NULL;

furi_record_close(RECORD_DIALOGS);
instance->dialogs = NULL;

mf_classic_free(instance->mf_classic_data);
furi_string_free(instance->file_path);

furi_string_free(instance->data_view_header);
furi_string_free(instance->data_view_text);

free(instance);
}

MfcEditorPromptResponse mfc_editor_load_file(MfcEditorApp* instance, FuriString* file_path) {
furi_assert(instance);
furi_assert(file_path);

MfcEditorPromptResponse result = MfcEditorPromptResponseSuccess;

NfcDevice* nfc_device = nfc_device_alloc();

if(!nfc_device_load(nfc_device, furi_string_get_cstr(file_path))) {
result = MfcEditorPromptResponseFailure;
dialog_message_show_storage_error(instance->dialogs, "Cannot load\nkey file");
} else {
if(nfc_device_get_protocol(nfc_device) == NfcProtocolMfClassic) {
const MfClassicData* mf_classic_data =
nfc_device_get_data(nfc_device, NfcProtocolMfClassic);
mf_classic_copy(instance->mf_classic_data, mf_classic_data);
instance->is_unsaved_changes = false;
} else {
result = MfcEditorPromptResponseNotMfClassic;
}
}

nfc_device_free(nfc_device);

return result;
}

bool mfc_editor_save_file(MfcEditorApp* instance) {
furi_assert(instance);
furi_assert(instance->file_path);
furi_assert(instance->mf_classic_data);

NfcDevice* nfc_device = nfc_device_alloc();

nfc_device_set_data(nfc_device, NfcProtocolMfClassic, instance->mf_classic_data);

bool result = nfc_device_save(nfc_device, furi_string_get_cstr(instance->file_path));
if(!result) {
dialog_message_show_storage_error(instance->dialogs, "Cannot save\nkey file");
}

nfc_device_free(nfc_device);

return result;
}

static DialogMessageButton mfc_editor_prompt_should_load_shadow(MfcEditorApp* instance) {
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "File has modifications", 63, 3, AlignCenter, AlignTop);
dialog_message_set_text(
message,
"Would you like to edit the\nmodified file (recommended)\nor the original file?",
63,
31,
AlignCenter,
AlignCenter);
dialog_message_set_buttons(message, "Original", NULL, "Modified");

DialogMessageButton message_button = dialog_message_show(instance->dialogs, message);

dialog_message_free(message);

return message_button;
}

static void mfc_editor_get_shadow_file_path(FuriString* file_path, FuriString* shadow_file_path) {
furi_assert(file_path);
furi_assert(shadow_file_path);

// Remove NFC extension from end of string then append shadow extension
furi_string_set_n(shadow_file_path, file_path, 0, furi_string_size(file_path) - 4);
furi_string_cat_printf(shadow_file_path, "%s", NFC_APP_SHADOW_EXTENSION);
}

static bool mfc_editor_file_has_shadow_file(MfcEditorApp* instance, FuriString* file_path) {
furi_assert(instance);
furi_assert(file_path);

FuriString* shadow_file_path = furi_string_alloc();
mfc_editor_get_shadow_file_path(file_path, shadow_file_path);
bool has_shadow_file =
storage_common_exists(instance->storage, furi_string_get_cstr(shadow_file_path));

furi_string_free(shadow_file_path);

return has_shadow_file;
}

MfcEditorPromptResponse mfc_editor_prompt_load_file(MfcEditorApp* instance) {
furi_assert(instance);

DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, NFC_APP_EXTENSION, &I_Nfc_10px);
browser_options.base_path = NFC_APP_FOLDER;
browser_options.hide_dot_files = true;

MfcEditorPromptResponse result = MfcEditorPromptResponseSuccess;
if(!dialog_file_browser_show(
instance->dialogs, instance->file_path, instance->file_path, &browser_options)) {
result = MfcEditorPromptResponseExitedFile;
} else {
if(mfc_editor_file_has_shadow_file(instance, instance->file_path)) {
DialogMessageButton message_button = mfc_editor_prompt_should_load_shadow(instance);

if(message_button == DialogMessageButtonRight) {
// User selected to use shadow file, so replace selected path with that path
FuriString* shadow_file_path = furi_string_alloc();
mfc_editor_get_shadow_file_path(instance->file_path, shadow_file_path);
furi_string_set(instance->file_path, shadow_file_path);
furi_string_free(shadow_file_path);
} else if(message_button == DialogMessageButtonBack) {
result = MfcEditorPromptResponseExitedShadow;
}
}

// Don't load the file if user was prompted for shadow file use but went back
if(result == MfcEditorPromptResponseSuccess) {
result = mfc_editor_load_file(instance, instance->file_path);
}
}

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;
}

MfcEditorSaveResponse mfc_editor_warn_unsaved_changes(MfcEditorApp* instance) {
DialogMessage* message = dialog_message_alloc();
dialog_message_set_header(message, "Unsaved changes", 63, 3, AlignCenter, AlignTop);
dialog_message_set_text(
message,
"Would you like to save?\nDiscarding your\nchanges is permanent.",
63,
31,
AlignCenter,
AlignCenter);
dialog_message_set_buttons(message, "Discrd", "Save", "Cancel");

DialogMessageButton message_button = dialog_message_show(instance->dialogs, message);

dialog_message_free(message);

if(message_button == DialogMessageButtonCenter) {
return MfcEditorSaveResponseSave;
} else if(message_button == DialogMessageButtonLeft) {
return MfcEditorSaveResponseDiscard;
} else {
return MfcEditorSaveResponseCancel;
}
}

int32_t mfc_editor_app(void* p) {
UNUSED(p);

MfcEditorApp* instance = mfc_editor_app_alloc();

scene_manager_next_scene(instance->scene_manager, MfcEditorSceneFileSelect);

view_dispatcher_run(instance->view_dispatcher);

mfc_editor_app_free(instance);

return 0;
}
3 changes: 3 additions & 0 deletions base_pack/mfc_editor/mfc_editor_app.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

typedef struct MfcEditorApp MfcEditorApp;
Loading

0 comments on commit f44ede8

Please sign in to comment.