Procházet zdrojové kódy

[FL-2529][FL-1628] New LF-RFID subsystem (#1601)

* Makefile: unit tests pack
* RFID: pulse joiner and its unit test
* Move pulse protocol helpers to appropriate place
* Drop pulse_joiner tests
* Generic protocol, protocols dictionary, unit test
* Protocol dict unit test
* iButton: protocols dictionary
* Lib: varint
* Lib: profiler
* Unit test: varint
* rfid: worker mockup
* LFRFID: em4100 unit test
* Storage: file_exist function
* rfid: fsk osc
* rfid: generic fsk demodulator
* rfid: protocol em4100
* rfid: protocol h10301
* rfid: protocol io prox xsf
* Unit test: rfid protocols
* rfid: new hal
* rfid: raw worker
* Unit test: fix error output
* rfid: worker
* rfid: plain c cli
* fw: migrate to scons
* lfrfid: full io prox support
* unit test: io prox protocol
* SubGHZ: move bit defines to source
* FSK oscillator: level duration compability
* libs: bit manipulation library
* lfrfid: ioprox protocol, use bit library and new level duration method of FSK ocillator
* bit lib: unit tests
* Bit lib: parity tests, remove every nth bit, copy bits
* Lfrfid: awid protocol
* bit lib: uint16 and uint32 getters, unit tests
* lfrfid: FDX-B read, draft version
* Minunit: better memeq assert
* bit lib: reverse, print, print regions
* Protocol dict: get protocol features, get protocol validate count
* lfrfid worker: improved read
* lfrfid raw worker: psk support
* Cli: rfid plain C cli
* protocol AWID: render
* protocol em4100: render
* protocol h10301: render
* protocol indala26: support every indala 26 scramble
* Protocol IO Prox: render
* Protocol FDX-B: advanced read
* lfrfid: remove unused test function
* lfrfid: fix os primitives
* bit lib: crc16 and unit tests
* FDX-B: save data
* lfrfid worker: increase stream size. Alloc raw worker only when needed.
* lfrfid: indala26 emulation
* lfrfid: prepare to write
* lfrfid: fdx-b emulation
* lfrfid: awid, ioprox write
* lfrfid: write t55xx w\o validation
* lfrfid: better t55xx block0 handling
* lfrfid: use new t5577 functions in worker
* lfrfid: improve protocol description
* lfrfid: write and verify
* lfrfid: delete cpp cli
* lfrfid: improve worker usage
* lfrfid-app: step to new worker
* lfrfid: old indala (I40134) load fallback
* lfrfid: indala26, recover wrong synced data
* lfrfid: remove old worker
* lfrfid app: dummy read screen
* lfrfid app: less dummy read screen
* lfrfid: generic 96-bit HID protocol (covers up to HID 37-bit)
* rename
* lfrfid: improve indala26 read
* lfrfid: generic 192-bit HID protocol (covers all HID extended)
* lfrfid: TODO about HID render
* lfrfid: new protocol FDX-A
* lfrfid-app: correct worker stop on exit
* misc fixes
* lfrfid: FDX-A and HID distinguishability has been fixed.
* lfrfid: decode HID size header and render it (#1612)
* lfrfid: rename HID96 and HID192 to HIDProx and HIDExt
* lfrfid: extra actions scene
* lfrfid: decode generic HID Proximity size lazily (#1618)
* lib: stream of data buffers concept
* lfrfid: raw file helper
* lfrfid: changed raw worker api
* lfrfid: packed varint pair
* lfrfid: read stream speedup
* lfrfid app: show read mode
* Documentation
* lfrfid app: raw read gui
* lfrfid app: storage check for raw read
* memleak fix
* review fixes
* lfrfid app: read blink color
* lfrfid app: reset key name after read
* review fixes
* lfrfid app: fix copypasted text
* review fixes
* lfrfid: disable debug gpio
* lfrfid: card detection events
* lfrfid: change validation color from magenta to green
* Update core_defines.
* lfrfid: prefix fdx-b id by zeroes
* lfrfid: parse up to 43-bit HID Proximity keys (#1640)
* Fbt: downgrade toolchain and fix PS1
* lfrfid: fix unit tests
* lfrfid app: remove printf
* lfrfid: indala26, use bit 55 as data
* lfrfid: indala26, better brief format
* lfrfid: indala26, loading fallback
* lfrfid: read timing tuning

Co-authored-by: James Ide <ide@users.noreply.github.com>
Co-authored-by: あく <alleteam@gmail.com>
SG před 3 roky
rodič
revize
9bfb641d3e
100 změnil soubory, kde provedl 2928 přidání a 3649 odebrání
  1. 4 14
      applications/archive/helpers/archive_apps.c
  2. 10 23
      applications/archive/helpers/archive_favorites.c
  3. 1 2
      applications/desktop/animations/animation_manager.c
  4. 0 50
      applications/lfrfid/helpers/decoder_analyzer.cpp
  5. 0 21
      applications/lfrfid/helpers/decoder_analyzer.h
  6. 0 72
      applications/lfrfid/helpers/decoder_emmarin.cpp
  7. 0 21
      applications/lfrfid/helpers/decoder_emmarin.h
  8. 0 15
      applications/lfrfid/helpers/decoder_gpio_out.cpp
  9. 0 14
      applications/lfrfid/helpers/decoder_gpio_out.h
  10. 0 98
      applications/lfrfid/helpers/decoder_hid26.cpp
  11. 0 24
      applications/lfrfid/helpers/decoder_hid26.h
  12. 0 76
      applications/lfrfid/helpers/decoder_indala.cpp
  13. 0 25
      applications/lfrfid/helpers/decoder_indala.h
  14. 0 107
      applications/lfrfid/helpers/decoder_ioprox.cpp
  15. 0 26
      applications/lfrfid/helpers/decoder_ioprox.h
  16. 0 15
      applications/lfrfid/helpers/emmarin.h
  17. 0 24
      applications/lfrfid/helpers/encoder_emmarin.cpp
  18. 0 22
      applications/lfrfid/helpers/encoder_emmarin.h
  19. 0 27
      applications/lfrfid/helpers/encoder_generic.h
  20. 0 46
      applications/lfrfid/helpers/encoder_hid_h10301.cpp
  21. 0 26
      applications/lfrfid/helpers/encoder_hid_h10301.h
  22. 0 36
      applications/lfrfid/helpers/encoder_indala_40134.cpp
  23. 0 23
      applications/lfrfid/helpers/encoder_indala_40134.h
  24. 0 32
      applications/lfrfid/helpers/encoder_ioprox.cpp
  25. 0 25
      applications/lfrfid/helpers/encoder_ioprox.h
  26. 0 76
      applications/lfrfid/helpers/key_info.cpp
  27. 0 17
      applications/lfrfid/helpers/key_info.h
  28. 0 20
      applications/lfrfid/helpers/osc_fsk.cpp
  29. 0 30
      applications/lfrfid/helpers/osc_fsk.h
  30. 0 150
      applications/lfrfid/helpers/protocols/protocol_emmarin.cpp
  31. 0 22
      applications/lfrfid/helpers/protocols/protocol_emmarin.h
  32. 0 60
      applications/lfrfid/helpers/protocols/protocol_generic.h
  33. 0 238
      applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp
  34. 0 22
      applications/lfrfid/helpers/protocols/protocol_hid_h10301.h
  35. 0 237
      applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp
  36. 0 22
      applications/lfrfid/helpers/protocols/protocol_indala_40134.h
  37. 0 193
      applications/lfrfid/helpers/protocols/protocol_ioprox.cpp
  38. 0 26
      applications/lfrfid/helpers/protocols/protocol_ioprox.h
  39. 0 95
      applications/lfrfid/helpers/pulse_joiner.cpp
  40. 0 36
      applications/lfrfid/helpers/pulse_joiner.h
  41. 0 65
      applications/lfrfid/helpers/rfid_key.cpp
  42. 0 27
      applications/lfrfid/helpers/rfid_key.h
  43. 0 175
      applications/lfrfid/helpers/rfid_reader.cpp
  44. 0 59
      applications/lfrfid/helpers/rfid_reader.h
  45. 0 56
      applications/lfrfid/helpers/rfid_timer_emulator.cpp
  46. 0 31
      applications/lfrfid/helpers/rfid_timer_emulator.h
  47. 0 136
      applications/lfrfid/helpers/rfid_worker.cpp
  48. 0 48
      applications/lfrfid/helpers/rfid_worker.h
  49. 0 183
      applications/lfrfid/helpers/rfid_writer.cpp
  50. 0 21
      applications/lfrfid/helpers/rfid_writer.h
  51. 0 50
      applications/lfrfid/helpers/state_sequencer.cpp
  52. 0 25
      applications/lfrfid/helpers/state_sequencer.h
  53. 45 63
      applications/lfrfid/lfrfid_app.cpp
  54. 42 9
      applications/lfrfid/lfrfid_app.h
  55. 575 0
      applications/lfrfid/lfrfid_cli.c
  56. 0 177
      applications/lfrfid/lfrfid_cli.cpp
  57. 11 36
      applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp
  58. 0 1
      applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h
  59. 8 15
      applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp
  60. 0 3
      applications/lfrfid/scene/lfrfid_app_scene_emulate.h
  61. 63 0
      applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp
  62. 13 0
      applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h
  63. 77 0
      applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp
  64. 12 0
      applications/lfrfid/scene/lfrfid_app_scene_raw_info.h
  65. 46 0
      applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp
  66. 12 0
      applications/lfrfid/scene/lfrfid_app_scene_raw_name.h
  67. 107 0
      applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp
  68. 15 0
      applications/lfrfid/scene/lfrfid_app_scene_raw_read.h
  69. 45 0
      applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp
  70. 13 0
      applications/lfrfid/scene/lfrfid_app_scene_raw_success.h
  71. 79 19
      applications/lfrfid/scene/lfrfid_app_scene_read.cpp
  72. 3 3
      applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp
  73. 36 89
      applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp
  74. 2 1
      applications/lfrfid/scene/lfrfid_app_scene_read_success.h
  75. 6 4
      applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp
  76. 8 10
      applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp
  77. 0 21
      applications/lfrfid/scene/lfrfid_app_scene_save_data.h
  78. 7 11
      applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp
  79. 8 8
      applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp
  80. 2 2
      applications/lfrfid/scene/lfrfid_app_scene_save_type.h
  81. 25 62
      applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp
  82. 1 4
      applications/lfrfid/scene/lfrfid_app_scene_saved_info.h
  83. 3 3
      applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp
  84. 11 4
      applications/lfrfid/scene/lfrfid_app_scene_start.cpp
  85. 54 41
      applications/lfrfid/scene/lfrfid_app_scene_write.cpp
  86. 0 4
      applications/lfrfid/scene/lfrfid_app_scene_write.h
  87. 9 0
      applications/storage/storage.h
  88. 12 0
      applications/storage/storage_external_api.c
  89. 473 0
      applications/unit_tests/lfrfid/bit_lib_test.c
  90. 464 0
      applications/unit_tests/lfrfid/lfrfid_protocols.c
  91. 101 73
      applications/unit_tests/minunit.h
  92. 222 0
      applications/unit_tests/protocol_dict/protocol_dict_test.c
  93. 6 0
      applications/unit_tests/test_index.c
  94. 88 0
      applications/unit_tests/varint/varint_test.c
  95. 1 2
      applications/updater/util/update_task.c
  96. 1 0
      firmware.scons
  97. 186 0
      firmware/targets/f7/furi_hal/furi_hal_rfid.c
  98. 18 0
      firmware/targets/furi_hal_include/furi_hal_rfid.h
  99. 2 0
      furi/core/core_defines.h
  100. 1 0
      lib/SConscript

+ 4 - 14
applications/archive/helpers/archive_apps.c

@@ -29,23 +29,13 @@ bool archive_app_is_available(void* context, const char* path) {
 
 
     if(app == ArchiveAppTypeU2f) {
     if(app == ArchiveAppTypeU2f) {
         bool file_exists = false;
         bool file_exists = false;
-        Storage* fs_api = furi_record_open(RECORD_STORAGE);
-        File* file = storage_file_alloc(fs_api);
-
-        file_exists =
-            storage_file_open(file, ANY_PATH("u2f/key.u2f"), FSAM_READ, FSOM_OPEN_EXISTING);
-        if(file_exists) {
-            storage_file_close(file);
-            file_exists =
-                storage_file_open(file, ANY_PATH("u2f/cnt.u2f"), FSAM_READ, FSOM_OPEN_EXISTING);
-            if(file_exists) {
-                storage_file_close(file);
-            }
+        Storage* storage = furi_record_open(RECORD_STORAGE);
+
+        if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) {
+            file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f"));
         }
         }
 
 
-        storage_file_free(file);
         furi_record_close(RECORD_STORAGE);
         furi_record_close(RECORD_STORAGE);
-
         return file_exists;
         return file_exists;
     } else {
     } else {
         return false;
         return false;

+ 10 - 23
applications/archive/helpers/archive_favorites.c

@@ -82,9 +82,8 @@ uint16_t archive_favorites_count(void* context) {
 static bool archive_favourites_rescan() {
 static bool archive_favourites_rescan() {
     string_t buffer;
     string_t buffer;
     string_init(buffer);
     string_init(buffer);
-    Storage* fs_api = furi_record_open(RECORD_STORAGE);
-    File* file = storage_file_alloc(fs_api);
-    File* fav_item_file = storage_file_alloc(fs_api);
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    File* file = storage_file_alloc(storage);
 
 
     bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
     bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
     if(result) {
     if(result) {
@@ -101,13 +100,8 @@ static bool archive_favourites_rescan() {
                     archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
                     archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
                 }
                 }
             } else {
             } else {
-                bool file_exists = storage_file_open(
-                    fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING);
-                if(file_exists) {
-                    storage_file_close(fav_item_file);
+                if(storage_file_exists(storage, string_get_cstr(buffer))) {
                     archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
                     archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
-                } else {
-                    storage_file_close(fav_item_file);
                 }
                 }
             }
             }
         }
         }
@@ -116,12 +110,11 @@ static bool archive_favourites_rescan() {
     string_clear(buffer);
     string_clear(buffer);
 
 
     storage_file_close(file);
     storage_file_close(file);
-    storage_common_remove(fs_api, ARCHIVE_FAV_PATH);
-    storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
-    storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);
+    storage_common_remove(storage, ARCHIVE_FAV_PATH);
+    storage_common_rename(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
+    storage_common_remove(storage, ARCHIVE_FAV_TEMP_PATH);
 
 
     storage_file_free(file);
     storage_file_free(file);
-    storage_file_free(fav_item_file);
     furi_record_close(RECORD_STORAGE);
     furi_record_close(RECORD_STORAGE);
 
 
     return result;
     return result;
@@ -131,9 +124,8 @@ bool archive_favorites_read(void* context) {
     furi_assert(context);
     furi_assert(context);
 
 
     ArchiveBrowserView* browser = context;
     ArchiveBrowserView* browser = context;
-    Storage* fs_api = furi_record_open(RECORD_STORAGE);
-    File* file = storage_file_alloc(fs_api);
-    File* fav_item_file = storage_file_alloc(fs_api);
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    File* file = storage_file_alloc(storage);
 
 
     string_t buffer;
     string_t buffer;
     FileInfo file_info;
     FileInfo file_info;
@@ -163,16 +155,12 @@ bool archive_favorites_read(void* context) {
                     need_refresh = true;
                     need_refresh = true;
                 }
                 }
             } else {
             } else {
-                bool file_exists = storage_file_open(
-                    fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING);
-                if(file_exists) {
-                    storage_common_stat(fs_api, string_get_cstr(buffer), &file_info);
-                    storage_file_close(fav_item_file);
+                if(storage_file_exists(storage, string_get_cstr(buffer))) {
+                    storage_common_stat(storage, string_get_cstr(buffer), &file_info);
                     archive_add_file_item(
                     archive_add_file_item(
                         browser, (file_info.flags & FSF_DIRECTORY), string_get_cstr(buffer));
                         browser, (file_info.flags & FSF_DIRECTORY), string_get_cstr(buffer));
                     file_count++;
                     file_count++;
                 } else {
                 } else {
-                    storage_file_close(fav_item_file);
                     need_refresh = true;
                     need_refresh = true;
                 }
                 }
             }
             }
@@ -183,7 +171,6 @@ bool archive_favorites_read(void* context) {
     storage_file_close(file);
     storage_file_close(file);
     string_clear(buffer);
     string_clear(buffer);
     storage_file_free(file);
     storage_file_free(file);
-    storage_file_free(fav_item_file);
     furi_record_close(RECORD_STORAGE);
     furi_record_close(RECORD_STORAGE);
 
 
     archive_set_item_count(browser, file_count);
     archive_set_item_count(browser, file_count);

+ 1 - 2
applications/desktop/animations/animation_manager.c

@@ -220,8 +220,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager
             furi_assert(blocking_animation);
             furi_assert(blocking_animation);
             animation_manager->sd_shown_sd_ok = true;
             animation_manager->sd_shown_sd_ok = true;
         } else if(!animation_manager->sd_shown_no_db) {
         } else if(!animation_manager->sd_shown_no_db) {
-            bool db_exists = storage_common_stat(storage, EXT_PATH("Manifest"), NULL) == FSE_OK;
-            if(!db_exists) {
+            if(!storage_file_exists(storage, EXT_PATH("Manifest"))) {
                 blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME);
                 blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME);
                 furi_assert(blocking_animation);
                 furi_assert(blocking_animation);
                 animation_manager->sd_shown_no_db = true;
                 animation_manager->sd_shown_no_db = true;

+ 0 - 50
applications/lfrfid/helpers/decoder_analyzer.cpp

@@ -1,50 +0,0 @@
-#include "decoder_analyzer.h"
-#include <furi.h>
-#include <furi_hal.h>
-
-// FIXME: unused args?
-bool DecoderAnalyzer::read(uint8_t* /* _data */, uint8_t /* _data_size */) {
-    bool result = false;
-
-    if(ready) {
-        result = true;
-
-        for(size_t i = 0; i < data_size; i++) {
-            printf("%lu ", data[i]);
-            if((i + 1) % 8 == 0) printf("\r\n");
-        }
-        printf("\r\n--------\r\n");
-
-        ready = false;
-    }
-
-    return result;
-}
-
-void DecoderAnalyzer::process_front(bool polarity, uint32_t time) {
-    UNUSED(polarity);
-    if(ready) return;
-
-    data[data_index] = time;
-
-    if(data_index < data_size) {
-        data_index++;
-    } else {
-        data_index = 0;
-        ready = true;
-    }
-}
-
-DecoderAnalyzer::DecoderAnalyzer() {
-    data = reinterpret_cast<uint32_t*>(calloc(data_size, sizeof(uint32_t)));
-    furi_check(data);
-    data_index = 0;
-    ready = false;
-}
-
-DecoderAnalyzer::~DecoderAnalyzer() {
-    free(data);
-}
-
-void DecoderAnalyzer::reset_state() {
-}

+ 0 - 21
applications/lfrfid/helpers/decoder_analyzer.h

@@ -1,21 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <atomic>
-
-class DecoderAnalyzer {
-public:
-    bool read(uint8_t* data, uint8_t data_size);
-    void process_front(bool polarity, uint32_t time);
-
-    DecoderAnalyzer();
-    ~DecoderAnalyzer();
-
-private:
-    void reset_state();
-
-    std::atomic<bool> ready;
-
-    static const uint32_t data_size = 2048;
-    uint32_t data_index = 0;
-    uint32_t* data;
-};

+ 0 - 72
applications/lfrfid/helpers/decoder_emmarin.cpp

@@ -1,72 +0,0 @@
-#include "emmarin.h"
-#include "decoder_emmarin.h"
-#include <furi.h>
-#include <furi_hal.h>
-
-constexpr uint32_t clocks_in_us = 64;
-constexpr uint32_t short_time = 255 * clocks_in_us;
-constexpr uint32_t long_time = 510 * clocks_in_us;
-constexpr uint32_t jitter_time = 100 * clocks_in_us;
-
-constexpr uint32_t short_time_low = short_time - jitter_time;
-constexpr uint32_t short_time_high = short_time + jitter_time;
-constexpr uint32_t long_time_low = long_time - jitter_time;
-constexpr uint32_t long_time_high = long_time + jitter_time;
-
-void DecoderEMMarin::reset_state() {
-    ready = false;
-    read_data = 0;
-    manchester_advance(
-        manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr);
-}
-
-bool DecoderEMMarin::read(uint8_t* data, uint8_t data_size) {
-    bool result = false;
-
-    if(ready) {
-        result = true;
-        em_marin.decode(
-            reinterpret_cast<const uint8_t*>(&read_data), sizeof(uint64_t), data, data_size);
-        ready = false;
-    }
-
-    return result;
-}
-
-void DecoderEMMarin::process_front(bool polarity, uint32_t time) {
-    if(ready) return;
-    if(time < short_time_low) return;
-
-    ManchesterEvent event = ManchesterEventReset;
-
-    if(time > short_time_low && time < short_time_high) {
-        if(polarity) {
-            event = ManchesterEventShortHigh;
-        } else {
-            event = ManchesterEventShortLow;
-        }
-    } else if(time > long_time_low && time < long_time_high) {
-        if(polarity) {
-            event = ManchesterEventLongHigh;
-        } else {
-            event = ManchesterEventLongLow;
-        }
-    }
-
-    if(event != ManchesterEventReset) {
-        bool data;
-        bool data_ok =
-            manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
-
-        if(data_ok) {
-            read_data = (read_data << 1) | data;
-
-            ready = em_marin.can_be_decoded(
-                reinterpret_cast<const uint8_t*>(&read_data), sizeof(uint64_t));
-        }
-    }
-}
-
-DecoderEMMarin::DecoderEMMarin() {
-    reset_state();
-}

+ 0 - 21
applications/lfrfid/helpers/decoder_emmarin.h

@@ -1,21 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <atomic>
-#include <lib/toolbox/manchester_decoder.h>
-#include "protocols/protocol_emmarin.h"
-class DecoderEMMarin {
-public:
-    bool read(uint8_t* data, uint8_t data_size);
-    void process_front(bool polarity, uint32_t time);
-
-    DecoderEMMarin();
-
-private:
-    void reset_state();
-
-    uint64_t read_data = 0;
-    std::atomic<bool> ready;
-
-    ManchesterState manchester_saved_state;
-    ProtocolEMMarin em_marin;
-};

+ 0 - 15
applications/lfrfid/helpers/decoder_gpio_out.cpp

@@ -1,15 +0,0 @@
-#include "decoder_gpio_out.h"
-#include <furi.h>
-#include <furi_hal.h>
-
-void DecoderGpioOut::process_front(bool polarity, uint32_t /* time */) {
-    furi_hal_gpio_write(&gpio_ext_pa7, polarity);
-}
-
-DecoderGpioOut::DecoderGpioOut() {
-    furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
-}
-
-DecoderGpioOut::~DecoderGpioOut() {
-    furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
-}

+ 0 - 14
applications/lfrfid/helpers/decoder_gpio_out.h

@@ -1,14 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <atomic>
-
-class DecoderGpioOut {
-public:
-    void process_front(bool polarity, uint32_t time);
-
-    DecoderGpioOut();
-    ~DecoderGpioOut();
-
-private:
-    void reset_state();
-};

+ 0 - 98
applications/lfrfid/helpers/decoder_hid26.cpp

@@ -1,98 +0,0 @@
-#include "decoder_hid26.h"
-#include <furi_hal.h>
-
-constexpr uint32_t clocks_in_us = 64;
-
-constexpr uint32_t jitter_time_us = 20;
-constexpr uint32_t min_time_us = 64;
-constexpr uint32_t max_time_us = 80;
-
-constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
-constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
-constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
-
-bool DecoderHID26::read(uint8_t* data, uint8_t data_size) {
-    bool result = false;
-    furi_assert(data_size >= 3);
-
-    if(ready) {
-        result = true;
-        hid.decode(
-            reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3, data, data_size);
-        ready = false;
-    }
-
-    return result;
-}
-
-void DecoderHID26::process_front(bool polarity, uint32_t time) {
-    if(ready) return;
-
-    if(polarity == true) {
-        last_pulse_time = time;
-    } else {
-        last_pulse_time += time;
-
-        if(last_pulse_time > min_time && last_pulse_time < max_time) {
-            bool pulse;
-
-            if(last_pulse_time < mid_time) {
-                // 6 pulses
-                pulse = false;
-            } else {
-                // 5 pulses
-                pulse = true;
-            }
-
-            if(last_pulse == pulse) {
-                pulse_count++;
-
-                if(pulse) {
-                    if(pulse_count > 4) {
-                        pulse_count = 0;
-                        store_data(1);
-                    }
-                } else {
-                    if(pulse_count > 5) {
-                        pulse_count = 0;
-                        store_data(0);
-                    }
-                }
-            } else {
-                if(last_pulse) {
-                    if(pulse_count > 2) {
-                        store_data(1);
-                    }
-                } else {
-                    if(pulse_count > 3) {
-                        store_data(0);
-                    }
-                }
-
-                pulse_count = 0;
-                last_pulse = pulse;
-            }
-        }
-    }
-}
-
-DecoderHID26::DecoderHID26() {
-    reset_state();
-}
-
-void DecoderHID26::store_data(bool data) {
-    stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1);
-    stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1);
-    stored_data[2] = (stored_data[2] << 1) | data;
-
-    if(hid.can_be_decoded(reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3)) {
-        ready = true;
-    }
-}
-
-void DecoderHID26::reset_state() {
-    last_pulse = false;
-    pulse_count = 0;
-    ready = false;
-    last_pulse_time = 0;
-}

+ 0 - 24
applications/lfrfid/helpers/decoder_hid26.h

@@ -1,24 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <atomic>
-#include "protocols/protocol_hid_h10301.h"
-
-class DecoderHID26 {
-public:
-    bool read(uint8_t* data, uint8_t data_size);
-    void process_front(bool polarity, uint32_t time);
-    DecoderHID26();
-
-private:
-    uint32_t last_pulse_time = 0;
-    bool last_pulse;
-    uint8_t pulse_count;
-
-    uint32_t stored_data[3] = {0, 0, 0};
-    void store_data(bool data);
-
-    std::atomic<bool> ready;
-
-    void reset_state();
-    ProtocolHID10301 hid;
-};

+ 0 - 76
applications/lfrfid/helpers/decoder_indala.cpp

@@ -1,76 +0,0 @@
-#include "decoder_indala.h"
-#include <furi_hal.h>
-
-constexpr uint32_t clocks_in_us = 64;
-constexpr uint32_t us_per_bit = 255;
-
-bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
-    bool result = false;
-
-    if(ready) {
-        result = true;
-        if(cursed_data_valid) {
-            indala.decode(
-                reinterpret_cast<const uint8_t*>(&cursed_raw_data),
-                sizeof(uint64_t),
-                data,
-                data_size);
-        } else {
-            indala.decode(
-                reinterpret_cast<const uint8_t*>(&raw_data), sizeof(uint64_t), data, data_size);
-        }
-        reset_state();
-    }
-
-    return result;
-}
-
-void DecoderIndala::process_front(bool polarity, uint32_t time) {
-    if(ready) return;
-
-    process_internal(polarity, time, &raw_data);
-    if(ready) return;
-
-    if(polarity) {
-        time = time + 110;
-    } else {
-        time = time - 110;
-    }
-
-    process_internal(!polarity, time, &cursed_raw_data);
-    if(ready) {
-        cursed_data_valid = true;
-    }
-}
-
-void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) {
-    time /= clocks_in_us;
-    time += (us_per_bit / 2);
-
-    uint32_t bit_count = (time / us_per_bit);
-
-    if(bit_count < 64) {
-        for(uint32_t i = 0; i < bit_count; i++) {
-            *data = (*data << 1) | polarity;
-
-            if((*data >> 32) == 0xa0000000ULL) {
-                if(indala.can_be_decoded(
-                       reinterpret_cast<const uint8_t*>(data), sizeof(uint64_t))) {
-                    ready = true;
-                    break;
-                }
-            }
-        }
-    }
-}
-
-DecoderIndala::DecoderIndala() {
-    reset_state();
-}
-
-void DecoderIndala::reset_state() {
-    raw_data = 0;
-    cursed_raw_data = 0;
-    ready = false;
-    cursed_data_valid = false;
-}

+ 0 - 25
applications/lfrfid/helpers/decoder_indala.h

@@ -1,25 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <limits.h>
-#include <atomic>
-#include "protocols/protocol_indala_40134.h"
-
-class DecoderIndala {
-public:
-    bool read(uint8_t* data, uint8_t data_size);
-    void process_front(bool polarity, uint32_t time);
-
-    void process_internal(bool polarity, uint32_t time, uint64_t* data);
-
-    DecoderIndala();
-
-private:
-    void reset_state();
-
-    uint64_t raw_data;
-    uint64_t cursed_raw_data;
-
-    std::atomic<bool> ready;
-    std::atomic<bool> cursed_data_valid;
-    ProtocolIndala40134 indala;
-};

+ 0 - 107
applications/lfrfid/helpers/decoder_ioprox.cpp

@@ -1,107 +0,0 @@
-#include "decoder_ioprox.h"
-#include <furi_hal.h>
-#include <cli/cli.h>
-#include <utility>
-
-constexpr uint32_t clocks_in_us = 64;
-
-constexpr uint32_t jitter_time_us = 20;
-constexpr uint32_t min_time_us = 64;
-constexpr uint32_t max_time_us = 80;
-constexpr uint32_t baud_time_us = 500;
-
-constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
-constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
-constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
-constexpr uint32_t baud_time = baud_time_us * clocks_in_us;
-
-bool DecoderIoProx::read(uint8_t* data, uint8_t data_size) {
-    bool result = false;
-    furi_assert(data_size >= 4);
-
-    if(ready) {
-        result = true;
-        ioprox.decode(raw_data, sizeof(raw_data), data, data_size);
-        ready = false;
-    }
-
-    return result;
-}
-
-void DecoderIoProx::process_front(bool is_rising_edge, uint32_t time) {
-    if(ready) {
-        return;
-    }
-
-    // Always track the time that's gone by.
-    current_period_duration += time;
-    demodulation_sample_duration += time;
-
-    // If a baud time has elapsed, we're at a sample point.
-    if(demodulation_sample_duration >= baud_time) {
-        // Start a new baud period...
-        demodulation_sample_duration = 0;
-        demodulated_value_invalid = false;
-
-        // ... and if we didn't have any baud errors, capture a sample.
-        if(!demodulated_value_invalid) {
-            store_data(current_demodulated_value);
-        }
-    }
-
-    //
-    // FSK demodulator.
-    //
-
-    // If this isn't a rising edge, this isn't a pulse of interest.
-    // We're done.
-    if(!is_rising_edge) {
-        return;
-    }
-
-    bool is_valid_low = (current_period_duration > min_time) &&
-                        (current_period_duration <= mid_time);
-    bool is_valid_high = (current_period_duration > mid_time) &&
-                         (current_period_duration < max_time);
-
-    // If this is between the minimum and our threshold, this is a logical 0.
-    if(is_valid_low) {
-        current_demodulated_value = false;
-    }
-    // Otherwise, if between our threshold and the max time, it's a logical 1.
-    else if(is_valid_high) {
-        current_demodulated_value = true;
-    }
-    // Otherwise, invalidate this sample.
-    else {
-        demodulated_value_invalid = true;
-    }
-
-    // We're starting a new period; track that.
-    current_period_duration = 0;
-}
-
-DecoderIoProx::DecoderIoProx() {
-    reset_state();
-}
-
-void DecoderIoProx::store_data(bool data) {
-    for(int i = 0; i < 7; ++i) {
-        raw_data[i] = (raw_data[i] << 1) | ((raw_data[i + 1] >> 7) & 1);
-    }
-    raw_data[7] = (raw_data[7] << 1) | data;
-
-    if(ioprox.can_be_decoded(raw_data, sizeof(raw_data))) {
-        ready = true;
-    }
-}
-
-void DecoderIoProx::reset_state() {
-    current_demodulated_value = false;
-    demodulated_value_invalid = false;
-
-    current_period_duration = 0;
-    demodulation_sample_duration = 0;
-
-    ready = false;
-}

+ 0 - 26
applications/lfrfid/helpers/decoder_ioprox.h

@@ -1,26 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <atomic>
-#include "protocols/protocol_ioprox.h"
-
-class DecoderIoProx {
-public:
-    bool read(uint8_t* data, uint8_t data_size);
-    void process_front(bool polarity, uint32_t time);
-    DecoderIoProx();
-
-private:
-    uint32_t current_period_duration = 0;
-    uint32_t demodulation_sample_duration = 0;
-
-    bool current_demodulated_value = false;
-    bool demodulated_value_invalid = false;
-
-    uint8_t raw_data[8] = {0};
-    void store_data(bool data);
-
-    std::atomic<bool> ready;
-
-    void reset_state();
-    ProtocolIoProx ioprox;
-};

+ 0 - 15
applications/lfrfid/helpers/emmarin.h

@@ -1,15 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-#define EM_HEADER_POS 55
-#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
-
-#define EM_FIRST_ROW_POS 50
-#define EM_ROW_COUNT 10
-
-#define EM_COLUMN_POS 4
-#define EM_STOP_POS 0
-#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
-
-#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
-#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)

+ 0 - 24
applications/lfrfid/helpers/encoder_emmarin.cpp

@@ -1,24 +0,0 @@
-#include "encoder_emmarin.h"
-#include "protocols/protocol_emmarin.h"
-#include <furi.h>
-
-void EncoderEM::init(const uint8_t* data, const uint8_t data_size) {
-    ProtocolEMMarin em_marin;
-    em_marin.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(uint64_t));
-
-    card_data_index = 0;
-}
-
-// data transmitted as manchester encoding
-// 0 - high2low
-// 1 - low2high
-void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
-    *period = clocks_per_bit;
-    *pulse = clocks_per_bit / 2;
-    *polarity = (card_data >> (63 - card_data_index)) & 1;
-
-    card_data_index++;
-    if(card_data_index >= 64) {
-        card_data_index = 0;
-    }
-}

+ 0 - 22
applications/lfrfid/helpers/encoder_emmarin.h

@@ -1,22 +0,0 @@
-#pragma once
-#include "encoder_generic.h"
-
-class EncoderEM : public EncoderGeneric {
-public:
-    /**
-     * @brief init data to emulate
-     * 
-     * @param data 1 byte FC, next 4 byte SN
-     * @param data_size must be 5
-     */
-    void init(const uint8_t* data, const uint8_t data_size) final;
-
-    void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
-
-private:
-    // clock pulses per bit
-    static const uint8_t clocks_per_bit = 64;
-
-    uint64_t card_data;
-    uint8_t card_data_index;
-};

+ 0 - 27
applications/lfrfid/helpers/encoder_generic.h

@@ -1,27 +0,0 @@
-#pragma once
-#include <stdbool.h>
-#include <stdint.h>
-
-class EncoderGeneric {
-public:
-    /**
-     * @brief init encoder
-     * 
-     * @param data data array
-     * @param data_size data array size
-     */
-    virtual void init(const uint8_t* data, const uint8_t data_size) = 0;
-
-    /**
-     * @brief Get the next timer pulse
-     * 
-     * @param polarity pulse polarity true = high2low, false = low2high
-     * @param period overall period time in timer clicks
-     * @param pulse pulse time in timer clicks
-     */
-    virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0;
-
-    virtual ~EncoderGeneric(){};
-
-private:
-};

+ 0 - 46
applications/lfrfid/helpers/encoder_hid_h10301.cpp

@@ -1,46 +0,0 @@
-#include "encoder_hid_h10301.h"
-#include "protocols/protocol_hid_h10301.h"
-#include <furi.h>
-
-void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) {
-    ProtocolHID10301 hid;
-    hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
-
-    card_data_index = 0;
-}
-
-void EncoderHID_H10301::write_bit(bool bit, uint8_t position) {
-    write_raw_bit(bit, position + 0);
-    write_raw_bit(!bit, position + 1);
-}
-
-void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) {
-    if(bit) {
-        card_data[position / 32] |= 1UL << (31 - (position % 32));
-    } else {
-        card_data[position / 32] &= ~(1UL << (31 - (position % 32)));
-    }
-}
-
-void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
-    uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
-
-    bool advance = fsk->next(bit, period);
-    if(advance) {
-        card_data_index++;
-        if(card_data_index >= (32 * card_data_max)) {
-            card_data_index = 0;
-        }
-    }
-
-    *polarity = true;
-    *pulse = *period / 2;
-}
-
-EncoderHID_H10301::EncoderHID_H10301() {
-    fsk = new OscFSK(8, 10, 50);
-}
-
-EncoderHID_H10301::~EncoderHID_H10301() {
-    delete fsk;
-}

+ 0 - 26
applications/lfrfid/helpers/encoder_hid_h10301.h

@@ -1,26 +0,0 @@
-#pragma once
-#include "encoder_generic.h"
-#include "osc_fsk.h"
-
-class EncoderHID_H10301 : public EncoderGeneric {
-public:
-    /**
-     * @brief init data to emulate
-     * 
-     * @param data 1 byte FC, next 2 byte SN
-     * @param data_size must be 3
-     */
-    void init(const uint8_t* data, const uint8_t data_size) final;
-    void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
-    EncoderHID_H10301();
-    ~EncoderHID_H10301();
-
-private:
-    static const uint8_t card_data_max = 3;
-    uint32_t card_data[card_data_max];
-    uint8_t card_data_index;
-    void write_bit(bool bit, uint8_t position);
-    void write_raw_bit(bool bit, uint8_t position);
-
-    OscFSK* fsk;
-};

+ 0 - 36
applications/lfrfid/helpers/encoder_indala_40134.cpp

@@ -1,36 +0,0 @@
-#include "encoder_indala_40134.h"
-#include "protocols/protocol_indala_40134.h"
-#include <furi.h>
-
-void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) {
-    ProtocolIndala40134 indala;
-    indala.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data));
-
-    last_bit = card_data & 1;
-    card_data_index = 0;
-    current_polarity = true;
-}
-
-void EncoderIndala_40134::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
-    *period = 2;
-    *pulse = 1;
-    *polarity = current_polarity;
-
-    bit_clock_index++;
-    if(bit_clock_index >= clock_per_bit) {
-        bit_clock_index = 0;
-
-        bool current_bit = (card_data >> (63 - card_data_index)) & 1;
-
-        if(current_bit != last_bit) {
-            current_polarity = !current_polarity;
-        }
-
-        last_bit = current_bit;
-
-        card_data_index++;
-        if(card_data_index >= 64) {
-            card_data_index = 0;
-        }
-    }
-}

+ 0 - 23
applications/lfrfid/helpers/encoder_indala_40134.h

@@ -1,23 +0,0 @@
-#pragma once
-#include "encoder_generic.h"
-
-class EncoderIndala_40134 : public EncoderGeneric {
-public:
-    /**
-     * @brief init data to emulate
-     * 
-     * @param data indala raw data
-     * @param data_size must be 5
-     */
-    void init(const uint8_t* data, const uint8_t data_size) final;
-
-    void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
-
-private:
-    uint64_t card_data;
-    uint8_t card_data_index;
-    uint8_t bit_clock_index;
-    bool last_bit;
-    bool current_polarity;
-    static const uint8_t clock_per_bit = 16;
-};

+ 0 - 32
applications/lfrfid/helpers/encoder_ioprox.cpp

@@ -1,32 +0,0 @@
-#include "encoder_ioprox.h"
-#include "protocols/protocol_ioprox.h"
-#include <furi.h>
-
-void EncoderIoProx::init(const uint8_t* data, const uint8_t data_size) {
-    ProtocolIoProx ioprox;
-    ioprox.encode(data, data_size, card_data, sizeof(card_data));
-    card_data_index = 0;
-}
-
-void EncoderIoProx::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
-    uint8_t bit = (card_data[card_data_index / 8] >> (7 - (card_data_index % 8))) & 1;
-
-    bool advance = fsk->next(bit, period);
-    if(advance) {
-        card_data_index++;
-        if(card_data_index >= (8 * card_data_max)) {
-            card_data_index = 0;
-        }
-    }
-
-    *polarity = true;
-    *pulse = *period / 2;
-}
-
-EncoderIoProx::EncoderIoProx() {
-    fsk = new OscFSK(8, 10, 64);
-}
-
-EncoderIoProx::~EncoderIoProx() {
-    delete fsk;
-}

+ 0 - 25
applications/lfrfid/helpers/encoder_ioprox.h

@@ -1,25 +0,0 @@
-#pragma once
-#include "encoder_generic.h"
-#include "osc_fsk.h"
-
-class EncoderIoProx : public EncoderGeneric {
-public:
-    /**
-     * @brief init data to emulate
-     * 
-     * @param data 1 byte FC, 1 byte Version, 2 bytes code
-     * @param data_size must be 4
-     */
-    void init(const uint8_t* data, const uint8_t data_size) final;
-    void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
-    EncoderIoProx();
-    ~EncoderIoProx();
-
-private:
-    static const uint8_t card_data_max = 8;
-
-    uint8_t card_data[card_data_max];
-    uint8_t card_data_index;
-
-    OscFSK* fsk;
-};

+ 0 - 76
applications/lfrfid/helpers/key_info.cpp

@@ -1,76 +0,0 @@
-#include "key_info.h"
-#include <string.h>
-
-const char* lfrfid_key_get_type_string(LfrfidKeyType type) {
-    switch(type) {
-    case LfrfidKeyType::KeyEM4100:
-        return "EM4100";
-        break;
-    case LfrfidKeyType::KeyH10301:
-        return "H10301";
-        break;
-    case LfrfidKeyType::KeyI40134:
-        return "I40134";
-        break;
-    case LfrfidKeyType::KeyIoProxXSF:
-        return "IoProxXSF";
-        break;
-    }
-
-    return "Unknown";
-}
-
-const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type) {
-    switch(type) {
-    case LfrfidKeyType::KeyEM4100:
-        return "EM-Marin";
-        break;
-    case LfrfidKeyType::KeyH10301:
-        return "HID";
-        break;
-    case LfrfidKeyType::KeyI40134:
-        return "Indala";
-        break;
-    case LfrfidKeyType::KeyIoProxXSF:
-        return "Kantech";
-    }
-
-    return "Unknown";
-}
-
-bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type) {
-    bool result = true;
-
-    if(strcmp("EM4100", string) == 0) {
-        *type = LfrfidKeyType::KeyEM4100;
-    } else if(strcmp("H10301", string) == 0) {
-        *type = LfrfidKeyType::KeyH10301;
-    } else if(strcmp("I40134", string) == 0) {
-        *type = LfrfidKeyType::KeyI40134;
-    } else if(strcmp("IoProxXSF", string) == 0) {
-        *type = LfrfidKeyType::KeyIoProxXSF;
-    } else {
-        result = false;
-    }
-
-    return result;
-}
-
-uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) {
-    switch(type) {
-    case LfrfidKeyType::KeyEM4100:
-        return 5;
-        break;
-    case LfrfidKeyType::KeyH10301:
-        return 3;
-        break;
-    case LfrfidKeyType::KeyI40134:
-        return 3;
-        break;
-    case LfrfidKeyType::KeyIoProxXSF:
-        return 4;
-        break;
-    }
-
-    return 0;
-}

+ 0 - 17
applications/lfrfid/helpers/key_info.h

@@ -1,17 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-static const uint8_t LFRFID_KEY_SIZE = 8;
-static const uint8_t LFRFID_KEY_NAME_SIZE = 22;
-
-enum class LfrfidKeyType : uint8_t {
-    KeyEM4100,
-    KeyH10301,
-    KeyI40134,
-    KeyIoProxXSF,
-};
-
-const char* lfrfid_key_get_type_string(LfrfidKeyType type);
-const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type);
-bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type);
-uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type);

+ 0 - 20
applications/lfrfid/helpers/osc_fsk.cpp

@@ -1,20 +0,0 @@
-#include "osc_fsk.h"
-
-OscFSK::OscFSK(uint16_t _freq_low, uint16_t _freq_hi, uint16_t _osc_phase_max)
-    : freq{_freq_low, _freq_hi}
-    , osc_phase_max(_osc_phase_max) {
-    osc_phase_current = 0;
-}
-
-bool OscFSK::next(bool bit, uint16_t* period) {
-    bool advance = false;
-    *period = freq[bit];
-    osc_phase_current += *period;
-
-    if(osc_phase_current > osc_phase_max) {
-        advance = true;
-        osc_phase_current -= osc_phase_max;
-    }
-
-    return advance;
-}

+ 0 - 30
applications/lfrfid/helpers/osc_fsk.h

@@ -1,30 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-/**
- * This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods.
- */
-class OscFSK {
-public:
-    /**
-     * Get next period
-     * @param bit bit value
-     * @param period return period
-     * @return bool whether to advance to the next bit
-     */
-    bool next(bool bit, uint16_t* period);
-
-    /**
-     * FSK ocillator constructor
-     * 
-     * @param freq_low bit 0 freq
-     * @param freq_hi bit 1 freq
-     * @param osc_phase_max max oscillator phase
-     */
-    OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max);
-
-private:
-    const uint16_t freq[2];
-    const uint16_t osc_phase_max;
-    int32_t osc_phase_current;
-};

+ 0 - 150
applications/lfrfid/helpers/protocols/protocol_emmarin.cpp

@@ -1,150 +0,0 @@
-#include "protocol_emmarin.h"
-#include <furi.h>
-
-#define EM_HEADER_POS 55
-#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
-
-#define EM_FIRST_ROW_POS 50
-
-#define EM_ROW_COUNT 10
-#define EM_COLUMN_COUNT 4
-#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
-
-#define EM_COLUMN_POS 4
-#define EM_STOP_POS 0
-#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
-
-#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
-#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
-
-typedef uint64_t EMMarinCardData;
-
-void write_nibble(bool low_nibble, uint8_t data, EMMarinCardData* card_data) {
-    uint8_t parity_sum = 0;
-    uint8_t start = 0;
-    if(!low_nibble) start = 4;
-
-    for(int8_t i = (start + 3); i >= start; i--) {
-        parity_sum += (data >> i) & 1;
-        *card_data = (*card_data << 1) | ((data >> i) & 1);
-    }
-
-    *card_data = (*card_data << 1) | ((parity_sum % 2) & 1);
-}
-
-uint8_t ProtocolEMMarin::get_encoded_data_size() {
-    return sizeof(EMMarinCardData);
-}
-
-uint8_t ProtocolEMMarin::get_decoded_data_size() {
-    return 5;
-}
-
-void ProtocolEMMarin::encode(
-    const uint8_t* decoded_data,
-    const uint8_t decoded_data_size,
-    uint8_t* encoded_data,
-    const uint8_t encoded_data_size) {
-    furi_check(decoded_data_size >= get_decoded_data_size());
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    EMMarinCardData card_data;
-
-    // header
-    card_data = 0b111111111;
-
-    // data
-    for(uint8_t i = 0; i < get_decoded_data_size(); i++) {
-        write_nibble(false, decoded_data[i], &card_data);
-        write_nibble(true, decoded_data[i], &card_data);
-    }
-
-    // column parity and stop bit
-    uint8_t parity_sum;
-
-    for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
-        parity_sum = 0;
-        for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {
-            uint8_t parity_bit = (card_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
-            parity_sum += parity_bit;
-        }
-        card_data = (card_data << 1) | ((parity_sum % 2) & 1);
-    }
-
-    // stop bit
-    card_data = (card_data << 1) | 0;
-
-    memcpy(encoded_data, &card_data, get_encoded_data_size());
-}
-
-void ProtocolEMMarin::decode(
-    const uint8_t* encoded_data,
-    const uint8_t encoded_data_size,
-    uint8_t* decoded_data,
-    const uint8_t decoded_data_size) {
-    furi_check(decoded_data_size >= get_decoded_data_size());
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    uint8_t decoded_data_index = 0;
-    EMMarinCardData card_data = *(reinterpret_cast<const EMMarinCardData*>(encoded_data));
-
-    // clean result
-    memset(decoded_data, 0, decoded_data_size);
-
-    // header
-    for(uint8_t i = 0; i < 9; i++) {
-        card_data = card_data << 1;
-    }
-
-    // nibbles
-    uint8_t value = 0;
-    for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
-        uint8_t nibble = 0;
-        for(uint8_t i = 0; i < 5; i++) {
-            if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0);
-            card_data = card_data << 1;
-        }
-        value = (value << 4) | nibble;
-        if(r % 2) {
-            decoded_data[decoded_data_index] |= value;
-            decoded_data_index++;
-            value = 0;
-        }
-    }
-}
-
-bool ProtocolEMMarin::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
-    furi_check(encoded_data_size >= get_encoded_data_size());
-    const EMMarinCardData* card_data = reinterpret_cast<const EMMarinCardData*>(encoded_data);
-
-    // check header and stop bit
-    if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
-
-    // check row parity
-    for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
-        uint8_t parity_sum = 0;
-
-        for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {
-            parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;
-        }
-
-        if((parity_sum % 2)) {
-            return false;
-        }
-    }
-
-    // check columns parity
-    for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {
-        uint8_t parity_sum = 0;
-
-        for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
-            parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;
-        }
-
-        if((parity_sum % 2)) {
-            return false;
-        }
-    }
-
-    return true;
-}

+ 0 - 22
applications/lfrfid/helpers/protocols/protocol_emmarin.h

@@ -1,22 +0,0 @@
-#pragma once
-#include "protocol_generic.h"
-
-class ProtocolEMMarin : public ProtocolGeneric {
-public:
-    uint8_t get_encoded_data_size() final;
-    uint8_t get_decoded_data_size() final;
-
-    void encode(
-        const uint8_t* decoded_data,
-        const uint8_t decoded_data_size,
-        uint8_t* encoded_data,
-        const uint8_t encoded_data_size) final;
-
-    void decode(
-        const uint8_t* encoded_data,
-        const uint8_t encoded_data_size,
-        uint8_t* decoded_data,
-        const uint8_t decoded_data_size) final;
-
-    bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
-};

+ 0 - 60
applications/lfrfid/helpers/protocols/protocol_generic.h

@@ -1,60 +0,0 @@
-#pragma once
-#include "stdint.h"
-#include "stdbool.h"
-
-class ProtocolGeneric {
-public:
-    /**
-     * @brief Get the encoded data size
-     * 
-     * @return uint8_t size of encoded data in bytes
-     */
-    virtual uint8_t get_encoded_data_size() = 0;
-
-    /**
-     * @brief Get the decoded data size
-     * 
-     * @return uint8_t size of decoded data in bytes
-     */
-    virtual uint8_t get_decoded_data_size() = 0;
-
-    /**
-     * @brief encode decoded data
-     * 
-     * @param decoded_data 
-     * @param decoded_data_size 
-     * @param encoded_data 
-     * @param encoded_data_size 
-     */
-    virtual void encode(
-        const uint8_t* decoded_data,
-        const uint8_t decoded_data_size,
-        uint8_t* encoded_data,
-        const uint8_t encoded_data_size) = 0;
-
-    /**
-     * @brief decode encoded data
-     * 
-     * @param encoded_data 
-     * @param encoded_data_size 
-     * @param decoded_data 
-     * @param decoded_data_size 
-     */
-    virtual void decode(
-        const uint8_t* encoded_data,
-        const uint8_t encoded_data_size,
-        uint8_t* decoded_data,
-        const uint8_t decoded_data_size) = 0;
-
-    /**
-     * @brief fast check that data can be correctly decoded
-     * 
-     * @param encoded_data 
-     * @param encoded_data_size 
-     * @return true - can be correctly decoded
-     * @return false - cannot be correctly decoded
-     */
-    virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0;
-
-    virtual ~ProtocolGeneric(){};
-};

+ 0 - 238
applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp

@@ -1,238 +0,0 @@
-#include "protocol_hid_h10301.h"
-#include <furi.h>
-
-typedef uint32_t HID10301CardData;
-constexpr uint8_t HID10301Count = 3;
-constexpr uint8_t HID10301BitSize = sizeof(HID10301CardData) * 8;
-
-static void write_raw_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
-    if(bit) {
-        card_data[position / HID10301BitSize] |=
-            1UL << (HID10301BitSize - (position % HID10301BitSize) - 1);
-    } else {
-        card_data[position / (sizeof(HID10301CardData) * 8)] &=
-            ~(1UL << (HID10301BitSize - (position % HID10301BitSize) - 1));
-    }
-}
-
-static void write_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
-    write_raw_bit(bit, position + 0, card_data);
-    write_raw_bit(!bit, position + 1, card_data);
-}
-
-uint8_t ProtocolHID10301::get_encoded_data_size() {
-    return sizeof(HID10301CardData) * HID10301Count;
-}
-
-uint8_t ProtocolHID10301::get_decoded_data_size() {
-    return 3;
-}
-
-void ProtocolHID10301::encode(
-    const uint8_t* decoded_data,
-    const uint8_t decoded_data_size,
-    uint8_t* encoded_data,
-    const uint8_t encoded_data_size) {
-    furi_check(decoded_data_size >= get_decoded_data_size());
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    HID10301CardData card_data[HID10301Count] = {0, 0, 0};
-
-    uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
-
-    // even parity sum calculation (high 12 bits of data)
-    uint8_t even_parity_sum = 0;
-    for(int8_t i = 12; i < 24; i++) {
-        if(((fc_cn >> i) & 1) == 1) {
-            even_parity_sum++;
-        }
-    }
-
-    // odd parity sum calculation (low 12 bits of data)
-    uint8_t odd_parity_sum = 1;
-    for(int8_t i = 0; i < 12; i++) {
-        if(((fc_cn >> i) & 1) == 1) {
-            odd_parity_sum++;
-        }
-    }
-
-    // 0x1D preamble
-    write_raw_bit(0, 0, card_data);
-    write_raw_bit(0, 1, card_data);
-    write_raw_bit(0, 2, card_data);
-    write_raw_bit(1, 3, card_data);
-    write_raw_bit(1, 4, card_data);
-    write_raw_bit(1, 5, card_data);
-    write_raw_bit(0, 6, card_data);
-    write_raw_bit(1, 7, card_data);
-
-    // company / OEM code 1
-    write_bit(0, 8, card_data);
-    write_bit(0, 10, card_data);
-    write_bit(0, 12, card_data);
-    write_bit(0, 14, card_data);
-    write_bit(0, 16, card_data);
-    write_bit(0, 18, card_data);
-    write_bit(1, 20, card_data);
-
-    // card format / length 1
-    write_bit(0, 22, card_data);
-    write_bit(0, 24, card_data);
-    write_bit(0, 26, card_data);
-    write_bit(0, 28, card_data);
-    write_bit(0, 30, card_data);
-    write_bit(0, 32, card_data);
-    write_bit(0, 34, card_data);
-    write_bit(0, 36, card_data);
-    write_bit(0, 38, card_data);
-    write_bit(0, 40, card_data);
-    write_bit(1, 42, card_data);
-
-    // even parity bit
-    write_bit((even_parity_sum % 2), 44, card_data);
-
-    // data
-    for(uint8_t i = 0; i < 24; i++) {
-        write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data);
-    }
-
-    // odd parity bit
-    write_bit((odd_parity_sum % 2), 94, card_data);
-
-    memcpy(encoded_data, &card_data, get_encoded_data_size());
-}
-
-void ProtocolHID10301::decode(
-    const uint8_t* encoded_data,
-    const uint8_t encoded_data_size,
-    uint8_t* decoded_data,
-    const uint8_t decoded_data_size) {
-    furi_check(decoded_data_size >= get_decoded_data_size());
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
-
-    // data decoding
-    uint32_t result = 0;
-
-    // decode from word 1
-    // coded with 01 = 0, 10 = 1 transitions
-    for(int8_t i = 9; i >= 0; i--) {
-        switch((*(card_data + 1) >> (2 * i)) & 0b11) {
-        case 0b01:
-            result = (result << 1) | 0;
-            break;
-        case 0b10:
-            result = (result << 1) | 1;
-            break;
-        default:
-            break;
-        }
-    }
-
-    // decode from word 2
-    // coded with 01 = 0, 10 = 1 transitions
-    for(int8_t i = 15; i >= 0; i--) {
-        switch((*(card_data + 2) >> (2 * i)) & 0b11) {
-        case 0b01:
-            result = (result << 1) | 0;
-            break;
-        case 0b10:
-            result = (result << 1) | 1;
-            break;
-        default:
-            break;
-        }
-    }
-
-    uint8_t data[3] = {(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)};
-
-    memcpy(decoded_data, &data, get_decoded_data_size());
-}
-
-bool ProtocolHID10301::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
-
-    // packet preamble
-    // raw data
-    if(*(encoded_data + 3) != 0x1D) {
-        return false;
-    }
-
-    // encoded company/oem
-    // coded with 01 = 0, 10 = 1 transitions
-    // stored in word 0
-    if((*card_data >> 10 & 0x3FFF) != 0x1556) {
-        return false;
-    }
-
-    // encoded format/length
-    // coded with 01 = 0, 10 = 1 transitions
-    // stored in word 0 and word 1
-    if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) {
-        return false;
-    }
-
-    // data decoding
-    uint32_t result = 0;
-
-    // decode from word 1
-    // coded with 01 = 0, 10 = 1 transitions
-    for(int8_t i = 9; i >= 0; i--) {
-        switch((*(card_data + 1) >> (2 * i)) & 0b11) {
-        case 0b01:
-            result = (result << 1) | 0;
-            break;
-        case 0b10:
-            result = (result << 1) | 1;
-            break;
-        default:
-            return false;
-            break;
-        }
-    }
-
-    // decode from word 2
-    // coded with 01 = 0, 10 = 1 transitions
-    for(int8_t i = 15; i >= 0; i--) {
-        switch((*(card_data + 2) >> (2 * i)) & 0b11) {
-        case 0b01:
-            result = (result << 1) | 0;
-            break;
-        case 0b10:
-            result = (result << 1) | 1;
-            break;
-        default:
-            return false;
-            break;
-        }
-    }
-
-    // trailing parity (odd) test
-    uint8_t parity_sum = 0;
-    for(int8_t i = 0; i < 13; i++) {
-        if(((result >> i) & 1) == 1) {
-            parity_sum++;
-        }
-    }
-
-    if((parity_sum % 2) != 1) {
-        return false;
-    }
-
-    // leading parity (even) test
-    parity_sum = 0;
-    for(int8_t i = 13; i < 26; i++) {
-        if(((result >> i) & 1) == 1) {
-            parity_sum++;
-        }
-    }
-
-    if((parity_sum % 2) == 1) {
-        return false;
-    }
-
-    return true;
-}

+ 0 - 22
applications/lfrfid/helpers/protocols/protocol_hid_h10301.h

@@ -1,22 +0,0 @@
-#pragma once
-#include "protocol_generic.h"
-
-class ProtocolHID10301 : public ProtocolGeneric {
-public:
-    uint8_t get_encoded_data_size() final;
-    uint8_t get_decoded_data_size() final;
-
-    void encode(
-        const uint8_t* decoded_data,
-        const uint8_t decoded_data_size,
-        uint8_t* encoded_data,
-        const uint8_t encoded_data_size) final;
-
-    void decode(
-        const uint8_t* encoded_data,
-        const uint8_t encoded_data_size,
-        uint8_t* decoded_data,
-        const uint8_t decoded_data_size) final;
-
-    bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
-};

+ 0 - 237
applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp

@@ -1,237 +0,0 @@
-#include "protocol_indala_40134.h"
-#include <furi.h>
-
-typedef uint64_t Indala40134CardData;
-
-static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data) {
-    position = (sizeof(Indala40134CardData) * 8) - 1 - position;
-    if(bit) {
-        *card_data |= 1ull << position;
-    } else {
-        *card_data &= ~(1ull << position);
-    }
-}
-
-static bool get_bit(uint8_t position, const Indala40134CardData* card_data) {
-    position = (sizeof(Indala40134CardData) * 8) - 1 - position;
-    return (*card_data >> position) & 1;
-}
-
-uint8_t ProtocolIndala40134::get_encoded_data_size() {
-    return sizeof(Indala40134CardData);
-}
-
-uint8_t ProtocolIndala40134::get_decoded_data_size() {
-    return 3;
-}
-
-void ProtocolIndala40134::encode(
-    const uint8_t* decoded_data,
-    const uint8_t decoded_data_size,
-    uint8_t* encoded_data,
-    const uint8_t encoded_data_size) {
-    furi_check(decoded_data_size >= get_decoded_data_size());
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    uint32_t fc_and_card = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
-    Indala40134CardData card_data = 0;
-
-    // preamble
-    set_bit(1, 0, &card_data);
-    set_bit(1, 2, &card_data);
-    set_bit(1, 32, &card_data);
-
-    // factory code
-    set_bit(((fc_and_card >> 23) & 1), 57, &card_data);
-    set_bit(((fc_and_card >> 22) & 1), 49, &card_data);
-    set_bit(((fc_and_card >> 21) & 1), 44, &card_data);
-    set_bit(((fc_and_card >> 20) & 1), 47, &card_data);
-    set_bit(((fc_and_card >> 19) & 1), 48, &card_data);
-    set_bit(((fc_and_card >> 18) & 1), 53, &card_data);
-    set_bit(((fc_and_card >> 17) & 1), 39, &card_data);
-    set_bit(((fc_and_card >> 16) & 1), 58, &card_data);
-
-    // card number
-    set_bit(((fc_and_card >> 15) & 1), 42, &card_data);
-    set_bit(((fc_and_card >> 14) & 1), 45, &card_data);
-    set_bit(((fc_and_card >> 13) & 1), 43, &card_data);
-    set_bit(((fc_and_card >> 12) & 1), 40, &card_data);
-    set_bit(((fc_and_card >> 11) & 1), 52, &card_data);
-    set_bit(((fc_and_card >> 10) & 1), 36, &card_data);
-    set_bit(((fc_and_card >> 9) & 1), 35, &card_data);
-    set_bit(((fc_and_card >> 8) & 1), 51, &card_data);
-    set_bit(((fc_and_card >> 7) & 1), 46, &card_data);
-    set_bit(((fc_and_card >> 6) & 1), 33, &card_data);
-    set_bit(((fc_and_card >> 5) & 1), 37, &card_data);
-    set_bit(((fc_and_card >> 4) & 1), 54, &card_data);
-    set_bit(((fc_and_card >> 3) & 1), 56, &card_data);
-    set_bit(((fc_and_card >> 2) & 1), 59, &card_data);
-    set_bit(((fc_and_card >> 1) & 1), 50, &card_data);
-    set_bit(((fc_and_card >> 0) & 1), 41, &card_data);
-
-    // checksum
-    uint8_t checksum = 0;
-    checksum += ((fc_and_card >> 14) & 1);
-    checksum += ((fc_and_card >> 12) & 1);
-    checksum += ((fc_and_card >> 9) & 1);
-    checksum += ((fc_and_card >> 8) & 1);
-    checksum += ((fc_and_card >> 6) & 1);
-    checksum += ((fc_and_card >> 5) & 1);
-    checksum += ((fc_and_card >> 2) & 1);
-    checksum += ((fc_and_card >> 0) & 1);
-
-    // wiegand parity bits
-    // even parity sum calculation (high 12 bits of data)
-    uint8_t even_parity_sum = 0;
-    for(int8_t i = 12; i < 24; i++) {
-        if(((fc_and_card >> i) & 1) == 1) {
-            even_parity_sum++;
-        }
-    }
-
-    // odd parity sum calculation (low 12 bits of data)
-    uint8_t odd_parity_sum = 1;
-    for(int8_t i = 0; i < 12; i++) {
-        if(((fc_and_card >> i) & 1) == 1) {
-            odd_parity_sum++;
-        }
-    }
-
-    // even parity bit
-    set_bit((even_parity_sum % 2), 34, &card_data);
-
-    // odd parity bit
-    set_bit((odd_parity_sum % 2), 38, &card_data);
-
-    // checksum
-    if((checksum & 1) == 1) {
-        set_bit(0, 62, &card_data);
-        set_bit(1, 63, &card_data);
-    } else {
-        set_bit(1, 62, &card_data);
-        set_bit(0, 63, &card_data);
-    }
-
-    memcpy(encoded_data, &card_data, get_encoded_data_size());
-}
-
-// factory code
-static uint8_t get_fc(const Indala40134CardData* card_data) {
-    uint8_t fc = 0;
-
-    fc = fc << 1 | get_bit(57, card_data);
-    fc = fc << 1 | get_bit(49, card_data);
-    fc = fc << 1 | get_bit(44, card_data);
-    fc = fc << 1 | get_bit(47, card_data);
-    fc = fc << 1 | get_bit(48, card_data);
-    fc = fc << 1 | get_bit(53, card_data);
-    fc = fc << 1 | get_bit(39, card_data);
-    fc = fc << 1 | get_bit(58, card_data);
-
-    return fc;
-}
-
-// card number
-static uint16_t get_cn(const Indala40134CardData* card_data) {
-    uint16_t cn = 0;
-
-    cn = cn << 1 | get_bit(42, card_data);
-    cn = cn << 1 | get_bit(45, card_data);
-    cn = cn << 1 | get_bit(43, card_data);
-    cn = cn << 1 | get_bit(40, card_data);
-    cn = cn << 1 | get_bit(52, card_data);
-    cn = cn << 1 | get_bit(36, card_data);
-    cn = cn << 1 | get_bit(35, card_data);
-    cn = cn << 1 | get_bit(51, card_data);
-    cn = cn << 1 | get_bit(46, card_data);
-    cn = cn << 1 | get_bit(33, card_data);
-    cn = cn << 1 | get_bit(37, card_data);
-    cn = cn << 1 | get_bit(54, card_data);
-    cn = cn << 1 | get_bit(56, card_data);
-    cn = cn << 1 | get_bit(59, card_data);
-    cn = cn << 1 | get_bit(50, card_data);
-    cn = cn << 1 | get_bit(41, card_data);
-
-    return cn;
-}
-
-void ProtocolIndala40134::decode(
-    const uint8_t* encoded_data,
-    const uint8_t encoded_data_size,
-    uint8_t* decoded_data,
-    const uint8_t decoded_data_size) {
-    furi_check(decoded_data_size >= get_decoded_data_size());
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    const Indala40134CardData* card_data =
-        reinterpret_cast<const Indala40134CardData*>(encoded_data);
-
-    uint8_t fc = get_fc(card_data);
-    uint16_t card = get_cn(card_data);
-
-    decoded_data[0] = fc;
-    decoded_data[1] = card >> 8;
-    decoded_data[2] = card;
-}
-
-bool ProtocolIndala40134::can_be_decoded(
-    const uint8_t* encoded_data,
-    const uint8_t encoded_data_size) {
-    furi_check(encoded_data_size >= get_encoded_data_size());
-    bool can_be_decoded = false;
-
-    const Indala40134CardData* card_data =
-        reinterpret_cast<const Indala40134CardData*>(encoded_data);
-
-    do {
-        // preambula
-        if((*card_data >> 32) != 0xa0000000UL) break;
-
-        // data
-        const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data);
-
-        // checksum
-        const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data);
-        uint8_t checksum_sum = 0;
-        checksum_sum += ((fc_and_card >> 14) & 1);
-        checksum_sum += ((fc_and_card >> 12) & 1);
-        checksum_sum += ((fc_and_card >> 9) & 1);
-        checksum_sum += ((fc_and_card >> 8) & 1);
-        checksum_sum += ((fc_and_card >> 6) & 1);
-        checksum_sum += ((fc_and_card >> 5) & 1);
-        checksum_sum += ((fc_and_card >> 2) & 1);
-        checksum_sum += ((fc_and_card >> 0) & 1);
-        checksum_sum = checksum_sum & 0b1;
-
-        if(checksum_sum == 1 && checksum == 0b01) {
-        } else if(checksum_sum == 0 && checksum == 0b10) {
-        } else {
-            break;
-        }
-
-        // wiegand parity bits
-        // even parity sum calculation (high 12 bits of data)
-        const bool even_parity = get_bit(34, card_data);
-        uint8_t even_parity_sum = 0;
-        for(int8_t i = 12; i < 24; i++) {
-            if(((fc_and_card >> i) & 1) == 1) {
-                even_parity_sum++;
-            }
-        }
-        if(even_parity_sum % 2 != even_parity) break;
-
-        // odd parity sum calculation (low 12 bits of data)
-        const bool odd_parity = get_bit(38, card_data);
-        uint8_t odd_parity_sum = 1;
-        for(int8_t i = 0; i < 12; i++) {
-            if(((fc_and_card >> i) & 1) == 1) {
-                odd_parity_sum++;
-            }
-        }
-        if(odd_parity_sum % 2 != odd_parity) break;
-
-        can_be_decoded = true;
-    } while(false);
-
-    return can_be_decoded;
-}

+ 0 - 22
applications/lfrfid/helpers/protocols/protocol_indala_40134.h

@@ -1,22 +0,0 @@
-#pragma once
-#include "protocol_generic.h"
-
-class ProtocolIndala40134 : public ProtocolGeneric {
-public:
-    uint8_t get_encoded_data_size() final;
-    uint8_t get_decoded_data_size() final;
-
-    void encode(
-        const uint8_t* decoded_data,
-        const uint8_t decoded_data_size,
-        uint8_t* encoded_data,
-        const uint8_t encoded_data_size) final;
-
-    void decode(
-        const uint8_t* encoded_data,
-        const uint8_t encoded_data_size,
-        uint8_t* decoded_data,
-        const uint8_t decoded_data_size) final;
-
-    bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
-};

+ 0 - 193
applications/lfrfid/helpers/protocols/protocol_ioprox.cpp

@@ -1,193 +0,0 @@
-#include "protocol_ioprox.h"
-#include <furi.h>
-#include <cli/cli.h>
-
-/**
- * Writes a bit into the output buffer.
- */
-static void write_bit(bool bit, uint8_t position, uint8_t* data) {
-    if(bit) {
-        data[position / 8] |= 1UL << (7 - (position % 8));
-    } else {
-        data[position / 8] &= ~(1UL << (7 - (position % 8)));
-    }
-}
-
-/**
- * Writes up to eight contiguous bits into the output buffer.
- */
-static void write_bits(uint8_t byte, uint8_t position, uint8_t* data, uint8_t length) {
-    furi_check(length <= 8);
-    furi_check(length > 0);
-
-    for(uint8_t i = 0; i < length; ++i) {
-        uint8_t shift = 7 - i;
-        write_bit((byte >> shift) & 1, position + i, data);
-    }
-}
-
-uint8_t ProtocolIoProx::get_encoded_data_size() {
-    return 8;
-}
-
-uint8_t ProtocolIoProx::get_decoded_data_size() {
-    return 4;
-}
-
-void ProtocolIoProx::encode(
-    const uint8_t* decoded_data,
-    const uint8_t decoded_data_size,
-    uint8_t* encoded_data,
-    const uint8_t encoded_data_size) {
-    furi_check(decoded_data_size >= get_decoded_data_size());
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    // Packet to transmit:
-    //
-    // 0           10          20          30          40          50          60
-    // v           v           v           v           v           v           v
-    // 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23
-    // -----------------------------------------------------------------------------
-    // 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11
-
-    // Preamble.
-    write_bits(0b00000000, 0, encoded_data, 8);
-    write_bit(0, 8, encoded_data);
-
-    write_bits(0b11110000, 9, encoded_data, 8);
-    write_bit(1, 17, encoded_data);
-
-    // Facility code.
-    write_bits(decoded_data[0], 18, encoded_data, 8);
-    write_bit(1, 26, encoded_data);
-
-    // Version
-    write_bits(decoded_data[1], 27, encoded_data, 8);
-    write_bit(1, 35, encoded_data);
-
-    // Code one
-    write_bits(decoded_data[2], 36, encoded_data, 8);
-    write_bit(1, 44, encoded_data);
-
-    // Code two
-    write_bits(decoded_data[3], 45, encoded_data, 8);
-    write_bit(1, 53, encoded_data);
-
-    // Checksum
-    write_bits(compute_checksum(encoded_data, 8), 54, encoded_data, 8);
-    write_bit(1, 62, encoded_data);
-    write_bit(1, 63, encoded_data);
-}
-
-void ProtocolIoProx::decode(
-    const uint8_t* encoded_data,
-    const uint8_t encoded_data_size,
-    uint8_t* decoded_data,
-    const uint8_t decoded_data_size) {
-    furi_check(decoded_data_size >= get_decoded_data_size());
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    // Packet structure:
-    // (Note: the second word seems fixed; but this may not be a guarantee;
-    //  it currently has no meaning.)
-    //
-    //0        1        2        3        4        5        6        7
-    //v        v        v        v        v        v        v        v
-    //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF
-    //-----------------------------------------------------------------------
-    //00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11
-    //
-    // F = facility code
-    // V = version
-    // C = code
-    // X = checksum
-
-    // Facility code
-    decoded_data[0] = (encoded_data[2] << 2) | (encoded_data[3] >> 6);
-
-    // Version code.
-    decoded_data[1] = (encoded_data[3] << 3) | (encoded_data[4] >> 5);
-
-    // Code bytes.
-    decoded_data[2] = (encoded_data[4] << 4) | (encoded_data[5] >> 4);
-    decoded_data[3] = (encoded_data[5] << 5) | (encoded_data[6] >> 3);
-}
-
-bool ProtocolIoProx::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
-    furi_check(encoded_data_size >= get_encoded_data_size());
-
-    // Packet framing
-    //
-    //0        1        2        3        4        5        6        7
-    //v        v        v        v        v        v        v        v
-    //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF
-    //-----------------------------------------------------------------------
-    //00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11
-    //
-    // _ = variable data
-    // 0 = preamble 0
-    // 1 = framing 1
-    // X = checksum
-
-    // Validate the packet preamble is there...
-    if(encoded_data[0] != 0b00000000) {
-        return false;
-    }
-    if((encoded_data[1] >> 6) != 0b01) {
-        return false;
-    }
-
-    // ... check for known ones...
-    if((encoded_data[2] & 0b01000000) == 0) {
-        return false;
-    }
-    if((encoded_data[3] & 0b00100000) == 0) {
-        return false;
-    }
-    if((encoded_data[4] & 0b00010000) == 0) {
-        return false;
-    }
-    if((encoded_data[5] & 0b00001000) == 0) {
-        return false;
-    }
-    if((encoded_data[6] & 0b00000100) == 0) {
-        return false;
-    }
-    if((encoded_data[7] & 0b00000011) == 0) {
-        return false;
-    }
-
-    // ... and validate our checksums.
-    uint8_t checksum = compute_checksum(encoded_data, 8);
-    uint8_t checkval = (encoded_data[6] << 6) | (encoded_data[7] >> 2);
-
-    if(checksum != checkval) {
-        return false;
-    }
-
-    return true;
-}
-
-uint8_t ProtocolIoProx::compute_checksum(const uint8_t* data, const uint8_t data_size) {
-    furi_check(data_size == get_encoded_data_size());
-
-    // Packet structure:
-    //
-    //0        1        2         3         4         5         6         7
-    //v        v        v         v         v         v         v         v
-    //01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF
-    //00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11
-    //
-    // algorithm as observed by the proxmark3 folks
-    // CHECKSUM == 0xFF - (V + W + X + Y + Z)
-
-    uint8_t checksum = 0;
-
-    checksum += (data[1] << 1) | (data[2] >> 7); // VVVVVVVVV
-    checksum += (data[2] << 2) | (data[3] >> 6); // WWWWWWWWW
-    checksum += (data[3] << 3) | (data[4] >> 5); // XXXXXXXXX
-    checksum += (data[4] << 4) | (data[5] >> 4); // YYYYYYYYY
-    checksum += (data[5] << 5) | (data[6] >> 3); // ZZZZZZZZZ
-
-    return 0xFF - checksum;
-}

+ 0 - 26
applications/lfrfid/helpers/protocols/protocol_ioprox.h

@@ -1,26 +0,0 @@
-#pragma once
-#include "protocol_generic.h"
-
-class ProtocolIoProx : public ProtocolGeneric {
-public:
-    uint8_t get_encoded_data_size() final;
-    uint8_t get_decoded_data_size() final;
-
-    void encode(
-        const uint8_t* decoded_data,
-        const uint8_t decoded_data_size,
-        uint8_t* encoded_data,
-        const uint8_t encoded_data_size) final;
-
-    void decode(
-        const uint8_t* encoded_data,
-        const uint8_t encoded_data_size,
-        uint8_t* decoded_data,
-        const uint8_t decoded_data_size) final;
-
-    bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
-
-private:
-    /**  Computes the IoProx checksum of the provided (decoded) data. */
-    uint8_t compute_checksum(const uint8_t* data, const uint8_t data_size);
-};

+ 0 - 95
applications/lfrfid/helpers/pulse_joiner.cpp

@@ -1,95 +0,0 @@
-#include "pulse_joiner.h"
-#include <furi.h>
-
-bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) {
-    bool result = false;
-    furi_check((pulse_index + 1) < pulse_max);
-
-    if(polarity == false && pulse_index == 0) {
-        // first negative pulse is ommited
-
-    } else {
-        pulses[pulse_index].polarity = polarity;
-        pulses[pulse_index].time = pulse;
-        pulse_index++;
-    }
-
-    if(period > pulse) {
-        pulses[pulse_index].polarity = !polarity;
-        pulses[pulse_index].time = period - pulse;
-        pulse_index++;
-    }
-
-    if(pulse_index >= 4) {
-        // we know that first pulse is always high
-        // so we wait 2 edges, hi2low and next low2hi
-
-        uint8_t edges_count = 0;
-        bool last_polarity = pulses[0].polarity;
-
-        for(uint8_t i = 1; i < pulse_index; i++) {
-            if(pulses[i].polarity != last_polarity) {
-                edges_count++;
-                last_polarity = pulses[i].polarity;
-            }
-        }
-
-        if(edges_count >= 2) {
-            result = true;
-        }
-    }
-
-    return result;
-}
-
-void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) {
-    furi_check(pulse_index <= (pulse_max + 1));
-
-    uint16_t tmp_period = 0;
-    uint16_t tmp_pulse = 0;
-    uint8_t edges_count = 0;
-    bool last_polarity = pulses[0].polarity;
-    uint8_t next_fist_pulse = 0;
-
-    for(uint8_t i = 0; i < pulse_max; i++) {
-        // count edges
-        if(pulses[i].polarity != last_polarity) {
-            edges_count++;
-            last_polarity = pulses[i].polarity;
-        }
-
-        // wait for 2 edges
-        if(edges_count == 2) {
-            next_fist_pulse = i;
-            break;
-        }
-
-        // sum pulse time
-        if(pulses[i].polarity) {
-            tmp_period += pulses[i].time;
-            tmp_pulse += pulses[i].time;
-        } else {
-            tmp_period += pulses[i].time;
-        }
-        pulse_index--;
-    }
-
-    *period = tmp_period;
-    *pulse = tmp_pulse;
-
-    // remove counted periods and shift data
-    for(uint8_t i = 0; i < pulse_max; i++) {
-        if((next_fist_pulse + i) < pulse_max) {
-            pulses[i].polarity = pulses[next_fist_pulse + i].polarity;
-            pulses[i].time = pulses[next_fist_pulse + i].time;
-        } else {
-            break;
-        }
-    }
-}
-
-PulseJoiner::PulseJoiner() {
-    for(uint8_t i = 0; i < pulse_max; i++) {
-        pulses[i] = {false, 0};
-    }
-}

+ 0 - 36
applications/lfrfid/helpers/pulse_joiner.h

@@ -1,36 +0,0 @@
-#pragma once
-#include "stdint.h"
-
-class PulseJoiner {
-public:
-    /**
-     * @brief Push timer pulse. First negative pulse is ommited.
-     * 
-     * @param polarity pulse polarity: true = high2low, false = low2high
-     * @param period overall period time in timer clicks
-     * @param pulse pulse time in timer clicks
-     * 
-     * @return true - next pulse can and must be popped immediatly
-     */
-    bool push_pulse(bool polarity, uint16_t period, uint16_t pulse);
-
-    /**
-     * @brief Get the next timer pulse. Call only if push_pulse returns true.
-     * 
-     * @param period overall period time in timer clicks
-     * @param pulse pulse time in timer clicks
-     */
-    void pop_pulse(uint16_t* period, uint16_t* pulse);
-
-    PulseJoiner();
-
-private:
-    struct Pulse {
-        bool polarity;
-        uint16_t time;
-    };
-
-    uint8_t pulse_index = 0;
-    static const uint8_t pulse_max = 6;
-    Pulse pulses[pulse_max];
-};

+ 0 - 65
applications/lfrfid/helpers/rfid_key.cpp

@@ -1,65 +0,0 @@
-#include "rfid_key.h"
-#include <core/check.h>
-#include <string.h>
-
-RfidKey::RfidKey() {
-    clear();
-}
-
-RfidKey::~RfidKey() {
-}
-
-void RfidKey::set_type(LfrfidKeyType _type) {
-    type = _type;
-}
-
-void RfidKey::set_data(const uint8_t* _data, const uint8_t _data_size) {
-    furi_assert(_data_size <= data.size());
-    for(uint8_t i = 0; i < _data_size; i++) {
-        data[i] = _data[i];
-    }
-}
-
-void RfidKey::set_name(const char* _name) {
-    strlcpy(name, _name, get_name_length());
-}
-
-LfrfidKeyType RfidKey::get_type() {
-    return type;
-}
-
-const uint8_t* RfidKey::get_data() {
-    return &data[0];
-}
-
-const char* RfidKey::get_type_text() {
-    return lfrfid_key_get_type_string(type);
-}
-
-uint8_t RfidKey::get_type_data_count() const {
-    return lfrfid_key_get_type_data_count(type);
-}
-
-char* RfidKey::get_name() {
-    return name;
-}
-
-uint8_t RfidKey::get_name_length() {
-    return LFRFID_KEY_NAME_SIZE;
-}
-
-void RfidKey::clear() {
-    set_name("");
-    set_type(LfrfidKeyType::KeyEM4100);
-    data.fill(0);
-}
-
-RfidKey& RfidKey::operator=(const RfidKey& rhs) {
-    if(this == &rhs) return *this;
-
-    set_type(rhs.type);
-    set_name(rhs.name);
-    set_data(&rhs.data[0], get_type_data_count());
-
-    return *this;
-}

+ 0 - 27
applications/lfrfid/helpers/rfid_key.h

@@ -1,27 +0,0 @@
-#pragma once
-#include "key_info.h"
-#include <array>
-
-class RfidKey {
-public:
-    RfidKey();
-    ~RfidKey();
-
-    void set_type(LfrfidKeyType type);
-    void set_data(const uint8_t* data, const uint8_t data_size);
-    void set_name(const char* name);
-
-    LfrfidKeyType get_type();
-    const uint8_t* get_data();
-    const char* get_type_text();
-    uint8_t get_type_data_count() const;
-    char* get_name();
-    uint8_t get_name_length();
-    void clear();
-    RfidKey& operator=(const RfidKey& rhs);
-
-private:
-    std::array<uint8_t, LFRFID_KEY_SIZE> data;
-    LfrfidKeyType type;
-    char name[LFRFID_KEY_NAME_SIZE + 1];
-};

+ 0 - 175
applications/lfrfid/helpers/rfid_reader.cpp

@@ -1,175 +0,0 @@
-#include "rfid_reader.h"
-#include <furi.h>
-#include <furi_hal.h>
-#include <stm32wbxx_ll_cortex.h>
-
-/**
- * @brief private violation assistant for RfidReader
- */
-struct RfidReaderAccessor {
-    static void decode(RfidReader& rfid_reader, bool polarity) {
-        rfid_reader.decode(polarity);
-    }
-};
-
-void RfidReader::decode(bool polarity) {
-    uint32_t current_dwt_value = DWT->CYCCNT;
-    uint32_t period = current_dwt_value - last_dwt_value;
-    last_dwt_value = current_dwt_value;
-
-#ifdef RFID_GPIO_DEBUG
-    decoder_gpio_out.process_front(polarity, period);
-#endif
-
-    switch(type) {
-    case Type::Normal:
-        decoder_em.process_front(polarity, period);
-        decoder_hid26.process_front(polarity, period);
-        decoder_ioprox.process_front(polarity, period);
-        break;
-    case Type::Indala:
-        decoder_em.process_front(polarity, period);
-        decoder_hid26.process_front(polarity, period);
-        decoder_ioprox.process_front(polarity, period);
-        decoder_indala.process_front(polarity, period);
-        break;
-    }
-
-    detect_ticks++;
-}
-
-bool RfidReader::switch_timer_elapsed() {
-    const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f;
-    return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch;
-}
-
-void RfidReader::switch_timer_reset() {
-    switch_os_tick_last = furi_get_tick();
-}
-
-void RfidReader::switch_mode() {
-    switch(type) {
-    case Type::Normal:
-        type = Type::Indala;
-        furi_hal_rfid_change_read_config(62500.0f, 0.25f);
-        break;
-    case Type::Indala:
-        type = Type::Normal;
-        furi_hal_rfid_change_read_config(125000.0f, 0.5f);
-        break;
-    }
-
-    switch_timer_reset();
-}
-
-static void comparator_trigger_callback(bool level, void* comp_ctx) {
-    RfidReader* _this = static_cast<RfidReader*>(comp_ctx);
-
-    RfidReaderAccessor::decode(*_this, !level);
-}
-
-RfidReader::RfidReader() {
-}
-
-void RfidReader::start() {
-    type = Type::Normal;
-
-    furi_hal_rfid_pins_read();
-    furi_hal_rfid_tim_read(125000, 0.5);
-    furi_hal_rfid_tim_read_start();
-    start_comparator();
-
-    switch_timer_reset();
-    last_read_count = 0;
-}
-
-void RfidReader::start_forced(RfidReader::Type _type) {
-    start();
-    if(_type == Type::Indala) {
-        switch_mode();
-    }
-}
-
-void RfidReader::stop() {
-    furi_hal_rfid_pins_reset();
-    furi_hal_rfid_tim_read_stop();
-    furi_hal_rfid_tim_reset();
-    stop_comparator();
-}
-
-bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) {
-    bool result = false;
-    bool something_read = false;
-
-    // reading
-    if(decoder_em.read(data, data_size)) {
-        *_type = LfrfidKeyType::KeyEM4100;
-        something_read = true;
-    }
-
-    if(decoder_hid26.read(data, data_size)) {
-        *_type = LfrfidKeyType::KeyH10301;
-        something_read = true;
-    }
-
-    if(decoder_ioprox.read(data, data_size)) {
-        *_type = LfrfidKeyType::KeyIoProxXSF;
-        something_read = true;
-    }
-
-    if(decoder_indala.read(data, data_size)) {
-        *_type = LfrfidKeyType::KeyI40134;
-        something_read = true;
-    }
-
-    // validation
-    if(something_read) {
-        switch_timer_reset();
-
-        if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) {
-            last_read_count = last_read_count + 1;
-
-            if(last_read_count > 2) {
-                result = true;
-            }
-        } else {
-            last_read_type = *_type;
-            memcpy(last_read_data, data, data_size);
-            last_read_count = 0;
-        }
-    }
-
-    // mode switching
-    if(switch_enable && switch_timer_elapsed()) {
-        switch_mode();
-        last_read_count = 0;
-    }
-
-    return result;
-}
-
-bool RfidReader::detect() {
-    bool detected = false;
-    if(detect_ticks > 10) {
-        detected = true;
-    }
-    detect_ticks = 0;
-
-    return detected;
-}
-
-bool RfidReader::any_read() {
-    return last_read_count > 0;
-}
-
-void RfidReader::start_comparator(void) {
-    furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this);
-    last_dwt_value = DWT->CYCCNT;
-
-    furi_hal_rfid_comp_start();
-}
-
-void RfidReader::stop_comparator(void) {
-    furi_hal_rfid_comp_stop();
-    furi_hal_rfid_comp_set_callback(NULL, NULL);
-}

+ 0 - 59
applications/lfrfid/helpers/rfid_reader.h

@@ -1,59 +0,0 @@
-#pragma once
-//#include "decoder_analyzer.h"
-#include "decoder_gpio_out.h"
-#include "decoder_emmarin.h"
-#include "decoder_hid26.h"
-#include "decoder_indala.h"
-#include "decoder_ioprox.h"
-#include "key_info.h"
-
-//#define RFID_GPIO_DEBUG 1
-
-class RfidReader {
-public:
-    enum class Type : uint8_t {
-        Normal,
-        Indala,
-    };
-
-    RfidReader();
-    void start();
-    void start_forced(RfidReader::Type type);
-    void stop();
-    bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size, bool switch_enable = true);
-
-    bool detect();
-    bool any_read();
-
-private:
-    friend struct RfidReaderAccessor;
-
-    //DecoderAnalyzer decoder_analyzer;
-#ifdef RFID_GPIO_DEBUG
-    DecoderGpioOut decoder_gpio_out;
-#endif
-    DecoderEMMarin decoder_em;
-    DecoderHID26 decoder_hid26;
-    DecoderIndala decoder_indala;
-    DecoderIoProx decoder_ioprox;
-
-    uint32_t last_dwt_value;
-
-    void start_comparator(void);
-    void stop_comparator(void);
-
-    void decode(bool polarity);
-
-    uint32_t detect_ticks;
-
-    uint32_t switch_os_tick_last;
-    bool switch_timer_elapsed();
-    void switch_timer_reset();
-    void switch_mode();
-
-    LfrfidKeyType last_read_type;
-    uint8_t last_read_data[LFRFID_KEY_SIZE];
-    uint8_t last_read_count;
-
-    Type type = Type::Normal;
-};

+ 0 - 56
applications/lfrfid/helpers/rfid_timer_emulator.cpp

@@ -1,56 +0,0 @@
-#include "rfid_timer_emulator.h"
-
-RfidTimerEmulator::RfidTimerEmulator() {
-}
-
-RfidTimerEmulator::~RfidTimerEmulator() {
-    std::map<LfrfidKeyType, EncoderGeneric*>::iterator it;
-
-    for(it = encoders.begin(); it != encoders.end(); ++it) {
-        delete it->second;
-    }
-
-    encoders.clear();
-}
-
-void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) {
-    if(encoders.count(type)) {
-        current_encoder = encoders.find(type)->second;
-
-        if(data_size >= lfrfid_key_get_type_data_count(type)) {
-            current_encoder->init(data, data_size);
-
-            furi_hal_rfid_tim_emulate(125000);
-            furi_hal_rfid_pins_emulate();
-
-            furi_hal_rfid_tim_emulate_start(RfidTimerEmulator::timer_update_callback, this);
-        }
-    } else {
-        // not found
-    }
-}
-
-void RfidTimerEmulator::stop() {
-    furi_hal_rfid_tim_emulate_stop();
-    furi_hal_rfid_tim_reset();
-    furi_hal_rfid_pins_reset();
-}
-
-void RfidTimerEmulator::timer_update_callback(void* ctx) {
-    RfidTimerEmulator* _this = static_cast<RfidTimerEmulator*>(ctx);
-
-    bool result;
-    bool polarity;
-    uint16_t period;
-    uint16_t pulse;
-
-    do {
-        _this->current_encoder->get_next(&polarity, &period, &pulse);
-        result = _this->pulse_joiner.push_pulse(polarity, period, pulse);
-    } while(result == false);
-
-    _this->pulse_joiner.pop_pulse(&period, &pulse);
-
-    furi_hal_rfid_set_emulate_period(period - 1);
-    furi_hal_rfid_set_emulate_pulse(pulse);
-}

+ 0 - 31
applications/lfrfid/helpers/rfid_timer_emulator.h

@@ -1,31 +0,0 @@
-#pragma once
-#include <furi_hal.h>
-#include "key_info.h"
-#include "encoder_generic.h"
-#include "encoder_emmarin.h"
-#include "encoder_hid_h10301.h"
-#include "encoder_indala_40134.h"
-#include "encoder_ioprox.h"
-#include "pulse_joiner.h"
-#include <map>
-
-class RfidTimerEmulator {
-public:
-    RfidTimerEmulator();
-    ~RfidTimerEmulator();
-    void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size);
-    void stop();
-
-private:
-    EncoderGeneric* current_encoder = nullptr;
-
-    std::map<LfrfidKeyType, EncoderGeneric*> encoders = {
-        {LfrfidKeyType::KeyEM4100, new EncoderEM()},
-        {LfrfidKeyType::KeyH10301, new EncoderHID_H10301()},
-        {LfrfidKeyType::KeyI40134, new EncoderIndala_40134()},
-        {LfrfidKeyType::KeyIoProxXSF, new EncoderIoProx()},
-    };
-
-    PulseJoiner pulse_joiner;
-    static void timer_update_callback(void* ctx);
-};

+ 0 - 136
applications/lfrfid/helpers/rfid_worker.cpp

@@ -1,136 +0,0 @@
-#include "rfid_worker.h"
-
-RfidWorker::RfidWorker() {
-}
-
-RfidWorker::~RfidWorker() {
-}
-
-void RfidWorker::start_read() {
-    reader.start();
-}
-
-bool RfidWorker::read() {
-    static const uint8_t data_size = LFRFID_KEY_SIZE;
-    uint8_t data[data_size] = {0};
-    LfrfidKeyType type;
-
-    bool result = reader.read(&type, data, data_size);
-
-    if(result) {
-        key.set_type(type);
-        key.set_data(data, data_size);
-    };
-
-    return result;
-}
-
-bool RfidWorker::detect() {
-    return reader.detect();
-}
-
-bool RfidWorker::any_read() {
-    return reader.any_read();
-}
-
-void RfidWorker::stop_read() {
-    reader.stop();
-}
-
-void RfidWorker::start_write() {
-    write_result = WriteResult::Nothing;
-    write_sequence = new TickSequencer();
-    validate_counts = 0;
-
-    write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this));
-    write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this));
-    write_sequence->do_every_tick(30, std::bind(&RfidWorker::sq_write_validate, this));
-    write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this));
-}
-
-RfidWorker::WriteResult RfidWorker::write() {
-    write_sequence->tick();
-    return write_result;
-}
-
-void RfidWorker::stop_write() {
-    delete write_sequence;
-    reader.stop();
-}
-
-void RfidWorker::start_emulate() {
-    emulator.start(key.get_type(), key.get_data(), key.get_type_data_count());
-}
-
-void RfidWorker::stop_emulate() {
-    emulator.stop();
-}
-
-void RfidWorker::sq_write() {
-    for(size_t i = 0; i < 5; i++) {
-        switch(key.get_type()) {
-        case LfrfidKeyType::KeyEM4100:
-            writer.start();
-            writer.write_em(key.get_data());
-            writer.stop();
-            break;
-        case LfrfidKeyType::KeyH10301:
-            writer.start();
-            writer.write_hid(key.get_data());
-            writer.stop();
-            break;
-        case LfrfidKeyType::KeyI40134:
-            writer.start();
-            writer.write_indala(key.get_data());
-            writer.stop();
-            break;
-        case LfrfidKeyType::KeyIoProxXSF:
-            writer.start();
-            writer.write_ioprox(key.get_data());
-            writer.stop();
-            break;
-        }
-    }
-}
-
-void RfidWorker::sq_write_start_validate() {
-    switch(key.get_type()) {
-    case LfrfidKeyType::KeyEM4100:
-    case LfrfidKeyType::KeyH10301:
-    case LfrfidKeyType::KeyIoProxXSF:
-        reader.start_forced(RfidReader::Type::Normal);
-        break;
-    case LfrfidKeyType::KeyI40134:
-        reader.start_forced(RfidReader::Type::Indala);
-        break;
-    }
-}
-
-void RfidWorker::sq_write_validate() {
-    static const uint8_t data_size = LFRFID_KEY_SIZE;
-    uint8_t data[data_size] = {0};
-    LfrfidKeyType type;
-
-    bool result = reader.read(&type, data, data_size);
-
-    if(result && (write_result != WriteResult::Ok)) {
-        if(validate_counts > (5 * 60)) {
-            write_result = WriteResult::NotWritable;
-        }
-
-        if(type == key.get_type()) {
-            if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) {
-                write_result = WriteResult::Ok;
-                validate_counts = 0;
-            } else {
-                validate_counts++;
-            }
-        } else {
-            validate_counts++;
-        }
-    };
-}
-
-void RfidWorker::sq_write_stop_validate() {
-    reader.stop();
-}

+ 0 - 48
applications/lfrfid/helpers/rfid_worker.h

@@ -1,48 +0,0 @@
-#pragma once
-#include "key_info.h"
-#include "rfid_reader.h"
-#include "rfid_writer.h"
-#include "rfid_timer_emulator.h"
-#include "rfid_key.h"
-#include "state_sequencer.h"
-
-class RfidWorker {
-public:
-    RfidWorker();
-    ~RfidWorker();
-
-    void start_read();
-    bool read();
-    bool detect();
-    bool any_read();
-    void stop_read();
-
-    enum class WriteResult : uint8_t {
-        Ok,
-        NotWritable,
-        Nothing,
-    };
-
-    void start_write();
-    WriteResult write();
-    void stop_write();
-
-    void start_emulate();
-    void stop_emulate();
-
-    RfidKey key;
-
-private:
-    RfidWriter writer;
-    RfidReader reader;
-    RfidTimerEmulator emulator;
-
-    WriteResult write_result;
-    TickSequencer* write_sequence;
-
-    void sq_write();
-    void sq_write_start_validate();
-    void sq_write_validate();
-    uint16_t validate_counts;
-    void sq_write_stop_validate();
-};

+ 0 - 183
applications/lfrfid/helpers/rfid_writer.cpp

@@ -1,183 +0,0 @@
-#include "rfid_writer.h"
-#include "protocols/protocol_ioprox.h"
-#include <furi_hal.h>
-#include "protocols/protocol_emmarin.h"
-#include "protocols/protocol_hid_h10301.h"
-#include "protocols/protocol_indala_40134.h"
-
-/**
- * @brief all timings are specified in field clocks (field clock = 125 kHz, 8 us)
- * 
- */
-class T55xxTiming {
-public:
-    constexpr static const uint16_t wait_time = 400;
-    constexpr static const uint8_t start_gap = 30;
-    constexpr static const uint8_t write_gap = 18;
-    constexpr static const uint8_t data_0 = 24;
-    constexpr static const uint8_t data_1 = 56;
-    constexpr static const uint16_t program = 700;
-};
-
-class T55xxCmd {
-public:
-    constexpr static const uint8_t opcode_page_0 = 0b10;
-    constexpr static const uint8_t opcode_page_1 = 0b11;
-    constexpr static const uint8_t opcode_reset = 0b00;
-};
-
-RfidWriter::RfidWriter() {
-}
-
-RfidWriter::~RfidWriter() {
-}
-
-void RfidWriter::start() {
-    furi_hal_rfid_tim_read(125000, 0.5);
-    furi_hal_rfid_pins_read();
-    furi_hal_rfid_tim_read_start();
-
-    // do not ground the antenna
-    furi_hal_rfid_pin_pull_release();
-}
-
-void RfidWriter::stop() {
-    furi_hal_rfid_tim_read_stop();
-    furi_hal_rfid_tim_reset();
-    furi_hal_rfid_pins_reset();
-}
-
-void RfidWriter::write_gap(uint32_t gap_time) {
-    furi_hal_rfid_tim_read_stop();
-    furi_delay_us(gap_time * 8);
-    furi_hal_rfid_tim_read_start();
-}
-
-void RfidWriter::write_bit(bool value) {
-    if(value) {
-        furi_delay_us(T55xxTiming::data_1 * 8);
-    } else {
-        furi_delay_us(T55xxTiming::data_0 * 8);
-    }
-    write_gap(T55xxTiming::write_gap);
-}
-
-void RfidWriter::write_byte(uint8_t value) {
-    for(uint8_t i = 0; i < 8; i++) {
-        write_bit((value >> i) & 1);
-    }
-}
-
-void RfidWriter::write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data) {
-    furi_delay_us(T55xxTiming::wait_time * 8);
-
-    // start gap
-    write_gap(T55xxTiming::start_gap);
-
-    // opcode
-    switch(page) {
-    case 0:
-        write_bit(1);
-        write_bit(0);
-        break;
-    case 1:
-        write_bit(1);
-        write_bit(1);
-        break;
-    default:
-        furi_check(false);
-        break;
-    }
-
-    // lock bit
-    write_bit(lock_bit);
-
-    // data
-    for(uint8_t i = 0; i < 32; i++) {
-        write_bit((data >> (31 - i)) & 1);
-    }
-
-    // block address
-    write_bit((block >> 2) & 1);
-    write_bit((block >> 1) & 1);
-    write_bit((block >> 0) & 1);
-
-    furi_delay_us(T55xxTiming::program * 8);
-
-    furi_delay_us(T55xxTiming::wait_time * 8);
-    write_reset();
-}
-
-void RfidWriter::write_reset() {
-    write_gap(T55xxTiming::start_gap);
-    write_bit(1);
-    write_bit(0);
-}
-
-void RfidWriter::write_em(const uint8_t em_data[5]) {
-    ProtocolEMMarin em_card;
-    uint64_t em_encoded_data;
-    em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t));
-    const uint32_t em_config_block_data = 0b00000000000101001000000001000000;
-
-    FURI_CRITICAL_ENTER();
-    write_block(0, 0, false, em_config_block_data);
-    write_block(0, 1, false, em_encoded_data);
-    write_block(0, 2, false, em_encoded_data >> 32);
-    write_reset();
-    FURI_CRITICAL_EXIT();
-}
-
-void RfidWriter::write_hid(const uint8_t hid_data[3]) {
-    ProtocolHID10301 hid_card;
-    uint32_t card_data[3];
-    hid_card.encode(hid_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
-
-    const uint32_t hid_config_block_data = 0b00000000000100000111000001100000;
-
-    FURI_CRITICAL_ENTER();
-    write_block(0, 0, false, hid_config_block_data);
-    write_block(0, 1, false, card_data[0]);
-    write_block(0, 2, false, card_data[1]);
-    write_block(0, 3, false, card_data[2]);
-    write_reset();
-    FURI_CRITICAL_EXIT();
-}
-
-/** Endian fixup. Translates an ioprox block into a t5577 block */
-static uint32_t ioprox_encode_block(const uint8_t block_data[4]) {
-    uint8_t raw_card_data[] = {block_data[3], block_data[2], block_data[1], block_data[0]};
-    return *reinterpret_cast<uint32_t*>(&raw_card_data);
-}
-
-void RfidWriter::write_ioprox(const uint8_t ioprox_data[4]) {
-    ProtocolIoProx ioprox_card;
-
-    uint8_t encoded_data[8];
-    ioprox_card.encode(ioprox_data, 4, encoded_data, sizeof(encoded_data));
-
-    const uint32_t ioprox_config_block_data = 0b00000000000101000111000001000000;
-
-    FURI_CRITICAL_ENTER();
-    write_block(0, 0, false, ioprox_config_block_data);
-    write_block(0, 1, false, ioprox_encode_block(&encoded_data[0]));
-    write_block(0, 2, false, ioprox_encode_block(&encoded_data[4]));
-    write_reset();
-    FURI_CRITICAL_EXIT();
-}
-
-void RfidWriter::write_indala(const uint8_t indala_data[3]) {
-    ProtocolIndala40134 indala_card;
-    uint32_t card_data[2];
-    indala_card.encode(
-        indala_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 2);
-
-    const uint32_t indala_config_block_data = 0b00000000000010000001000001000000;
-
-    FURI_CRITICAL_ENTER();
-    write_block(0, 0, false, indala_config_block_data);
-    write_block(0, 1, false, card_data[0]);
-    write_block(0, 2, false, card_data[1]);
-    write_reset();
-    FURI_CRITICAL_EXIT();
-}

+ 0 - 21
applications/lfrfid/helpers/rfid_writer.h

@@ -1,21 +0,0 @@
-#pragma once
-#include "stdint.h"
-
-class RfidWriter {
-public:
-    RfidWriter();
-    ~RfidWriter();
-    void start();
-    void stop();
-    void write_em(const uint8_t em_data[5]);
-    void write_hid(const uint8_t hid_data[3]);
-    void write_ioprox(const uint8_t ioprox_data[4]);
-    void write_indala(const uint8_t indala_data[3]);
-
-private:
-    void write_gap(uint32_t gap_time);
-    void write_bit(bool value);
-    void write_byte(uint8_t value);
-    void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data);
-    void write_reset();
-};

+ 0 - 50
applications/lfrfid/helpers/state_sequencer.cpp

@@ -1,50 +0,0 @@
-#include "state_sequencer.h"
-#include "stdio.h"
-
-TickSequencer::TickSequencer() {
-}
-
-TickSequencer::~TickSequencer() {
-}
-
-void TickSequencer::tick() {
-    if(tick_count == list_it->first) {
-        tick_count = 0;
-
-        list_it++;
-        if(list_it == list.end()) {
-            list_it = list.begin();
-        }
-    }
-
-    list_it->second();
-    tick_count++;
-}
-
-void TickSequencer::reset() {
-    list_it = list.begin();
-    tick_count = 0;
-}
-
-void TickSequencer::clear() {
-    list.clear();
-    reset();
-}
-
-void TickSequencer::do_every_tick(uint32_t tick_count, std::function<void(void)> fn) {
-    list.push_back(std::make_pair(tick_count, fn));
-    reset();
-}
-
-void TickSequencer::do_after_tick(uint32_t tick_count, std::function<void(void)> fn) {
-    if(tick_count > 1) {
-        list.push_back(
-            std::make_pair(tick_count - 1, std::bind(&TickSequencer::do_nothing, this)));
-    }
-    list.push_back(std::make_pair(1, fn));
-
-    reset();
-}
-
-void TickSequencer::do_nothing() {
-}

+ 0 - 25
applications/lfrfid/helpers/state_sequencer.h

@@ -1,25 +0,0 @@
-#pragma once
-#include "stdint.h"
-#include <list>
-#include <functional>
-
-class TickSequencer {
-public:
-    TickSequencer();
-    ~TickSequencer();
-
-    void tick();
-    void reset();
-    void clear();
-
-    void do_every_tick(uint32_t tick_count, std::function<void(void)> fn);
-    void do_after_tick(uint32_t tick_count, std::function<void(void)> fn);
-
-private:
-    std::list<std::pair<uint32_t, std::function<void(void)> > > list;
-    std::list<std::pair<uint32_t, std::function<void(void)> > >::iterator list_it;
-
-    uint32_t tick_count;
-
-    void do_nothing();
-};

+ 45 - 63
applications/lfrfid/lfrfid_app.cpp

@@ -21,6 +21,11 @@
 #include "scene/lfrfid_app_scene_delete_confirm.h"
 #include "scene/lfrfid_app_scene_delete_confirm.h"
 #include "scene/lfrfid_app_scene_delete_success.h"
 #include "scene/lfrfid_app_scene_delete_success.h"
 #include "scene/lfrfid_app_scene_rpc.h"
 #include "scene/lfrfid_app_scene_rpc.h"
+#include "scene/lfrfid_app_scene_extra_actions.h"
+#include "scene/lfrfid_app_scene_raw_info.h"
+#include "scene/lfrfid_app_scene_raw_name.h"
+#include "scene/lfrfid_app_scene_raw_read.h"
+#include "scene/lfrfid_app_scene_raw_success.h"
 
 
 #include <toolbox/path.h>
 #include <toolbox/path.h>
 #include <flipper_format/flipper_format.h>
 #include <flipper_format/flipper_format.h>
@@ -28,24 +33,44 @@
 #include <rpc/rpc_app.h>
 #include <rpc/rpc_app.h>
 
 
 const char* LfRfidApp::app_folder = ANY_PATH("lfrfid");
 const char* LfRfidApp::app_folder = ANY_PATH("lfrfid");
+const char* LfRfidApp::app_sd_folder = EXT_PATH("lfrfid");
 const char* LfRfidApp::app_extension = ".rfid";
 const char* LfRfidApp::app_extension = ".rfid";
 const char* LfRfidApp::app_filetype = "Flipper RFID key";
 const char* LfRfidApp::app_filetype = "Flipper RFID key";
 
 
 LfRfidApp::LfRfidApp()
 LfRfidApp::LfRfidApp()
     : scene_controller{this}
     : scene_controller{this}
-    , notification{"notification"}
-    , storage{"storage"}
-    , dialogs{"dialogs"}
+    , notification{RECORD_NOTIFICATION}
+    , storage{RECORD_STORAGE}
+    , dialogs{RECORD_DIALOGS}
     , text_store(40) {
     , text_store(40) {
+    string_init(file_name);
+    string_init(raw_file_name);
     string_init_set_str(file_path, app_folder);
     string_init_set_str(file_path, app_folder);
+
+    dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+
+    size_t size = protocol_dict_get_max_data_size(dict);
+    new_key_data = (uint8_t*)malloc(size);
+    old_key_data = (uint8_t*)malloc(size);
+
+    lfworker = lfrfid_worker_alloc(dict);
 }
 }
 
 
 LfRfidApp::~LfRfidApp() {
 LfRfidApp::~LfRfidApp() {
+    string_clear(raw_file_name);
+    string_clear(file_name);
     string_clear(file_path);
     string_clear(file_path);
+    protocol_dict_free(dict);
+
+    lfrfid_worker_free(lfworker);
+
     if(rpc_ctx) {
     if(rpc_ctx) {
         rpc_system_app_set_callback(rpc_ctx, NULL, NULL);
         rpc_system_app_set_callback(rpc_ctx, NULL, NULL);
         rpc_system_app_send_exited(rpc_ctx);
         rpc_system_app_send_exited(rpc_ctx);
     }
     }
+
+    free(new_key_data);
+    free(old_key_data);
 }
 }
 
 
 static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
 static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
@@ -88,7 +113,7 @@ void LfRfidApp::run(void* _args) {
             scene_controller.process(100, SceneType::Rpc);
             scene_controller.process(100, SceneType::Rpc);
         } else {
         } else {
             string_set_str(file_path, args);
             string_set_str(file_path, args);
-            load_key_data(file_path, &worker.key, true);
+            load_key_data(file_path, true);
             view_controller.attach_to_gui(ViewDispatcherTypeFullscreen);
             view_controller.attach_to_gui(ViewDispatcherTypeFullscreen);
             scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate());
             scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate());
             scene_controller.process(100, SceneType::Emulate);
             scene_controller.process(100, SceneType::Emulate);
@@ -114,11 +139,16 @@ void LfRfidApp::run(void* _args) {
         scene_controller.add_scene(SceneType::SavedInfo, new LfRfidAppSceneSavedInfo());
         scene_controller.add_scene(SceneType::SavedInfo, new LfRfidAppSceneSavedInfo());
         scene_controller.add_scene(SceneType::DeleteConfirm, new LfRfidAppSceneDeleteConfirm());
         scene_controller.add_scene(SceneType::DeleteConfirm, new LfRfidAppSceneDeleteConfirm());
         scene_controller.add_scene(SceneType::DeleteSuccess, new LfRfidAppSceneDeleteSuccess());
         scene_controller.add_scene(SceneType::DeleteSuccess, new LfRfidAppSceneDeleteSuccess());
+        scene_controller.add_scene(SceneType::ExtraActions, new LfRfidAppSceneExtraActions());
+        scene_controller.add_scene(SceneType::RawInfo, new LfRfidAppSceneRawInfo());
+        scene_controller.add_scene(SceneType::RawName, new LfRfidAppSceneRawName());
+        scene_controller.add_scene(SceneType::RawRead, new LfRfidAppSceneRawRead());
+        scene_controller.add_scene(SceneType::RawSuccess, new LfRfidAppSceneRawSuccess());
         scene_controller.process(100);
         scene_controller.process(100);
     }
     }
 }
 }
 
 
-bool LfRfidApp::save_key(RfidKey* key) {
+bool LfRfidApp::save_key() {
     bool result = false;
     bool result = false;
 
 
     make_app_folder();
     make_app_folder();
@@ -128,9 +158,9 @@ bool LfRfidApp::save_key(RfidKey* key) {
         string_left(file_path, filename_start);
         string_left(file_path, filename_start);
     }
     }
 
 
-    string_cat_printf(file_path, "/%s%s", key->get_name(), app_extension);
+    string_cat_printf(file_path, "/%s%s", string_get_cstr(file_name), app_extension);
 
 
-    result = save_key_data(file_path, key);
+    result = save_key_data(file_path);
     return result;
     return result;
 }
 }
 
 
@@ -143,56 +173,27 @@ bool LfRfidApp::load_key_from_file_select(bool need_restore) {
         dialogs, file_path, file_path, app_extension, true, &I_125_10px, true);
         dialogs, file_path, file_path, app_extension, true, &I_125_10px, true);
 
 
     if(result) {
     if(result) {
-        result = load_key_data(file_path, &worker.key, true);
+        result = load_key_data(file_path, true);
     }
     }
 
 
     return result;
     return result;
 }
 }
 
 
-bool LfRfidApp::delete_key(RfidKey* key) {
-    UNUSED(key);
+bool LfRfidApp::delete_key() {
     return storage_simply_remove(storage, string_get_cstr(file_path));
     return storage_simply_remove(storage, string_get_cstr(file_path));
 }
 }
 
 
-bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) {
-    FlipperFormat* file = flipper_format_file_alloc(storage);
+bool LfRfidApp::load_key_data(string_t path, bool show_dialog) {
     bool result = false;
     bool result = false;
-    string_t str_result;
-    string_init(str_result);
 
 
     do {
     do {
-        if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break;
-
-        // header
-        uint32_t version;
-        if(!flipper_format_read_header(file, str_result, &version)) break;
-        if(string_cmp_str(str_result, app_filetype) != 0) break;
-        if(version != 1) break;
-
-        // key type
-        LfrfidKeyType type;
-        RfidKey loaded_key;
-
-        if(!flipper_format_read_string(file, "Key type", str_result)) break;
-        if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &type)) break;
-        loaded_key.set_type(type);
+        protocol_id = lfrfid_dict_file_load(dict, string_get_cstr(path));
+        if(protocol_id == PROTOCOL_NO) break;
 
 
-        // key data
-        uint8_t key_data[loaded_key.get_type_data_count()] = {};
-        if(!flipper_format_read_hex(file, "Data", key_data, loaded_key.get_type_data_count()))
-            break;
-        loaded_key.set_data(key_data, loaded_key.get_type_data_count());
-
-        path_extract_filename(path, str_result, true);
-        loaded_key.set_name(string_get_cstr(str_result));
-
-        *key = loaded_key;
+        path_extract_filename(path, file_name, true);
         result = true;
         result = true;
     } while(0);
     } while(0);
 
 
-    flipper_format_free(file);
-    string_clear(str_result);
-
     if((!result) && (show_dialog)) {
     if((!result) && (show_dialog)) {
         dialog_message_show_storage_error(dialogs, "Cannot load\nkey file");
         dialog_message_show_storage_error(dialogs, "Cannot load\nkey file");
     }
     }
@@ -200,27 +201,8 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) {
     return result;
     return result;
 }
 }
 
 
-bool LfRfidApp::save_key_data(string_t path, RfidKey* key) {
-    FlipperFormat* file = flipper_format_file_alloc(storage);
-    bool result = false;
-
-    do {
-        if(!flipper_format_file_open_always(file, string_get_cstr(path))) break;
-        if(!flipper_format_write_header_cstr(file, app_filetype, 1)) break;
-        if(!flipper_format_write_comment_cstr(file, "Key type can be EM4100, H10301 or I40134"))
-            break;
-        if(!flipper_format_write_string_cstr(
-               file, "Key type", lfrfid_key_get_type_string(key->get_type())))
-            break;
-        if(!flipper_format_write_comment_cstr(
-               file, "Data size for EM4100 is 5, for H10301 is 3, for I40134 is 3"))
-            break;
-        if(!flipper_format_write_hex(file, "Data", key->get_data(), key->get_type_data_count()))
-            break;
-        result = true;
-    } while(0);
-
-    flipper_format_free(file);
+bool LfRfidApp::save_key_data(string_t path) {
+    bool result = lfrfid_dict_file_save(dict, protocol_id, string_get_cstr(path));
 
 
     if(!result) {
     if(!result) {
         dialog_message_show_storage_error(dialogs, "Cannot save\nkey file");
         dialog_message_show_storage_error(dialogs, "Cannot save\nkey file");

+ 42 - 9
applications/lfrfid/lfrfid_app.h

@@ -20,9 +20,15 @@
 #include <storage/storage.h>
 #include <storage/storage.h>
 #include <dialogs/dialogs.h>
 #include <dialogs/dialogs.h>
 
 
-#include "helpers/rfid_worker.h"
 #include "rpc/rpc_app.h"
 #include "rpc/rpc_app.h"
 
 
+#include <toolbox/protocols/protocol_dict.h>
+#include <lfrfid/lfrfid_dict_file.h>
+#include <lfrfid/protocols/lfrfid_protocols.h>
+#include <lfrfid/lfrfid_worker.h>
+
+#define LFRFID_KEY_NAME_SIZE 22
+
 class LfRfidApp {
 class LfRfidApp {
 public:
 public:
     enum class EventType : uint8_t {
     enum class EventType : uint8_t {
@@ -32,7 +38,19 @@ public:
         Stay,
         Stay,
         Retry,
         Retry,
         Exit,
         Exit,
-        EmulateStart,
+        ReadEventSenseStart,
+        ReadEventSenseEnd,
+        ReadEventSenseCardStart,
+        ReadEventSenseCardEnd,
+        ReadEventStartASK,
+        ReadEventStartPSK,
+        ReadEventDone,
+        ReadEventOverrun,
+        ReadEventError,
+        WriteEventOK,
+        WriteEventProtocolCannotBeWritten,
+        WriteEventFobCannotBeWritten,
+        WriteEventTooLongToWrite,
         RpcLoadFile,
         RpcLoadFile,
         RpcSessionClose,
         RpcSessionClose,
     };
     };
@@ -57,12 +75,17 @@ public:
         DeleteConfirm,
         DeleteConfirm,
         DeleteSuccess,
         DeleteSuccess,
         Rpc,
         Rpc,
+        ExtraActions,
+        RawInfo,
+        RawName,
+        RawRead,
+        RawSuccess,
     };
     };
 
 
     class Event {
     class Event {
     public:
     public:
         union {
         union {
-            int32_t menu_index;
+            int32_t signed_int;
         } payload;
         } payload;
 
 
         EventType type;
         EventType type;
@@ -79,8 +102,6 @@ public:
     RecordController<Storage> storage;
     RecordController<Storage> storage;
     RecordController<DialogsApp> dialogs;
     RecordController<DialogsApp> dialogs;
 
 
-    RfidWorker worker;
-
     TextStore text_store;
     TextStore text_store;
 
 
     string_t file_path;
     string_t file_path;
@@ -90,15 +111,27 @@ public:
     void run(void* args);
     void run(void* args);
 
 
     static const char* app_folder;
     static const char* app_folder;
+    static const char* app_sd_folder;
     static const char* app_extension;
     static const char* app_extension;
     static const char* app_filetype;
     static const char* app_filetype;
 
 
-    bool save_key(RfidKey* key);
+    bool save_key();
     bool load_key_from_file_select(bool need_restore);
     bool load_key_from_file_select(bool need_restore);
-    bool delete_key(RfidKey* key);
+    bool delete_key();
 
 
-    bool load_key_data(string_t path, RfidKey* key, bool show_dialog);
-    bool save_key_data(string_t path, RfidKey* key);
+    bool load_key_data(string_t path, bool show_dialog);
+    bool save_key_data(string_t path);
 
 
     void make_app_folder();
     void make_app_folder();
+
+    ProtocolDict* dict;
+    LFRFIDWorker* lfworker;
+    string_t file_name;
+    ProtocolId protocol_id;
+    LFRFIDWorkerReadType read_type;
+
+    uint8_t* old_key_data;
+    uint8_t* new_key_data;
+
+    string_t raw_file_name;
 };
 };

+ 575 - 0
applications/lfrfid/lfrfid_cli.c

@@ -0,0 +1,575 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include <stdarg.h>
+#include <cli/cli.h>
+#include <lib/toolbox/args.h>
+#include <lib/lfrfid/lfrfid_worker.h>
+#include <storage/storage.h>
+#include <toolbox/stream/file_stream.h>
+
+#include <toolbox/varint.h>
+
+#include <toolbox/protocols/protocol_dict.h>
+#include <lfrfid/protocols/lfrfid_protocols.h>
+#include <lfrfid/lfrfid_raw_file.h>
+#include <toolbox/pulse_protocols/pulse_glue.h>
+
+static void lfrfid_cli(Cli* cli, string_t args, void* context);
+
+// app cli function
+void lfrfid_on_system_start() {
+    Cli* cli = furi_record_open(RECORD_CLI);
+    cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
+    furi_record_close(RECORD_CLI);
+}
+
+static void lfrfid_cli_print_usage() {
+    printf("Usage:\r\n");
+    printf("rfid read <optional: normal | indala>\r\n");
+    printf("rfid <write | emulate> <key_type> <key_data>\r\n");
+    printf("rfid raw_read <ask | psk> <filename>\r\n");
+    printf("rfid raw_emulate <filename>\r\n");
+};
+
+typedef struct {
+    ProtocolId protocol;
+    FuriEventFlag* event;
+} LFRFIDCliReadContext;
+
+static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto, void* ctx) {
+    furi_assert(ctx);
+    LFRFIDCliReadContext* context = ctx;
+    if(result == LFRFIDWorkerReadDone) {
+        context->protocol = proto;
+        FURI_SW_MEMBARRIER();
+    }
+    furi_event_flag_set(context->event, 1 << result);
+}
+
+static void lfrfid_cli_read(Cli* cli, string_t args) {
+    string_t type_string;
+    string_init(type_string);
+    LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;
+
+    if(args_read_string_and_trim(args, type_string)) {
+        if(string_cmp_str(type_string, "normal") == 0 || string_cmp_str(type_string, "ask") == 0) {
+            // ask
+            type = LFRFIDWorkerReadTypeASKOnly;
+        } else if(
+            string_cmp_str(type_string, "indala") == 0 ||
+            string_cmp_str(type_string, "psk") == 0) {
+            // psk
+            type = LFRFIDWorkerReadTypePSKOnly;
+        } else {
+            lfrfid_cli_print_usage();
+            string_clear(type_string);
+            return;
+        }
+    }
+    string_clear(type_string);
+
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
+    LFRFIDCliReadContext context;
+    context.protocol = PROTOCOL_NO;
+    context.event = furi_event_flag_alloc();
+
+    lfrfid_worker_start_thread(worker);
+
+    printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n");
+
+    const uint32_t available_flags = (1 << LFRFIDWorkerReadDone);
+
+    lfrfid_worker_read_start(worker, type, lfrfid_cli_read_callback, &context);
+
+    while(true) {
+        uint32_t flags =
+            furi_event_flag_wait(context.event, available_flags, FuriFlagWaitAny, 100);
+
+        if(flags != FuriFlagErrorTimeout) {
+            if(FURI_BIT(flags, LFRFIDWorkerReadDone)) {
+                break;
+            }
+        }
+
+        if(cli_cmd_interrupt_received(cli)) break;
+    }
+
+    lfrfid_worker_stop(worker);
+    lfrfid_worker_stop_thread(worker);
+    lfrfid_worker_free(worker);
+
+    if(context.protocol != PROTOCOL_NO) {
+        printf("%s ", protocol_dict_get_name(dict, context.protocol));
+
+        size_t size = protocol_dict_get_data_size(dict, context.protocol);
+        uint8_t* data = malloc(size);
+        protocol_dict_get_data(dict, context.protocol, data, size);
+        for(size_t i = 0; i < size; i++) {
+            printf("%02X", data[i]);
+        }
+        printf("\r\n");
+        free(data);
+
+        string_t info;
+        string_init(info);
+        protocol_dict_render_data(dict, info, context.protocol);
+        if(string_size(info) > 0) {
+            printf("%s\r\n", string_get_cstr(info));
+        }
+        string_clear(info);
+    }
+
+    printf("Reading stopped\r\n");
+    protocol_dict_free(dict);
+
+    furi_event_flag_free(context.event);
+}
+
+static bool lfrfid_cli_parse_args(string_t args, ProtocolDict* dict, ProtocolId* protocol) {
+    bool result = false;
+    string_t protocol_name, data_text;
+    string_init(protocol_name);
+    string_init(data_text);
+    size_t data_size = protocol_dict_get_max_data_size(dict);
+    uint8_t* data = malloc(data_size);
+
+    do {
+        // load args
+        if(!args_read_string_and_trim(args, protocol_name) ||
+           !args_read_string_and_trim(args, data_text)) {
+            lfrfid_cli_print_usage();
+            break;
+        }
+
+        // check protocol arg
+        *protocol = protocol_dict_get_protocol_by_name(dict, string_get_cstr(protocol_name));
+        if(*protocol == PROTOCOL_NO) {
+            printf(
+                "Unknown protocol: %s\r\n"
+                "Available protocols:\r\n",
+                string_get_cstr(protocol_name));
+
+            for(ProtocolId i = 0; i < LFRFIDProtocolMax; i++) {
+                printf(
+                    "\t%s, %d bytes long\r\n",
+                    protocol_dict_get_name(dict, i),
+                    protocol_dict_get_data_size(dict, i));
+            }
+            break;
+        }
+
+        data_size = protocol_dict_get_data_size(dict, *protocol);
+
+        // check data arg
+        if(!args_read_hex_bytes(data_text, data, data_size)) {
+            printf(
+                "%s data needs to be %d bytes long\r\n",
+                protocol_dict_get_name(dict, *protocol),
+                data_size);
+            break;
+        }
+
+        // load data to protocol
+        protocol_dict_set_data(dict, *protocol, data, data_size);
+
+        result = true;
+    } while(false);
+
+    free(data);
+    string_clear(protocol_name);
+    string_clear(data_text);
+    return result;
+}
+
+static void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx) {
+    furi_assert(ctx);
+    FuriEventFlag* events = ctx;
+    furi_event_flag_set(events, 1 << result);
+}
+
+static void lfrfid_cli_write(Cli* cli, string_t args) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    ProtocolId protocol;
+
+    if(!lfrfid_cli_parse_args(args, dict, &protocol)) {
+        protocol_dict_free(dict);
+        return;
+    }
+
+    LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
+    FuriEventFlag* event = furi_event_flag_alloc();
+
+    lfrfid_worker_start_thread(worker);
+    lfrfid_worker_write_start(worker, protocol, lfrfid_cli_write_callback, event);
+
+    printf("Writing RFID...\r\nPress Ctrl+C to abort\r\n");
+    const uint32_t available_flags = (1 << LFRFIDWorkerWriteOK) |
+                                     (1 << LFRFIDWorkerWriteProtocolCannotBeWritten) |
+                                     (1 << LFRFIDWorkerWriteFobCannotBeWritten);
+
+    while(!cli_cmd_interrupt_received(cli)) {
+        uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
+        if(flags != FuriFlagErrorTimeout) {
+            if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) {
+                printf("Written!\r\n");
+                break;
+            }
+
+            if(FURI_BIT(flags, LFRFIDWorkerWriteProtocolCannotBeWritten)) {
+                printf("This protocol cannot be written.\r\n");
+                break;
+            }
+
+            if(FURI_BIT(flags, LFRFIDWorkerWriteFobCannotBeWritten)) {
+                printf("Seems this fob cannot be written.\r\n");
+            }
+        }
+    }
+    printf("Writing stopped\r\n");
+
+    lfrfid_worker_stop(worker);
+    lfrfid_worker_stop_thread(worker);
+    lfrfid_worker_free(worker);
+    protocol_dict_free(dict);
+    furi_event_flag_free(event);
+}
+
+static void lfrfid_cli_emulate(Cli* cli, string_t args) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    ProtocolId protocol;
+
+    if(!lfrfid_cli_parse_args(args, dict, &protocol)) {
+        protocol_dict_free(dict);
+        return;
+    }
+
+    LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
+
+    lfrfid_worker_start_thread(worker);
+    lfrfid_worker_emulate_start(worker, protocol);
+
+    printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n");
+    while(!cli_cmd_interrupt_received(cli)) {
+        furi_delay_ms(100);
+    }
+    printf("Emulation stopped\r\n");
+
+    lfrfid_worker_stop(worker);
+    lfrfid_worker_stop_thread(worker);
+    lfrfid_worker_free(worker);
+    protocol_dict_free(dict);
+}
+
+static void lfrfid_cli_raw_analyze(Cli* cli, string_t args) {
+    UNUSED(cli);
+    string_t filepath, info_string;
+    string_init(filepath);
+    string_init(info_string);
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);
+
+    do {
+        float frequency = 0;
+        float duty_cycle = 0;
+
+        if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
+            lfrfid_cli_print_usage();
+            break;
+        }
+
+        if(!lfrfid_raw_file_open_read(file, string_get_cstr(filepath))) {
+            printf("Failed to open file\r\n");
+            break;
+        }
+
+        if(!lfrfid_raw_file_read_header(file, &frequency, &duty_cycle)) {
+            printf("Invalid header\r\n");
+            break;
+        }
+
+        bool file_end = false;
+        uint32_t total_warns = 0;
+        uint32_t total_duration = 0;
+        uint32_t total_pulse = 0;
+        ProtocolId total_protocol = PROTOCOL_NO;
+
+        ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+        protocol_dict_decoders_start(dict);
+
+        while(!file_end) {
+            uint32_t pulse = 0;
+            uint32_t duration = 0;
+            if(lfrfid_raw_file_read_pair(file, &duration, &pulse, &file_end)) {
+                bool warn = false;
+
+                if(pulse > duration || pulse <= 0 || duration <= 0) {
+                    total_warns += 1;
+                    warn = true;
+                }
+
+                string_printf(info_string, "[%ld %ld]", pulse, duration);
+                printf("%-16s", string_get_cstr(info_string));
+                string_printf(info_string, "[%ld %ld]", pulse, duration - pulse);
+                printf("%-16s", string_get_cstr(info_string));
+
+                if(warn) {
+                    printf(" <<----");
+                }
+
+                if(total_protocol == PROTOCOL_NO) {
+                    total_protocol = protocol_dict_decoders_feed(dict, true, pulse);
+                    if(total_protocol == PROTOCOL_NO) {
+                        total_protocol =
+                            protocol_dict_decoders_feed(dict, false, duration - pulse);
+                    }
+
+                    if(total_protocol != PROTOCOL_NO) {
+                        printf(" <FOUND %s>", protocol_dict_get_name(dict, total_protocol));
+                    }
+                }
+
+                printf("\r\n");
+
+                total_pulse += pulse;
+                total_duration += duration;
+
+                if(total_protocol != PROTOCOL_NO) {
+                    break;
+                }
+            } else {
+                printf("Failed to read pair\r\n");
+                break;
+            }
+        }
+
+        printf("   Frequency: %f\r\n", (double)frequency);
+        printf("  Duty Cycle: %f\r\n", (double)duty_cycle);
+        printf("       Warns: %ld\r\n", total_warns);
+        printf("   Pulse sum: %ld\r\n", total_pulse);
+        printf("Duration sum: %ld\r\n", total_duration);
+        printf("     Average: %f\r\n", (double)((float)total_pulse / (float)total_duration));
+        printf("    Protocol: ");
+
+        if(total_protocol != PROTOCOL_NO) {
+            size_t data_size = protocol_dict_get_data_size(dict, total_protocol);
+            uint8_t* data = malloc(data_size);
+            protocol_dict_get_data(dict, total_protocol, data, data_size);
+
+            printf("%s [", protocol_dict_get_name(dict, total_protocol));
+            for(size_t i = 0; i < data_size; i++) {
+                printf("%02X", data[i]);
+                if(i < data_size - 1) {
+                    printf(" ");
+                }
+            }
+            printf("]\r\n");
+
+            protocol_dict_render_data(dict, info_string, total_protocol);
+            printf("%s\r\n", string_get_cstr(info_string));
+
+            free(data);
+        } else {
+            printf("not found\r\n");
+        }
+
+        protocol_dict_free(dict);
+    } while(false);
+
+    string_clear(filepath);
+    string_clear(info_string);
+    lfrfid_raw_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+}
+
+static void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void* context) {
+    furi_assert(context);
+    FuriEventFlag* event = context;
+    furi_event_flag_set(event, 1 << result);
+}
+
+static void lfrfid_cli_raw_read(Cli* cli, string_t args) {
+    UNUSED(cli);
+
+    string_t filepath, type_string;
+    string_init(filepath);
+    string_init(type_string);
+    LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;
+
+    do {
+        if(args_read_string_and_trim(args, type_string)) {
+            if(string_cmp_str(type_string, "normal") == 0 ||
+               string_cmp_str(type_string, "ask") == 0) {
+                // ask
+                type = LFRFIDWorkerReadTypeASKOnly;
+            } else if(
+                string_cmp_str(type_string, "indala") == 0 ||
+                string_cmp_str(type_string, "psk") == 0) {
+                // psk
+                type = LFRFIDWorkerReadTypePSKOnly;
+            } else {
+                lfrfid_cli_print_usage();
+                break;
+            }
+        }
+
+        if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
+            lfrfid_cli_print_usage();
+            break;
+        }
+
+        ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+        LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
+        FuriEventFlag* event = furi_event_flag_alloc();
+
+        lfrfid_worker_start_thread(worker);
+
+        bool overrun = false;
+
+        const uint32_t available_flags = (1 << LFRFIDWorkerReadRawFileError) |
+                                         (1 << LFRFIDWorkerReadRawOverrun);
+
+        lfrfid_worker_read_raw_start(
+            worker, string_get_cstr(filepath), type, lfrfid_cli_raw_read_callback, event);
+        while(true) {
+            uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
+
+            if(flags != FuriFlagErrorTimeout) {
+                if(FURI_BIT(flags, LFRFIDWorkerReadRawFileError)) {
+                    printf("File is not RFID raw file\r\n");
+                    break;
+                }
+
+                if(FURI_BIT(flags, LFRFIDWorkerReadRawOverrun)) {
+                    if(!overrun) {
+                        printf("Overrun\r\n");
+                        overrun = true;
+                    }
+                }
+            }
+
+            if(cli_cmd_interrupt_received(cli)) break;
+        }
+
+        if(overrun) {
+            printf("An overrun occurred during read\r\n");
+        }
+
+        lfrfid_worker_stop(worker);
+
+        lfrfid_worker_stop_thread(worker);
+        lfrfid_worker_free(worker);
+        protocol_dict_free(dict);
+
+        furi_event_flag_free(event);
+
+    } while(false);
+
+    string_clear(filepath);
+    string_clear(type_string);
+}
+
+static void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) {
+    furi_assert(context);
+    FuriEventFlag* event = context;
+    furi_event_flag_set(event, 1 << result);
+}
+
+static void lfrfid_cli_raw_emulate(Cli* cli, string_t args) {
+    UNUSED(cli);
+
+    string_t filepath;
+    string_init(filepath);
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    do {
+        if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
+            lfrfid_cli_print_usage();
+            break;
+        }
+
+        if(!storage_file_exists(storage, string_get_cstr(filepath))) {
+            printf("File not found: \"%s\"\r\n", string_get_cstr(filepath));
+            break;
+        }
+
+        ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+        LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
+        FuriEventFlag* event = furi_event_flag_alloc();
+
+        lfrfid_worker_start_thread(worker);
+
+        bool overrun = false;
+
+        const uint32_t available_flags = (1 << LFRFIDWorkerEmulateRawFileError) |
+                                         (1 << LFRFIDWorkerEmulateRawOverrun);
+
+        lfrfid_worker_emulate_raw_start(
+            worker, string_get_cstr(filepath), lfrfid_cli_raw_emulate_callback, event);
+        while(true) {
+            uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
+
+            if(flags != FuriFlagErrorTimeout) {
+                if(FURI_BIT(flags, LFRFIDWorkerEmulateRawFileError)) {
+                    printf("File is not RFID raw file\r\n");
+                    break;
+                }
+
+                if(FURI_BIT(flags, LFRFIDWorkerEmulateRawOverrun)) {
+                    if(!overrun) {
+                        printf("Overrun\r\n");
+                        overrun = true;
+                    }
+                }
+            }
+
+            if(cli_cmd_interrupt_received(cli)) break;
+        }
+
+        if(overrun) {
+            printf("An overrun occurred during emulation\r\n");
+        }
+
+        lfrfid_worker_stop(worker);
+
+        lfrfid_worker_stop_thread(worker);
+        lfrfid_worker_free(worker);
+        protocol_dict_free(dict);
+
+        furi_event_flag_free(event);
+
+    } while(false);
+
+    furi_record_close(RECORD_STORAGE);
+    string_clear(filepath);
+}
+
+static void lfrfid_cli(Cli* cli, string_t args, void* context) {
+    UNUSED(context);
+    string_t cmd;
+    string_init(cmd);
+
+    if(!args_read_string_and_trim(args, cmd)) {
+        string_clear(cmd);
+        lfrfid_cli_print_usage();
+        return;
+    }
+
+    if(string_cmp_str(cmd, "read") == 0) {
+        lfrfid_cli_read(cli, args);
+    } else if(string_cmp_str(cmd, "write") == 0) {
+        lfrfid_cli_write(cli, args);
+    } else if(string_cmp_str(cmd, "emulate") == 0) {
+        lfrfid_cli_emulate(cli, args);
+    } else if(string_cmp_str(cmd, "raw_read") == 0) {
+        lfrfid_cli_raw_read(cli, args);
+    } else if(string_cmp_str(cmd, "raw_emulate") == 0) {
+        lfrfid_cli_raw_emulate(cli, args);
+    } else if(string_cmp_str(cmd, "raw_analyze") == 0) {
+        lfrfid_cli_raw_analyze(cli, args);
+    } else {
+        lfrfid_cli_print_usage();
+    }
+
+    string_clear(cmd);
+}

+ 0 - 177
applications/lfrfid/lfrfid_cli.cpp

@@ -1,177 +0,0 @@
-#include <furi.h>
-#include <furi_hal.h>
-#include <stdarg.h>
-#include <cli/cli.h>
-#include <lib/toolbox/args.h>
-
-#include "helpers/rfid_reader.h"
-#include "helpers/rfid_timer_emulator.h"
-
-static void lfrfid_cli(Cli* cli, string_t args, void* context);
-
-// app cli function
-extern "C" void lfrfid_on_system_start() {
-#ifdef SRV_CLI
-    Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
-    cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
-    furi_record_close("cli");
-#else
-    UNUSED(lfrfid_cli);
-#endif
-}
-
-void lfrfid_cli_print_usage() {
-    printf("Usage:\r\n");
-    printf("rfid read <optional: normal | indala>\r\n");
-    printf("rfid <write | emulate> <key_type> <key_data>\r\n");
-    printf("\t<key_type> choose from:\r\n");
-    printf("\tEM4100, EM-Marin (5 bytes key_data)\r\n");
-    printf("\tH10301, HID26 (3 bytes key_data)\r\n");
-    printf("\tI40134, Indala (3 bytes key_data)\r\n");
-    printf("\tIoProxXSF, IoProx (4 bytes key_data)\r\n");
-    printf("\t<key_data> are hex-formatted\r\n");
-};
-
-static bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
-    bool result = false;
-
-    if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) {
-        result = true;
-        *type = LfrfidKeyType::KeyEM4100;
-    } else if(string_cmp_str(data, "H10301") == 0 || string_cmp_str(data, "HID26") == 0) {
-        result = true;
-        *type = LfrfidKeyType::KeyH10301;
-    } else if(string_cmp_str(data, "I40134") == 0 || string_cmp_str(data, "Indala") == 0) {
-        result = true;
-        *type = LfrfidKeyType::KeyI40134;
-    } else if(string_cmp_str(data, "IoProxXSF") == 0 || string_cmp_str(data, "IoProx") == 0) {
-        result = true;
-        *type = LfrfidKeyType::KeyIoProxXSF;
-    }
-
-    return result;
-}
-
-static void lfrfid_cli_read(Cli* cli, string_t args) {
-    RfidReader reader;
-    string_t type_string;
-    string_init(type_string);
-    bool simple_mode = true;
-    LfrfidKeyType type;
-    RfidReader::Type reader_type = RfidReader::Type::Normal;
-    static const uint8_t data_size = LFRFID_KEY_SIZE;
-    uint8_t data[data_size] = {0};
-
-    if(args_read_string_and_trim(args, type_string)) {
-        simple_mode = false;
-
-        if(string_cmp_str(type_string, "normal") == 0) {
-            reader_type = RfidReader::Type::Normal;
-        } else if(string_cmp_str(type_string, "indala") == 0) {
-            reader_type = RfidReader::Type::Indala;
-        } else {
-            lfrfid_cli_print_usage();
-            string_clear(type_string);
-            return;
-        }
-    }
-
-    if(simple_mode) {
-        reader.start();
-    } else {
-        reader.start_forced(reader_type);
-    }
-
-    printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n");
-    while(!cli_cmd_interrupt_received(cli)) {
-        if(reader.read(&type, data, data_size, simple_mode)) {
-            printf("%s", lfrfid_key_get_type_string(type));
-            printf(" ");
-
-            for(uint8_t i = 0; i < lfrfid_key_get_type_data_count(type); i++) {
-                printf("%02X", data[i]);
-            }
-            printf("\r\n");
-            break;
-        }
-        furi_delay_ms(100);
-    }
-
-    printf("Reading stopped\r\n");
-    reader.stop();
-
-    string_clear(type_string);
-}
-
-static void lfrfid_cli_write(Cli* cli, string_t args) {
-    UNUSED(cli);
-    UNUSED(args);
-    // TODO implement rfid write
-    printf("Not Implemented :(\r\n");
-}
-
-static void lfrfid_cli_emulate(Cli* cli, string_t args) {
-    string_t data;
-    string_init(data);
-    RfidTimerEmulator emulator;
-
-    static const uint8_t data_size = LFRFID_KEY_SIZE;
-    uint8_t key_data[data_size] = {0};
-    uint8_t key_data_size = 0;
-    LfrfidKeyType type;
-
-    if(!args_read_string_and_trim(args, data)) {
-        lfrfid_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    if(!lfrfid_cli_get_key_type(data, &type)) {
-        lfrfid_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    key_data_size = lfrfid_key_get_type_data_count(type);
-
-    if(!args_read_hex_bytes(args, key_data, key_data_size)) {
-        lfrfid_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    emulator.start(type, key_data, key_data_size);
-
-    printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n");
-    while(!cli_cmd_interrupt_received(cli)) {
-        furi_delay_ms(100);
-    }
-    printf("Emulation stopped\r\n");
-    emulator.stop();
-
-    string_clear(data);
-}
-
-static void lfrfid_cli(Cli* cli, string_t args, void* context) {
-    UNUSED(context);
-    string_t cmd;
-    string_init(cmd);
-
-    if(!args_read_string_and_trim(args, cmd)) {
-        string_clear(cmd);
-        lfrfid_cli_print_usage();
-        return;
-    }
-
-    if(string_cmp_str(cmd, "read") == 0) {
-        lfrfid_cli_read(cli, args);
-    } else if(string_cmp_str(cmd, "write") == 0) {
-        lfrfid_cli_write(cli, args);
-    } else if(string_cmp_str(cmd, "emulate") == 0) {
-        lfrfid_cli_emulate(cli, args);
-    } else {
-        lfrfid_cli_print_usage();
-    }
-
-    string_clear(cmd);
-}

+ 11 - 36
applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp

@@ -5,7 +5,6 @@
 
 
 void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) {
 void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) {
     string_init(string_data);
     string_init(string_data);
-    string_init(string_decrypted);
     string_init(string_header);
     string_init(string_header);
 
 
     auto container = app->view_controller.get<ContainerVM>();
     auto container = app->view_controller.get<ContainerVM>();
@@ -21,49 +20,26 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore
     auto line_1 = container->add<StringElement>();
     auto line_1 = container->add<StringElement>();
     auto line_2 = container->add<StringElement>();
     auto line_2 = container->add<StringElement>();
     auto line_3 = container->add<StringElement>();
     auto line_3 = container->add<StringElement>();
-    auto line_4 = container->add<StringElement>();
 
 
-    RfidKey& key = app->worker.key;
-    const uint8_t* data = key.get_data();
-
-    for(uint8_t i = 0; i < key.get_type_data_count(); i++) {
+    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);
+    for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) {
         if(i != 0) {
         if(i != 0) {
             string_cat_printf(string_data, " ");
             string_cat_printf(string_data, " ");
         }
         }
+
         string_cat_printf(string_data, "%02X", data[i]);
         string_cat_printf(string_data, "%02X", data[i]);
     }
     }
+    free(data);
 
 
-    string_printf(string_header, "Delete %s?", key.get_name());
+    string_printf(string_header, "Delete %s?", string_get_cstr(app->file_name));
     line_1->set_text(
     line_1->set_text(
-        string_get_cstr(string_header), 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary);
+        string_get_cstr(string_header), 64, 0, 128 - 2, AlignCenter, AlignTop, FontPrimary);
     line_2->set_text(
     line_2->set_text(
-        string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontSecondary);
-
-    switch(key.get_type()) {
-    case LfrfidKeyType::KeyEM4100:
-        string_printf(
-            string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4])));
-
-        break;
-    case LfrfidKeyType::KeyH10301:
-    case LfrfidKeyType::KeyI40134:
-        string_printf(
-            string_decrypted, "FC: %u    ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2])));
-        break;
-    case LfrfidKeyType::KeyIoProxXSF:
-        string_printf(
-            string_decrypted,
-            "FC: %u   VC: %u   ID: %u",
-            data[0],
-            data[1],
-            (uint16_t)((data[2] << 8) | (data[3])));
-        break;
-    }
+        string_get_cstr(string_data), 64, 19, 0, AlignCenter, AlignTop, FontSecondary);
     line_3->set_text(
     line_3->set_text(
-        string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary);
-
-    line_4->set_text(
-        lfrfid_key_get_type_string(key.get_type()),
+        protocol_dict_get_name(app->dict, app->protocol_id),
         64,
         64,
         49,
         49,
         0,
         0,
@@ -78,7 +54,7 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve
     bool consumed = false;
     bool consumed = false;
 
 
     if(event->type == LfRfidApp::EventType::Next) {
     if(event->type == LfRfidApp::EventType::Next) {
-        app->delete_key(&app->worker.key);
+        app->delete_key();
         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess);
         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess);
         consumed = true;
         consumed = true;
     } else if(event->type == LfRfidApp::EventType::Stay) {
     } else if(event->type == LfRfidApp::EventType::Stay) {
@@ -94,7 +70,6 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve
 void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) {
     app->view_controller.get<ContainerVM>()->clean();
     app->view_controller.get<ContainerVM>()->clean();
     string_clear(string_data);
     string_clear(string_data);
-    string_clear(string_decrypted);
     string_clear(string_header);
     string_clear(string_header);
 }
 }
 
 

+ 0 - 1
applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h

@@ -13,5 +13,4 @@ private:
 
 
     string_t string_header;
     string_t string_header;
     string_t string_data;
     string_t string_data;
-    string_t string_decrypted;
 };
 };

+ 8 - 15
applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp

@@ -3,28 +3,21 @@
 #include <dolphin/dolphin.h>
 #include <dolphin/dolphin.h>
 
 
 void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) {
 void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) {
-    string_init(data_string);
-
     DOLPHIN_DEED(DolphinDeedRfidEmulate);
     DOLPHIN_DEED(DolphinDeedRfidEmulate);
-    const uint8_t* data = app->worker.key.get_data();
-
-    for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
-        string_cat_printf(data_string, "%02X", data[i]);
-    }
-
     auto popup = app->view_controller.get<PopupVM>();
     auto popup = app->view_controller.get<PopupVM>();
 
 
     popup->set_header("Emulating", 89, 30, AlignCenter, AlignTop);
     popup->set_header("Emulating", 89, 30, AlignCenter, AlignTop);
-    if(strlen(app->worker.key.get_name())) {
-        popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop);
+    if(string_size(app->file_name)) {
+        popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
     } else {
     } else {
-        popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop);
+        popup->set_text(
+            protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop);
     }
     }
     popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61);
     popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61);
 
 
     app->view_controller.switch_to<PopupVM>();
     app->view_controller.switch_to<PopupVM>();
-    app->worker.start_emulate();
-
+    lfrfid_worker_start_thread(app->lfworker);
+    lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);
     notification_message(app->notification, &sequence_blink_start_magenta);
     notification_message(app->notification, &sequence_blink_start_magenta);
 }
 }
 
 
@@ -37,7 +30,7 @@ bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 
 
 void LfRfidAppSceneEmulate::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneEmulate::on_exit(LfRfidApp* app) {
     app->view_controller.get<PopupVM>()->clean();
     app->view_controller.get<PopupVM>()->clean();
-    app->worker.stop_emulate();
-    string_clear(data_string);
+    lfrfid_worker_stop(app->lfworker);
+    lfrfid_worker_stop_thread(app->lfworker);
     notification_message(app->notification, &sequence_blink_stop);
     notification_message(app->notification, &sequence_blink_stop);
 }
 }

+ 0 - 3
applications/lfrfid/scene/lfrfid_app_scene_emulate.h

@@ -6,7 +6,4 @@ public:
     void on_enter(LfRfidApp* app, bool need_restore) final;
     void on_enter(LfRfidApp* app, bool need_restore) final;
     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
     void on_exit(LfRfidApp* app) final;
     void on_exit(LfRfidApp* app) final;
-
-private:
-    string_t data_string;
 };
 };

+ 63 - 0
applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp

@@ -0,0 +1,63 @@
+#include "lfrfid_app_scene_extra_actions.h"
+
+typedef enum {
+    SubmenuASK,
+    SubmenuPSK,
+    SubmenuRAW,
+} SubmenuIndex;
+
+void LfRfidAppSceneExtraActions::on_enter(LfRfidApp* app, bool need_restore) {
+    auto submenu = app->view_controller.get<SubmenuVM>();
+
+    submenu->add_item("Read ASK (Animal, Ordinary Card)", SubmenuASK, submenu_callback, app);
+    submenu->add_item("Read PSK (Indala)", SubmenuPSK, submenu_callback, app);
+
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        submenu->add_item("Read RAW RFID data", SubmenuRAW, submenu_callback, app);
+    }
+
+    if(need_restore) {
+        submenu->set_selected_item(submenu_item_selected);
+    }
+
+    app->view_controller.switch_to<SubmenuVM>();
+}
+
+bool LfRfidAppSceneExtraActions::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
+    bool consumed = false;
+
+    if(event->type == LfRfidApp::EventType::MenuSelected) {
+        submenu_item_selected = event->payload.signed_int;
+        switch(event->payload.signed_int) {
+        case SubmenuASK:
+            app->read_type = LFRFIDWorkerReadTypeASKOnly;
+            app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read);
+            break;
+        case SubmenuPSK:
+            app->read_type = LFRFIDWorkerReadTypePSKOnly;
+            app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read);
+            break;
+        case SubmenuRAW:
+            app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawName);
+            break;
+        }
+
+        consumed = true;
+    }
+
+    return consumed;
+}
+
+void LfRfidAppSceneExtraActions::on_exit(LfRfidApp* app) {
+    app->view_controller.get<SubmenuVM>()->clean();
+}
+
+void LfRfidAppSceneExtraActions::submenu_callback(void* context, uint32_t index) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(context);
+    LfRfidApp::Event event;
+
+    event.type = LfRfidApp::EventType::MenuSelected;
+    event.payload.signed_int = index;
+
+    app->view_controller.send_event(&event);
+}

+ 13 - 0
applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h

@@ -0,0 +1,13 @@
+#pragma once
+#include "../lfrfid_app.h"
+
+class LfRfidAppSceneExtraActions : public GenericScene<LfRfidApp> {
+public:
+    void on_enter(LfRfidApp* app, bool need_restore) final;
+    bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
+    void on_exit(LfRfidApp* app) final;
+
+private:
+    static void submenu_callback(void* context, uint32_t index);
+    uint32_t submenu_item_selected = 0;
+};

+ 77 - 0
applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp

@@ -0,0 +1,77 @@
+#include "lfrfid_app_scene_raw_info.h"
+#include "../view/elements/button_element.h"
+#include "../view/elements/icon_element.h"
+#include "../view/elements/string_element.h"
+
+static void ok_callback(void* context) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(context);
+    LfRfidApp::Event event;
+    event.type = LfRfidApp::EventType::Next;
+    app->view_controller.send_event(&event);
+}
+
+static void back_callback(void* context) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(context);
+    LfRfidApp::Event event;
+    event.type = LfRfidApp::EventType::Back;
+    app->view_controller.send_event(&event);
+}
+
+void LfRfidAppSceneRawInfo::on_enter(LfRfidApp* app, bool /* need_restore */) {
+    string_init(string_info);
+
+    auto container = app->view_controller.get<ContainerVM>();
+
+    bool sd_exist = storage_sd_status(app->storage) == FSE_OK;
+    if(!sd_exist) {
+        auto icon = container->add<IconElement>();
+        icon->set_icon(0, 0, &I_SDQuestion_35x43);
+        auto line = container->add<StringElement>();
+        line->set_text(
+            "No SD card found.\nThis function will not\nwork without\nSD card.",
+            81,
+            4,
+            0,
+            AlignCenter,
+            AlignTop,
+            FontSecondary);
+
+        auto button = container->add<ButtonElement>();
+        button->set_type(ButtonElement::Type::Left, "Back");
+        button->set_callback(app, back_callback);
+    } else {
+        string_printf(
+            string_info,
+            "RAW RFID data reader\r\n"
+            "1) Put the Flipper on your card\r\n"
+            "2) Press OK\r\n"
+            "3) Wait until data is read");
+
+        auto line = container->add<StringElement>();
+        line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary);
+
+        auto button = container->add<ButtonElement>();
+        button->set_type(ButtonElement::Type::Center, "OK");
+        button->set_callback(app, ok_callback);
+    }
+
+    app->view_controller.switch_to<ContainerVM>();
+}
+
+bool LfRfidAppSceneRawInfo::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
+    bool consumed = false;
+    if(event->type == LfRfidApp::EventType::Next) {
+        app->scene_controller.switch_to_scene({LfRfidApp::SceneType::RawRead});
+        consumed = true;
+    } else if(event->type == LfRfidApp::EventType::Back) {
+        app->scene_controller.search_and_switch_to_previous_scene(
+            {LfRfidApp::SceneType::ExtraActions});
+        consumed = true;
+    }
+    return consumed;
+}
+
+void LfRfidAppSceneRawInfo::on_exit(LfRfidApp* app) {
+    app->view_controller.get<ContainerVM>()->clean();
+    string_clear(string_info);
+}

+ 12 - 0
applications/lfrfid/scene/lfrfid_app_scene_raw_info.h

@@ -0,0 +1,12 @@
+#pragma once
+#include "../lfrfid_app.h"
+
+class LfRfidAppSceneRawInfo : public GenericScene<LfRfidApp> {
+public:
+    void on_enter(LfRfidApp* app, bool need_restore) final;
+    bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
+    void on_exit(LfRfidApp* app) final;
+
+private:
+    string_t string_info;
+};

+ 46 - 0
applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp

@@ -0,0 +1,46 @@
+
+#include "lfrfid_app_scene_raw_name.h"
+#include "m-string.h"
+#include <lib/toolbox/random_name.h>
+#include <lib/toolbox/path.h>
+
+void LfRfidAppSceneRawName::on_enter(LfRfidApp* app, bool /* need_restore */) {
+    const char* key_name = string_get_cstr(app->raw_file_name);
+
+    bool key_name_empty = (string_size(app->raw_file_name) == 0);
+    if(key_name_empty) {
+        app->text_store.set("RfidRecord");
+    } else {
+        app->text_store.set("%s", key_name);
+    }
+
+    auto text_input = app->view_controller.get<TextInputVM>();
+    text_input->set_header_text("Name the raw file");
+
+    text_input->set_result_callback(
+        save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty);
+
+    app->view_controller.switch_to<TextInputVM>();
+}
+
+bool LfRfidAppSceneRawName::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
+    bool consumed = false;
+
+    if(event->type == LfRfidApp::EventType::Next) {
+        string_set_str(app->raw_file_name, app->text_store.text);
+        app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawInfo);
+    }
+
+    return consumed;
+}
+
+void LfRfidAppSceneRawName::on_exit(LfRfidApp* app) {
+    app->view_controller.get<TextInputVM>()->clean();
+}
+
+void LfRfidAppSceneRawName::save_callback(void* context) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(context);
+    LfRfidApp::Event event;
+    event.type = LfRfidApp::EventType::Next;
+    app->view_controller.send_event(&event);
+}

+ 12 - 0
applications/lfrfid/scene/lfrfid_app_scene_raw_name.h

@@ -0,0 +1,12 @@
+#pragma once
+#include "../lfrfid_app.h"
+
+class LfRfidAppSceneRawName : public GenericScene<LfRfidApp> {
+public:
+    void on_enter(LfRfidApp* app, bool need_restore) final;
+    bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
+    void on_exit(LfRfidApp* app) final;
+
+private:
+    static void save_callback(void* context);
+};

+ 107 - 0
applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp

@@ -0,0 +1,107 @@
+#include "lfrfid_app_scene_raw_read.h"
+#include <dolphin/dolphin.h>
+
+#define RAW_READ_TIME 5000
+
+static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* ctx) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(ctx);
+    LfRfidApp::Event event;
+
+    switch(result) {
+    case LFRFIDWorkerReadRawFileError:
+        event.type = LfRfidApp::EventType::ReadEventError;
+        break;
+    case LFRFIDWorkerReadRawOverrun:
+        event.type = LfRfidApp::EventType::ReadEventOverrun;
+        break;
+    }
+
+    app->view_controller.send_event(&event);
+}
+
+static void timer_callback(void* ctx) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(ctx);
+    LfRfidApp::Event event;
+    event.type = LfRfidApp::EventType::ReadEventDone;
+    app->view_controller.send_event(&event);
+}
+
+void LfRfidAppSceneRawRead::on_enter(LfRfidApp* app, bool /* need_restore */) {
+    string_init(string_file_name);
+    auto popup = app->view_controller.get<PopupVM>();
+    popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61);
+    app->view_controller.switch_to<PopupVM>();
+    lfrfid_worker_start_thread(app->lfworker);
+    app->make_app_folder();
+
+    timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app);
+    furi_timer_start(timer, RAW_READ_TIME);
+    string_printf(
+        string_file_name, "%s/%s.ask.raw", app->app_sd_folder, string_get_cstr(app->raw_file_name));
+    popup->set_header("Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop);
+    lfrfid_worker_read_raw_start(
+        app->lfworker,
+        string_get_cstr(string_file_name),
+        LFRFIDWorkerReadTypeASKOnly,
+        lfrfid_read_callback,
+        app);
+
+    notification_message(app->notification, &sequence_blink_start_cyan);
+
+    is_psk = false;
+    error = false;
+}
+
+bool LfRfidAppSceneRawRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
+    UNUSED(app);
+    bool consumed = true;
+    auto popup = app->view_controller.get<PopupVM>();
+
+    switch(event->type) {
+    case LfRfidApp::EventType::ReadEventError:
+        error = true;
+        popup->set_header("Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop);
+        notification_message(app->notification, &sequence_blink_start_red);
+        furi_timer_stop(timer);
+        break;
+    case LfRfidApp::EventType::ReadEventDone:
+        if(!error) {
+            if(is_psk) {
+                notification_message(app->notification, &sequence_success);
+                app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawSuccess);
+            } else {
+                popup->set_header("Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop);
+                notification_message(app->notification, &sequence_blink_start_yellow);
+                lfrfid_worker_stop(app->lfworker);
+                string_printf(
+                    string_file_name,
+                    "%s/%s.psk.raw",
+                    app->app_sd_folder,
+                    string_get_cstr(app->raw_file_name));
+                lfrfid_worker_read_raw_start(
+                    app->lfworker,
+                    string_get_cstr(string_file_name),
+                    LFRFIDWorkerReadTypePSKOnly,
+                    lfrfid_read_callback,
+                    app);
+                furi_timer_start(timer, RAW_READ_TIME);
+                is_psk = true;
+            }
+        }
+        break;
+    default:
+        consumed = false;
+        break;
+    }
+
+    return consumed;
+}
+
+void LfRfidAppSceneRawRead::on_exit(LfRfidApp* app) {
+    notification_message(app->notification, &sequence_blink_stop);
+    app->view_controller.get<PopupVM>()->clean();
+    lfrfid_worker_stop(app->lfworker);
+    lfrfid_worker_stop_thread(app->lfworker);
+    furi_timer_free(timer);
+    string_clear(string_file_name);
+}

+ 15 - 0
applications/lfrfid/scene/lfrfid_app_scene_raw_read.h

@@ -0,0 +1,15 @@
+#pragma once
+#include "../lfrfid_app.h"
+
+class LfRfidAppSceneRawRead : public GenericScene<LfRfidApp> {
+public:
+    void on_enter(LfRfidApp* app, bool need_restore) final;
+    bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
+    void on_exit(LfRfidApp* app) final;
+
+private:
+    string_t string_file_name;
+    FuriTimer* timer;
+    bool is_psk;
+    bool error;
+};

+ 45 - 0
applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp

@@ -0,0 +1,45 @@
+#include "lfrfid_app_scene_raw_success.h"
+#include "../view/elements/button_element.h"
+#include "../view/elements/icon_element.h"
+#include "../view/elements/string_element.h"
+
+void LfRfidAppSceneRawSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) {
+    string_init(string_info);
+
+    string_printf(string_info, "RAW RFID read success!\r\n");
+    string_cat_printf(string_info, "Now you can analyze files\r\n");
+    string_cat_printf(string_info, "Or send them to developers");
+
+    auto container = app->view_controller.get<ContainerVM>();
+
+    auto line = container->add<StringElement>();
+    line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary);
+
+    auto button = container->add<ButtonElement>();
+    button->set_type(ButtonElement::Type::Center, "OK");
+    button->set_callback(app, LfRfidAppSceneRawSuccess::ok_callback);
+
+    app->view_controller.switch_to<ContainerVM>();
+}
+
+bool LfRfidAppSceneRawSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
+    bool consumed = false;
+    if(event->type == LfRfidApp::EventType::Next) {
+        app->scene_controller.search_and_switch_to_previous_scene(
+            {LfRfidApp::SceneType::ExtraActions});
+        consumed = true;
+    }
+    return consumed;
+}
+
+void LfRfidAppSceneRawSuccess::on_exit(LfRfidApp* app) {
+    app->view_controller.get<ContainerVM>()->clean();
+    string_clear(string_info);
+}
+
+void LfRfidAppSceneRawSuccess::ok_callback(void* context) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(context);
+    LfRfidApp::Event event;
+    event.type = LfRfidApp::EventType::Next;
+    app->view_controller.send_event(&event);
+}

+ 13 - 0
applications/lfrfid/scene/lfrfid_app_scene_raw_success.h

@@ -0,0 +1,13 @@
+#pragma once
+#include "../lfrfid_app.h"
+
+class LfRfidAppSceneRawSuccess : public GenericScene<LfRfidApp> {
+public:
+    void on_enter(LfRfidApp* app, bool need_restore) final;
+    bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
+    void on_exit(LfRfidApp* app) final;
+
+private:
+    string_t string_info;
+    static void ok_callback(void* context);
+};

+ 79 - 19
applications/lfrfid/scene/lfrfid_app_scene_read.cpp

@@ -1,40 +1,100 @@
 #include "lfrfid_app_scene_read.h"
 #include "lfrfid_app_scene_read.h"
 #include <dolphin/dolphin.h>
 #include <dolphin/dolphin.h>
 
 
+static void lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* ctx) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(ctx);
+    LfRfidApp::Event event;
+
+    switch(result) {
+    case LFRFIDWorkerReadSenseStart:
+        event.type = LfRfidApp::EventType::ReadEventSenseStart;
+        break;
+    case LFRFIDWorkerReadSenseEnd:
+        event.type = LfRfidApp::EventType::ReadEventSenseEnd;
+        break;
+    case LFRFIDWorkerReadSenseCardStart:
+        event.type = LfRfidApp::EventType::ReadEventSenseCardStart;
+        break;
+    case LFRFIDWorkerReadSenseCardEnd:
+        event.type = LfRfidApp::EventType::ReadEventSenseCardEnd;
+        break;
+    case LFRFIDWorkerReadDone:
+        event.type = LfRfidApp::EventType::ReadEventDone;
+        break;
+    case LFRFIDWorkerReadStartASK:
+        event.type = LfRfidApp::EventType::ReadEventStartASK;
+        break;
+    case LFRFIDWorkerReadStartPSK:
+        event.type = LfRfidApp::EventType::ReadEventStartPSK;
+        break;
+    }
+
+    event.payload.signed_int = protocol;
+
+    app->view_controller.send_event(&event);
+}
+
 void LfRfidAppSceneRead::on_enter(LfRfidApp* app, bool /* need_restore */) {
 void LfRfidAppSceneRead::on_enter(LfRfidApp* app, bool /* need_restore */) {
     auto popup = app->view_controller.get<PopupVM>();
     auto popup = app->view_controller.get<PopupVM>();
 
 
     DOLPHIN_DEED(DolphinDeedRfidRead);
     DOLPHIN_DEED(DolphinDeedRfidRead);
-    popup->set_header("Reading\nLF RFID", 89, 34, AlignCenter, AlignTop);
+    if(app->read_type == LFRFIDWorkerReadTypePSKOnly) {
+        popup->set_header("Reading\nLF RFID\nPSK", 89, 30, AlignCenter, AlignTop);
+    } else {
+        popup->set_header("Reading\nLF RFID\nASK", 89, 30, AlignCenter, AlignTop);
+    }
+
     popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61);
     popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61);
 
 
     app->view_controller.switch_to<PopupVM>();
     app->view_controller.switch_to<PopupVM>();
-    app->worker.start_read();
+    lfrfid_worker_start_thread(app->lfworker);
+    lfrfid_worker_read_start(app->lfworker, app->read_type, lfrfid_read_callback, app);
+
+    notification_message(app->notification, &sequence_blink_start_cyan);
 }
 }
 
 
 bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
-    bool consumed = false;
-
-    if(event->type == LfRfidApp::EventType::Tick) {
-        if(app->worker.read()) {
-            DOLPHIN_DEED(DolphinDeedRfidReadSuccess);
-            notification_message(app->notification, &sequence_success);
-            app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess);
-        } else {
-            if(app->worker.any_read()) {
-                notification_message(app->notification, &sequence_blink_yellow_10);
-            } else if(app->worker.detect()) {
-                notification_message(app->notification, &sequence_blink_yellow_10);
-            } else {
-                notification_message(app->notification, &sequence_blink_cyan_10);
-            }
-        }
+    bool consumed = true;
+    auto popup = app->view_controller.get<PopupVM>();
+
+    switch(event->type) {
+    case LfRfidApp::EventType::ReadEventSenseStart:
+        notification_message(app->notification, &sequence_blink_stop);
+        notification_message(app->notification, &sequence_blink_start_yellow);
+        break;
+    case LfRfidApp::EventType::ReadEventSenseCardStart:
+        notification_message(app->notification, &sequence_blink_stop);
+        notification_message(app->notification, &sequence_blink_start_green);
+        break;
+    case LfRfidApp::EventType::ReadEventSenseEnd:
+    case LfRfidApp::EventType::ReadEventSenseCardEnd:
+        notification_message(app->notification, &sequence_blink_stop);
+        notification_message(app->notification, &sequence_blink_start_cyan);
+        break;
+    case LfRfidApp::EventType::ReadEventDone:
+        app->protocol_id = event->payload.signed_int;
+        DOLPHIN_DEED(DolphinDeedRfidReadSuccess);
+        notification_message(app->notification, &sequence_success);
+        string_reset(app->file_name);
+        app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess);
+        break;
+    case LfRfidApp::EventType::ReadEventStartPSK:
+        popup->set_header("Reading\nLF RFID\nPSK", 89, 30, AlignCenter, AlignTop);
+        break;
+    case LfRfidApp::EventType::ReadEventStartASK:
+        popup->set_header("Reading\nLF RFID\nASK", 89, 30, AlignCenter, AlignTop);
+        break;
+    default:
+        consumed = false;
+        break;
     }
     }
 
 
     return consumed;
     return consumed;
 }
 }
 
 
 void LfRfidAppSceneRead::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneRead::on_exit(LfRfidApp* app) {
+    notification_message(app->notification, &sequence_blink_stop);
     app->view_controller.get<PopupVM>()->clean();
     app->view_controller.get<PopupVM>()->clean();
-    app->worker.stop_read();
+    lfrfid_worker_stop(app->lfworker);
+    lfrfid_worker_stop_thread(app->lfworker);
 }
 }

+ 3 - 3
applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp

@@ -24,8 +24,8 @@ bool LfRfidAppSceneReadKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event
     bool consumed = false;
     bool consumed = false;
 
 
     if(event->type == LfRfidApp::EventType::MenuSelected) {
     if(event->type == LfRfidApp::EventType::MenuSelected) {
-        submenu_item_selected = event->payload.menu_index;
-        switch(event->payload.menu_index) {
+        submenu_item_selected = event->payload.signed_int;
+        switch(event->payload.signed_int) {
         case SubmenuWrite:
         case SubmenuWrite:
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write);
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write);
             break;
             break;
@@ -54,7 +54,7 @@ void LfRfidAppSceneReadKeyMenu::submenu_callback(void* context, uint32_t index)
     LfRfidApp::Event event;
     LfRfidApp::Event event;
 
 
     event.type = LfRfidApp::EventType::MenuSelected;
     event.type = LfRfidApp::EventType::MenuSelected;
-    event.payload.menu_index = index;
+    event.payload.signed_int = index;
 
 
     app->view_controller.send_event(&event);
     app->view_controller.send_event(&event);
 }
 }

+ 36 - 89
applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp

@@ -4,10 +4,37 @@
 #include "../view/elements/string_element.h"
 #include "../view/elements/string_element.h"
 
 
 void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) {
 void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) {
-    string_init(string[0]);
-    string_init(string[1]);
-    string_init(string[2]);
-    string_init(string[3]);
+    string_init(string_info);
+    string_init(string_header);
+
+    string_init_printf(
+        string_header,
+        "%s[%s]",
+        protocol_dict_get_name(app->dict, app->protocol_id),
+        protocol_dict_get_manufacturer(app->dict, app->protocol_id));
+
+    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);
+    for(uint8_t i = 0; i < size; i++) {
+        if(i != 0) {
+            string_cat_printf(string_info, " ");
+        }
+
+        if(i >= 9) {
+            string_cat_printf(string_info, "...");
+            break;
+        } else {
+            string_cat_printf(string_info, "%02X", data[i]);
+        }
+    }
+    free(data);
+
+    string_t render_data;
+    string_init(render_data);
+    protocol_dict_render_brief_data(app->dict, render_data, app->protocol_id);
+    string_cat_printf(string_info, "\r\n%s", string_get_cstr(render_data));
+    string_clear(render_data);
 
 
     auto container = app->view_controller.get<ContainerVM>();
     auto container = app->view_controller.get<ContainerVM>();
 
 
@@ -19,90 +46,11 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */
     button->set_type(ButtonElement::Type::Right, "More");
     button->set_type(ButtonElement::Type::Right, "More");
     button->set_callback(app, LfRfidAppSceneReadSuccess::more_callback);
     button->set_callback(app, LfRfidAppSceneReadSuccess::more_callback);
 
 
-    auto icon = container->add<IconElement>();
-    icon->set_icon(3, 12, &I_RFIDBigChip_37x36);
-
     auto header = container->add<StringElement>();
     auto header = container->add<StringElement>();
-    header->set_text(app->worker.key.get_type_text(), 89, 3, 0, AlignCenter);
-
-    auto line_1_text = container->add<StringElement>();
-    auto line_2l_text = container->add<StringElement>();
-    auto line_2r_text = container->add<StringElement>();
-    auto line_3_text = container->add<StringElement>();
-
-    auto line_1_value = container->add<StringElement>();
-    auto line_2l_value = container->add<StringElement>();
-    auto line_2r_value = container->add<StringElement>();
-    auto line_3_value = container->add<StringElement>();
-
-    const uint8_t* data = app->worker.key.get_data();
-
-    switch(app->worker.key.get_type()) {
-    case LfrfidKeyType::KeyEM4100:
-        line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary);
-        line_2l_text->set_text("Mod:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary);
-        line_3_text->set_text("ID:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary);
+    header->set_text(string_get_cstr(string_header), 0, 2, 0, AlignLeft, AlignTop, FontPrimary);
 
 
-        for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
-            string_cat_printf(string[0], "%02X", data[i]);
-        }
-
-        string_printf(string[1], "Manchester");
-        string_printf(string[2], "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4])));
-
-        line_1_value->set_text(
-            string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary);
-        line_2l_value->set_text(
-            string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary);
-        line_3_value->set_text(
-            string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary);
-        break;
-    case LfrfidKeyType::KeyH10301:
-    case LfrfidKeyType::KeyI40134:
-        line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary);
-        line_2l_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary);
-        line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary);
-
-        for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
-            string_cat_printf(string[0], "%02X", data[i]);
-        }
-
-        string_printf(string[1], "%u", data[0]);
-        string_printf(string[2], "%u", (uint16_t)((data[1] << 8) | (data[2])));
-
-        line_1_value->set_text(
-            string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary);
-        line_2l_value->set_text(
-            string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary);
-        line_3_value->set_text(
-            string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary);
-        break;
-
-    case LfrfidKeyType::KeyIoProxXSF:
-        line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary);
-        line_2l_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary);
-        line_2r_text->set_text("VС:", 95, 35, 0, AlignRight, AlignBottom, FontSecondary);
-        line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary);
-
-        for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
-            string_cat_printf(string[0], "%02X", data[i]);
-        }
-
-        string_printf(string[1], "%u", data[0]);
-        string_printf(string[2], "%u", (uint16_t)((data[2] << 8) | (data[3])));
-        string_printf(string[3], "%u", data[1]);
-
-        line_1_value->set_text(
-            string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary);
-        line_2l_value->set_text(
-            string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary);
-        line_2r_value->set_text(
-            string_get_cstr(string[3]), 98, 35, 0, AlignLeft, AlignBottom, FontSecondary);
-        line_3_value->set_text(
-            string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary);
-
-        break;
-    }
+    auto text = container->add<StringElement>();
+    text->set_text(string_get_cstr(string_info), 0, 16, 0, AlignLeft, AlignTop, FontSecondary);
 
 
     app->view_controller.switch_to<ContainerVM>();
     app->view_controller.switch_to<ContainerVM>();
 
 
@@ -129,9 +77,8 @@ bool LfRfidAppSceneReadSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event
 void LfRfidAppSceneReadSuccess::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneReadSuccess::on_exit(LfRfidApp* app) {
     notification_message_block(app->notification, &sequence_reset_green);
     notification_message_block(app->notification, &sequence_reset_green);
     app->view_controller.get<ContainerVM>()->clean();
     app->view_controller.get<ContainerVM>()->clean();
-    string_clear(string[0]);
-    string_clear(string[1]);
-    string_clear(string[2]);
+    string_clear(string_info);
+    string_clear(string_header);
 }
 }
 
 
 void LfRfidAppSceneReadSuccess::back_callback(void* context) {
 void LfRfidAppSceneReadSuccess::back_callback(void* context) {

+ 2 - 1
applications/lfrfid/scene/lfrfid_app_scene_read_success.h

@@ -11,5 +11,6 @@ private:
     static void back_callback(void* context);
     static void back_callback(void* context);
     static void more_callback(void* context);
     static void more_callback(void* context);
 
 
-    string_t string[3];
+    string_t string_header;
+    string_t string_info;
 };
 };

+ 6 - 4
applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp

@@ -37,12 +37,13 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
         bool result = false;
         bool result = false;
         if(arg && !emulating) {
         if(arg && !emulating) {
             string_set_str(app->file_path, arg);
             string_set_str(app->file_path, arg);
-            if(app->load_key_data(app->file_path, &(app->worker.key), false)) {
-                app->worker.start_emulate();
+            if(app->load_key_data(app->file_path, false)) {
+                lfrfid_worker_start_thread(app->lfworker);
+                lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);
                 emulating = true;
                 emulating = true;
 
 
                 auto popup = app->view_controller.get<PopupVM>();
                 auto popup = app->view_controller.get<PopupVM>();
-                app->text_store.set("emulating\n%s", app->worker.key.get_name());
+                app->text_store.set("emulating\n%s", string_get_cstr(app->file_name));
                 popup->set_text(app->text_store.text, 89, 44, AlignCenter, AlignTop);
                 popup->set_text(app->text_store.text, 89, 44, AlignCenter, AlignTop);
 
 
                 notification_message(app->notification, &sequence_blink_start_magenta);
                 notification_message(app->notification, &sequence_blink_start_magenta);
@@ -57,7 +58,8 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 
 
 void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) {
     if(emulating) {
     if(emulating) {
-        app->worker.stop_emulate();
+        lfrfid_worker_stop(app->lfworker);
+        lfrfid_worker_stop_thread(app->lfworker);
         notification_message(app->notification, &sequence_blink_stop);
         notification_message(app->notification, &sequence_blink_stop);
     }
     }
     app->view_controller.get<PopupVM>()->clean();
     app->view_controller.get<PopupVM>()->clean();

+ 8 - 10
applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp

@@ -3,31 +3,29 @@
 
 
 void LfRfidAppSceneSaveData::on_enter(LfRfidApp* app, bool need_restore) {
 void LfRfidAppSceneSaveData::on_enter(LfRfidApp* app, bool need_restore) {
     auto byte_input = app->view_controller.get<ByteInputVM>();
     auto byte_input = app->view_controller.get<ByteInputVM>();
-    RfidKey& key = app->worker.key;
-
-    if(need_restore) printf("restored\r\n");
+    size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
 
 
     if(need_restore) {
     if(need_restore) {
-        key.set_data(old_key_data, key.get_type_data_count());
+        protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
     } else {
     } else {
-        memcpy(old_key_data, key.get_data(), key.get_type_data_count());
+        protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);
     }
     }
 
 
-    memcpy(new_key_data, key.get_data(), key.get_type_data_count());
+    protocol_dict_get_data(app->dict, app->protocol_id, app->new_key_data, size);
+
     byte_input->set_header_text("Enter the data in hex");
     byte_input->set_header_text("Enter the data in hex");
 
 
-    byte_input->set_result_callback(
-        save_callback, NULL, app, new_key_data, app->worker.key.get_type_data_count());
+    byte_input->set_result_callback(save_callback, NULL, app, app->new_key_data, size);
 
 
     app->view_controller.switch_to<ByteInputVM>();
     app->view_controller.switch_to<ByteInputVM>();
 }
 }
 
 
 bool LfRfidAppSceneSaveData::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 bool LfRfidAppSceneSaveData::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
     bool consumed = false;
     bool consumed = false;
-    RfidKey& key = app->worker.key;
 
 
     if(event->type == LfRfidApp::EventType::Next) {
     if(event->type == LfRfidApp::EventType::Next) {
-        key.set_data(new_key_data, key.get_type_data_count());
+        size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
+        protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size);
         DOLPHIN_DEED(DolphinDeedRfidAdd);
         DOLPHIN_DEED(DolphinDeedRfidAdd);
         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName);
         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName);
     }
     }

+ 0 - 21
applications/lfrfid/scene/lfrfid_app_scene_save_data.h

@@ -9,25 +9,4 @@ public:
 
 
 private:
 private:
     static void save_callback(void* context);
     static void save_callback(void* context);
-    uint8_t old_key_data[LFRFID_KEY_SIZE] = {
-        0xAA,
-        0xAA,
-        0xAA,
-        0xAA,
-        0xAA,
-        0xAA,
-        0xAA,
-        0xAA,
-    };
-
-    uint8_t new_key_data[LFRFID_KEY_SIZE] = {
-        0xBB,
-        0xBB,
-        0xBB,
-        0xBB,
-        0xBB,
-        0xBB,
-        0xBB,
-        0xBB,
-    };
 };
 };

+ 7 - 11
applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp

@@ -4,9 +4,9 @@
 #include <lib/toolbox/path.h>
 #include <lib/toolbox/path.h>
 
 
 void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) {
 void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) {
-    const char* key_name = app->worker.key.get_name();
+    const char* key_name = string_get_cstr(app->file_name);
 
 
-    bool key_name_empty = !strcmp(key_name, "");
+    bool key_name_empty = (string_size(app->file_name) == 0);
     if(key_name_empty) {
     if(key_name_empty) {
         string_set_str(app->file_path, app->app_folder);
         string_set_str(app->file_path, app->app_folder);
         set_random_name(app->text_store.text, app->text_store.text_size);
         set_random_name(app->text_store.text, app->text_store.text_size);
@@ -18,11 +18,7 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) {
     text_input->set_header_text("Name the card");
     text_input->set_header_text("Name the card");
 
 
     text_input->set_result_callback(
     text_input->set_result_callback(
-        save_callback,
-        app,
-        app->text_store.text,
-        app->worker.key.get_name_length(),
-        key_name_empty);
+        save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty);
 
 
     string_t folder_path;
     string_t folder_path;
     string_init(folder_path);
     string_init(folder_path);
@@ -42,13 +38,13 @@ bool LfRfidAppSceneSaveName::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
     bool consumed = false;
     bool consumed = false;
 
 
     if(event->type == LfRfidApp::EventType::Next) {
     if(event->type == LfRfidApp::EventType::Next) {
-        if(strlen(app->worker.key.get_name())) {
-            app->delete_key(&app->worker.key);
+        if(string_size(app->file_name) > 0) {
+            app->delete_key();
         }
         }
 
 
-        app->worker.key.set_name(app->text_store.text);
+        string_set_str(app->file_name, app->text_store.text);
 
 
-        if(app->save_key(&app->worker.key)) {
+        if(app->save_key()) {
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess);
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess);
         } else {
         } else {
             app->scene_controller.search_and_switch_to_previous_scene(
             app->scene_controller.search_and_switch_to_previous_scene(

+ 8 - 8
applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp

@@ -3,12 +3,12 @@
 void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) {
 void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) {
     auto submenu = app->view_controller.get<SubmenuVM>();
     auto submenu = app->view_controller.get<SubmenuVM>();
 
 
-    for(uint8_t i = 0; i <= keys_count; i++) {
+    for(uint8_t i = 0; i < keys_count; i++) {
         string_init_printf(
         string_init_printf(
             submenu_name[i],
             submenu_name[i],
             "%s %s",
             "%s %s",
-            lfrfid_key_get_manufacturer_string(static_cast<LfrfidKeyType>(i)),
-            lfrfid_key_get_type_string(static_cast<LfrfidKeyType>(i)));
+            protocol_dict_get_manufacturer(app->dict, i),
+            protocol_dict_get_name(app->dict, i));
         submenu->add_item(string_get_cstr(submenu_name[i]), i, submenu_callback, app);
         submenu->add_item(string_get_cstr(submenu_name[i]), i, submenu_callback, app);
     }
     }
 
 
@@ -19,15 +19,15 @@ void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) {
     app->view_controller.switch_to<SubmenuVM>();
     app->view_controller.switch_to<SubmenuVM>();
 
 
     // clear key name
     // clear key name
-    app->worker.key.set_name("");
+    string_reset(app->file_name);
 }
 }
 
 
 bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
     bool consumed = false;
     bool consumed = false;
 
 
     if(event->type == LfRfidApp::EventType::MenuSelected) {
     if(event->type == LfRfidApp::EventType::MenuSelected) {
-        submenu_item_selected = event->payload.menu_index;
-        app->worker.key.set_type(static_cast<LfrfidKeyType>(event->payload.menu_index));
+        submenu_item_selected = event->payload.signed_int;
+        app->protocol_id = event->payload.signed_int;
         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData);
         app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData);
         consumed = true;
         consumed = true;
     }
     }
@@ -37,7 +37,7 @@ bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 
 
 void LfRfidAppSceneSaveType::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneSaveType::on_exit(LfRfidApp* app) {
     app->view_controller.get<SubmenuVM>()->clean();
     app->view_controller.get<SubmenuVM>()->clean();
-    for(uint8_t i = 0; i <= keys_count; i++) {
+    for(uint8_t i = 0; i < keys_count; i++) {
         string_clear(submenu_name[i]);
         string_clear(submenu_name[i]);
     }
     }
 }
 }
@@ -47,7 +47,7 @@ void LfRfidAppSceneSaveType::submenu_callback(void* context, uint32_t index) {
     LfRfidApp::Event event;
     LfRfidApp::Event event;
 
 
     event.type = LfRfidApp::EventType::MenuSelected;
     event.type = LfRfidApp::EventType::MenuSelected;
-    event.payload.menu_index = index;
+    event.payload.signed_int = index;
 
 
     app->view_controller.send_event(&event);
     app->view_controller.send_event(&event);
 }
 }

+ 2 - 2
applications/lfrfid/scene/lfrfid_app_scene_save_type.h

@@ -10,6 +10,6 @@ public:
 private:
 private:
     static void submenu_callback(void* context, uint32_t index);
     static void submenu_callback(void* context, uint32_t index);
     uint32_t submenu_item_selected = 0;
     uint32_t submenu_item_selected = 0;
-    static const uint8_t keys_count = static_cast<uint8_t>(LfrfidKeyType::KeyIoProxXSF);
-    string_t submenu_name[keys_count + 1];
+    static const uint8_t keys_count = static_cast<uint8_t>(LFRFIDProtocol::LFRFIDProtocolMax);
+    string_t submenu_name[keys_count];
 };
 };

+ 25 - 62
applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp

@@ -4,65 +4,36 @@
 #include "../view/elements/string_element.h"
 #include "../view/elements/string_element.h"
 
 
 void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool /* need_restore */) {
 void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool /* need_restore */) {
-    string_init(string_data);
-    string_init(string_decrypted);
-
-    auto container = app->view_controller.get<ContainerVM>();
-
-    auto button = container->add<ButtonElement>();
-    button->set_type(ButtonElement::Type::Left, "Back");
-    button->set_callback(app, LfRfidAppSceneSavedInfo::back_callback);
-
-    auto line_1 = container->add<StringElement>();
-    auto line_2 = container->add<StringElement>();
-    auto line_3 = container->add<StringElement>();
-    auto line_4 = container->add<StringElement>();
-
-    RfidKey& key = app->worker.key;
-    const uint8_t* data = key.get_data();
-
-    for(uint8_t i = 0; i < key.get_type_data_count(); i++) {
+    string_init(string_info);
+
+    string_printf(
+        string_info,
+        "%s [%s]\r\n",
+        string_get_cstr(app->file_name),
+        protocol_dict_get_name(app->dict, app->protocol_id));
+
+    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);
+    for(uint8_t i = 0; i < size; i++) {
         if(i != 0) {
         if(i != 0) {
-            string_cat_printf(string_data, " ");
+            string_cat_printf(string_info, " ");
         }
         }
-        string_cat_printf(string_data, "%02X", data[i]);
-    }
 
 
-    line_1->set_text(key.get_name(), 64, 17, 128 - 2, AlignCenter, AlignBottom, FontSecondary);
-    line_2->set_text(
-        string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontPrimary);
+        string_cat_printf(string_info, "%02X", data[i]);
+    }
+    free(data);
 
 
-    switch(key.get_type()) {
-    case LfrfidKeyType::KeyEM4100:
-        string_printf(
-            string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4])));
+    string_t render_data;
+    string_init(render_data);
+    protocol_dict_render_data(app->dict, render_data, app->protocol_id);
+    string_cat_printf(string_info, "\r\n%s", string_get_cstr(render_data));
+    string_clear(render_data);
 
 
-        break;
-    case LfrfidKeyType::KeyH10301:
-    case LfrfidKeyType::KeyI40134:
-        string_printf(
-            string_decrypted, "FC: %u    ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2])));
-        break;
-    case LfrfidKeyType::KeyIoProxXSF:
-        string_printf(
-            string_decrypted,
-            "FC: %u   VC: %u   ID: %u",
-            data[0],
-            data[1],
-            (uint16_t)((data[2] << 8) | (data[3])));
-        break;
-    }
-    line_3->set_text(
-        string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary);
+    auto container = app->view_controller.get<ContainerVM>();
 
 
-    line_4->set_text(
-        lfrfid_key_get_type_string(key.get_type()),
-        64,
-        49,
-        0,
-        AlignCenter,
-        AlignBottom,
-        FontSecondary);
+    auto line_1 = container->add<StringElement>();
+    line_1->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary);
 
 
     app->view_controller.switch_to<ContainerVM>();
     app->view_controller.switch_to<ContainerVM>();
 }
 }
@@ -73,13 +44,5 @@ bool LfRfidAppSceneSavedInfo::on_event(LfRfidApp* /* app */, LfRfidApp::Event* /
 
 
 void LfRfidAppSceneSavedInfo::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneSavedInfo::on_exit(LfRfidApp* app) {
     app->view_controller.get<ContainerVM>()->clean();
     app->view_controller.get<ContainerVM>()->clean();
-    string_clear(string_data);
-    string_clear(string_decrypted);
-}
-
-void LfRfidAppSceneSavedInfo::back_callback(void* context) {
-    LfRfidApp* app = static_cast<LfRfidApp*>(context);
-    LfRfidApp::Event event;
-    event.type = LfRfidApp::EventType::Back;
-    app->view_controller.send_event(&event);
+    string_clear(string_info);
 }
 }

+ 1 - 4
applications/lfrfid/scene/lfrfid_app_scene_saved_info.h

@@ -8,8 +8,5 @@ public:
     void on_exit(LfRfidApp* app) final;
     void on_exit(LfRfidApp* app) final;
 
 
 private:
 private:
-    static void back_callback(void* context);
-
-    string_t string_data;
-    string_t string_decrypted;
+    string_t string_info;
 };
 };

+ 3 - 3
applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp

@@ -28,8 +28,8 @@ bool LfRfidAppSceneSavedKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* even
     bool consumed = false;
     bool consumed = false;
 
 
     if(event->type == LfRfidApp::EventType::MenuSelected) {
     if(event->type == LfRfidApp::EventType::MenuSelected) {
-        submenu_item_selected = event->payload.menu_index;
-        switch(event->payload.menu_index) {
+        submenu_item_selected = event->payload.signed_int;
+        switch(event->payload.signed_int) {
         case SubmenuEmulate:
         case SubmenuEmulate:
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate);
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate);
             break;
             break;
@@ -61,7 +61,7 @@ void LfRfidAppSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index)
     LfRfidApp::Event event;
     LfRfidApp::Event event;
 
 
     event.type = LfRfidApp::EventType::MenuSelected;
     event.type = LfRfidApp::EventType::MenuSelected;
-    event.payload.menu_index = index;
+    event.payload.signed_int = index;
 
 
     app->view_controller.send_event(&event);
     app->view_controller.send_event(&event);
 }
 }

+ 11 - 4
applications/lfrfid/scene/lfrfid_app_scene_start.cpp

@@ -4,6 +4,7 @@ typedef enum {
     SubmenuRead,
     SubmenuRead,
     SubmenuSaved,
     SubmenuSaved,
     SubmenuAddManually,
     SubmenuAddManually,
+    SubmenuExtraActions,
 } SubmenuIndex;
 } SubmenuIndex;
 
 
 void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) {
 void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) {
@@ -12,6 +13,7 @@ void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) {
     submenu->add_item("Read", SubmenuRead, submenu_callback, app);
     submenu->add_item("Read", SubmenuRead, submenu_callback, app);
     submenu->add_item("Saved", SubmenuSaved, submenu_callback, app);
     submenu->add_item("Saved", SubmenuSaved, submenu_callback, app);
     submenu->add_item("Add Manually", SubmenuAddManually, submenu_callback, app);
     submenu->add_item("Add Manually", SubmenuAddManually, submenu_callback, app);
+    submenu->add_item("Extra Actions", SubmenuExtraActions, submenu_callback, app);
 
 
     if(need_restore) {
     if(need_restore) {
         submenu->set_selected_item(submenu_item_selected);
         submenu->set_selected_item(submenu_item_selected);
@@ -20,15 +22,17 @@ void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) {
     app->view_controller.switch_to<SubmenuVM>();
     app->view_controller.switch_to<SubmenuVM>();
 
 
     // clear key
     // clear key
-    app->worker.key.clear();
+    string_reset(app->file_name);
+    app->protocol_id = PROTOCOL_NO;
+    app->read_type = LFRFIDWorkerReadTypeAuto;
 }
 }
 
 
 bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
     bool consumed = false;
     bool consumed = false;
 
 
     if(event->type == LfRfidApp::EventType::MenuSelected) {
     if(event->type == LfRfidApp::EventType::MenuSelected) {
-        submenu_item_selected = event->payload.menu_index;
-        switch(event->payload.menu_index) {
+        submenu_item_selected = event->payload.signed_int;
+        switch(event->payload.signed_int) {
         case SubmenuRead:
         case SubmenuRead:
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read);
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read);
             break;
             break;
@@ -38,6 +42,9 @@ bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
         case SubmenuAddManually:
         case SubmenuAddManually:
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveType);
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveType);
             break;
             break;
+        case SubmenuExtraActions:
+            app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ExtraActions);
+            break;
         }
         }
         consumed = true;
         consumed = true;
     }
     }
@@ -54,7 +61,7 @@ void LfRfidAppSceneStart::submenu_callback(void* context, uint32_t index) {
     LfRfidApp::Event event;
     LfRfidApp::Event event;
 
 
     event.type = LfRfidApp::EventType::MenuSelected;
     event.type = LfRfidApp::EventType::MenuSelected;
-    event.payload.menu_index = index;
+    event.payload.signed_int = index;
 
 
     app->view_controller.send_event(&event);
     app->view_controller.send_event(&event);
 }
 }

+ 54 - 41
applications/lfrfid/scene/lfrfid_app_scene_write.cpp

@@ -1,66 +1,79 @@
 #include "lfrfid_app_scene_write.h"
 #include "lfrfid_app_scene_write.h"
 
 
-void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) {
-    card_not_supported = false;
-    string_init(data_string);
-
-    const uint8_t* data = app->worker.key.get_data();
+static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* ctx) {
+    LfRfidApp* app = static_cast<LfRfidApp*>(ctx);
+    LfRfidApp::Event event;
 
 
-    for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
-        string_cat_printf(data_string, "%02X", data[i]);
+    switch(result) {
+    case LFRFIDWorkerWriteOK:
+        event.type = LfRfidApp::EventType::WriteEventOK;
+        break;
+    case LFRFIDWorkerWriteProtocolCannotBeWritten:
+        event.type = LfRfidApp::EventType::WriteEventProtocolCannotBeWritten;
+        break;
+    case LFRFIDWorkerWriteFobCannotBeWritten:
+        event.type = LfRfidApp::EventType::WriteEventFobCannotBeWritten;
+        break;
+    case LFRFIDWorkerWriteTooLongToWrite:
+        event.type = LfRfidApp::EventType::WriteEventTooLongToWrite;
+        break;
     }
     }
 
 
+    app->view_controller.send_event(&event);
+}
+
+void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) {
     auto popup = app->view_controller.get<PopupVM>();
     auto popup = app->view_controller.get<PopupVM>();
 
 
     popup->set_header("Writing", 89, 30, AlignCenter, AlignTop);
     popup->set_header("Writing", 89, 30, AlignCenter, AlignTop);
-    if(strlen(app->worker.key.get_name())) {
-        popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop);
+    if(string_size(app->file_name)) {
+        popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
     } else {
     } else {
-        popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop);
+        popup->set_text(
+            protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop);
     }
     }
     popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61);
     popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61);
 
 
     app->view_controller.switch_to<PopupVM>();
     app->view_controller.switch_to<PopupVM>();
-    app->worker.start_write();
+    lfrfid_worker_start_thread(app->lfworker);
+    lfrfid_worker_write_start(
+        app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app);
+    notification_message(app->notification, &sequence_blink_start_magenta);
 }
 }
 
 
 bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
-    bool consumed = false;
-
-    if(event->type == LfRfidApp::EventType::Tick) {
-        RfidWorker::WriteResult result = app->worker.write();
+    bool consumed = true;
+    auto popup = app->view_controller.get<PopupVM>();
 
 
-        switch(result) {
-        case RfidWorker::WriteResult::Nothing:
-            notification_message(app->notification, &sequence_blink_magenta_10);
-            break;
-        case RfidWorker::WriteResult::Ok:
-            notification_message(app->notification, &sequence_success);
-            app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::WriteSuccess);
-            break;
-        case RfidWorker::WriteResult::NotWritable:
-            if(!card_not_supported) {
-                auto popup = app->view_controller.get<PopupVM>();
-                popup->set_icon(72, 17, &I_DolphinCommon_56x48);
-                popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop);
-                popup->set_text(
-                    "Make sure this\ncard is writable\nand not\nprotected.",
-                    3,
-                    17,
-                    AlignLeft,
-                    AlignTop);
-                card_not_supported = true;
-            }
-            notification_message(app->notification, &sequence_blink_yellow_10);
-            break;
-        }
+    switch(event->type) {
+    case LfRfidApp::EventType::WriteEventOK:
+        notification_message(app->notification, &sequence_success);
+        app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::WriteSuccess);
+        break;
+    case LfRfidApp::EventType::WriteEventProtocolCannotBeWritten:
+        popup->set_icon(72, 17, &I_DolphinCommon_56x48);
+        popup->set_header("Error", 64, 3, AlignCenter, AlignTop);
+        popup->set_text("This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop);
+        notification_message(app->notification, &sequence_blink_start_red);
+        break;
+    case LfRfidApp::EventType::WriteEventFobCannotBeWritten:
+    case LfRfidApp::EventType::WriteEventTooLongToWrite:
+        popup->set_icon(72, 17, &I_DolphinCommon_56x48);
+        popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop);
+        popup->set_text(
+            "Make sure this\ncard is writable\nand not\nprotected.", 3, 17, AlignLeft, AlignTop);
+        notification_message(app->notification, &sequence_blink_start_yellow);
+        break;
+    default:
+        consumed = false;
     }
     }
 
 
     return consumed;
     return consumed;
 }
 }
 
 
 void LfRfidAppSceneWrite::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneWrite::on_exit(LfRfidApp* app) {
+    notification_message(app->notification, &sequence_blink_stop);
     app->view_controller.get<PopupVM>()->clean();
     app->view_controller.get<PopupVM>()->clean();
-    app->worker.stop_write();
-    string_clear(data_string);
+    lfrfid_worker_stop(app->lfworker);
+    lfrfid_worker_stop_thread(app->lfworker);
 }
 }

+ 0 - 4
applications/lfrfid/scene/lfrfid_app_scene_write.h

@@ -6,8 +6,4 @@ public:
     void on_enter(LfRfidApp* app, bool need_restore) final;
     void on_enter(LfRfidApp* app, bool need_restore) final;
     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
     bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
     void on_exit(LfRfidApp* app) final;
     void on_exit(LfRfidApp* app) final;
-
-private:
-    string_t data_string;
-    bool card_not_supported;
 };
 };

+ 9 - 0
applications/storage/storage.h

@@ -136,6 +136,15 @@ bool storage_file_sync(File* file);
  */
  */
 bool storage_file_eof(File* file);
 bool storage_file_eof(File* file);
 
 
+/**
+ * @brief Check that file exists
+ * 
+ * @param storage 
+ * @param path 
+ * @return true if file exists
+ */
+bool storage_file_exists(Storage* storage, const char* path);
+
 /******************* Dir Functions *******************/
 /******************* Dir Functions *******************/
 
 
 /** Opens a directory to get objects from it
 /** Opens a directory to get objects from it

+ 12 - 0
applications/storage/storage_external_api.c

@@ -240,6 +240,18 @@ bool storage_file_eof(File* file) {
     return S_RETURN_BOOL;
     return S_RETURN_BOOL;
 }
 }
 
 
+bool storage_file_exists(Storage* storage, const char* path) {
+    bool exist = false;
+    FileInfo fileinfo;
+    FS_Error error = storage_common_stat(storage, path, &fileinfo);
+
+    if(error == FSE_OK && !(fileinfo.flags & FSF_DIRECTORY)) {
+        exist = true;
+    }
+
+    return exist;
+}
+
 /****************** DIR ******************/
 /****************** DIR ******************/
 
 
 static bool storage_dir_open_internal(File* file, const char* path) {
 static bool storage_dir_open_internal(File* file, const char* path) {

+ 473 - 0
applications/unit_tests/lfrfid/bit_lib_test.c

@@ -0,0 +1,473 @@
+#include <furi.h>
+#include "../minunit.h"
+#include <lfrfid/tools/bit_lib.h>
+
+MU_TEST(test_bit_lib_increment_index) {
+    uint32_t index = 0;
+
+    // test increment
+    for(uint32_t i = 0; i < 31; ++i) {
+        bit_lib_increment_index(index, 32);
+        mu_assert_int_eq(i + 1, index);
+    }
+
+    // test wrap around
+    for(uint32_t i = 0; i < 512; ++i) {
+        bit_lib_increment_index(index, 32);
+        mu_assert_int_less_than(32, index);
+    }
+}
+
+MU_TEST(test_bit_lib_is_set) {
+    uint32_t value = 0x0000FFFF;
+
+    for(uint32_t i = 0; i < 16; ++i) {
+        mu_check(bit_lib_bit_is_set(value, i));
+        mu_check(!bit_lib_bit_is_not_set(value, i));
+    }
+
+    for(uint32_t i = 16; i < 32; ++i) {
+        mu_check(!bit_lib_bit_is_set(value, i));
+        mu_check(bit_lib_bit_is_not_set(value, i));
+    }
+}
+
+MU_TEST(test_bit_lib_push) {
+#define TEST_BIT_LIB_PUSH_DATA_SIZE 4
+    uint8_t data[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0};
+    uint8_t expected_data_1[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x0F, 0xFF};
+    uint8_t expected_data_2[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0xFF, 0xF0, 0x00};
+    uint8_t expected_data_3[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0x00, 0x00, 0xFF};
+    uint8_t expected_data_4[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF};
+    uint8_t expected_data_5[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x00, 0x00};
+    uint8_t expected_data_6[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xCC, 0xCC, 0xCC, 0xCC};
+
+    for(uint32_t i = 0; i < 12; ++i) {
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
+    }
+    mu_assert_mem_eq(expected_data_1, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
+
+    for(uint32_t i = 0; i < 12; ++i) {
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
+    }
+    mu_assert_mem_eq(expected_data_2, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
+
+    for(uint32_t i = 0; i < 4; ++i) {
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
+    }
+    for(uint32_t i = 0; i < 8; ++i) {
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
+    }
+    mu_assert_mem_eq(expected_data_3, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
+
+    for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) {
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
+    }
+    mu_assert_mem_eq(expected_data_4, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
+
+    for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) {
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
+    }
+    mu_assert_mem_eq(expected_data_5, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
+
+    for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 2; ++i) {
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
+        bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
+    }
+    mu_assert_mem_eq(expected_data_6, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
+}
+
+MU_TEST(test_bit_lib_set_bit) {
+    uint8_t value[2] = {0x00, 0xFF};
+    bit_lib_set_bit(value, 15, false);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFE}), 2);
+    bit_lib_set_bit(value, 14, false);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFC}), 2);
+    bit_lib_set_bit(value, 13, false);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF8}), 2);
+    bit_lib_set_bit(value, 12, false);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF0}), 2);
+    bit_lib_set_bit(value, 11, false);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xE0}), 2);
+    bit_lib_set_bit(value, 10, false);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xC0}), 2);
+    bit_lib_set_bit(value, 9, false);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x80}), 2);
+    bit_lib_set_bit(value, 8, false);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x00}), 2);
+
+    bit_lib_set_bit(value, 7, true);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x01, 0x00}), 2);
+    bit_lib_set_bit(value, 6, true);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x03, 0x00}), 2);
+    bit_lib_set_bit(value, 5, true);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x07, 0x00}), 2);
+    bit_lib_set_bit(value, 4, true);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x0F, 0x00}), 2);
+    bit_lib_set_bit(value, 3, true);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x1F, 0x00}), 2);
+    bit_lib_set_bit(value, 2, true);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x3F, 0x00}), 2);
+    bit_lib_set_bit(value, 1, true);
+    mu_assert_mem_eq(value, ((uint8_t[]){0x7F, 0x00}), 2);
+    bit_lib_set_bit(value, 0, true);
+    mu_assert_mem_eq(value, ((uint8_t[]){0xFF, 0x00}), 2);
+}
+
+MU_TEST(test_bit_lib_set_bits) {
+    uint8_t value[2] = {0b00000000, 0b11111111};
+    // set 4 bits to 0b0100 from 12 index
+    bit_lib_set_bits(value, 12, 0b0100, 4);
+    //                                                    [0100]
+    mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11110100}), 2);
+
+    // set 2 bits to 0b11 from 11 index
+    bit_lib_set_bits(value, 11, 0b11, 2);
+    //                                                    [11]
+    mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11111100}), 2);
+
+    // set 3 bits to 0b111 from 0 index
+    bit_lib_set_bits(value, 0, 0b111, 3);
+    //                                    [111]
+    mu_assert_mem_eq(value, ((uint8_t[]){0b11100000, 0b11111100}), 2);
+
+    // set 8 bits to 0b11111000 from 3 index
+    bit_lib_set_bits(value, 3, 0b11111000, 8);
+    //                                       [11111    000]
+    mu_assert_mem_eq(value, ((uint8_t[]){0b11111111, 0b00011100}), 2);
+}
+
+MU_TEST(test_bit_lib_get_bit) {
+    uint8_t value[2] = {0b00000000, 0b11111111};
+    for(uint32_t i = 0; i < 8; ++i) {
+        mu_check(bit_lib_get_bit(value, i) == false);
+    }
+    for(uint32_t i = 8; i < 16; ++i) {
+        mu_check(bit_lib_get_bit(value, i) == true);
+    }
+}
+
+MU_TEST(test_bit_lib_get_bits) {
+    uint8_t value[2] = {0b00000000, 0b11111111};
+    mu_assert_int_eq(0b00000000, bit_lib_get_bits(value, 0, 8));
+    mu_assert_int_eq(0b00000001, bit_lib_get_bits(value, 1, 8));
+    mu_assert_int_eq(0b00000011, bit_lib_get_bits(value, 2, 8));
+    mu_assert_int_eq(0b00000111, bit_lib_get_bits(value, 3, 8));
+    mu_assert_int_eq(0b00001111, bit_lib_get_bits(value, 4, 8));
+    mu_assert_int_eq(0b00011111, bit_lib_get_bits(value, 5, 8));
+    mu_assert_int_eq(0b00111111, bit_lib_get_bits(value, 6, 8));
+    mu_assert_int_eq(0b01111111, bit_lib_get_bits(value, 7, 8));
+    mu_assert_int_eq(0b11111111, bit_lib_get_bits(value, 8, 8));
+}
+
+MU_TEST(test_bit_lib_get_bits_16) {
+    uint8_t value[2] = {0b00001001, 0b10110001};
+    mu_assert_int_eq(0b0, bit_lib_get_bits_16(value, 0, 1));
+    mu_assert_int_eq(0b00, bit_lib_get_bits_16(value, 0, 2));
+    mu_assert_int_eq(0b000, bit_lib_get_bits_16(value, 0, 3));
+    mu_assert_int_eq(0b0000, bit_lib_get_bits_16(value, 0, 4));
+    mu_assert_int_eq(0b00001, bit_lib_get_bits_16(value, 0, 5));
+    mu_assert_int_eq(0b000010, bit_lib_get_bits_16(value, 0, 6));
+    mu_assert_int_eq(0b0000100, bit_lib_get_bits_16(value, 0, 7));
+    mu_assert_int_eq(0b00001001, bit_lib_get_bits_16(value, 0, 8));
+    mu_assert_int_eq(0b000010011, bit_lib_get_bits_16(value, 0, 9));
+    mu_assert_int_eq(0b0000100110, bit_lib_get_bits_16(value, 0, 10));
+    mu_assert_int_eq(0b00001001101, bit_lib_get_bits_16(value, 0, 11));
+    mu_assert_int_eq(0b000010011011, bit_lib_get_bits_16(value, 0, 12));
+    mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_16(value, 0, 13));
+    mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_16(value, 0, 14));
+    mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_16(value, 0, 15));
+    mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_16(value, 0, 16));
+}
+
+MU_TEST(test_bit_lib_get_bits_32) {
+    uint8_t value[4] = {0b00001001, 0b10110001, 0b10001100, 0b01100010};
+    mu_assert_int_eq(0b0, bit_lib_get_bits_32(value, 0, 1));
+    mu_assert_int_eq(0b00, bit_lib_get_bits_32(value, 0, 2));
+    mu_assert_int_eq(0b000, bit_lib_get_bits_32(value, 0, 3));
+    mu_assert_int_eq(0b0000, bit_lib_get_bits_32(value, 0, 4));
+    mu_assert_int_eq(0b00001, bit_lib_get_bits_32(value, 0, 5));
+    mu_assert_int_eq(0b000010, bit_lib_get_bits_32(value, 0, 6));
+    mu_assert_int_eq(0b0000100, bit_lib_get_bits_32(value, 0, 7));
+    mu_assert_int_eq(0b00001001, bit_lib_get_bits_32(value, 0, 8));
+    mu_assert_int_eq(0b000010011, bit_lib_get_bits_32(value, 0, 9));
+    mu_assert_int_eq(0b0000100110, bit_lib_get_bits_32(value, 0, 10));
+    mu_assert_int_eq(0b00001001101, bit_lib_get_bits_32(value, 0, 11));
+    mu_assert_int_eq(0b000010011011, bit_lib_get_bits_32(value, 0, 12));
+    mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_32(value, 0, 13));
+    mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_32(value, 0, 14));
+    mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_32(value, 0, 15));
+    mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_32(value, 0, 16));
+    mu_assert_int_eq(0b00001001101100011, bit_lib_get_bits_32(value, 0, 17));
+    mu_assert_int_eq(0b000010011011000110, bit_lib_get_bits_32(value, 0, 18));
+    mu_assert_int_eq(0b0000100110110001100, bit_lib_get_bits_32(value, 0, 19));
+    mu_assert_int_eq(0b00001001101100011000, bit_lib_get_bits_32(value, 0, 20));
+    mu_assert_int_eq(0b000010011011000110001, bit_lib_get_bits_32(value, 0, 21));
+    mu_assert_int_eq(0b0000100110110001100011, bit_lib_get_bits_32(value, 0, 22));
+    mu_assert_int_eq(0b00001001101100011000110, bit_lib_get_bits_32(value, 0, 23));
+    mu_assert_int_eq(0b000010011011000110001100, bit_lib_get_bits_32(value, 0, 24));
+    mu_assert_int_eq(0b0000100110110001100011000, bit_lib_get_bits_32(value, 0, 25));
+    mu_assert_int_eq(0b00001001101100011000110001, bit_lib_get_bits_32(value, 0, 26));
+    mu_assert_int_eq(0b000010011011000110001100011, bit_lib_get_bits_32(value, 0, 27));
+    mu_assert_int_eq(0b0000100110110001100011000110, bit_lib_get_bits_32(value, 0, 28));
+    mu_assert_int_eq(0b00001001101100011000110001100, bit_lib_get_bits_32(value, 0, 29));
+    mu_assert_int_eq(0b000010011011000110001100011000, bit_lib_get_bits_32(value, 0, 30));
+    mu_assert_int_eq(0b0000100110110001100011000110001, bit_lib_get_bits_32(value, 0, 31));
+    mu_assert_int_eq(0b00001001101100011000110001100010, bit_lib_get_bits_32(value, 0, 32));
+}
+
+MU_TEST(test_bit_lib_test_parity_u32) {
+    // test even parity
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityEven), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityEven), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityEven), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityEven), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityEven), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityEven), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityEven), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityEven), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityEven), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityEven), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityEven), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityEven), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityEven), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityEven), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityEven), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityEven), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityEven), 1);
+
+    // test odd parity
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityOdd), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityOdd), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityOdd), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityOdd), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityOdd), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityOdd), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityOdd), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityOdd), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityOdd), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityOdd), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityOdd), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityOdd), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityOdd), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityOdd), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityOdd), 0);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityOdd), 1);
+    mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityOdd), 0);
+}
+
+MU_TEST(test_bit_lib_test_parity) {
+    // next data contains valid parity for 1-3 nibble and invalid for 4 nibble
+    uint8_t data_always_0_parity[2] = {0b11101110, 0b11101111};
+    uint8_t data_always_1_parity[2] = {0b00010001, 0b00010000};
+    uint8_t data_always_odd_parity[2] = {0b00000011, 0b11110111};
+    uint8_t data_always_even_parity[2] = {0b00010111, 0b10110011};
+
+    // test alawys 0 parity
+    mu_check(bit_lib_test_parity(data_always_0_parity, 0, 12, BitLibParityAlways0, 4));
+    mu_check(bit_lib_test_parity(data_always_0_parity, 4, 8, BitLibParityAlways0, 4));
+    mu_check(bit_lib_test_parity(data_always_0_parity, 8, 4, BitLibParityAlways0, 4));
+    mu_check(bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways0, 4));
+
+    mu_check(!bit_lib_test_parity(data_always_0_parity, 0, 16, BitLibParityAlways0, 4));
+    mu_check(!bit_lib_test_parity(data_always_0_parity, 4, 12, BitLibParityAlways0, 4));
+    mu_check(!bit_lib_test_parity(data_always_0_parity, 8, 8, BitLibParityAlways0, 4));
+    mu_check(!bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways0, 4));
+
+    // test alawys 1 parity
+    mu_check(bit_lib_test_parity(data_always_1_parity, 0, 12, BitLibParityAlways1, 4));
+    mu_check(bit_lib_test_parity(data_always_1_parity, 4, 8, BitLibParityAlways1, 4));
+    mu_check(bit_lib_test_parity(data_always_1_parity, 8, 4, BitLibParityAlways1, 4));
+    mu_check(bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways1, 4));
+
+    mu_check(!bit_lib_test_parity(data_always_1_parity, 0, 16, BitLibParityAlways1, 4));
+    mu_check(!bit_lib_test_parity(data_always_1_parity, 4, 12, BitLibParityAlways1, 4));
+    mu_check(!bit_lib_test_parity(data_always_1_parity, 8, 8, BitLibParityAlways1, 4));
+    mu_check(!bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways1, 4));
+
+    // test odd parity
+    mu_check(bit_lib_test_parity(data_always_odd_parity, 0, 12, BitLibParityOdd, 4));
+    mu_check(bit_lib_test_parity(data_always_odd_parity, 4, 8, BitLibParityOdd, 4));
+    mu_check(bit_lib_test_parity(data_always_odd_parity, 8, 4, BitLibParityOdd, 4));
+    mu_check(bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityOdd, 4));
+
+    mu_check(!bit_lib_test_parity(data_always_odd_parity, 0, 16, BitLibParityOdd, 4));
+    mu_check(!bit_lib_test_parity(data_always_odd_parity, 4, 12, BitLibParityOdd, 4));
+    mu_check(!bit_lib_test_parity(data_always_odd_parity, 8, 8, BitLibParityOdd, 4));
+    mu_check(!bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityOdd, 4));
+
+    // test even parity
+    mu_check(bit_lib_test_parity(data_always_even_parity, 0, 12, BitLibParityEven, 4));
+    mu_check(bit_lib_test_parity(data_always_even_parity, 4, 8, BitLibParityEven, 4));
+    mu_check(bit_lib_test_parity(data_always_even_parity, 8, 4, BitLibParityEven, 4));
+    mu_check(bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityEven, 4));
+
+    mu_check(!bit_lib_test_parity(data_always_even_parity, 0, 16, BitLibParityEven, 4));
+    mu_check(!bit_lib_test_parity(data_always_even_parity, 4, 12, BitLibParityEven, 4));
+    mu_check(!bit_lib_test_parity(data_always_even_parity, 8, 8, BitLibParityEven, 4));
+    mu_check(!bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityEven, 4));
+}
+
+MU_TEST(test_bit_lib_remove_bit_every_nth) {
+    // TODO: more tests
+    uint8_t data_i[1] = {0b00001111};
+    uint8_t data_o[1] = {0b00011111};
+    size_t length;
+
+    length = bit_lib_remove_bit_every_nth(data_i, 0, 8, 3);
+    mu_assert_int_eq(6, length);
+    mu_assert_mem_eq(data_o, data_i, 1);
+}
+
+MU_TEST(test_bit_lib_reverse_bits) {
+    uint8_t data_1_i[2] = {0b11001010, 0b00011111};
+    uint8_t data_1_o[2] = {0b11111000, 0b01010011};
+
+    // reverse bits [0..15]
+    bit_lib_reverse_bits(data_1_i, 0, 16);
+    mu_assert_mem_eq(data_1_o, data_1_i, 2);
+
+    uint8_t data_2_i[2] = {0b11001010, 0b00011111};
+    uint8_t data_2_o[2] = {0b11001000, 0b01011111};
+
+    // reverse bits [4..11]
+    bit_lib_reverse_bits(data_2_i, 4, 8);
+    mu_assert_mem_eq(data_2_o, data_2_i, 2);
+}
+
+MU_TEST(test_bit_lib_copy_bits) {
+    uint8_t data_1_i[2] = {0b11001010, 0b00011111};
+    uint8_t data_1_o[2] = {0};
+
+    // data_1_o[0..15] = data_1_i[0..15]
+    bit_lib_copy_bits(data_1_o, 0, 16, data_1_i, 0);
+    mu_assert_mem_eq(data_1_i, data_1_o, 2);
+
+    memset(data_1_o, 0, 2);
+    // data_1_o[4..11] = data_1_i[0..7]
+    bit_lib_copy_bits(data_1_o, 4, 8, data_1_i, 0);
+    mu_assert_mem_eq(((uint8_t[]){0b00001100, 0b10100000}), data_1_o, 2);
+}
+
+MU_TEST(test_bit_lib_get_bit_count) {
+    mu_assert_int_eq(0, bit_lib_get_bit_count(0));
+    mu_assert_int_eq(1, bit_lib_get_bit_count(0b1));
+    mu_assert_int_eq(1, bit_lib_get_bit_count(0b10));
+    mu_assert_int_eq(2, bit_lib_get_bit_count(0b11));
+    mu_assert_int_eq(4, bit_lib_get_bit_count(0b11000011));
+    mu_assert_int_eq(6, bit_lib_get_bit_count(0b11000011000011));
+    mu_assert_int_eq(8, bit_lib_get_bit_count(0b11111111));
+    mu_assert_int_eq(16, bit_lib_get_bit_count(0b11111110000000000000000111111111));
+    mu_assert_int_eq(32, bit_lib_get_bit_count(0b11111111111111111111111111111111));
+}
+
+MU_TEST(test_bit_lib_reverse_16_fast) {
+    mu_assert_int_eq(0b0000000000000000, bit_lib_reverse_16_fast(0b0000000000000000));
+    mu_assert_int_eq(0b1000000000000000, bit_lib_reverse_16_fast(0b0000000000000001));
+    mu_assert_int_eq(0b1100000000000000, bit_lib_reverse_16_fast(0b0000000000000011));
+    mu_assert_int_eq(0b0000100000001001, bit_lib_reverse_16_fast(0b1001000000010000));
+}
+
+MU_TEST(test_bit_lib_crc16) {
+    uint8_t data[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
+    uint8_t data_size = 9;
+
+    // Algorithm
+    // Check	Poly	Init	RefIn	RefOut	XorOut
+    // CRC-16/CCITT-FALSE
+    // 0x29B1	0x1021	0xFFFF	false	false	0x0000
+    mu_assert_int_eq(0x29B1, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0x0000));
+    // CRC-16/ARC
+    // 0xBB3D	0x8005	0x0000	true	true	0x0000
+    mu_assert_int_eq(0xBB3D, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0x0000));
+    // CRC-16/AUG-CCITT
+    // 0xE5CC	0x1021	0x1D0F	false	false	0x0000
+    mu_assert_int_eq(0xE5CC, bit_lib_crc16(data, data_size, 0x1021, 0x1D0F, false, false, 0x0000));
+    // CRC-16/BUYPASS
+    // 0xFEE8	0x8005	0x0000	false	false	0x0000
+    mu_assert_int_eq(0xFEE8, bit_lib_crc16(data, data_size, 0x8005, 0x0000, false, false, 0x0000));
+    // CRC-16/CDMA2000
+    // 0x4C06	0xC867	0xFFFF	false	false	0x0000
+    mu_assert_int_eq(0x4C06, bit_lib_crc16(data, data_size, 0xC867, 0xFFFF, false, false, 0x0000));
+    // CRC-16/DDS-110
+    // 0x9ECF	0x8005	0x800D	false	false	0x0000
+    mu_assert_int_eq(0x9ECF, bit_lib_crc16(data, data_size, 0x8005, 0x800D, false, false, 0x0000));
+    // CRC-16/DECT-R
+    // 0x007E	0x0589	0x0000	false	false	0x0001
+    mu_assert_int_eq(0x007E, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0001));
+    // CRC-16/DECT-X
+    // 0x007F	0x0589	0x0000	false	false	0x0000
+    mu_assert_int_eq(0x007F, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0000));
+    // CRC-16/DNP
+    // 0xEA82	0x3D65	0x0000	true	true	0xFFFF
+    mu_assert_int_eq(0xEA82, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, true, true, 0xFFFF));
+    // CRC-16/EN-13757
+    // 0xC2B7	0x3D65	0x0000	false	false	0xFFFF
+    mu_assert_int_eq(0xC2B7, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, false, false, 0xFFFF));
+    // CRC-16/GENIBUS
+    // 0xD64E	0x1021	0xFFFF	false	false	0xFFFF
+    mu_assert_int_eq(0xD64E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0xFFFF));
+    // CRC-16/MAXIM
+    // 0x44C2	0x8005	0x0000	true	true	0xFFFF
+    mu_assert_int_eq(0x44C2, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0xFFFF));
+    // CRC-16/MCRF4XX
+    // 0x6F91	0x1021	0xFFFF	true	true	0x0000
+    mu_assert_int_eq(0x6F91, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0x0000));
+    // CRC-16/RIELLO
+    // 0x63D0	0x1021	0xB2AA	true	true	0x0000
+    mu_assert_int_eq(0x63D0, bit_lib_crc16(data, data_size, 0x1021, 0xB2AA, true, true, 0x0000));
+    // CRC-16/T10-DIF
+    // 0xD0DB	0x8BB7	0x0000	false	false	0x0000
+    mu_assert_int_eq(0xD0DB, bit_lib_crc16(data, data_size, 0x8BB7, 0x0000, false, false, 0x0000));
+    // CRC-16/TELEDISK
+    // 0x0FB3	0xA097	0x0000	false	false	0x0000
+    mu_assert_int_eq(0x0FB3, bit_lib_crc16(data, data_size, 0xA097, 0x0000, false, false, 0x0000));
+    // CRC-16/TMS37157
+    // 0x26B1	0x1021	0x89EC	true	true	0x0000
+    mu_assert_int_eq(0x26B1, bit_lib_crc16(data, data_size, 0x1021, 0x89EC, true, true, 0x0000));
+    // CRC-16/USB
+    // 0xB4C8	0x8005	0xFFFF	true	true	0xFFFF
+    mu_assert_int_eq(0xB4C8, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0xFFFF));
+    // CRC-A
+    // 0xBF05	0x1021	0xC6C6	true	true	0x0000
+    mu_assert_int_eq(0xBF05, bit_lib_crc16(data, data_size, 0x1021, 0xC6C6, true, true, 0x0000));
+    // CRC-16/KERMIT
+    // 0x2189	0x1021	0x0000	true	true	0x0000
+    mu_assert_int_eq(0x2189, bit_lib_crc16(data, data_size, 0x1021, 0x0000, true, true, 0x0000));
+    // CRC-16/MODBUS
+    // 0x4B37	0x8005	0xFFFF	true	true	0x0000
+    mu_assert_int_eq(0x4B37, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0x0000));
+    // CRC-16/X-25
+    // 0x906E	0x1021	0xFFFF	true	true	0xFFFF
+    mu_assert_int_eq(0x906E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0xFFFF));
+    // CRC-16/XMODEM
+    // 0x31C3	0x1021	0x0000	false	false	0x0000
+    mu_assert_int_eq(0x31C3, bit_lib_crc16(data, data_size, 0x1021, 0x0000, false, false, 0x0000));
+}
+
+MU_TEST_SUITE(test_bit_lib) {
+    MU_RUN_TEST(test_bit_lib_increment_index);
+    MU_RUN_TEST(test_bit_lib_is_set);
+    MU_RUN_TEST(test_bit_lib_push);
+    MU_RUN_TEST(test_bit_lib_set_bit);
+    MU_RUN_TEST(test_bit_lib_set_bits);
+    MU_RUN_TEST(test_bit_lib_get_bit);
+    MU_RUN_TEST(test_bit_lib_get_bits);
+    MU_RUN_TEST(test_bit_lib_get_bits_16);
+    MU_RUN_TEST(test_bit_lib_get_bits_32);
+    MU_RUN_TEST(test_bit_lib_test_parity_u32);
+    MU_RUN_TEST(test_bit_lib_test_parity);
+    MU_RUN_TEST(test_bit_lib_remove_bit_every_nth);
+    MU_RUN_TEST(test_bit_lib_copy_bits);
+    MU_RUN_TEST(test_bit_lib_reverse_bits);
+    MU_RUN_TEST(test_bit_lib_get_bit_count);
+    MU_RUN_TEST(test_bit_lib_reverse_16_fast);
+    MU_RUN_TEST(test_bit_lib_crc16);
+}
+
+int run_minunit_test_bit_lib() {
+    MU_RUN_SUITE(test_bit_lib);
+    return MU_EXIT_CODE;
+}

+ 464 - 0
applications/unit_tests/lfrfid/lfrfid_protocols.c

@@ -0,0 +1,464 @@
+#include <furi.h>
+#include "../minunit.h"
+#include <toolbox/protocols/protocol_dict.h>
+#include <lfrfid/protocols/lfrfid_protocols.h>
+#include <toolbox/pulse_protocols/pulse_glue.h>
+
+#define LF_RFID_READ_TIMING_MULTIPLIER 8
+
+#define EM_TEST_DATA \
+    { 0x58, 0x00, 0x85, 0x64, 0x02 }
+#define EM_TEST_DATA_SIZE 5
+#define EM_TEST_EMULATION_TIMINGS_COUNT (64 * 2)
+
+const int8_t em_test_timings[EM_TEST_EMULATION_TIMINGS_COUNT] = {
+    32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, -32,
+    32,  32,  -32, -32, 32,  32,  -32, -32, 32,  32,  -32, -32, 32,  -32, 32,  -32, 32,  32,  -32,
+    -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32,
+    32,  32,  -32, -32, 32,  -32, 32,  -32, 32,  32,  -32, -32, 32,  32,  -32, -32, 32,  32,  -32,
+    -32, 32,  -32, 32,  32,  -32, 32,  -32, -32, 32,  -32, 32,  -32, 32,  32,  -32, -32, 32,  -32,
+    32,  32,  -32, -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,  32,  -32,
+    -32, 32,  32,  -32, -32, 32,  -32, 32,  -32, 32,  -32, 32,  -32, 32,
+};
+
+#define HID10301_TEST_DATA \
+    { 0x8D, 0x48, 0xA8 }
+#define HID10301_TEST_DATA_SIZE 3
+#define HID10301_TEST_EMULATION_TIMINGS_COUNT (541 * 2)
+
+const int8_t hid10301_test_timings[HID10301_TEST_EMULATION_TIMINGS_COUNT] = {
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+};
+
+#define IOPROX_XSF_TEST_DATA \
+    { 0x65, 0x01, 0x05, 0x39 }
+#define IOPROX_XSF_TEST_DATA_SIZE 4
+#define IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT (468 * 2)
+
+const int8_t ioprox_xsf_test_timings[IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] = {
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
+    4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
+    4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+    5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
+};
+
+#define INDALA26_EMULATION_TIMINGS_COUNT (1024 * 2)
+#define INDALA26_TEST_DATA \
+    { 0x3B, 0x73, 0x64, 0xA8 }
+#define INDALA26_TEST_DATA_SIZE 4
+
+const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = {
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1,
+    1,  -1, 1,  -1, 1,  -1, 1,  -1, -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,  -1, 1,
+    -1, 1,  -1, 1,  -1, 1,  -1, 1,
+};
+
+MU_TEST(test_lfrfid_protocol_em_read_simple) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100));
+    mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100));
+    mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100));
+
+    const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA;
+
+    protocol_dict_decoders_start(dict);
+
+    ProtocolId protocol = PROTOCOL_NO;
+    PulseGlue* pulse_glue = pulse_glue_alloc();
+
+    for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
+        bool pulse_pop = pulse_glue_push(
+            pulse_glue,
+            em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT] >= 0,
+            abs(em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT]) *
+                LF_RFID_READ_TIMING_MULTIPLIER);
+
+        if(pulse_pop) {
+            uint32_t length, period;
+            pulse_glue_pop(pulse_glue, &length, &period);
+
+            protocol = protocol_dict_decoders_feed(dict, true, period);
+            if(protocol != PROTOCOL_NO) break;
+
+            protocol = protocol_dict_decoders_feed(dict, false, length - period);
+            if(protocol != PROTOCOL_NO) break;
+        }
+    }
+
+    pulse_glue_free(pulse_glue);
+
+    mu_assert_int_eq(LFRFIDProtocolEM4100, protocol);
+    uint8_t received_data[EM_TEST_DATA_SIZE] = {0};
+    protocol_dict_get_data(dict, protocol, received_data, EM_TEST_DATA_SIZE);
+
+    mu_assert_mem_eq(data, received_data, EM_TEST_DATA_SIZE);
+
+    protocol_dict_free(dict);
+}
+
+MU_TEST(test_lfrfid_protocol_em_emulate_simple) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100));
+    mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100));
+    mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100));
+
+    const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA;
+
+    protocol_dict_set_data(dict, LFRFIDProtocolEM4100, data, EM_TEST_DATA_SIZE);
+    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolEM4100));
+
+    for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT; i++) {
+        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolEM4100);
+
+        if(level_duration_get_level(level_duration)) {
+            mu_assert_int_eq(em_test_timings[i], level_duration_get_duration(level_duration));
+        } else {
+            mu_assert_int_eq(em_test_timings[i], -level_duration_get_duration(level_duration));
+        }
+    }
+
+    protocol_dict_free(dict);
+}
+
+MU_TEST(test_lfrfid_protocol_h10301_read_simple) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    mu_assert_int_eq(
+        HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301));
+    mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301));
+    mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301));
+
+    const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA;
+
+    protocol_dict_decoders_start(dict);
+
+    ProtocolId protocol = PROTOCOL_NO;
+    PulseGlue* pulse_glue = pulse_glue_alloc();
+
+    for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
+        bool pulse_pop = pulse_glue_push(
+            pulse_glue,
+            hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT] >= 0,
+            abs(hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT]) *
+                LF_RFID_READ_TIMING_MULTIPLIER);
+
+        if(pulse_pop) {
+            uint32_t length, period;
+            pulse_glue_pop(pulse_glue, &length, &period);
+
+            protocol = protocol_dict_decoders_feed(dict, true, period);
+            if(protocol != PROTOCOL_NO) break;
+
+            protocol = protocol_dict_decoders_feed(dict, false, length - period);
+            if(protocol != PROTOCOL_NO) break;
+        }
+    }
+
+    pulse_glue_free(pulse_glue);
+
+    mu_assert_int_eq(LFRFIDProtocolH10301, protocol);
+    uint8_t received_data[HID10301_TEST_DATA_SIZE] = {0};
+    protocol_dict_get_data(dict, protocol, received_data, HID10301_TEST_DATA_SIZE);
+
+    mu_assert_mem_eq(data, received_data, HID10301_TEST_DATA_SIZE);
+
+    protocol_dict_free(dict);
+}
+
+MU_TEST(test_lfrfid_protocol_h10301_emulate_simple) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    mu_assert_int_eq(
+        HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301));
+    mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301));
+    mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301));
+
+    const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA;
+
+    protocol_dict_set_data(dict, LFRFIDProtocolH10301, data, HID10301_TEST_DATA_SIZE);
+    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolH10301));
+
+    for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT; i++) {
+        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolH10301);
+
+        if(level_duration_get_level(level_duration)) {
+            mu_assert_int_eq(
+                hid10301_test_timings[i], level_duration_get_duration(level_duration));
+        } else {
+            mu_assert_int_eq(
+                hid10301_test_timings[i], -level_duration_get_duration(level_duration));
+        }
+    }
+
+    protocol_dict_free(dict);
+}
+
+MU_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    mu_assert_int_eq(
+        IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF));
+    mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF));
+    mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF));
+
+    const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA;
+
+    protocol_dict_decoders_start(dict);
+
+    ProtocolId protocol = PROTOCOL_NO;
+    PulseGlue* pulse_glue = pulse_glue_alloc();
+
+    for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
+        bool pulse_pop = pulse_glue_push(
+            pulse_glue,
+            ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] >= 0,
+            abs(ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT]) *
+                LF_RFID_READ_TIMING_MULTIPLIER);
+
+        if(pulse_pop) {
+            uint32_t length, period;
+            pulse_glue_pop(pulse_glue, &length, &period);
+
+            protocol = protocol_dict_decoders_feed(dict, true, period);
+            if(protocol != PROTOCOL_NO) break;
+
+            protocol = protocol_dict_decoders_feed(dict, false, length - period);
+            if(protocol != PROTOCOL_NO) break;
+        }
+    }
+
+    pulse_glue_free(pulse_glue);
+
+    mu_assert_int_eq(LFRFIDProtocolIOProxXSF, protocol);
+    uint8_t received_data[IOPROX_XSF_TEST_DATA_SIZE] = {0};
+    protocol_dict_get_data(dict, protocol, received_data, IOPROX_XSF_TEST_DATA_SIZE);
+
+    mu_assert_mem_eq(data, received_data, IOPROX_XSF_TEST_DATA_SIZE);
+
+    protocol_dict_free(dict);
+}
+
+MU_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    mu_assert_int_eq(
+        IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF));
+    mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF));
+    mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF));
+
+    const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA;
+
+    protocol_dict_set_data(dict, LFRFIDProtocolIOProxXSF, data, IOPROX_XSF_TEST_DATA_SIZE);
+    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIOProxXSF));
+
+    for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT; i++) {
+        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIOProxXSF);
+
+        if(level_duration_get_level(level_duration)) {
+            mu_assert_int_eq(
+                ioprox_xsf_test_timings[i], level_duration_get_duration(level_duration));
+        } else {
+            mu_assert_int_eq(
+                ioprox_xsf_test_timings[i], -level_duration_get_duration(level_duration));
+        }
+    }
+
+    protocol_dict_free(dict);
+}
+
+MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) {
+    ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
+    mu_assert_int_eq(
+        INDALA26_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIndala26));
+    mu_assert_string_eq("Indala26", protocol_dict_get_name(dict, LFRFIDProtocolIndala26));
+    mu_assert_string_eq("Motorola", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIndala26));
+
+    const uint8_t data[INDALA26_TEST_DATA_SIZE] = INDALA26_TEST_DATA;
+
+    protocol_dict_set_data(dict, LFRFIDProtocolIndala26, data, INDALA26_TEST_DATA_SIZE);
+    mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala26));
+
+    for(size_t i = 0; i < INDALA26_EMULATION_TIMINGS_COUNT; i++) {
+        LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala26);
+
+        if(level_duration_get_level(level_duration)) {
+            mu_assert_int_eq(
+                indala26_test_timings[i], level_duration_get_duration(level_duration));
+        } else {
+            mu_assert_int_eq(
+                indala26_test_timings[i], -level_duration_get_duration(level_duration));
+        }
+    }
+
+    protocol_dict_free(dict);
+}
+
+MU_TEST_SUITE(test_lfrfid_protocols_suite) {
+    MU_RUN_TEST(test_lfrfid_protocol_em_read_simple);
+    MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple);
+
+    MU_RUN_TEST(test_lfrfid_protocol_h10301_read_simple);
+    MU_RUN_TEST(test_lfrfid_protocol_h10301_emulate_simple);
+
+    MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple);
+    MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple);
+
+    MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple);
+}
+
+int run_minunit_test_lfrfid_protocols() {
+    MU_RUN_SUITE(test_lfrfid_protocols_suite);
+    return MU_EXIT_CODE;
+}

+ 101 - 73
applications/unit_tests/minunit.h

@@ -151,46 +151,46 @@ void minunit_print_fail(const char* error);
 #define MU_EXIT_CODE minunit_fail
 #define MU_EXIT_CODE minunit_fail
 
 
 /*  Assertions */
 /*  Assertions */
-#define mu_check(test)                     \
-    MU__SAFE_BLOCK(                        \
-        minunit_assert++; if(!(test)) {    \
-            snprintf(                      \
-                minunit_last_message,      \
-                MINUNIT_MESSAGE_LEN,       \
-                "%s failed:\n\t%s:%d: %s", \
-                __func__,                  \
-                __FILE__,                  \
-                __LINE__,                  \
-                #test);                    \
-            minunit_status = 1;            \
-            return;                        \
+#define mu_check(test)                       \
+    MU__SAFE_BLOCK(                          \
+        minunit_assert++; if(!(test)) {      \
+            snprintf(                        \
+                minunit_last_message,        \
+                MINUNIT_MESSAGE_LEN,         \
+                "%s failed:\r\n\t%s:%d: %s", \
+                __func__,                    \
+                __FILE__,                    \
+                __LINE__,                    \
+                #test);                      \
+            minunit_status = 1;              \
+            return;                          \
         } else { minunit_print_progress(); })
         } else { minunit_print_progress(); })
 
 
-#define mu_fail(message)                          \
-    MU__SAFE_BLOCK(minunit_assert++; snprintf(    \
-                       minunit_last_message,      \
-                       MINUNIT_MESSAGE_LEN,       \
-                       "%s failed:\n\t%s:%d: %s", \
-                       __func__,                  \
-                       __FILE__,                  \
-                       __LINE__,                  \
-                       message);                  \
-                   minunit_status = 1;            \
+#define mu_fail(message)                            \
+    MU__SAFE_BLOCK(minunit_assert++; snprintf(      \
+                       minunit_last_message,        \
+                       MINUNIT_MESSAGE_LEN,         \
+                       "%s failed:\r\n\t%s:%d: %s", \
+                       __func__,                    \
+                       __FILE__,                    \
+                       __LINE__,                    \
+                       message);                    \
+                   minunit_status = 1;              \
                    return;)
                    return;)
 
 
-#define mu_assert(test, message)           \
-    MU__SAFE_BLOCK(                        \
-        minunit_assert++; if(!(test)) {    \
-            snprintf(                      \
-                minunit_last_message,      \
-                MINUNIT_MESSAGE_LEN,       \
-                "%s failed:\n\t%s:%d: %s", \
-                __func__,                  \
-                __FILE__,                  \
-                __LINE__,                  \
-                message);                  \
-            minunit_status = 1;            \
-            return;                        \
+#define mu_assert(test, message)             \
+    MU__SAFE_BLOCK(                          \
+        minunit_assert++; if(!(test)) {      \
+            snprintf(                        \
+                minunit_last_message,        \
+                MINUNIT_MESSAGE_LEN,         \
+                "%s failed:\r\n\t%s:%d: %s", \
+                __func__,                    \
+                __FILE__,                    \
+                __LINE__,                    \
+                message);                    \
+            minunit_status = 1;              \
+            return;                          \
         } else { minunit_print_progress(); })
         } else { minunit_print_progress(); })
 
 
 #define mu_assert_int_eq(expected, result)                                                  \
 #define mu_assert_int_eq(expected, result)                                                  \
@@ -201,7 +201,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                       \
             snprintf(                                                                       \
                 minunit_last_message,                                                       \
                 minunit_last_message,                                                       \
                 MINUNIT_MESSAGE_LEN,                                                        \
                 MINUNIT_MESSAGE_LEN,                                                        \
-                "%s failed:\n\t%s:%d: %d expected but was %d",                              \
+                "%s failed:\r\n\t%s:%d: %d expected but was %d",                            \
                 __func__,                                                                   \
                 __func__,                                                                   \
                 __FILE__,                                                                   \
                 __FILE__,                                                                   \
                 __LINE__,                                                                   \
                 __LINE__,                                                                   \
@@ -219,7 +219,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                       \
             snprintf(                                                                       \
                 minunit_last_message,                                                       \
                 minunit_last_message,                                                       \
                 MINUNIT_MESSAGE_LEN,                                                        \
                 MINUNIT_MESSAGE_LEN,                                                        \
-                "%s failed:\n\t%s:%d: expected different results but both were %d",         \
+                "%s failed:\r\n\t%s:%d: expected different results but both were %d",       \
                 __func__,                                                                   \
                 __func__,                                                                   \
                 __FILE__,                                                                   \
                 __FILE__,                                                                   \
                 __LINE__,                                                                   \
                 __LINE__,                                                                   \
@@ -236,7 +236,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                  \
             snprintf(                                                                  \
                 minunit_last_message,                                                  \
                 minunit_last_message,                                                  \
                 MINUNIT_MESSAGE_LEN,                                                   \
                 MINUNIT_MESSAGE_LEN,                                                   \
-                "%s failed:\n\t%s:%d: %d <= %d",                                       \
+                "%s failed:\r\n\t%s:%d: %d <= %d",                                     \
                 __func__,                                                              \
                 __func__,                                                              \
                 __FILE__,                                                              \
                 __FILE__,                                                              \
                 __LINE__,                                                              \
                 __LINE__,                                                              \
@@ -254,7 +254,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                  \
             snprintf(                                                                  \
                 minunit_last_message,                                                  \
                 minunit_last_message,                                                  \
                 MINUNIT_MESSAGE_LEN,                                                   \
                 MINUNIT_MESSAGE_LEN,                                                   \
-                "%s failed:\n\t%s:%d: %d >= %d",                                       \
+                "%s failed:\r\n\t%s:%d: %d >= %d",                                     \
                 __func__,                                                              \
                 __func__,                                                              \
                 __FILE__,                                                              \
                 __FILE__,                                                              \
                 __LINE__,                                                              \
                 __LINE__,                                                              \
@@ -274,7 +274,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                              \
             snprintf(                                                              \
                 minunit_last_message,                                              \
                 minunit_last_message,                                              \
                 MINUNIT_MESSAGE_LEN,                                               \
                 MINUNIT_MESSAGE_LEN,                                               \
-                "%s failed:\n\t%s:%d: %d was not between (inclusive) %d and %d",   \
+                "%s failed:\r\n\t%s:%d: %d was not between (inclusive) %d and %d", \
                 __func__,                                                          \
                 __func__,                                                          \
                 __FILE__,                                                          \
                 __FILE__,                                                          \
                 __LINE__,                                                          \
                 __LINE__,                                                          \
@@ -302,7 +302,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                    \
             snprintf(                                                                    \
                 minunit_last_message,                                                    \
                 minunit_last_message,                                                    \
                 MINUNIT_MESSAGE_LEN,                                                     \
                 MINUNIT_MESSAGE_LEN,                                                     \
-                "%s failed:\n\t%s:%d: expected to be one of %s but was %d",              \
+                "%s failed:\r\n\t%s:%d: expected to be one of %s but was %d",            \
                 __func__,                                                                \
                 __func__,                                                                \
                 __FILE__,                                                                \
                 __FILE__,                                                                \
                 __LINE__,                                                                \
                 __LINE__,                                                                \
@@ -321,7 +321,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                             \
             snprintf(                                                                             \
                 minunit_last_message,                                                             \
                 minunit_last_message,                                                             \
                 MINUNIT_MESSAGE_LEN,                                                              \
                 MINUNIT_MESSAGE_LEN,                                                              \
-                "%s failed:\n\t%s:%d: %.*g expected but was %.*g",                                \
+                "%s failed:\r\n\t%s:%d: %.*g expected but was %.*g",                              \
                 __func__,                                                                         \
                 __func__,                                                                         \
                 __FILE__,                                                                         \
                 __FILE__,                                                                         \
                 __LINE__,                                                                         \
                 __LINE__,                                                                         \
@@ -341,7 +341,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                        \
             snprintf(                                                                        \
                 minunit_last_message,                                                        \
                 minunit_last_message,                                                        \
                 MINUNIT_MESSAGE_LEN,                                                         \
                 MINUNIT_MESSAGE_LEN,                                                         \
-                "%s failed:\n\t%s:%d: %f <= %f",                                             \
+                "%s failed:\r\n\t%s:%d: %f <= %f",                                           \
                 __func__,                                                                    \
                 __func__,                                                                    \
                 __FILE__,                                                                    \
                 __FILE__,                                                                    \
                 __LINE__,                                                                    \
                 __LINE__,                                                                    \
@@ -359,7 +359,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                        \
             snprintf(                                                                        \
                 minunit_last_message,                                                        \
                 minunit_last_message,                                                        \
                 MINUNIT_MESSAGE_LEN,                                                         \
                 MINUNIT_MESSAGE_LEN,                                                         \
-                "%s failed:\n\t%s:%d: %f >= %f",                                             \
+                "%s failed:\r\n\t%s:%d: %f >= %f",                                           \
                 __func__,                                                                    \
                 __func__,                                                                    \
                 __FILE__,                                                                    \
                 __FILE__,                                                                    \
                 __LINE__,                                                                    \
                 __LINE__,                                                                    \
@@ -379,7 +379,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                       \
             snprintf(                                                                       \
                 minunit_last_message,                                                       \
                 minunit_last_message,                                                       \
                 MINUNIT_MESSAGE_LEN,                                                        \
                 MINUNIT_MESSAGE_LEN,                                                        \
-                "%s failed:\n\t%s:%d: %f was not between (inclusive) %f and %f",            \
+                "%s failed:\r\n\t%s:%d: %f was not between (inclusive) %f and %f",          \
                 __func__,                                                                   \
                 __func__,                                                                   \
                 __FILE__,                                                                   \
                 __FILE__,                                                                   \
                 __LINE__,                                                                   \
                 __LINE__,                                                                   \
@@ -400,7 +400,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                                 \
             snprintf(                                                                 \
                 minunit_last_message,                                                 \
                 minunit_last_message,                                                 \
                 MINUNIT_MESSAGE_LEN,                                                  \
                 MINUNIT_MESSAGE_LEN,                                                  \
-                "%s failed:\n\t%s:%d: '%s' expected but was '%s'",                    \
+                "%s failed:\r\n\t%s:%d: '%s' expected but was '%s'",                  \
                 __func__,                                                             \
                 __func__,                                                             \
                 __FILE__,                                                             \
                 __FILE__,                                                             \
                 __LINE__,                                                             \
                 __LINE__,                                                             \
@@ -410,13 +410,41 @@ void minunit_print_fail(const char* error);
             return;                                                                   \
             return;                                                                   \
         } else { minunit_print_progress(); })
         } else { minunit_print_progress(); })
 
 
+#define mu_assert_mem_eq(expected, result, size)                                   \
+    MU__SAFE_BLOCK(                                                                \
+        const void* minunit_tmp_e = expected; const void* minunit_tmp_r = result;  \
+        minunit_assert++;                                                          \
+        if(memcmp(minunit_tmp_e, minunit_tmp_r, size)) {                           \
+            snprintf(                                                              \
+                minunit_last_message,                                              \
+                MINUNIT_MESSAGE_LEN,                                               \
+                "%s failed:\r\n\t%s:%d: mem not equal\r\n\tEXP  RES",              \
+                __func__,                                                          \
+                __FILE__,                                                          \
+                __LINE__);                                                         \
+            for(size_t __index = 0; __index < size; __index++) {                   \
+                if(strlen(minunit_last_message) > MINUNIT_MESSAGE_LEN - 20) break; \
+                uint8_t __e = ((uint8_t*)minunit_tmp_e)[__index];                  \
+                uint8_t __r = ((uint8_t*)minunit_tmp_r)[__index];                  \
+                snprintf(                                                          \
+                    minunit_last_message + strlen(minunit_last_message),           \
+                    MINUNIT_MESSAGE_LEN - strlen(minunit_last_message),            \
+                    "\r\n\t%02X %s %02X",                                          \
+                    __e,                                                           \
+                    ((__e == __r) ? ".." : "!="),                                  \
+                    __r);                                                          \
+            }                                                                      \
+            minunit_status = 1;                                                    \
+            return;                                                                \
+        } else { minunit_print_progress(); })
+
 #define mu_assert_null(result)                                                    \
 #define mu_assert_null(result)                                                    \
     MU__SAFE_BLOCK(                                                               \
     MU__SAFE_BLOCK(                                                               \
         minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \
         minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \
             snprintf(                                                             \
             snprintf(                                                             \
                 minunit_last_message,                                             \
                 minunit_last_message,                                             \
                 MINUNIT_MESSAGE_LEN,                                              \
                 MINUNIT_MESSAGE_LEN,                                              \
-                "%s failed:\n\t%s:%d: Expected result was not NULL",              \
+                "%s failed:\r\n\t%s:%d: Expected result was not NULL",            \
                 __func__,                                                         \
                 __func__,                                                         \
                 __FILE__,                                                         \
                 __FILE__,                                                         \
                 __LINE__);                                                        \
                 __LINE__);                                                        \
@@ -430,7 +458,7 @@ void minunit_print_fail(const char* error);
             snprintf(                                                             \
             snprintf(                                                             \
                 minunit_last_message,                                             \
                 minunit_last_message,                                             \
                 MINUNIT_MESSAGE_LEN,                                              \
                 MINUNIT_MESSAGE_LEN,                                              \
-                "%s failed:\n\t%s:%d: Expected result was not NULL",              \
+                "%s failed:\r\n\t%s:%d: Expected result was not NULL",            \
                 __func__,                                                         \
                 __func__,                                                         \
                 __FILE__,                                                         \
                 __FILE__,                                                         \
                 __LINE__);                                                        \
                 __LINE__);                                                        \
@@ -438,32 +466,32 @@ void minunit_print_fail(const char* error);
             return;                                                               \
             return;                                                               \
         })
         })
 
 
-#define mu_assert_pointers_eq(pointer1, pointer2)                                                  \
-    MU__SAFE_BLOCK(                                                                                \
-        minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else {            \
-            snprintf(                                                                              \
-                minunit_last_message,                                                              \
-                MINUNIT_MESSAGE_LEN,                                                               \
-                "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", \
-                __func__,                                                                          \
-                __FILE__,                                                                          \
-                __LINE__);                                                                         \
-            minunit_status = 1;                                                                    \
-            return;                                                                                \
+#define mu_assert_pointers_eq(pointer1, pointer2)                                                    \
+    MU__SAFE_BLOCK(                                                                                  \
+        minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else {              \
+            snprintf(                                                                                \
+                minunit_last_message,                                                                \
+                MINUNIT_MESSAGE_LEN,                                                                 \
+                "%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \
+                __func__,                                                                            \
+                __FILE__,                                                                            \
+                __LINE__);                                                                           \
+            minunit_status = 1;                                                                      \
+            return;                                                                                  \
         })
         })
 
 
-#define mu_assert_pointers_not_eq(pointer1, pointer2)                                              \
-    MU__SAFE_BLOCK(                                                                                \
-        minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else {            \
-            snprintf(                                                                              \
-                minunit_last_message,                                                              \
-                MINUNIT_MESSAGE_LEN,                                                               \
-                "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", \
-                __func__,                                                                          \
-                __FILE__,                                                                          \
-                __LINE__);                                                                         \
-            minunit_status = 1;                                                                    \
-            return;                                                                                \
+#define mu_assert_pointers_not_eq(pointer1, pointer2)                                                \
+    MU__SAFE_BLOCK(                                                                                  \
+        minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else {              \
+            snprintf(                                                                                \
+                minunit_last_message,                                                                \
+                MINUNIT_MESSAGE_LEN,                                                                 \
+                "%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \
+                __func__,                                                                            \
+                __FILE__,                                                                            \
+                __LINE__);                                                                           \
+            minunit_status = 1;                                                                      \
+            return;                                                                                  \
         })
         })
 
 
 /*
 /*

+ 222 - 0
applications/unit_tests/protocol_dict/protocol_dict_test.c

@@ -0,0 +1,222 @@
+#include <furi.h>
+#include "../minunit.h"
+#include <toolbox/protocols/protocol_dict.h>
+
+typedef enum {
+    TestDictProtocol0,
+    TestDictProtocol1,
+
+    TestDictProtocolMax,
+} TestDictProtocols;
+
+/*********************** PROTOCOL 0 START ***********************/
+
+typedef struct {
+    uint32_t data;
+    size_t encoder_counter;
+} Protocol0Data;
+
+static const uint32_t protocol_0_decoder_result = 0xDEADBEEF;
+
+static void* protocol_0_alloc() {
+    void* data = malloc(sizeof(Protocol0Data));
+    return data;
+}
+
+static void protocol_0_free(Protocol0Data* data) {
+    free(data);
+}
+
+static uint8_t* protocol_0_get_data(Protocol0Data* data) {
+    return (uint8_t*)&data->data;
+}
+
+static void protocol_0_decoder_start(Protocol0Data* data) {
+    data->data = 0;
+}
+
+static bool protocol_0_decoder_feed(Protocol0Data* data, bool level, uint32_t duration) {
+    if(level && duration == 666) {
+        data->data = protocol_0_decoder_result;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static bool protocol_0_encoder_start(Protocol0Data* data) {
+    data->encoder_counter = 0;
+    return true;
+}
+
+static LevelDuration protocol_0_encoder_yield(Protocol0Data* data) {
+    data->encoder_counter++;
+    return level_duration_make(data->encoder_counter % 2, data->data);
+}
+
+/*********************** PROTOCOL 1 START ***********************/
+
+typedef struct {
+    uint64_t data;
+    size_t encoder_counter;
+} Protocol1Data;
+
+static const uint64_t protocol_1_decoder_result = 0x1234567890ABCDEF;
+
+static void* protocol_1_alloc() {
+    void* data = malloc(sizeof(Protocol1Data));
+    return data;
+}
+
+static void protocol_1_free(Protocol1Data* data) {
+    free(data);
+}
+
+static uint8_t* protocol_1_get_data(Protocol1Data* data) {
+    return (uint8_t*)&data->data;
+}
+
+static void protocol_1_decoder_start(Protocol1Data* data) {
+    data->data = 0;
+}
+
+static bool protocol_1_decoder_feed(Protocol1Data* data, bool level, uint32_t duration) {
+    if(level && duration == 543) {
+        data->data = 0x1234567890ABCDEF;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static bool protocol_1_encoder_start(Protocol1Data* data) {
+    data->encoder_counter = 0;
+    return true;
+}
+
+static LevelDuration protocol_1_encoder_yield(Protocol1Data* data) {
+    data->encoder_counter++;
+    return level_duration_make(!(data->encoder_counter % 2), 100);
+}
+
+/*********************** PROTOCOLS DESCRIPTION ***********************/
+static const ProtocolBase protocol_0 = {
+    .name = "Protocol 0",
+    .manufacturer = "Manufacturer 0",
+    .data_size = 4,
+    .alloc = (ProtocolAlloc)protocol_0_alloc,
+    .free = (ProtocolFree)protocol_0_free,
+    .get_data = (ProtocolGetData)protocol_0_get_data,
+    .decoder =
+        {
+            .start = (ProtocolDecoderStart)protocol_0_decoder_start,
+            .feed = (ProtocolDecoderFeed)protocol_0_decoder_feed,
+        },
+    .encoder =
+        {
+            .start = (ProtocolEncoderStart)protocol_0_encoder_start,
+            .yield = (ProtocolEncoderYield)protocol_0_encoder_yield,
+        },
+};
+
+static const ProtocolBase protocol_1 = {
+    .name = "Protocol 1",
+    .manufacturer = "Manufacturer 1",
+    .data_size = 8,
+    .alloc = (ProtocolAlloc)protocol_1_alloc,
+    .free = (ProtocolFree)protocol_1_free,
+    .get_data = (ProtocolGetData)protocol_1_get_data,
+    .decoder =
+        {
+            .start = (ProtocolDecoderStart)protocol_1_decoder_start,
+            .feed = (ProtocolDecoderFeed)protocol_1_decoder_feed,
+        },
+    .encoder =
+        {
+            .start = (ProtocolEncoderStart)protocol_1_encoder_start,
+            .yield = (ProtocolEncoderYield)protocol_1_encoder_yield,
+        },
+};
+
+static const ProtocolBase* test_protocols_base[] = {
+    [TestDictProtocol0] = &protocol_0,
+    [TestDictProtocol1] = &protocol_1,
+};
+
+MU_TEST(test_protocol_dict) {
+    ProtocolDict* dict = protocol_dict_alloc(test_protocols_base, TestDictProtocolMax);
+    size_t max_data_size = protocol_dict_get_max_data_size(dict);
+    mu_assert_int_eq(8, max_data_size);
+    uint8_t* data = malloc(max_data_size);
+
+    protocol_dict_decoders_start(dict);
+    ProtocolId protocol_id = PROTOCOL_NO;
+
+    for(size_t i = 0; i < 100; i++) {
+        protocol_id = protocol_dict_decoders_feed(dict, i % 2, 100);
+        mu_assert_int_eq(PROTOCOL_NO, protocol_id);
+    }
+
+    // trigger protocol 1
+    protocol_id = protocol_dict_decoders_feed(dict, true, 543);
+    mu_assert_int_eq(TestDictProtocol1, protocol_id);
+
+    mu_assert_string_eq("Protocol 1", protocol_dict_get_name(dict, protocol_id));
+    mu_assert_string_eq("Manufacturer 1", protocol_dict_get_manufacturer(dict, protocol_id));
+
+    size_t data_size = protocol_dict_get_data_size(dict, protocol_id);
+    mu_assert_int_eq(8, data_size);
+
+    protocol_dict_get_data(dict, protocol_id, data, data_size);
+    mu_assert_mem_eq(&protocol_1_decoder_result, data, data_size);
+
+    // trigger protocol 0
+    protocol_id = protocol_dict_decoders_feed(dict, true, 666);
+    mu_assert_int_eq(TestDictProtocol0, protocol_id);
+
+    mu_assert_string_eq("Protocol 0", protocol_dict_get_name(dict, protocol_id));
+    mu_assert_string_eq("Manufacturer 0", protocol_dict_get_manufacturer(dict, protocol_id));
+
+    data_size = protocol_dict_get_data_size(dict, protocol_id);
+    mu_assert_int_eq(4, data_size);
+
+    protocol_dict_get_data(dict, protocol_id, data, data_size);
+    mu_assert_mem_eq(&protocol_0_decoder_result, data, data_size);
+
+    protocol_dict_decoders_start(dict);
+
+    protocol_id = TestDictProtocol0;
+
+    const uint8_t protocol_0_test_data[4] = {100, 0, 0, 0};
+    protocol_dict_set_data(dict, protocol_id, protocol_0_test_data, 4);
+
+    mu_check(protocol_dict_encoder_start(dict, protocol_id));
+
+    LevelDuration level;
+    level = protocol_dict_encoder_yield(dict, protocol_id);
+    mu_assert_int_eq(true, level_duration_get_level(level));
+    mu_assert_int_eq(100, level_duration_get_duration(level));
+    level = protocol_dict_encoder_yield(dict, protocol_id);
+    mu_assert_int_eq(false, level_duration_get_level(level));
+    mu_assert_int_eq(100, level_duration_get_duration(level));
+    level = protocol_dict_encoder_yield(dict, protocol_id);
+    mu_assert_int_eq(true, level_duration_get_level(level));
+    mu_assert_int_eq(100, level_duration_get_duration(level));
+
+    mu_check(protocol_dict_encoder_start(dict, protocol_id));
+    level = protocol_dict_encoder_yield(dict, protocol_id);
+    mu_assert_int_eq(true, level_duration_get_level(level));
+    mu_assert_int_eq(100, level_duration_get_duration(level));
+
+    protocol_dict_free(dict);
+    free(data);
+}
+
+MU_TEST_SUITE(test_protocol_dict_suite) {
+    MU_RUN_TEST(test_protocol_dict);
+}
+
+int run_minunit_test_protocol_dict() {
+    MU_RUN_SUITE(test_protocol_dict_suite);
+    return MU_EXIT_CODE;
+}

+ 6 - 0
applications/unit_tests/test_index.c

@@ -19,7 +19,10 @@ int run_minunit_test_stream();
 int run_minunit_test_storage();
 int run_minunit_test_storage();
 int run_minunit_test_subghz();
 int run_minunit_test_subghz();
 int run_minunit_test_dirwalk();
 int run_minunit_test_dirwalk();
+int run_minunit_test_protocol_dict();
+int run_minunit_test_lfrfid_protocols();
 int run_minunit_test_nfc();
 int run_minunit_test_nfc();
+int run_minunit_test_bit_lib();
 
 
 typedef int (*UnitTestEntry)();
 typedef int (*UnitTestEntry)();
 
 
@@ -39,6 +42,9 @@ const UnitTest unit_tests[] = {
     {.name = "subghz", .entry = run_minunit_test_subghz},
     {.name = "subghz", .entry = run_minunit_test_subghz},
     {.name = "infrared", .entry = run_minunit_test_infrared},
     {.name = "infrared", .entry = run_minunit_test_infrared},
     {.name = "nfc", .entry = run_minunit_test_nfc},
     {.name = "nfc", .entry = run_minunit_test_nfc},
+    {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict},
+    {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols},
+    {.name = "bit_lib", .entry = run_minunit_test_bit_lib},
 };
 };
 
 
 void minunit_print_progress() {
 void minunit_print_progress() {

+ 88 - 0
applications/unit_tests/varint/varint_test.c

@@ -0,0 +1,88 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include "../minunit.h"
+#include <toolbox/varint.h>
+#include <toolbox/profiler.h>
+
+MU_TEST(test_varint_basic_u) {
+    mu_assert_int_eq(1, varint_uint32_length(0));
+    mu_assert_int_eq(5, varint_uint32_length(UINT32_MAX));
+
+    uint8_t data[8] = {};
+    uint32_t out_value;
+
+    mu_assert_int_eq(1, varint_uint32_pack(0, data));
+    mu_assert_int_eq(1, varint_uint32_unpack(&out_value, data, 8));
+    mu_assert_int_eq(0, out_value);
+
+    mu_assert_int_eq(5, varint_uint32_pack(UINT32_MAX, data));
+    mu_assert_int_eq(5, varint_uint32_unpack(&out_value, data, 8));
+    mu_assert_int_eq(UINT32_MAX, out_value);
+}
+
+MU_TEST(test_varint_basic_i) {
+    mu_assert_int_eq(5, varint_int32_length(INT32_MIN / 2));
+    mu_assert_int_eq(1, varint_int32_length(0));
+    mu_assert_int_eq(5, varint_int32_length(INT32_MAX / 2));
+
+    mu_assert_int_eq(2, varint_int32_length(127));
+    mu_assert_int_eq(2, varint_int32_length(-127));
+
+    uint8_t data[8] = {};
+    int32_t out_value;
+    mu_assert_int_eq(1, varint_int32_pack(0, data));
+    mu_assert_int_eq(1, varint_int32_unpack(&out_value, data, 8));
+    mu_assert_int_eq(0, out_value);
+
+    mu_assert_int_eq(2, varint_int32_pack(127, data));
+    mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8));
+    mu_assert_int_eq(127, out_value);
+
+    mu_assert_int_eq(2, varint_int32_pack(-127, data));
+    mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8));
+    mu_assert_int_eq(-127, out_value);
+
+    mu_assert_int_eq(5, varint_int32_pack(INT32_MAX, data));
+    mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8));
+    mu_assert_int_eq(INT32_MAX, out_value);
+
+    mu_assert_int_eq(5, varint_int32_pack(INT32_MIN / 2 + 1, data));
+    mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8));
+    mu_assert_int_eq(INT32_MIN / 2 + 1, out_value);
+}
+
+MU_TEST(test_varint_rand_u) {
+    uint8_t data[8] = {};
+    uint32_t out_value;
+
+    for(size_t i = 0; i < 200000; i++) {
+        uint32_t rand_value = rand();
+        mu_assert_int_eq(
+            varint_uint32_pack(rand_value, data), varint_uint32_unpack(&out_value, data, 8));
+        mu_assert_int_eq(rand_value, out_value);
+    }
+}
+
+MU_TEST(test_varint_rand_i) {
+    uint8_t data[8] = {};
+    int32_t out_value;
+
+    for(size_t i = 0; i < 200000; i++) {
+        int32_t rand_value = rand() + (INT32_MIN / 2 + 1);
+        mu_assert_int_eq(
+            varint_int32_pack(rand_value, data), varint_int32_unpack(&out_value, data, 8));
+        mu_assert_int_eq(rand_value, out_value);
+    }
+}
+
+MU_TEST_SUITE(test_varint_suite) {
+    MU_RUN_TEST(test_varint_basic_u);
+    MU_RUN_TEST(test_varint_basic_i);
+    MU_RUN_TEST(test_varint_rand_u);
+    MU_RUN_TEST(test_varint_rand_i);
+}
+
+int run_minunit_test_varint() {
+    MU_RUN_SUITE(test_varint_suite);
+    return MU_EXIT_CODE;
+}

+ 1 - 2
applications/updater/util/update_task.c

@@ -170,8 +170,7 @@ static bool update_task_check_file_exists(UpdateTask* update_task, string_t file
     string_t tmp_path;
     string_t tmp_path;
     string_init_set(tmp_path, update_task->update_path);
     string_init_set(tmp_path, update_task->update_path);
     path_append(tmp_path, string_get_cstr(filename));
     path_append(tmp_path, string_get_cstr(filename));
-    bool exists =
-        (storage_common_stat(update_task->storage, string_get_cstr(tmp_path), NULL) == FSE_OK);
+    bool exists = storage_file_exists(update_task->storage, string_get_cstr(tmp_path));
     string_clear(tmp_path);
     string_clear(tmp_path);
     return exists;
     return exists;
 }
 }

+ 1 - 0
firmware.scons

@@ -200,6 +200,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program(
         "misc",
         "misc",
         "mbedtls",
         "mbedtls",
         "loclass",
         "loclass",
+        "lfrfid",
         # 2nd round
         # 2nd round
         "flipperformat",
         "flipperformat",
         "toolbox",
         "toolbox",

+ 186 - 0
firmware/targets/f7/furi_hal/furi_hal_rfid.c

@@ -6,6 +6,7 @@
 
 
 #include <stm32wbxx_ll_tim.h>
 #include <stm32wbxx_ll_tim.h>
 #include <stm32wbxx_ll_comp.h>
 #include <stm32wbxx_ll_comp.h>
+#include <stm32wbxx_ll_dma.h>
 
 
 #define FURI_HAL_RFID_READ_TIMER TIM1
 #define FURI_HAL_RFID_READ_TIMER TIM1
 #define FURI_HAL_RFID_READ_TIMER_CHANNEL LL_TIM_CHANNEL_CH1N
 #define FURI_HAL_RFID_READ_TIMER_CHANNEL LL_TIM_CHANNEL_CH1N
@@ -16,8 +17,14 @@
 #define FURI_HAL_RFID_EMULATE_TIMER_IRQ FuriHalInterruptIdTIM2
 #define FURI_HAL_RFID_EMULATE_TIMER_IRQ FuriHalInterruptIdTIM2
 #define FURI_HAL_RFID_EMULATE_TIMER_CHANNEL LL_TIM_CHANNEL_CH3
 #define FURI_HAL_RFID_EMULATE_TIMER_CHANNEL LL_TIM_CHANNEL_CH3
 
 
+#define RFID_CAPTURE_TIM TIM2
+#define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3
+#define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4
+
 typedef struct {
 typedef struct {
     FuriHalRfidEmulateCallback callback;
     FuriHalRfidEmulateCallback callback;
+    FuriHalRfidDMACallback dma_callback;
+    FuriHalRfidReadCaptureCallback read_capture_callback;
     void* context;
     void* context;
 } FuriHalRfid;
 } FuriHalRfid;
 
 
@@ -212,6 +219,185 @@ void furi_hal_rfid_tim_emulate_stop() {
     furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL);
     furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL);
 }
 }
 
 
+static void furi_hal_capture_dma_isr(void* context) {
+    UNUSED(context);
+
+    // Channel 3, positive level
+    if(LL_TIM_IsActiveFlag_CC3(RFID_CAPTURE_TIM)) {
+        LL_TIM_ClearFlag_CC3(RFID_CAPTURE_TIM);
+        furi_hal_rfid->read_capture_callback(
+            true, LL_TIM_IC_GetCaptureCH3(RFID_CAPTURE_TIM), furi_hal_rfid->context);
+    }
+
+    // Channel 4, overall level
+    if(LL_TIM_IsActiveFlag_CC4(RFID_CAPTURE_TIM)) {
+        LL_TIM_ClearFlag_CC4(RFID_CAPTURE_TIM);
+        LL_TIM_SetCounter(RFID_CAPTURE_TIM, 0);
+        furi_hal_rfid->read_capture_callback(
+            false, LL_TIM_IC_GetCaptureCH4(RFID_CAPTURE_TIM), furi_hal_rfid->context);
+    }
+}
+
+void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context) {
+    FURI_CRITICAL_ENTER();
+    LL_TIM_DeInit(RFID_CAPTURE_TIM);
+    FURI_CRITICAL_EXIT();
+
+    furi_assert(furi_hal_rfid);
+
+    furi_hal_rfid->read_capture_callback = callback;
+    furi_hal_rfid->context = context;
+
+    // Timer: base
+    LL_TIM_InitTypeDef TIM_InitStruct = {0};
+    TIM_InitStruct.Prescaler = 64 - 1;
+    TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
+    TIM_InitStruct.Autoreload = UINT32_MAX;
+    TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
+    LL_TIM_Init(RFID_CAPTURE_TIM, &TIM_InitStruct);
+
+    // Timer: advanced
+    LL_TIM_SetClockSource(RFID_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL);
+    LL_TIM_DisableARRPreload(RFID_CAPTURE_TIM);
+    LL_TIM_SetTriggerInput(RFID_CAPTURE_TIM, LL_TIM_TS_TI2FP2);
+    LL_TIM_SetSlaveMode(RFID_CAPTURE_TIM, LL_TIM_SLAVEMODE_DISABLED);
+    LL_TIM_SetTriggerOutput(RFID_CAPTURE_TIM, LL_TIM_TRGO_RESET);
+    LL_TIM_EnableMasterSlaveMode(RFID_CAPTURE_TIM);
+    LL_TIM_DisableDMAReq_TRIG(RFID_CAPTURE_TIM);
+    LL_TIM_DisableIT_TRIG(RFID_CAPTURE_TIM);
+    LL_TIM_SetRemap(RFID_CAPTURE_TIM, LL_TIM_TIM2_TI4_RMP_COMP1);
+
+    // Timer: channel 3 indirect
+    LL_TIM_IC_SetActiveInput(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI);
+    LL_TIM_IC_SetPrescaler(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_ICPSC_DIV1);
+    LL_TIM_IC_SetPolarity(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_IC_POLARITY_FALLING);
+    LL_TIM_IC_SetFilter(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_IC_FILTER_FDIV1);
+
+    // Timer: channel 4 direct
+    LL_TIM_IC_SetActiveInput(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI);
+    LL_TIM_IC_SetPrescaler(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_ICPSC_DIV1);
+    LL_TIM_IC_SetPolarity(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_IC_POLARITY_RISING);
+    LL_TIM_IC_SetFilter(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_IC_FILTER_FDIV1);
+
+    furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, furi_hal_capture_dma_isr, NULL);
+
+    LL_TIM_EnableIT_CC3(RFID_CAPTURE_TIM);
+    LL_TIM_EnableIT_CC4(RFID_CAPTURE_TIM);
+    LL_TIM_CC_EnableChannel(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH);
+    LL_TIM_CC_EnableChannel(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH);
+    LL_TIM_SetCounter(RFID_CAPTURE_TIM, 0);
+    LL_TIM_EnableCounter(RFID_CAPTURE_TIM);
+
+    furi_hal_rfid_comp_start();
+}
+
+void furi_hal_rfid_tim_read_capture_stop() {
+    furi_hal_rfid_comp_stop();
+
+    furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL);
+
+    FURI_CRITICAL_ENTER();
+    LL_TIM_DeInit(RFID_CAPTURE_TIM);
+    FURI_CRITICAL_EXIT();
+}
+
+static void furi_hal_rfid_dma_isr() {
+    if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
+        LL_DMA_ClearFlag_HT1(DMA1);
+        furi_hal_rfid->dma_callback(true, furi_hal_rfid->context);
+    }
+
+    if(LL_DMA_IsActiveFlag_TC1(DMA1)) {
+        LL_DMA_ClearFlag_TC1(DMA1);
+        furi_hal_rfid->dma_callback(false, furi_hal_rfid->context);
+    }
+}
+
+void furi_hal_rfid_tim_emulate_dma_start(
+    uint32_t* duration,
+    uint32_t* pulse,
+    size_t length,
+    FuriHalRfidDMACallback callback,
+    void* context) {
+    furi_assert(furi_hal_rfid);
+
+    // setup interrupts
+    furi_hal_rfid->dma_callback = callback;
+    furi_hal_rfid->context = context;
+
+    // setup pins
+    furi_hal_rfid_pins_emulate();
+
+    // configure timer
+    furi_hal_rfid_tim_emulate(125000);
+    LL_TIM_OC_SetPolarity(
+        FURI_HAL_RFID_EMULATE_TIMER, FURI_HAL_RFID_EMULATE_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH);
+    LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_EMULATE_TIMER);
+
+    // configure DMA "mem -> ARR" channel
+    LL_DMA_InitTypeDef dma_config = {0};
+    dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_EMULATE_TIMER->ARR);
+    dma_config.MemoryOrM2MDstAddress = (uint32_t)duration;
+    dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
+    dma_config.Mode = LL_DMA_MODE_CIRCULAR;
+    dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
+    dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
+    dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
+    dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
+    dma_config.NbData = length;
+    dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
+    dma_config.Priority = LL_DMA_MODE_NORMAL;
+    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config);
+    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
+
+    // configure DMA "mem -> CCR3" channel
+#if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3
+    dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_EMULATE_TIMER->CCR3);
+#else
+#error Update this code. Would you kindly?
+#endif
+    dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse;
+    dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
+    dma_config.Mode = LL_DMA_MODE_CIRCULAR;
+    dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
+    dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
+    dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
+    dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
+    dma_config.NbData = length;
+    dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
+    dma_config.Priority = LL_DMA_MODE_NORMAL;
+    LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config);
+    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
+
+    // attach interrupt to one of DMA channels
+    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_rfid_dma_isr, NULL);
+    LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
+    LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1);
+
+    // start
+    LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER);
+
+    LL_TIM_SetCounter(FURI_HAL_RFID_EMULATE_TIMER, 0);
+    LL_TIM_EnableCounter(FURI_HAL_RFID_EMULATE_TIMER);
+}
+
+void furi_hal_rfid_tim_emulate_dma_stop() {
+    LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER);
+    LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER);
+
+    furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
+    LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
+    LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1);
+
+    FURI_CRITICAL_ENTER();
+
+    LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1);
+    LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2);
+    LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER);
+
+    FURI_CRITICAL_EXIT();
+}
+
 void furi_hal_rfid_tim_reset() {
 void furi_hal_rfid_tim_reset() {
     FURI_CRITICAL_ENTER();
     FURI_CRITICAL_ENTER();
 
 

+ 18 - 0
firmware/targets/furi_hal_include/furi_hal_rfid.h

@@ -7,6 +7,7 @@
 
 
 #include <stdint.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <stdbool.h>
+#include <stddef.h>
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
@@ -63,6 +64,23 @@ typedef void (*FuriHalRfidEmulateCallback)(void* context);
  */
  */
 void furi_hal_rfid_tim_emulate_start(FuriHalRfidEmulateCallback callback, void* context);
 void furi_hal_rfid_tim_emulate_start(FuriHalRfidEmulateCallback callback, void* context);
 
 
+typedef void (*FuriHalRfidReadCaptureCallback)(bool level, uint32_t duration, void* context);
+
+void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context);
+
+void furi_hal_rfid_tim_read_capture_stop();
+
+typedef void (*FuriHalRfidDMACallback)(bool half, void* context);
+
+void furi_hal_rfid_tim_emulate_dma_start(
+    uint32_t* duration,
+    uint32_t* pulse,
+    size_t length,
+    FuriHalRfidDMACallback callback,
+    void* context);
+
+void furi_hal_rfid_tim_emulate_dma_stop();
+
 /** Stop emulation timer
 /** Stop emulation timer
  */
  */
 void furi_hal_rfid_tim_emulate_stop();
 void furi_hal_rfid_tim_emulate_stop();

+ 2 - 0
furi/core/core_defines.h

@@ -92,6 +92,8 @@ extern "C" {
 #define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n)))
 #define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n)))
 #endif
 #endif
 
 
+#define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory")
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 1 - 0
lib/SConscript

@@ -77,6 +77,7 @@ libs = env.BuildModules(
         "appframe",
         "appframe",
         "misc",
         "misc",
         "loclass",
         "loclass",
+        "lfrfid",
     ],
     ],
 )
 )
 
 

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů