Procházet zdrojové kódy

Add t5577_multiwriter from https://github.com/xMasterX/all-the-plugins

git-subtree-dir: t5577_multiwriter
git-subtree-mainline: 1457614a6a250c46463e7c37c5aad2a0328cee81
git-subtree-split: 7929507b5776f74a008df0827849164b8a3a6561
Willy-JL před 1 rokem
rodič
revize
c43c75edb5

+ 1 - 0
t5577_multiwriter/.gitsubtree

@@ -0,0 +1 @@
+https://github.com/xMasterX/all-the-plugins dev non_catalog_apps/t5577_multiwriter

+ 3 - 0
t5577_multiwriter/README.md

@@ -0,0 +1,3 @@
+ # T5577 multivriter fap for flipper zero
+
+ PoC of https://forum.dangerousthings.com/t/hack-store-2-to-3-different-em41xx-on-a-single-t5577/12116

+ 14 - 0
t5577_multiwriter/application.fam

@@ -0,0 +1,14 @@
+App(
+    appid="t5577_multiwriter",
+    name="T5577 multiwriter",
+    apptype=FlipperAppType.EXTERNAL,
+    targets=["f7"],
+    entry_point="t5577_multiwriter_app",
+    icon="A_125khz_14",
+    stack_size=2 * 1024,
+    order=20,
+    fap_description="Application for writing several keys to one t5577",
+    fap_version="0.2",
+    fap_icon="icon.png",
+    fap_category="RFID",
+)

+ 109 - 0
t5577_multiwriter/assets/assets_icons.c

@@ -0,0 +1,109 @@
+#include "assets_icons.h"
+
+#include <gui/icon_i.h>
+
+const uint8_t _I_125_10px_0[] = {
+    0x00, 0xe0, 0x00, 0x00, 0x01, 0x0e, 0x02, 0x31, 0x02, 0x45, 0x02,
+    0x91, 0x00, 0xaa, 0x00, 0x92, 0x00, 0x44, 0x00, 0x38, 0x00,
+};
+const uint8_t* const _I_125_10px[] = {_I_125_10px_0};
+
+const uint8_t _I_DolphinSuccess_91x55_0[] = {
+    0x01, 0x00, 0xb4, 0x01, 0x00, 0x0f, 0x03, 0xfe, 0x0b, 0x9d, 0xc4, 0x02, 0x1e, 0x0b, 0x88, 0x34,
+    0x02, 0x60, 0x0a, 0x7c, 0x04, 0x2c, 0x4a, 0x20, 0xb7, 0xb0, 0x40, 0x40, 0x63, 0x03, 0x05, 0xb6,
+    0xc2, 0x04, 0x03, 0x18, 0x20, 0x2d, 0xab, 0x00, 0x32, 0x84, 0x02, 0xdb, 0x58, 0x03, 0x28, 0x80,
+    0x2d, 0xa2, 0xc0, 0x32, 0x90, 0x02, 0xda, 0x56, 0x0b, 0xa8, 0x28, 0x0c, 0xa8, 0x05, 0xe6, 0x95,
+    0x02, 0xe8, 0x2e, 0x33, 0xe0, 0x5b, 0xf0, 0x21, 0x58, 0x0e, 0x08, 0x40, 0x41, 0x05, 0x44, 0x32,
+    0x0d, 0x02, 0x03, 0x40, 0xbe, 0x40, 0x41, 0x6c, 0x17, 0x19, 0x06, 0x7a, 0x06, 0x0b, 0x2a, 0x05,
+    0x22, 0x84, 0x8e, 0x29, 0x04, 0x3d, 0x1c, 0x29, 0x31, 0xc0, 0x52, 0x24, 0x50, 0x0d, 0x04, 0x2a,
+    0x21, 0x0f, 0x83, 0x82, 0xc8, 0x04, 0x27, 0xc1, 0x0b, 0x8e, 0x43, 0x3e, 0x0b, 0x40, 0x01, 0x05,
+    0xc7, 0x21, 0xff, 0x1a, 0x8f, 0xc4, 0x4c, 0x3b, 0xc8, 0x1c, 0x1a, 0xa1, 0x00, 0xc8, 0x6f, 0xdb,
+    0x23, 0x0f, 0x86, 0x46, 0x20, 0x58, 0x48, 0x0e, 0xc0, 0x2e, 0x50, 0x7e, 0x00, 0xb0, 0x6c, 0x18,
+    0x28, 0x4c, 0x00, 0x2e, 0x32, 0x0b, 0xfc, 0x0e, 0x70, 0x9d, 0x28, 0x14, 0x49, 0xac, 0x25, 0xf1,
+    0xff, 0xc0, 0xc7, 0x82, 0x0b, 0x1c, 0x14, 0x54, 0x2e, 0x54, 0x08, 0x1b, 0xb8, 0xa4, 0x17, 0x8c,
+    0x16, 0x56, 0x0e, 0x17, 0x1c, 0x04, 0x05, 0xdc, 0x53, 0xf1, 0x18, 0x81, 0x04, 0x2e, 0x28, 0xc3,
+    0xf0, 0x0b, 0xc8, 0xd6, 0x21, 0x72, 0x05, 0x94, 0x60, 0x16, 0x81, 0x71, 0x00, 0x1c, 0x1a, 0x41,
+    0x60, 0x81, 0x64, 0x0b, 0xe4, 0xd0, 0x35, 0x00, 0x9a, 0xc6, 0x21, 0x06, 0xac, 0x3e, 0x4e, 0xc1,
+    0x00, 0x10, 0x0f, 0xf2, 0x1f, 0x00, 0xd4, 0x05, 0xc4, 0x16, 0x31, 0x88, 0x04, 0x21, 0xe8, 0x61,
+    0x72, 0x02, 0x0a, 0x40, 0x60, 0x84, 0x11, 0x00, 0x88, 0x5c, 0x9e, 0x02, 0xb2, 0x18, 0x1d, 0x01,
+    0xc6, 0x30, 0x10, 0xb9, 0x40, 0x27, 0xc6, 0x33, 0x77, 0x90, 0x5c, 0x80, 0x04, 0x3c, 0x18, 0x05,
+    0x08, 0x64, 0x35, 0xc2, 0xfa, 0x11, 0x84, 0x16, 0x7a, 0xc9, 0x50, 0x2c, 0xe1, 0x81, 0x14, 0xaf,
+    0x3a, 0x91, 0x30, 0x33, 0xc0, 0x29, 0x6b, 0x82, 0xe7, 0xc0, 0x5a, 0x8d, 0x09, 0xb6, 0x30, 0x0a,
+    0xfc, 0x14, 0x16, 0x5f, 0x80, 0x58, 0xe0, 0x66, 0x01, 0xb1, 0xd7, 0xea, 0x81, 0x65, 0xff, 0x81,
+    0x60, 0x30, 0x10, 0x82, 0x98, 0x85, 0xcf, 0x80, 0x0b, 0x1b, 0x06, 0x02, 0x08, 0x0b, 0x10, 0xb8,
+    0xb4, 0x0a, 0x7e, 0xa4, 0x1c, 0x04, 0x08, 0x2e, 0x8e, 0x22, 0x83, 0x42, 0x1d, 0x26, 0x2f, 0x25,
+    0x28, 0xc0, 0x21, 0xd0, 0x64, 0x28, 0x87, 0xc6, 0xae, 0x4a, 0x14, 0x01, 0x7f, 0xa0, 0x2a, 0xc1,
+    0x23, 0x91, 0x7c, 0x43, 0xc3, 0x00, 0xbf, 0x2e, 0x08, 0x2e, 0x45, 0x22, 0x87, 0x40, 0x1a, 0x62,
+    0xd0, 0x10, 0x88, 0x85, 0xca, 0x01, 0xf0, 0xff, 0xba, 0x0c, 0x16, 0x21, 0x73, 0x54, 0xa3, 0x08,
+    0xb5, 0xeb, 0xf4, 0x05, 0x90, 0xc0, 0x60, 0x11, 0x70, 0xbb, 0x4a, 0x01, 0x65, 0xab, 0x0b, 0xb5,
+    0x50, 0x60, 0x21, 0x78, 0xd5, 0x44, 0x80, 0x62,
+};
+const uint8_t* const _I_DolphinSuccess_91x55[] = {_I_DolphinSuccess_91x55_0};
+
+const uint8_t _I_WarningDolphinFlip_45x42_0[] = {
+    0x01, 0x00, 0xc0, 0x00, 0x84, 0x40, 0x80, 0xb5, 0x01, 0xfe, 0x47, 0xc0, 0xa7, 0x03, 0xb0, 0x01,
+    0x47, 0x11, 0xc0, 0x80, 0x60, 0x20, 0x20, 0x51, 0x90, 0x02, 0x44, 0x34, 0x51, 0x00, 0x10, 0xc1,
+    0x20, 0x14, 0x88, 0x40, 0x6c, 0x80, 0x48, 0x0e, 0x40, 0x29, 0x4f, 0x00, 0xa3, 0x1e, 0x10, 0x0f,
+    0x08, 0x40, 0x32, 0x10, 0x28, 0x8d, 0x07, 0x01, 0x5e, 0xa0, 0x01, 0x46, 0x19, 0x40, 0xdd, 0x81,
+    0x46, 0x1f, 0x30, 0xa0, 0x6e, 0x40, 0xa3, 0x3e, 0xe0, 0x50, 0x37, 0xe0, 0x51, 0xbe, 0x40, 0x00,
+    0xa3, 0x80, 0x80, 0x7e, 0x20, 0x21, 0xd1, 0x02, 0x8f, 0xc0, 0x46, 0x32, 0xf0, 0x28, 0xf8, 0x20,
+    0xf4, 0x08, 0x80, 0x14, 0x78, 0x10, 0xf8, 0x04, 0x80, 0x0a, 0x38, 0x03, 0x79, 0x01, 0x47, 0xfd,
+    0x61, 0x02, 0x9d, 0xe4, 0xde, 0x36, 0x00, 0x28, 0xe0, 0x7f, 0xf1, 0xfc, 0x80, 0x14, 0x4f, 0x23,
+    0x00, 0x02, 0x8c, 0x04, 0xf2, 0x40, 0x52, 0x86, 0x01, 0x4e, 0x07, 0x70, 0x38, 0x14, 0x3c, 0x7e,
+    0x3f, 0xf0, 0x58, 0xc4, 0x20, 0xf0, 0x09, 0xfc, 0x02, 0xe1, 0x20, 0x02, 0x88, 0x90, 0x69, 0x12,
+    0x08, 0x70, 0x14, 0xe5, 0x00, 0x55, 0x8f, 0x01, 0x4b, 0x11, 0x27, 0x8e, 0x01, 0x4a, 0x21, 0x47,
+    0x02, 0x9c, 0x82, 0x81, 0x1c, 0x24, 0x89, 0xf8, 0x70, 0x11, 0x80, 0x2c, 0xc0, 0x23, 0x20, 0x54,
+    0x88, 0x81, 0x52, 0x24,
+};
+const uint8_t* const _I_WarningDolphinFlip_45x42[] = {_I_WarningDolphinFlip_45x42_0};
+
+const uint8_t _I_RFIDDolphinSend_97x61_0[] = {
+    0x01, 0x00, 0x8d, 0x01, 0x00, 0x0f, 0xfa, 0x3e, 0x04, 0x2a, 0x00, 0x2d, 0x78, 0x10, 0x1f, 0x04,
+    0x04, 0x0a, 0x38, 0x00, 0x62, 0xcc, 0x00, 0x43, 0x06, 0x06, 0x44, 0x30, 0x04, 0x31, 0x80, 0x31,
+    0x07, 0x48, 0x00, 0x50, 0x20, 0x10, 0xc8, 0x01, 0x64, 0x0c, 0x1d, 0x04, 0x28, 0x24, 0x83, 0xd2,
+    0x81, 0x04, 0xc4, 0x18, 0x42, 0xc3, 0x01, 0x90, 0x30, 0xbe, 0x05, 0x51, 0x29, 0xa0, 0x74, 0x60,
+    0x80, 0xc1, 0x84, 0x0b, 0x44, 0x5e, 0x43, 0x73, 0x82, 0x41, 0x20, 0x1e, 0x4a, 0x68, 0x31, 0x27,
+    0x90, 0x48, 0x84, 0x20, 0x18, 0x31, 0x7e, 0x64, 0x06, 0x20, 0x0c, 0x2a, 0x14, 0x12, 0x40, 0x0c,
+    0x28, 0xa0, 0xc4, 0x41, 0x87, 0x81, 0x17, 0x08, 0x30, 0xa0, 0xfd, 0x08, 0x0c, 0x20, 0xfc, 0x38,
+    0x08, 0xc4, 0x24, 0x32, 0x95, 0x02, 0x18, 0xc2, 0x61, 0x18, 0x09, 0x20, 0x31, 0x03, 0x25, 0x84,
+    0x1d, 0x88, 0x30, 0x62, 0x21, 0x96, 0xe2, 0x44, 0x22, 0x00, 0xc2, 0x26, 0xa0, 0x64, 0x68, 0x80,
+    0xc4, 0x33, 0x9e, 0x92, 0x9f, 0x00, 0xa3, 0x48, 0x24, 0x00, 0xc4, 0x40, 0xa4, 0xa8, 0x18, 0xa9,
+    0xb5, 0x9b, 0x48, 0x28, 0x05, 0xa1, 0x06, 0x22, 0xd4, 0xa3, 0x7e, 0x05, 0x98, 0xe0, 0x4f, 0x22,
+    0xcf, 0x58, 0x6f, 0x80, 0x10, 0x34, 0x24, 0x31, 0x3a, 0x52, 0x0f, 0xe0, 0x03, 0x0c, 0xf1, 0xee,
+    0x2d, 0x63, 0x00, 0x0c, 0x0f, 0xe0, 0x13, 0x28, 0xa0, 0x31, 0xa0, 0x3f, 0x08, 0x18, 0x10, 0x45,
+    0xa2, 0xe3, 0x40, 0x00, 0xf4, 0x3f, 0xe1, 0xa1, 0x84, 0x02, 0x94, 0x18, 0xb0, 0xc0, 0x63, 0xc6,
+    0x3f, 0xe0, 0x31, 0x87, 0x03, 0x1e, 0x11, 0x3c, 0x80, 0x47, 0xc1, 0x90, 0x56, 0x1b, 0x06, 0x01,
+    0xc0, 0x20, 0x06, 0x17, 0x88, 0xf8, 0x60, 0xa0, 0xc7, 0x31, 0x8a, 0x58, 0x60, 0xe1, 0x99, 0x00,
+    0x08, 0x9a, 0x01, 0x06, 0xd9, 0x10, 0x03, 0x1f, 0x44, 0x19, 0x43, 0xc3, 0x40, 0xc4, 0x2c, 0x19,
+    0x58, 0x08, 0x29, 0xa0, 0x60, 0x0c, 0xf2, 0x00, 0x27, 0x02, 0x05, 0x20, 0x06, 0x4d, 0x02, 0x0b,
+    0xc0, 0x02, 0x08, 0x3c, 0x80, 0x09, 0xa0, 0x39, 0x0a, 0xd4, 0x41, 0x8f, 0x50, 0x05, 0x09, 0xa4,
+    0x5b, 0x4d, 0x00, 0xd8, 0x23, 0xc4, 0x96, 0x20, 0xc7, 0xac, 0x40, 0x2d, 0x53, 0x00, 0x64, 0x6b,
+    0x20, 0x1d, 0x4a, 0x08, 0x32, 0x2a, 0x90, 0x0d, 0x46, 0x0e, 0x02, 0x0c, 0x79, 0x51, 0x08, 0x61,
+    0xf0, 0x20, 0x63, 0xc5, 0x4b, 0x83, 0x1e, 0xfe, 0x57, 0xd3, 0x51, 0x40, 0xbe, 0xc0, 0x08, 0x42,
+    0x00, 0x53, 0x30, 0xe8, 0x3f, 0x50, 0x14, 0x73, 0x80, 0x0b, 0xeb, 0x07, 0x61, 0x40, 0x00, 0x7d,
+    0x5f, 0xf8, 0x38, 0x32, 0x7a, 0x03, 0xf7, 0x55, 0xa6, 0x78, 0x19, 0x54, 0x0c, 0xa8, 0x32, 0xa0,
+    0x19, 0xa0, 0x65, 0xc4, 0x0b, 0xe2, 0x00, 0x98, 0x40, 0x33, 0xc1, 0x92, 0xfa, 0x10, 0x67, 0x80,
+    0x08,
+};
+const uint8_t* const _I_RFIDDolphinSend_97x61[] = {_I_RFIDDolphinSend_97x61_0};
+
+const Icon I_DolphinSuccess_91x55 = {
+    .width = 91,
+    .height = 55,
+    .frame_count = 1,
+    .frame_rate = 0,
+    .frames = _I_DolphinSuccess_91x55};
+const Icon I_WarningDolphinFlip_45x42 = {
+    .width = 45,
+    .height = 42,
+    .frame_count = 1,
+    .frame_rate = 0,
+    .frames = _I_WarningDolphinFlip_45x42};
+const Icon I_RFIDDolphinSend_97x61 = {
+    .width = 97,
+    .height = 61,
+    .frame_count = 1,
+    .frame_rate = 0,
+    .frames = _I_RFIDDolphinSend_97x61};
+const Icon I_125_10px =
+    {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_125_10px};

+ 8 - 0
t5577_multiwriter/assets/assets_icons.h

@@ -0,0 +1,8 @@
+#pragma once
+
+#include <gui/icon.h>
+
+extern const Icon I_125_10px;
+extern const Icon I_DolphinSuccess_91x55;
+extern const Icon I_WarningDolphinFlip_45x42;
+extern const Icon I_RFIDDolphinSend_97x61;

binární
t5577_multiwriter/icon.png


+ 110 - 0
t5577_multiwriter/protocols/EM41XX.c

@@ -0,0 +1,110 @@
+#include "EM41XX.h"
+#include "core/check.h"
+#include "core/log.h"
+
+#define EM41XX_LINES (10)
+#define EM41XX_COLUMNS (4)
+
+#define EM41XX_CONFIG_1_KEY (0b00000000000101001000000001000000)
+#define EM41XX_CONFIG_2_KEYS (0b00000000000101001000000010000000)
+#define EM41XX_CONFIG_3_KEYS (0b00000000000101001000000011000000)
+#define EM41XX_CONFIG_BLANK (0b00000000000101001000000000000000)
+
+#define EM41XX_HEADER (0b111111111)
+
+#define T5577_MAX_BLOCKS (8)
+#define EM41XX_BLOCKS (2)
+
+bool get_parity(uint16_t data) {
+    bool result = 0;
+    for(int i = 0; i < 16; i++) result ^= ((data >> i) & 1);
+    return result;
+}
+
+bool get_line_parity_bit(uint8_t line_num, uint64_t data) {
+    uint8_t line = (data >> (EM41XX_COLUMNS * line_num)) & 0x0F;
+    return get_parity(line);
+}
+
+bool get_column_parity_bit(uint8_t column_num, uint64_t data) {
+    uint16_t column = 0;
+
+    for(int i = 0; i < EM41XX_LINES; i++) {
+        column <<= 1;
+        column |= (data >> (EM41XX_COLUMNS * i + column_num)) & 1;
+    }
+
+    return get_parity(column);
+}
+
+uint64_t em41xx_encode(uint64_t data) {
+    uint64_t result = EM41XX_HEADER;
+
+    for(int i = EM41XX_LINES - 1; i >= 0; i--) {
+        result <<= EM41XX_COLUMNS;
+        uint8_t line = (data >> (i * EM41XX_COLUMNS)) & 0x0F;
+        result |= line;
+
+        result <<= 1;
+        result |= get_line_parity_bit(i, data);
+    }
+
+    for(int i = EM41XX_COLUMNS - 1; i >= 0; i--) {
+        result <<= 1;
+        result |= get_column_parity_bit(i, data);
+    }
+
+    result <<= 1;
+
+    return result;
+}
+
+bool add_em41xx_data(LFRFIDT5577* data, uint64_t key, uint8_t from_index) {
+    if(from_index + EM41XX_BLOCKS > (T5577_MAX_BLOCKS - 1)) return false;
+
+    uint64_t blocks_data = em41xx_encode(key);
+    data->block[from_index] = blocks_data >> 32;
+    data->block[from_index + 1] = blocks_data & 0xFFFFFFFF;
+    data->blocks_to_write = T5577_MAX_BLOCKS;
+
+    uint8_t mask_addition = (1 << from_index);
+    mask_addition |= (1 << (from_index + 1));
+
+    data->mask |= mask_addition;
+
+    FURI_LOG_D("ADDDATA", "%u", data->mask);
+
+    return true;
+}
+
+uint32_t get_config(uint8_t keys_count) {
+    if(keys_count > 3) return 0;
+
+    uint32_t result = EM41XX_CONFIG_BLANK;
+    result |= ((keys_count * EM41XX_BLOCKS) << 5);
+
+    return result;
+}
+
+bool set_em41xx_config(LFRFIDT5577* data, uint8_t keys_count) {
+    if(keys_count > 3) return false;
+
+    data->block[0] = get_config(keys_count);
+
+    data->mask |= 1;
+    FURI_LOG_D("SETCONFIG", "%u", data->mask);
+
+    return true;
+}
+
+uint64_t bytes2num(const uint8_t* src, uint8_t len) {
+    furi_assert(src);
+    furi_assert(len <= 8);
+
+    uint64_t res = 0;
+    while(len--) {
+        res = (res << 8) | (*src);
+        src++;
+    }
+    return res;
+}

+ 7 - 0
t5577_multiwriter/protocols/EM41XX.h

@@ -0,0 +1,7 @@
+#include <lfrfid/tools/t5577.h>
+
+bool add_em41xx_data(LFRFIDT5577* data, uint64_t key, uint8_t from_index);
+
+bool set_em41xx_config(LFRFIDT5577* data, uint8_t keys_count);
+
+uint64_t bytes2num(const uint8_t* src, uint8_t len);

+ 30 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene.c

@@ -0,0 +1,30 @@
+#include "t5577_multiwriter_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const t5577_multiwriter_on_enter_handlers[])(void*) = {
+#include "t5577_multiwriter_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const t5577_multiwriter_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "t5577_multiwriter_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const t5577_multiwriter_on_exit_handlers[])(void* context) = {
+#include "t5577_multiwriter_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers t5577_multiwriter_scene_handlers = {
+    .on_enter_handlers = t5577_multiwriter_on_enter_handlers,
+    .on_event_handlers = t5577_multiwriter_on_event_handlers,
+    .on_exit_handlers = t5577_multiwriter_on_exit_handlers,
+    .scene_num = LfRfidSceneNum,
+};

+ 29 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) LfRfidScene##id,
+typedef enum {
+#include "t5577_multiwriter_scene_config.h"
+    LfRfidSceneNum,
+} LfRfidScene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers t5577_multiwriter_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "t5577_multiwriter_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_event handlers declaration
+#define ADD_SCENE(prefix, name, id) \
+    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
+#include "t5577_multiwriter_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
+#include "t5577_multiwriter_scene_config.h"
+#undef ADD_SCENE

+ 9 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_config.h

@@ -0,0 +1,9 @@
+ADD_SCENE(t5577_multiwriter, start, Start)
+ADD_SCENE(t5577_multiwriter, exit_confirm, ExitConfirm)
+ADD_SCENE(t5577_multiwriter, write_success, WriteSuccess)
+ADD_SCENE(t5577_multiwriter, select_first_key, SelectFirstKey)
+ADD_SCENE(t5577_multiwriter, write_first_key, WriteFirstKey)
+ADD_SCENE(t5577_multiwriter, select_second_key, SelectSecondKey)
+ADD_SCENE(t5577_multiwriter, write_second_key, WriteSecondKey)
+ADD_SCENE(t5577_multiwriter, select_third_key, SelectThirdKey)
+ADD_SCENE(t5577_multiwriter, write_third_key, WriteThirdKey)

+ 41 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_exit_confirm.c

@@ -0,0 +1,41 @@
+#include "../t5577_multiwriter_i.h"
+
+void t5577_multiwriter_scene_exit_confirm_on_enter(void* context) {
+    LfRfid* app = context;
+    Widget* widget = app->widget;
+
+    widget_add_button_element(
+        widget, GuiButtonTypeLeft, "Exit", t5577_multiwriter_widget_callback, app);
+    widget_add_button_element(
+        widget, GuiButtonTypeRight, "Stay", t5577_multiwriter_widget_callback, app);
+    widget_add_string_element(
+        widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to RFID Menu?");
+    widget_add_string_element(
+        widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget);
+}
+
+bool t5577_multiwriter_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
+    LfRfid* app = context;
+    SceneManager* scene_manager = app->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        consumed = true; // Ignore Back button presses
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == GuiButtonTypeLeft) {
+            scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneStart);
+        } else if(event.event == GuiButtonTypeRight) {
+            scene_manager_previous_scene(scene_manager);
+        }
+    }
+
+    return consumed;
+}
+
+void t5577_multiwriter_scene_exit_confirm_on_exit(void* context) {
+    LfRfid* app = context;
+    widget_reset(app->widget);
+}

+ 22 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_select_first_key.c

@@ -0,0 +1,22 @@
+#include "../t5577_multiwriter_i.h"
+
+void t5577_multiwriter_scene_select_first_key_on_enter(void* context) {
+    LfRfid* app = context;
+
+    if(t5577_multiwriter_load_key_from_file_select(app)) {
+        scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteFirstKey);
+    } else {
+        scene_manager_previous_scene(app->scene_manager);
+    }
+}
+
+bool t5577_multiwriter_scene_select_first_key_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    bool consumed = false;
+    return consumed;
+}
+
+void t5577_multiwriter_scene_select_first_key_on_exit(void* context) {
+    UNUSED(context);
+}

+ 22 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_select_second_key.c

@@ -0,0 +1,22 @@
+#include "../t5577_multiwriter_i.h"
+
+void t5577_multiwriter_scene_select_second_key_on_enter(void* context) {
+    LfRfid* app = context;
+
+    if(t5577_multiwriter_load_key_from_file_select(app)) {
+        scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSecondKey);
+    } else {
+        scene_manager_previous_scene(app->scene_manager);
+    }
+}
+
+bool t5577_multiwriter_scene_select_second_key_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    bool consumed = false;
+    return consumed;
+}
+
+void t5577_multiwriter_scene_select_second_key_on_exit(void* context) {
+    UNUSED(context);
+}

+ 22 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_select_third_key.c

@@ -0,0 +1,22 @@
+#include "../t5577_multiwriter_i.h"
+
+void t5577_multiwriter_scene_select_third_key_on_enter(void* context) {
+    LfRfid* app = context;
+
+    if(t5577_multiwriter_load_key_from_file_select(app)) {
+        scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteThirdKey);
+    } else {
+        scene_manager_previous_scene(app->scene_manager);
+    }
+}
+
+bool t5577_multiwriter_scene_select_third_key_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    bool consumed = false;
+    return consumed;
+}
+
+void t5577_multiwriter_scene_select_third_key_on_exit(void* context) {
+    UNUSED(context);
+}

+ 80 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_start.c

@@ -0,0 +1,80 @@
+#include "../t5577_multiwriter_i.h"
+#include <dolphin/dolphin.h>
+
+typedef enum {
+    SubmenuIndexWriteFirstKey,
+    SubmenuIndexWriteSecondKey,
+    SubmenuIndexWriteThirdKey,
+} SubmenuIndex;
+
+static void t5577_multiwriter_scene_start_submenu_callback(void* context, uint32_t index) {
+    LfRfid* app = context;
+
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void t5577_multiwriter_scene_start_on_enter(void* context) {
+    LfRfid* app = context;
+    Submenu* submenu = app->submenu;
+
+    submenu_add_item(
+        submenu,
+        "Write first key",
+        SubmenuIndexWriteFirstKey,
+        t5577_multiwriter_scene_start_submenu_callback,
+        app);
+    submenu_add_item(
+        submenu,
+        "Write second key",
+        SubmenuIndexWriteSecondKey,
+        t5577_multiwriter_scene_start_submenu_callback,
+        app);
+    submenu_add_item(
+        submenu,
+        "Write third key",
+        SubmenuIndexWriteThirdKey,
+        t5577_multiwriter_scene_start_submenu_callback,
+        app);
+
+    submenu_set_selected_item(
+        submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneStart));
+
+    // clear key
+    furi_string_reset(app->file_name);
+    app->protocol_id = PROTOCOL_NO;
+    app->read_type = LFRFIDWorkerReadTypeAuto;
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu);
+}
+
+bool t5577_multiwriter_scene_start_on_event(void* context, SceneManagerEvent event) {
+    LfRfid* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexWriteFirstKey) {
+            scene_manager_set_scene_state(
+                app->scene_manager, LfRfidSceneStart, SubmenuIndexWriteFirstKey);
+            scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectFirstKey);
+            consumed = true;
+        } else if(event.event == SubmenuIndexWriteSecondKey) {
+            scene_manager_set_scene_state(
+                app->scene_manager, LfRfidSceneStart, SubmenuIndexWriteSecondKey);
+            scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectSecondKey);
+            consumed = true;
+        } else if(event.event == SubmenuIndexWriteThirdKey) {
+            scene_manager_set_scene_state(
+                app->scene_manager, LfRfidSceneStart, SubmenuIndexWriteThirdKey);
+            scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectThirdKey);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void t5577_multiwriter_scene_start_on_exit(void* context) {
+    LfRfid* app = context;
+
+    submenu_reset(app->submenu);
+}

+ 102 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_write_first_key.c

@@ -0,0 +1,102 @@
+#include "../t5577_multiwriter_i.h"
+#include "../protocols/EM41XX.h"
+
+static void
+    t5577_multiwriter_write_first_key_callback(LFRFIDWorkerWriteResult result, void* context) {
+    LfRfid* app = context;
+    uint32_t event = 0;
+
+    if(result == LFRFIDWorkerWriteOK) {
+        event = LfRfidEventWriteOK;
+    } else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) {
+        event = LfRfidEventWriteProtocolCannotBeWritten;
+    } else if(result == LFRFIDWorkerWriteFobCannotBeWritten) {
+        event = LfRfidEventWriteFobCannotBeWritten;
+    } else if(result == LFRFIDWorkerWriteTooLongToWrite) {
+        event = LfRfidEventWriteTooLongToWrite;
+    }
+
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void t5577_multiwriter_scene_write_first_key_on_enter(void* context) {
+    LfRfid* app = context;
+    Popup* popup = app->popup;
+
+    popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop);
+    if(!furi_string_empty(app->file_name)) {
+        popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
+    } else {
+        popup_set_text(
+            popup,
+            protocol_dict_get_name(app->dict, app->protocol_id),
+            89,
+            43,
+            AlignCenter,
+            AlignTop);
+    }
+    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
+    notification_message(app->notifications, &sequence_blink_start_magenta);
+
+    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+
+    uint8_t* data = (uint8_t*)malloc(size);
+    protocol_dict_get_data(app->dict, app->protocol_id, data, size);
+
+    uint64_t key = bytes2num(data, size);
+    LFRFIDT5577 data_to_write = {0};
+
+    add_em41xx_data(&data_to_write, key, 1);
+    set_em41xx_config(&data_to_write, 1);
+
+    t5577_write_with_mask(&data_to_write, 0, 0, 0);
+
+    t5577_multiwriter_write_first_key_callback(LFRFIDWorkerWriteOK, context);
+}
+
+bool t5577_multiwriter_scene_write_first_key_on_event(void* context, SceneManagerEvent event) {
+    LfRfid* app = context;
+    Popup* popup = app->popup;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == LfRfidEventWriteOK) {
+            notification_message(app->notifications, &sequence_success);
+            scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);
+            consumed = true;
+        } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {
+            popup_set_icon(popup, 72, 17, &I_WarningDolphinFlip_45x42);
+            popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop);
+            popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
+            notification_message(app->notifications, &sequence_blink_start_red);
+            consumed = true;
+        } else if(
+            (event.event == LfRfidEventWriteFobCannotBeWritten) ||
+            (event.event == LfRfidEventWriteTooLongToWrite)) {
+            popup_set_icon(popup, 72, 17, &I_WarningDolphinFlip_45x42);
+            popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop);
+            popup_set_text(
+                popup,
+                "Make sure this\ncard is writable\nand not\nprotected.",
+                3,
+                17,
+                AlignLeft,
+                AlignTop);
+            notification_message(app->notifications, &sequence_blink_start_yellow);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void t5577_multiwriter_scene_write_first_key_on_exit(void* context) {
+    LfRfid* app = context;
+    notification_message(app->notifications, &sequence_blink_stop);
+    popup_reset(app->popup);
+
+    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+    protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
+}

+ 102 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_write_second_key.c

@@ -0,0 +1,102 @@
+#include "../t5577_multiwriter_i.h"
+#include "../protocols/EM41XX.h"
+
+static void
+    t5577_multiwriter_write_second_key_callback(LFRFIDWorkerWriteResult result, void* context) {
+    LfRfid* app = context;
+    uint32_t event = 0;
+
+    if(result == LFRFIDWorkerWriteOK) {
+        event = LfRfidEventWriteOK;
+    } else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) {
+        event = LfRfidEventWriteProtocolCannotBeWritten;
+    } else if(result == LFRFIDWorkerWriteFobCannotBeWritten) {
+        event = LfRfidEventWriteFobCannotBeWritten;
+    } else if(result == LFRFIDWorkerWriteTooLongToWrite) {
+        event = LfRfidEventWriteTooLongToWrite;
+    }
+
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void t5577_multiwriter_scene_write_second_key_on_enter(void* context) {
+    LfRfid* app = context;
+    Popup* popup = app->popup;
+
+    popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop);
+    if(!furi_string_empty(app->file_name)) {
+        popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
+    } else {
+        popup_set_text(
+            popup,
+            protocol_dict_get_name(app->dict, app->protocol_id),
+            89,
+            43,
+            AlignCenter,
+            AlignTop);
+    }
+    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
+    notification_message(app->notifications, &sequence_blink_start_magenta);
+
+    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+
+    uint8_t* data = (uint8_t*)malloc(size);
+    protocol_dict_get_data(app->dict, app->protocol_id, data, size);
+
+    uint64_t key = bytes2num(data, size);
+    LFRFIDT5577 data_to_write = {0};
+
+    add_em41xx_data(&data_to_write, key, 3);
+    set_em41xx_config(&data_to_write, 2);
+
+    t5577_write_with_mask(&data_to_write, 0, 0, 0);
+
+    t5577_multiwriter_write_second_key_callback(LFRFIDWorkerWriteOK, context);
+}
+
+bool t5577_multiwriter_scene_write_second_key_on_event(void* context, SceneManagerEvent event) {
+    LfRfid* app = context;
+    Popup* popup = app->popup;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == LfRfidEventWriteOK) {
+            notification_message(app->notifications, &sequence_success);
+            scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);
+            consumed = true;
+        } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {
+            popup_set_icon(popup, 72, 17, &I_WarningDolphinFlip_45x42);
+            popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop);
+            popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
+            notification_message(app->notifications, &sequence_blink_start_red);
+            consumed = true;
+        } else if(
+            (event.event == LfRfidEventWriteFobCannotBeWritten) ||
+            (event.event == LfRfidEventWriteTooLongToWrite)) {
+            popup_set_icon(popup, 72, 17, &I_WarningDolphinFlip_45x42);
+            popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop);
+            popup_set_text(
+                popup,
+                "Make sure this\ncard is writable\nand not\nprotected.",
+                3,
+                17,
+                AlignLeft,
+                AlignTop);
+            notification_message(app->notifications, &sequence_blink_start_yellow);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void t5577_multiwriter_scene_write_second_key_on_exit(void* context) {
+    LfRfid* app = context;
+    notification_message(app->notifications, &sequence_blink_stop);
+    popup_reset(app->popup);
+
+    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+    protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
+}

+ 38 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_write_success.c

@@ -0,0 +1,38 @@
+#include "../t5577_multiwriter_i.h"
+
+void t5577_multiwriter_scene_write_success_on_enter(void* context) {
+    LfRfid* app = context;
+    Popup* popup = app->popup;
+
+    popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 6, &I_DolphinSuccess_91x55);
+    popup_set_context(popup, app);
+    popup_set_callback(popup, t5577_multiwriter_popup_timeout_callback);
+    popup_set_timeout(popup, 1500);
+    popup_enable_timeout(popup);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
+    notification_message_block(app->notifications, &sequence_set_green_255);
+}
+
+bool t5577_multiwriter_scene_write_success_on_event(void* context, SceneManagerEvent event) {
+    LfRfid* app = context;
+    bool consumed = false;
+
+    const uint32_t prev_scenes[] = {LfRfidSceneStart};
+
+    if((event.type == SceneManagerEventTypeBack) ||
+       ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) {
+        scene_manager_search_and_switch_to_previous_scene_one_of(
+            app->scene_manager, prev_scenes, COUNT_OF(prev_scenes));
+        consumed = true;
+    }
+
+    return consumed;
+}
+
+void t5577_multiwriter_scene_write_success_on_exit(void* context) {
+    LfRfid* app = context;
+    notification_message_block(app->notifications, &sequence_reset_green);
+    popup_reset(app->popup);
+}

+ 102 - 0
t5577_multiwriter/scenes/t5577_multiwriter_scene_write_third_key.c

@@ -0,0 +1,102 @@
+#include "../t5577_multiwriter_i.h"
+#include "../protocols/EM41XX.h"
+
+static void
+    t5577_multiwriter_write_third_key_callback(LFRFIDWorkerWriteResult result, void* context) {
+    LfRfid* app = context;
+    uint32_t event = 0;
+
+    if(result == LFRFIDWorkerWriteOK) {
+        event = LfRfidEventWriteOK;
+    } else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) {
+        event = LfRfidEventWriteProtocolCannotBeWritten;
+    } else if(result == LFRFIDWorkerWriteFobCannotBeWritten) {
+        event = LfRfidEventWriteFobCannotBeWritten;
+    } else if(result == LFRFIDWorkerWriteTooLongToWrite) {
+        event = LfRfidEventWriteTooLongToWrite;
+    }
+
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void t5577_multiwriter_scene_write_third_key_on_enter(void* context) {
+    LfRfid* app = context;
+    Popup* popup = app->popup;
+
+    popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop);
+    if(!furi_string_empty(app->file_name)) {
+        popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
+    } else {
+        popup_set_text(
+            popup,
+            protocol_dict_get_name(app->dict, app->protocol_id),
+            89,
+            43,
+            AlignCenter,
+            AlignTop);
+    }
+    popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup);
+    notification_message(app->notifications, &sequence_blink_start_magenta);
+
+    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+
+    uint8_t* data = (uint8_t*)malloc(size);
+    protocol_dict_get_data(app->dict, app->protocol_id, data, size);
+
+    uint64_t key = bytes2num(data, size);
+    LFRFIDT5577 data_to_write = {0};
+
+    add_em41xx_data(&data_to_write, key, 5);
+    set_em41xx_config(&data_to_write, 3);
+
+    t5577_write_with_mask(&data_to_write, 0, 0, 0);
+
+    t5577_multiwriter_write_third_key_callback(LFRFIDWorkerWriteOK, context);
+}
+
+bool t5577_multiwriter_scene_write_third_key_on_event(void* context, SceneManagerEvent event) {
+    LfRfid* app = context;
+    Popup* popup = app->popup;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == LfRfidEventWriteOK) {
+            notification_message(app->notifications, &sequence_success);
+            scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess);
+            consumed = true;
+        } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) {
+            popup_set_icon(popup, 72, 17, &I_WarningDolphinFlip_45x42);
+            popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop);
+            popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
+            notification_message(app->notifications, &sequence_blink_start_red);
+            consumed = true;
+        } else if(
+            (event.event == LfRfidEventWriteFobCannotBeWritten) ||
+            (event.event == LfRfidEventWriteTooLongToWrite)) {
+            popup_set_icon(popup, 72, 17, &I_WarningDolphinFlip_45x42);
+            popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop);
+            popup_set_text(
+                popup,
+                "Make sure this\ncard is writable\nand not\nprotected.",
+                3,
+                17,
+                AlignLeft,
+                AlignTop);
+            notification_message(app->notifications, &sequence_blink_start_yellow);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void t5577_multiwriter_scene_write_third_key_on_exit(void* context) {
+    LfRfid* app = context;
+    notification_message(app->notifications, &sequence_blink_stop);
+    popup_reset(app->popup);
+
+    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+    protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
+}

+ 247 - 0
t5577_multiwriter/t5577_multiwriter.c

@@ -0,0 +1,247 @@
+#include "core/core_defines.h"
+#include "t5577_multiwriter_i.h"
+#include <dolphin/dolphin.h>
+
+static bool t5577_multiwriter_debug_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    LfRfid* app = context;
+    return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+static bool t5577_multiwriter_debug_back_event_callback(void* context) {
+    furi_assert(context);
+    LfRfid* app = context;
+    return scene_manager_handle_back_event(app->scene_manager);
+}
+
+static LfRfid* t5577_multiwriter_alloc() {
+    LfRfid* t5577_multiwriter = malloc(sizeof(LfRfid));
+
+    t5577_multiwriter->storage = furi_record_open(RECORD_STORAGE);
+    t5577_multiwriter->dialogs = furi_record_open(RECORD_DIALOGS);
+
+    t5577_multiwriter->file_name = furi_string_alloc();
+    t5577_multiwriter->file_path = furi_string_alloc_set(LFRFID_APP_FOLDER);
+
+    t5577_multiwriter->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+
+    size_t size = protocol_dict_get_max_data_size(t5577_multiwriter->dict);
+    t5577_multiwriter->new_key_data = (uint8_t*)malloc(size);
+    t5577_multiwriter->old_key_data = (uint8_t*)malloc(size);
+
+    t5577_multiwriter->lfworker = lfrfid_worker_alloc(t5577_multiwriter->dict);
+
+    t5577_multiwriter->view_dispatcher = view_dispatcher_alloc();
+    t5577_multiwriter->scene_manager =
+        scene_manager_alloc(&t5577_multiwriter_scene_handlers, t5577_multiwriter);
+    view_dispatcher_enable_queue(t5577_multiwriter->view_dispatcher);
+    view_dispatcher_set_event_callback_context(
+        t5577_multiwriter->view_dispatcher, t5577_multiwriter);
+    view_dispatcher_set_custom_event_callback(
+        t5577_multiwriter->view_dispatcher, t5577_multiwriter_debug_custom_event_callback);
+    view_dispatcher_set_navigation_event_callback(
+        t5577_multiwriter->view_dispatcher, t5577_multiwriter_debug_back_event_callback);
+
+    // Open GUI record
+    t5577_multiwriter->gui = furi_record_open(RECORD_GUI);
+
+    // Open Notification record
+    t5577_multiwriter->notifications = furi_record_open(RECORD_NOTIFICATION);
+
+    // Submenu
+    t5577_multiwriter->submenu = submenu_alloc();
+    view_dispatcher_add_view(
+        t5577_multiwriter->view_dispatcher,
+        LfRfidViewSubmenu,
+        submenu_get_view(t5577_multiwriter->submenu));
+
+    // Dialog
+    t5577_multiwriter->dialog_ex = dialog_ex_alloc();
+    view_dispatcher_add_view(
+        t5577_multiwriter->view_dispatcher,
+        LfRfidViewDialogEx,
+        dialog_ex_get_view(t5577_multiwriter->dialog_ex));
+
+    // Popup
+    t5577_multiwriter->popup = popup_alloc();
+    view_dispatcher_add_view(
+        t5577_multiwriter->view_dispatcher,
+        LfRfidViewPopup,
+        popup_get_view(t5577_multiwriter->popup));
+
+    // Widget
+    t5577_multiwriter->widget = widget_alloc();
+    view_dispatcher_add_view(
+        t5577_multiwriter->view_dispatcher,
+        LfRfidViewWidget,
+        widget_get_view(t5577_multiwriter->widget));
+
+    // Text Input
+    t5577_multiwriter->text_input = text_input_alloc();
+    view_dispatcher_add_view(
+        t5577_multiwriter->view_dispatcher,
+        LfRfidViewTextInput,
+        text_input_get_view(t5577_multiwriter->text_input));
+
+    // Byte Input
+    t5577_multiwriter->byte_input = byte_input_alloc();
+    view_dispatcher_add_view(
+        t5577_multiwriter->view_dispatcher,
+        LfRfidViewByteInput,
+        byte_input_get_view(t5577_multiwriter->byte_input));
+
+    return t5577_multiwriter;
+} //-V773
+
+static void t5577_multiwriter_free(LfRfid* t5577_multiwriter) {
+    furi_assert(t5577_multiwriter);
+
+    furi_string_free(t5577_multiwriter->file_name);
+    furi_string_free(t5577_multiwriter->file_path);
+    protocol_dict_free(t5577_multiwriter->dict);
+
+    lfrfid_worker_free(t5577_multiwriter->lfworker);
+
+    free(t5577_multiwriter->new_key_data);
+    free(t5577_multiwriter->old_key_data);
+
+    // Submenu
+    view_dispatcher_remove_view(t5577_multiwriter->view_dispatcher, LfRfidViewSubmenu);
+    submenu_free(t5577_multiwriter->submenu);
+
+    // DialogEx
+    view_dispatcher_remove_view(t5577_multiwriter->view_dispatcher, LfRfidViewDialogEx);
+    dialog_ex_free(t5577_multiwriter->dialog_ex);
+
+    // Popup
+    view_dispatcher_remove_view(t5577_multiwriter->view_dispatcher, LfRfidViewPopup);
+    popup_free(t5577_multiwriter->popup);
+
+    // Widget
+    view_dispatcher_remove_view(t5577_multiwriter->view_dispatcher, LfRfidViewWidget);
+    widget_free(t5577_multiwriter->widget);
+
+    // TextInput
+    view_dispatcher_remove_view(t5577_multiwriter->view_dispatcher, LfRfidViewTextInput);
+    text_input_free(t5577_multiwriter->text_input);
+
+    // ByteInput
+    view_dispatcher_remove_view(t5577_multiwriter->view_dispatcher, LfRfidViewByteInput);
+    byte_input_free(t5577_multiwriter->byte_input);
+
+    // View Dispatcher
+    view_dispatcher_free(t5577_multiwriter->view_dispatcher);
+
+    // Scene Manager
+    scene_manager_free(t5577_multiwriter->scene_manager);
+
+    // GUI
+    furi_record_close(RECORD_GUI);
+    t5577_multiwriter->gui = NULL;
+
+    // Notifications
+    furi_record_close(RECORD_NOTIFICATION);
+    t5577_multiwriter->notifications = NULL;
+
+    furi_record_close(RECORD_STORAGE);
+    furi_record_close(RECORD_DIALOGS);
+
+    free(t5577_multiwriter);
+}
+
+int32_t t5577_multiwriter_app(void* p) {
+    LfRfid* app = t5577_multiwriter_alloc();
+    UNUSED(p);
+
+    t5577_multiwriter_make_app_folder(app);
+
+    {
+        view_dispatcher_attach_to_gui(
+            app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+        scene_manager_next_scene(app->scene_manager, LfRfidSceneStart);
+    }
+
+    view_dispatcher_run(app->view_dispatcher);
+
+    t5577_multiwriter_free(app);
+
+    return 0;
+}
+
+bool t5577_multiwriter_load_key_from_file_select(LfRfid* app) {
+    furi_assert(app);
+
+    DialogsFileBrowserOptions browser_options;
+    dialog_file_browser_set_basic_options(
+        &browser_options, LFRFID_APP_FILENAME_EXTENSION, &I_125_10px);
+    browser_options.base_path = LFRFID_APP_FOLDER;
+
+    // Input events and views are managed by file_browser
+    bool result =
+        dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options);
+
+    if(result) {
+        result = t5577_multiwriter_load_key_data(app, app->file_path, true);
+    }
+
+    return result;
+}
+
+bool t5577_multiwriter_load_key_data(LfRfid* app, FuriString* path, bool show_dialog) {
+    bool result = false;
+
+    do {
+        app->protocol_id = lfrfid_dict_file_load(app->dict, furi_string_get_cstr(path));
+        if(app->protocol_id == PROTOCOL_NO) break;
+        if(app->protocol_id != LFRFIDProtocolEM4100) break;
+
+        path_extract_filename(path, app->file_name, true);
+        result = true;
+    } while(0);
+
+    if((!result) && (show_dialog)) {
+        dialog_message_show_storage_error(app->dialogs, "Unsupported\nlfrfid protocol!");
+    }
+
+    return result;
+}
+
+void t5577_multiwriter_make_app_folder(LfRfid* app) {
+    furi_assert(app);
+
+    if(!storage_simply_mkdir(app->storage, LFRFID_APP_FOLDER)) {
+        dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
+    }
+}
+
+void t5577_multiwriter_text_store_set(LfRfid* app, const char* text, ...) {
+    furi_assert(app);
+    va_list args;
+    va_start(args, text);
+
+    vsnprintf(app->text_store, LFRFID_TEXT_STORE_SIZE, text, args);
+
+    va_end(args);
+}
+
+void t5577_multiwriter_text_store_clear(LfRfid* app) {
+    furi_assert(app);
+    memset(app->text_store, 0, sizeof(app->text_store));
+}
+
+void t5577_multiwriter_popup_timeout_callback(void* context) {
+    LfRfid* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventPopupClosed);
+}
+
+void t5577_multiwriter_widget_callback(GuiButtonType result, InputType type, void* context) {
+    LfRfid* app = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(app->view_dispatcher, result);
+    }
+}
+
+void t5577_multiwriter_text_input_callback(void* context) {
+    LfRfid* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventNext);
+}

+ 115 - 0
t5577_multiwriter/t5577_multiwriter_i.h

@@ -0,0 +1,115 @@
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+
+#include <gui/gui.h>
+#include <gui/view.h>
+#include "assets/assets_icons.h"
+#include <gui/view_dispatcher.h>
+#include <gui/scene_manager.h>
+#include <cli/cli.h>
+#include <notification/notification_messages.h>
+
+#include <gui/modules/submenu.h>
+#include <gui/modules/dialog_ex.h>
+#include <gui/modules/popup.h>
+#include <gui/modules/text_input.h>
+#include <gui/modules/byte_input.h>
+#include <gui/modules/widget.h>
+
+#include <notification/notification_messages.h>
+#include <dialogs/dialogs.h>
+#include <storage/storage.h>
+#include <flipper_format/flipper_format.h>
+
+#include <rpc/rpc_app.h>
+
+#include <toolbox/protocols/protocol_dict.h>
+#include <toolbox/path.h>
+#include <lfrfid/lfrfid_dict_file.h>
+#include <lfrfid/protocols/lfrfid_protocols.h>
+#include <lfrfid/lfrfid_worker.h>
+
+#include "scenes/t5577_multiwriter_scene.h"
+
+#define LFRFID_KEY_NAME_SIZE 22
+#define LFRFID_TEXT_STORE_SIZE 40
+
+#define LFRFID_APP_FOLDER ANY_PATH("lfrfid")
+#define LFRFID_SD_FOLDER EXT_PATH("lfrfid")
+#define LFRFID_APP_FILENAME_PREFIX "RFID"
+#define LFRFID_APP_FILENAME_EXTENSION ".rfid"
+#define LFRFID_APP_SHADOW_FILENAME_EXTENSION ".shd"
+
+enum LfRfidCustomEvent {
+    LfRfidEventNext = 100,
+    LfRfidEventExit,
+    LfRfidEventPopupClosed,
+    LfRfidEventWriteOK,
+    LfRfidEventWriteProtocolCannotBeWritten,
+    LfRfidEventWriteFobCannotBeWritten,
+    LfRfidEventWriteTooLongToWrite,
+};
+
+typedef enum {
+    LfRfidRpcStateIdle,
+    LfRfidRpcStateEmulating,
+} LfRfidRpcState;
+
+typedef struct LfRfid LfRfid;
+
+struct LfRfid {
+    LFRFIDWorker* lfworker;
+    ViewDispatcher* view_dispatcher;
+    Gui* gui;
+    NotificationApp* notifications;
+    SceneManager* scene_manager;
+    Storage* storage;
+    DialogsApp* dialogs;
+    Widget* widget;
+
+    char text_store[LFRFID_TEXT_STORE_SIZE + 1];
+    FuriString* file_path;
+    FuriString* file_name;
+
+    ProtocolDict* dict;
+    ProtocolId protocol_id;
+    ProtocolId protocol_id_next;
+    LFRFIDWorkerReadType read_type;
+
+    uint8_t* old_key_data;
+    uint8_t* new_key_data;
+
+    // Common Views
+    Submenu* submenu;
+    DialogEx* dialog_ex;
+    Popup* popup;
+    TextInput* text_input;
+    ByteInput* byte_input;
+};
+
+typedef enum {
+    LfRfidViewSubmenu,
+    LfRfidViewDialogEx,
+    LfRfidViewPopup,
+    LfRfidViewWidget,
+    LfRfidViewTextInput,
+    LfRfidViewByteInput,
+} LfRfidView;
+
+bool t5577_multiwriter_load_key_from_file_select(LfRfid* app);
+
+bool t5577_multiwriter_load_key_data(LfRfid* app, FuriString* path, bool show_dialog);
+
+void t5577_multiwriter_make_app_folder(LfRfid* app);
+
+void t5577_multiwriter_text_store_set(LfRfid* app, const char* text, ...);
+
+void t5577_multiwriter_text_store_clear(LfRfid* app);
+
+void t5577_multiwriter_popup_timeout_callback(void* context);
+
+void t5577_multiwriter_widget_callback(GuiButtonType result, InputType type, void* context);
+
+void t5577_multiwriter_text_input_callback(void* context);