1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 13:09:49 +04:00

Compare commits

..

29 Commits

Author SHA1 Message Date
MX
107d574c02 added rfid fuzzer example to sdcard.zip 2022-09-05 22:28:41 +03:00
MX
84204a3233 return some plugins back 2022-09-05 22:10:31 +03:00
MX
48691b2296 Merge branch 'fz-dev' into dev 2022-09-05 20:53:59 +03:00
あく
3a767c9c02 [FL-2794] Lib: update LFS to v2.5.0, lower update free page limit (#1706)
* Lib: update lfs to v2.5.0

* Storage: set minimum free pages for update on internal storage to 3

* Updater: lower min int free space limit

* lfs: disabled debug and trace logs by default

Co-authored-by: hedger <hedger@nanode.su>
2022-09-05 20:40:20 +03:00
MX
1a4d928e5c fix rfid fuzzer gui 2022-09-05 20:01:26 +03:00
MX
a8858e38b5 Merge branch 'fz-dev' into dev 2022-09-05 19:23:34 +03:00
MX
120b5d7c90 update changelog and readme 2022-09-05 19:00:33 +03:00
Max Andreev
b7a6d18186 Fix CI/CD in tags #1703 2022-09-05 22:40:54 +09:00
VVX7
8d8481b17f fix buffer overflow in mifar ul load (#1697)
Co-authored-by: gornekich <n.gorbadey@gmail.com>
2022-09-05 20:42:59 +09:00
MX
ca8217b692 some fixes for rfid fuzzer 2022-09-05 14:05:43 +03:00
MX
361895c689 Merge pull request #55 from Ganapati/dev
Fix memory issues and add new attack
2022-09-05 13:20:45 +03:00
Ganapati
789230458b Increase time between card to handle slow readers 2022-09-05 09:39:11 +02:00
Ganapati
6f77f537e3 Fi memory issues ad add new attack 2022-09-04 23:32:58 +02:00
MX
b6254227b9 debug pack 2022-09-03 23:47:15 +03:00
MX
8323877120 formatted, attempt to fix rfid fuzzer crash
crash doesn’t fixed with this commit
2022-09-03 23:10:07 +03:00
MX
f99c1a8c0a Merge pull request #54 from Ganapati/dev
FlipFrid - upgrade
2022-09-03 19:22:53 +03:00
MX
c41555b579 Merge branch 'fz-dev' into dev 2022-09-03 16:30:13 +03:00
Dig
97b27261d5 fbt: fbtenv_chck_many_source, fix typos + grep logic (#1699)
Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 21:53:43 +09:00
gornekich
1853359d78 [FL-2759], [FL-2766] NFC collect params for mfkey32 attack (#1643)
* nfc: start nfc over rpc
* nfc: add detect reader state
* nfc: add reader analyzer
* nfc: rework reader analyzer
* reader_analyzer: print collected nonces to debug
* reader analyzer: add save on SD card
* reader_analyzer: separate mfkey related part to different file
* mfkey32: add logic for collecting parameters
* nfc: rework pcap with reader analyzer
* nfc: add logger for reader
* nfc: clean up
* nfc: add detect reader view
* nfc: add detect reader and mfkey nonces scenes
* nfc: add mfkey comlplete scene
* nfc: add new assets
* nfc: fix gui
* nfc: fix iso14443-4 UID emulation
* nfc: add no sd card notification
* nfc: fix grammar

Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 21:25:36 +09:00
Georgii Surkov
ed2c607dd3 [FL-2776] IR CLI Decode Command (#1692)
* Add decode option to Infrared app
* Implement saving results to file
* Refactor code
* Correct formatting
* Refactor code
* Better command line arguments handling
* Accept generic IR files
* Remove unneeded define

Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 19:46:07 +09:00
Max Andreev
bd54c2b342 Fix CI/CD (#1698)
* Test
newline
commit

* Test
newline
commit

* Fix some
variables
and test newline "quotted" commit
2022-09-03 19:18:24 +09:00
Max Andreev
53aa5c71a0 Amap workflow, "toolchain improvements" (#1685)
* fix quotes in amap
* try to fix quotes
* try to read "commit_message"
* Add new actions anv parser
* fix amap_anayse
* fix script ssl error
* test build with new get commit details method
* fix build.yml
* add fbt envs to get_env.py
* fix envs
* using new commit info "way"
* try to fix report link in PR page
* fix "pvs_studio.yml" again
* fix vars
* fix "build.yml"

Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 16:09:03 +09:00
Skorpionm
a3932cfa6d [FL-2787] SubGhz: add protocol Clemsa, fix decoder BETT (#1696)
* SubGhz: add protocol Clemsa
* SubGhz: fix decoder BETT protocol

Co-authored-by: あく <alleteam@gmail.com>
2022-09-03 15:19:01 +09:00
Sebastian Mauer
1d787e6da8 Add support for Keri tags (#1689)
Co-authored-by: SG <who.just.the.doctor@gmail.com>
2022-09-02 21:36:13 +10:00
Sebastian Mauer
10b0a611cf Add support for Gallagher tags (#1680)
* Add Gallagher protocol
2022-09-02 21:15:34 +10:00
Ganapati
f537ccfe14 Fix warnings 2022-09-01 17:46:08 +02:00
Ganapati
d37dbb29bf Rewrite plugin + new file attack 2022-09-01 17:03:18 +02:00
Skorpionm
0ee4573a65 SubGhz: add protocol Intertechno_V3 (#1622)
* SubGhz: add decode Intertechno_V3
* SubGhz: add encoder Intertechno V3
* SubGhz: add uni_test Intertechno V3
* SubGhz: fix syntax
* SubGhz: add Intertechno V3 dimming mode
* SubGhz: fix parsing event Magellen protocol
* SubGhz: fix syntax
* SubGhz: fix encoder dimm mode

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-08-31 23:27:34 +09:00
Skorpionm
311b60f815 [FL-2771] SubGhz: add protocol Prastel #1674
Co-authored-by: あく <alleteam@gmail.com>
2022-08-31 23:21:36 +09:00
81 changed files with 4596 additions and 1412 deletions

View File

@@ -36,6 +36,7 @@ steps:
- mkdir -p sd-card/nfc/assets
- mkdir -p sd-card/infrared/assets
- mkdir -p sd-card/unirf
- mkdir -p sd-card/rfidfuzzer
- mkdir -p sd-card/badusb/layouts
- cp assets/resources/badusb/layouts/* sd-card/badusb/layouts/
- cp assets/resources/subghz/assets/dangerous_settings sd-card/subghz/assets/dangerous_settings
@@ -48,6 +49,7 @@ steps:
- cp assets/resources/infrared/assets/projectors.ir sd-card/infrared/assets/projectors.ir
- cp assets/resources/infrared/assets/audio.ir sd-card/infrared/assets/audio.ir
- cp assets/resources/unirf/unirf_map_example.txt sd-card/unirf/unirf_map_example.txt
- cp assets/resources/rfidfuzzer/example_uids.txt sd-card/rfidfuzzer/example_uids.txt
- cp assets/resources/Manifest sd-card/Manifest
- zip -r artifacts-default/sd-card-${DRONE_TAG}.zip sd-card
- rm -rf sd-card

View File

@@ -1,19 +1,8 @@
### New changes
* NRF24 sniffer - tweak sniff parameters for more speed and reliability (by @mothball187) (PR #51)
* Fixed text in LF RFID -> Extra Actions
* Updated universal remote assets (by @Amec0e)
* OFW PR: SubGHz decode raw gui (by @qistoph) (PR 1667) / xMasterX: Replaced custom image with default one & Fixed Led don't stop blink after pressing Send from decoder scene
* WAV Player plugin excluded from releases to save space, you can enable it in `applications\meta` for your builds
* OFW PR: SubGhz: add protocol Intertechno_V3 - OFW PR 1622
* OFW PR: SubGhz: add protocol Prastel - OFW PR 1674
* OFW PR: Fix displaying LFRFID protocol names - OFW PR 1682 / xMasterX: Fixed display for N/A manufacturer
* OFW: LF RFID - PAC/Stanley, Paradox, Jablotron, Viking, Pyramid protocols support
* OFW: Picopass write (PR 1658)
* OFW: SubGhz: fix CLI "subghz tx"
* OFW: IR: Fix crash after cancelling Learn New Remote
* OFW: SubGhz: output debug data to external pin
* OFW: Speedup SD card & enlarge your RAM
* OFW: Other small changes
* WAV Player, Arkanoid, TicTacToe, Barcode generator - plugins enabled and included in releases again
* Debug apps disabled in release build
* WAV Player moved to Games menu
* OFW: Lib: update LFS to v2.5.0, lower update free page limit
**Note: To avoid issues prefer installing using web updater or by self update package, all needed assets will be installed**

View File

@@ -60,12 +60,12 @@ See changelog in releases for latest updates!
- ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module)
- WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module)
- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
- `Excluded from releases` - WAV player plugin (fixed) [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player)
- WAV player plugin (fixed) [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player)
- UPC-A Barcode generator plugin [(by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
- GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion)
- NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
- Simple Clock (fixed) !! New version WIP, wait for updates !! [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
- Simple Clock (fixed) [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
- UniversalRF Remix / Sub-GHz Remote [(by ESurge)](https://github.com/ESurge/flipperzero-firmware-unirfremix)[(updated and all protocol support added by darmiel & xMasterX)](https://github.com/darmiel/flipper-playlist/tree/feat/unirf-protocols)
- Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
- Spectrum Analyzer (with changes) [(by jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) - [Ultra Narrow mode & scan channels non-consecutively](https://github.com/theY4Kman/flipperzero-firmware/commits?author=theY4Kman)

View File

@@ -1,4 +1,21 @@
# FlipFrid
# Flipfrid
A simple implementation of ZigFrid on Flipper zero (+bonus)
(https://z4ziggy.wordpress.com/2017/07/21/zigfrid-a-passive-rfid-fuzzer/)
Basic EM4100 Fuzzer
## Why
Flipfrid is a simple Rfid fuzzer using EM4100 protocol (125khz).
Objective is to provide a simple to use fuzzer to test readers by emulating various cards.
EM4100 cards use a 1 byte customer id and 4 bytes card id.
## How
There is 4 modes :
- Default key loop over 16 factory/default keys and emulate each one after one ;
- BF customer id. just an iteration from 0X00 to 0XFF on the first byte ;
- Load Dump file : Load an existing EM4100 dump generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF;
- Uids list: loop over a text file (one uid per line)
TODO :
- blank screen on back press

View File

@@ -1,123 +1,38 @@
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <stdlib.h>
#include <lib/lfrfid/lfrfid_worker.h>
#include "raw_em4100.h"
#include <lfrfid/protocols/lfrfid_protocols.h>
#include "flipfrid.h"
#define NUMBER_OF_ATTACKS 3
#define TIME_BETWEEN_CARDS \
5 // Emulate 2 cards per second : (5 * (configTICK_RATE_HZ_RAW/10)) == (5*(1000/10)) == (5*100) == (500)ms
#define TAG "FLIPFRID"
#include "scene/flipfrid_scene_entrypoint.h"
#include "scene/flipfrid_scene_load_file.h"
#include "scene/flipfrid_scene_select_field.h"
#include "scene/flipfrid_scene_run_attack.h"
#include "scene/flipfrid_scene_load_custom_uids.h"
uint8_t id_list[16][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
{0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
{0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22
{0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33
{0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44
{0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55
{0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66
{0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77
{0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
{0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
{0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID
{0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha
{0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha
{0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha
{0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha
};
typedef enum {
DefaultKeys,
BruteForceCustomerId,
BadCrc,
} AttackType;
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType evt_type;
InputKey key;
InputType input_type;
} FlipFridEvent;
// STRUCTS
typedef struct {
bool emitting;
AttackType current_attack_type;
uint8_t* current_uid;
uint8_t meta_data;
NotificationApp* notify;
} FlipFridState;
#define RFIDFUZZER_APP_FOLDER "/ext/rfidfuzzer"
static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) {
const FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100);
FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100);
if(flipfrid_state == NULL) {
return;
}
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
// Frame
canvas_draw_frame(canvas, 0, 0, 128, 64);
// Title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignTop, "Flip/Frid");
// Badge Type
char uid[16];
char badge_type[21];
switch(flipfrid_state->current_attack_type) {
case DefaultKeys:
strcpy(badge_type, " Default Values >");
// Draw correct Canvas
switch(flipfrid_state->current_scene) {
case NoneScene:
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_draw(canvas, flipfrid_state);
break;
case BruteForceCustomerId:
strcpy(badge_type, "< BF Customer ID >");
case SceneSelectFile:
flipfrid_scene_load_file_on_draw(canvas, flipfrid_state);
break;
case BadCrc:
strcpy(badge_type, "< Bad CRC ");
case SceneSelectField:
flipfrid_scene_select_field_on_draw(canvas, flipfrid_state);
break;
default:
case SceneAttack:
flipfrid_scene_run_attack_on_draw(canvas, flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_draw(canvas, flipfrid_state);
break;
}
if(flipfrid_state->current_attack_type == BruteForceCustomerId) {
snprintf(uid, sizeof(uid), " ID : %2X ", flipfrid_state->current_uid[0]);
} else if(flipfrid_state->current_attack_type == BadCrc) {
snprintf(uid, sizeof(uid), "Sending packets");
} else {
snprintf(
uid,
sizeof(uid),
"%2X:%2X:%2X:%2X:%2X",
flipfrid_state->current_uid[0],
flipfrid_state->current_uid[1],
flipfrid_state->current_uid[2],
flipfrid_state->current_uid[3],
flipfrid_state->current_uid[4]);
}
// Badge infos
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, badge_type);
if(flipfrid_state->emitting) {
canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignCenter, uid);
} else {
canvas_draw_str_aligned(
canvas, 64, 42, AlignCenter, AlignCenter, "Press OK to start/stop");
}
release_mutex((ValueMutex*)ctx, flipfrid_state);
@@ -133,173 +48,194 @@ void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_qu
static void flipfrid_timer_callback(FuriMessageQueue* event_queue) {
furi_assert(event_queue);
FlipFridEvent event = {
.evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease};
furi_message_queue_put(event_queue, &event, 25);
}
FlipFridState* flipfrid_alloc() {
FlipFridState* flipfrid = malloc(sizeof(FlipFridState));
string_init(flipfrid->notification_msg);
string_init(flipfrid->attack_name);
flipfrid->previous_scene = NoneScene;
flipfrid->current_scene = SceneEntryPoint;
flipfrid->is_running = true;
flipfrid->is_attacking = false;
flipfrid->key_index = 0;
flipfrid->menu_index = 0;
flipfrid->attack = FlipFridAttackDefaultValues;
flipfrid->notify = furi_record_open(RECORD_NOTIFICATION);
flipfrid->data[0] = 0x00;
flipfrid->data[1] = 0x00;
flipfrid->data[2] = 0x00;
flipfrid->data[3] = 0x00;
flipfrid->data[4] = 0x00;
flipfrid->payload[0] = 0x00;
flipfrid->payload[1] = 0x00;
flipfrid->payload[2] = 0x00;
flipfrid->payload[3] = 0x00;
flipfrid->payload[4] = 0x00;
//Dialog
flipfrid->dialogs = furi_record_open(RECORD_DIALOGS);
return flipfrid;
}
void flipfrid_free(FlipFridState* flipfrid) {
//Dialog
furi_record_close(RECORD_DIALOGS);
notification_message(flipfrid->notify, &sequence_blink_stop);
// Strings
string_clear(flipfrid->notification_msg);
string_clear(flipfrid->attack_name);
free(flipfrid->data);
free(flipfrid->payload);
// The rest
free(flipfrid);
}
// ENTRYPOINT
int32_t flipfrid_start(void* p) {
UNUSED(p);
// Input
FURI_LOG_I(TAG, "Initializing input");
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent));
FlipFridState* flipfrid_state = (FlipFridState*)malloc(sizeof(FlipFridState));
FlipFridState* flipfrid_state = flipfrid_alloc();
ValueMutex flipfrid_state_mutex;
// Mutex
FURI_LOG_I(TAG, "Initializing flipfrid mutex");
if(!init_mutex(&flipfrid_state_mutex, flipfrid_state, sizeof(FlipFridState))) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_DIALOGS);
free(flipfrid_state);
return 255;
}
Storage* storage = furi_record_open(RECORD_STORAGE);
if(!storage_simply_mkdir(storage, RFIDFUZZER_APP_FOLDER)) {
FURI_LOG_E(TAG, "Could not create folder %s", RFIDFUZZER_APP_FOLDER);
}
furi_record_close(RECORD_STORAGE);
// Configure view port
FURI_LOG_I(TAG, "Initializing viewport");
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, flipfrid_draw_callback, &flipfrid_state_mutex);
view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue);
// Configure timer
FURI_LOG_I(TAG, "Initializing timer");
FuriTimer* timer =
furi_timer_alloc(flipfrid_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // configTICK_RATE_HZ_RAW 1000
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second
// Register view port in GUI
FURI_LOG_I(TAG, "Initializing gui");
Gui* gui = (Gui*)furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
// Init values
FlipFridEvent event;
flipfrid_state->emitting = false;
flipfrid_state->current_uid = id_list[0];
flipfrid_state->current_attack_type = DefaultKeys;
flipfrid_state->meta_data = 0;
flipfrid_state->notify = furi_record_open(RECORD_NOTIFICATION);
// RFID Configuration
size_t data_size = 5; // Default EM4100 data size is 5 (1 customer id + 4 uid)
LFRFIDWorker* worker;
const ProtocolBase* lfrfid_protocol[] = {&protocol_raw_em4100, &protocol_raw_wrong_crc_em4100};
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocol, 2);
worker = lfrfid_worker_alloc(dict);
uint8_t selectedProtocol = CLEAN;
// Application state
int menu_selected_item_index = 0; // Menu current item index
uint8_t counter = 0; // Used to count the step and wqaiting time between each test
uint8_t attack_state = 0; // Used to store the current attack state
uint8_t candidate[5]; // uid candidate
bool running = true; // Used to stop the application
// Main loop
while(running) {
while(flipfrid_state->is_running) {
// Get next event
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25);
if(event_status == FuriStatusOk) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
counter = 0;
switch(event.key) {
case InputKeyUp:
case InputKeyDown:
// OSEF
break;
case InputKeyRight:
// Next badge type
flipfrid_state->emitting = false;
attack_state = 0;
notification_message(flipfrid_state->notify, &sequence_blink_stop);
if(menu_selected_item_index < (NUMBER_OF_ATTACKS - 1)) {
menu_selected_item_index++;
flipfrid_state->current_attack_type =
(AttackType)menu_selected_item_index;
}
break;
case InputKeyLeft:
// Previous badge type
flipfrid_state->emitting = false;
attack_state = 0;
notification_message(flipfrid_state->notify, &sequence_blink_stop);
if(menu_selected_item_index > 0) {
menu_selected_item_index--;
flipfrid_state->current_attack_type =
(AttackType)menu_selected_item_index;
}
break;
case InputKeyOk:
if(flipfrid_state->emitting) {
flipfrid_state->emitting = false;
attack_state = 0;
// TODO FIX BLINK
notification_message(flipfrid_state->notify, &sequence_blink_stop);
} else {
flipfrid_state->emitting = true;
attack_state = 0;
// TODO FIX BLINK
notification_message(
flipfrid_state->notify, &sequence_blink_start_blue);
}
break;
case InputKeyBack:
notification_message(flipfrid_state->notify, &sequence_blink_stop);
flipfrid_state->emitting = false;
running = false;
break;
}
//Handle event key
switch(flipfrid_state->current_scene) {
case NoneScene:
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_event(event, flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_event(event, flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_event(event, flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_event(event, flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_event(event, flipfrid_state);
break;
}
} else if(event.evt_type == EventTypeTick) {
// Emulate card
if(flipfrid_state->emitting) {
if(1 == counter) {
protocol_dict_set_data(
dict, (LFRFIDProtocol)0, flipfrid_state->current_uid, data_size);
worker = lfrfid_worker_alloc(dict);
lfrfid_worker_start_thread(worker);
lfrfid_worker_emulate_start(worker, (LFRFIDProtocol)selectedProtocol);
} else if(0 == counter) {
lfrfid_worker_stop(worker);
lfrfid_worker_stop_thread(worker);
// set next value
switch(flipfrid_state->current_attack_type) {
case DefaultKeys: {
selectedProtocol = CLEAN;
data_size = 5;
flipfrid_state->current_uid = id_list[attack_state];
attack_state = attack_state + 1;
if(attack_state >= sizeof(id_list) / sizeof(id_list[0])) {
attack_state = 0;
}
break;
}
case BruteForceCustomerId: {
data_size = 5;
selectedProtocol = CLEAN;
candidate[0] = attack_state;
flipfrid_state->current_uid = candidate;
attack_state = attack_state + 1;
if((attack_state + 1) == 256) {
attack_state = 0;
}
break;
}
case BadCrc: {
selectedProtocol = BAD_CRC;
data_size = 5;
candidate[0] = 0xFF;
candidate[1] = 0xDE;
candidate[2] = 0xAD;
candidate[3] = 0xBE;
candidate[4] = 0xEF;
flipfrid_state->current_uid = candidate;
break;
}
}
//Handle event tick
if(flipfrid_state->current_scene != flipfrid_state->previous_scene) {
// Trigger Exit Scene
switch(flipfrid_state->previous_scene) {
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_exit(flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_exit(flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_exit(flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_exit(flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_exit(flipfrid_state);
break;
case NoneScene:
break;
}
if(counter > TIME_BETWEEN_CARDS) {
counter = 0;
} else {
counter++;
// Trigger Entry Scene
switch(flipfrid_state->current_scene) {
case NoneScene:
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_enter(flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_enter(flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_enter(flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_enter(flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_enter(flipfrid_state);
break;
}
flipfrid_state->previous_scene = flipfrid_state->current_scene;
}
// Trigger Tick Scene
switch(flipfrid_state->current_scene) {
case NoneScene:
case SceneEntryPoint:
flipfrid_scene_entrypoint_on_tick(flipfrid_state);
break;
case SceneSelectFile:
flipfrid_scene_load_file_on_tick(flipfrid_state);
break;
case SceneSelectField:
flipfrid_scene_select_field_on_tick(flipfrid_state);
break;
case SceneAttack:
flipfrid_scene_run_attack_on_tick(flipfrid_state);
break;
case SceneLoadCustomUids:
flipfrid_scene_load_custom_uids_on_tick(flipfrid_state);
break;
}
view_port_update(view_port);
}
@@ -307,17 +243,16 @@ int32_t flipfrid_start(void* p) {
}
// Cleanup
notification_message(flipfrid_state->notify, &sequence_blink_stop);
lfrfid_worker_stop(worker);
lfrfid_worker_stop_thread(worker);
lfrfid_worker_free(worker);
protocol_dict_free(dict);
furi_timer_stop(timer);
furi_timer_free(timer);
free(flipfrid_state);
FURI_LOG_I(TAG, "Cleaning up");
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
flipfrid_free(flipfrid_state);
return 0;
}

View File

@@ -1,7 +1,76 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <gui/gui.h>
#include <gui/modules/submenu.h>
#include "m-string.h"
#include <dialogs/dialogs.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include "raw_em4100.h"
#include <toolbox/stream/stream.h>
#include <flipper_format/flipper_format_i.h>
int32_t flipfrid_start(void* p);
#include <toolbox/stream/stream.h>
#include <toolbox/stream/string_stream.h>
#include <toolbox/stream/file_stream.h>
#include <toolbox/stream/buffered_file_stream.h>
#include <lib/lfrfid/lfrfid_worker.h>
#include <lfrfid/protocols/lfrfid_protocols.h>
#define TAG "FlipFrid"
typedef enum {
FlipFridAttackDefaultValues,
FlipFridAttackBfCustomerId,
FlipFridAttackLoadFile,
FlipFridAttackLoadFileCustomUids,
} FlipFridAttacks;
typedef enum {
NoneScene,
SceneEntryPoint,
SceneSelectFile,
SceneSelectField,
SceneAttack,
SceneLoadCustomUids,
} FlipFridScene;
typedef enum {
EventTypeTick,
EventTypeKey,
} EventType;
typedef struct {
EventType evt_type;
InputKey key;
InputType input_type;
} FlipFridEvent;
// STRUCTS
typedef struct {
bool is_running;
bool is_attacking;
FlipFridScene current_scene;
FlipFridScene previous_scene;
NotificationApp* notify;
u_int8_t menu_index;
string_t data_str;
uint8_t data[5];
uint8_t payload[5];
uint8_t attack_step;
FlipFridAttacks attack;
string_t attack_name;
DialogsApp* dialogs;
string_t notification_msg;
uint8_t key_index;
LFRFIDWorker* worker;
ProtocolDict* dict;
ProtocolId protocol;
// Used for custom dictionnary
Stream* uids_stream;
} FlipFridState;

View File

@@ -1,106 +0,0 @@
#include "raw_em4100.h"
#include <lib/lfrfid/protocols/protocol_em4100.c>
bool protocol_em4100_raw_encoder_start(ProtocolEM4100* proto) {
FURI_LOG_D("RAW_EM4100", "encoder_start : CLEAN");
// header
proto->encoded_data = 0b111111111;
// data
for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) {
em4100_write_nibble(false, proto->data[i], &proto->encoded_data);
em4100_write_nibble(true, proto->data[i], &proto->encoded_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 = (proto->encoded_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
parity_sum += parity_bit;
}
proto->encoded_data = (proto->encoded_data << 1) | ((parity_sum % 2) & 1);
}
// stop bit
proto->encoded_data = (proto->encoded_data << 1) | 0;
proto->encoded_data_index = 0;
proto->encoded_polarity = true;
return true;
};
bool protocol_em4100_wrong_crc_encoder_start(ProtocolEM4100* proto) {
FURI_LOG_D("RAW_EM4100", "encoder_start : WRONG CRC");
// header
proto->encoded_data = 0b111111111;
// data
for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) {
em4100_write_nibble(false, proto->data[i], &proto->encoded_data);
em4100_write_nibble(true, proto->data[i], &proto->encoded_data);
}
for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
proto->encoded_data = (proto->encoded_data << 1) | ((0 % 2) & 1);
}
// stop bit
proto->encoded_data = (proto->encoded_data << 1) | 1;
proto->encoded_data_index = 0;
proto->encoded_polarity = true;
return true;
};
const ProtocolBase protocol_raw_em4100 = {
.name = "RawEM4100",
.manufacturer = "EM-Micro",
.data_size = EM4100_DECODED_DATA_SIZE,
.features = LFRFIDFeatureASK | LFRFIDFeaturePSK,
.validate_count = 3,
.alloc = (ProtocolAlloc)protocol_em4100_alloc,
.free = (ProtocolFree)protocol_em4100_free,
.get_data = (ProtocolGetData)protocol_em4100_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_em4100_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_em4100_raw_encoder_start,
.yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield,
},
.render_data = (ProtocolRenderData)protocol_em4100_render_data,
.render_brief_data = (ProtocolRenderData)protocol_em4100_render_data,
.write_data = (ProtocolWriteData)protocol_em4100_write_data,
};
const ProtocolBase protocol_raw_wrong_crc_em4100 = {
.name = "RawEM4100",
.manufacturer = "EM-Micro",
.data_size = EM4100_DECODED_DATA_SIZE,
.features = LFRFIDFeatureASK | LFRFIDFeaturePSK,
.validate_count = 3,
.alloc = (ProtocolAlloc)protocol_em4100_alloc,
.free = (ProtocolFree)protocol_em4100_free,
.get_data = (ProtocolGetData)protocol_em4100_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_em4100_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_em4100_wrong_crc_encoder_start,
.yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield,
},
.render_data = (ProtocolRenderData)protocol_em4100_render_data,
.render_brief_data = (ProtocolRenderData)protocol_em4100_render_data,
.write_data = (ProtocolWriteData)protocol_em4100_write_data,
};

View File

@@ -1,11 +0,0 @@
#pragma once
#include <toolbox/protocols/protocol.h>
enum FlipFridProtocol {
CLEAN,
BAD_CRC,
};
extern const ProtocolBase protocol_raw_em4100;
extern const ProtocolBase protocol_raw_wrong_crc_em4100;

View File

@@ -0,0 +1,123 @@
#include "flipfrid_scene_entrypoint.h"
string_t menu_items[4];
void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t index) {
switch(index) {
case FlipFridAttackDefaultValues:
context->attack = FlipFridAttackDefaultValues;
context->current_scene = SceneAttack;
string_set_str(context->attack_name, "Default Values");
break;
case FlipFridAttackBfCustomerId:
context->attack = FlipFridAttackBfCustomerId;
context->current_scene = SceneAttack;
string_set_str(context->attack_name, "Bad Customer ID");
break;
case FlipFridAttackLoadFile:
context->attack = FlipFridAttackLoadFile;
context->current_scene = SceneSelectFile;
string_set_str(context->attack_name, "Load File");
break;
case FlipFridAttackLoadFileCustomUids:
context->attack = FlipFridAttackLoadFileCustomUids;
context->current_scene = SceneLoadCustomUids;
string_set_str(context->attack_name, "Load Custom UIDs");
break;
default:
break;
}
}
void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
// Clear the previous payload
context->payload[0] = 0x00;
context->payload[1] = 0x00;
context->payload[2] = 0x00;
context->payload[3] = 0x00;
context->payload[4] = 0x00;
context->menu_index = 0;
for(uint32_t i = 0; i < 4; i++) {
string_init(menu_items[i]);
}
string_set(menu_items[0], "Default Values");
string_set(menu_items[1], "BF Customer ID");
string_set(menu_items[2], "Load File");
string_set(menu_items[3], "Load uids from file");
}
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) {
UNUSED(context);
for(uint32_t i = 0; i < 4; i++) {
string_clear(menu_items[i]);
}
}
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
switch(event.key) {
case InputKeyDown:
if(context->menu_index < FlipFridAttackLoadFileCustomUids) {
context->menu_index++;
}
break;
case InputKeyUp:
if(context->menu_index > FlipFridAttackDefaultValues) {
context->menu_index--;
}
break;
case InputKeyLeft:
case InputKeyRight:
break;
case InputKeyOk:
flipfrid_scene_entrypoint_menu_callback(context, context->menu_index);
break;
case InputKeyBack:
context->is_running = false;
break;
}
}
}
}
void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
// Title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignTop, "RFID Fuzzer");
if(context->menu_index > FlipFridAttackDefaultValues) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas,
64,
24,
AlignCenter,
AlignTop,
string_get_cstr(menu_items[context->menu_index - 1]));
}
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas, 64, 36, AlignCenter, AlignTop, string_get_cstr(menu_items[context->menu_index]));
if(context->menu_index < FlipFridAttackLoadFileCustomUids) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas,
64,
48,
AlignCenter,
AlignTop,
string_get_cstr(menu_items[context->menu_index + 1]));
}
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_entrypoint_on_enter(FlipFridState* context);
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context);
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context);
void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context);

View File

@@ -0,0 +1,79 @@
#include "flipfrid_scene_load_custom_uids.h"
#include "flipfrid_scene_run_attack.h"
#include "flipfrid_scene_entrypoint.h"
#define LFRFID_UIDS_EXTENSION ".txt"
#define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer"
bool flipfrid_load_uids(FlipFridState* context, const char* file_path) {
bool result = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
context->uids_stream = buffered_file_stream_alloc(storage);
result =
buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING);
// Close if loading fails
if(!result) {
buffered_file_stream_close(context->uids_stream);
return false;
}
return result;
}
bool flipfrid_load_custom_uids_from_file(FlipFridState* context) {
// Input events and views are managed by file_select
string_t uid_path;
string_init(uid_path);
string_set_str(uid_path, RFIDFUZZER_APP_PATH_FOLDER);
bool res = dialog_file_browser_show(
context->dialogs, uid_path, uid_path, LFRFID_UIDS_EXTENSION, true, &I_125_10px, false);
if(res) {
res = flipfrid_load_uids(context, string_get_cstr(uid_path));
}
string_clear(uid_path);
return res;
}
void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context) {
if(flipfrid_load_custom_uids_from_file(context)) {
// Force context loading
flipfrid_scene_run_attack_on_enter(context);
context->current_scene = SceneAttack;
} else {
flipfrid_scene_entrypoint_on_enter(context);
context->current_scene = SceneEntryPoint;
}
}
void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
switch(event.key) {
case InputKeyDown:
case InputKeyUp:
case InputKeyLeft:
case InputKeyRight:
case InputKeyOk:
case InputKeyBack:
context->current_scene = SceneEntryPoint;
break;
}
}
}
}
void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context) {
UNUSED(context);
UNUSED(canvas);
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context);
void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context);
void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context);
void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context);
bool flipfrid_load_custom_uids_from_file(FlipFridState* context);

View File

@@ -0,0 +1,146 @@
#include "flipfrid_scene_load_file.h"
#include "flipfrid_scene_entrypoint.h"
#define LFRFID_APP_EXTENSION ".rfid"
#define LFRFID_APP_PATH_FOLDER "/ext/lfrfid"
bool flipfrid_load(FlipFridState* context, const char* file_path) {
bool result = false;
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
string_t temp_str;
string_init(temp_str);
do {
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
FURI_LOG_E(TAG, "Error open file %s", file_path);
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Error open file");
break;
}
// FileType
if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) {
FURI_LOG_E(TAG, "Missing or incorrect Filetype");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Missing or incorrect Filetypes");
break;
} else {
FURI_LOG_I(TAG, "Filetype: %s", string_get_cstr(temp_str));
}
// Key type
if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) {
FURI_LOG_E(TAG, "Missing or incorrect Key type");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Missing or incorrect Key type");
break;
} else {
FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str));
if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) {
FURI_LOG_E(TAG, "Unsupported Key type");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Unsupported Key type");
break;
}
}
// Data
if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) {
FURI_LOG_E(TAG, "Missing or incorrect Data");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Missing or incorrect Key");
break;
} else {
FURI_LOG_I(TAG, "Key: %s", string_get_cstr(context->data_str));
// Check data size
if(string_size(context->data_str) != 14) {
FURI_LOG_E(TAG, "Incorrect Key length");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Incorrect Key length");
break;
}
// String to uint8_t
for(uint8_t i = 0; i < 5; i++) {
char temp_str2[3];
temp_str2[0] = string_get_cstr(context->data_str)[i * 3];
temp_str2[1] = string_get_cstr(context->data_str)[i * 3 + 1];
temp_str2[2] = '\0';
context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16);
}
}
result = true;
} while(0);
string_clear(temp_str);
flipper_format_free(fff_data_file);
if(result) {
FURI_LOG_I(TAG, "Loaded successfully");
string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Source loaded.");
}
return result;
}
void flipfrid_scene_load_file_on_enter(FlipFridState* context) {
if(flipfrid_load_protocol_from_file(context)) {
context->current_scene = SceneSelectField;
} else {
flipfrid_scene_entrypoint_on_enter(context);
context->current_scene = SceneEntryPoint;
}
}
void flipfrid_scene_load_file_on_exit(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_load_file_on_tick(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
switch(event.key) {
case InputKeyDown:
case InputKeyUp:
case InputKeyLeft:
case InputKeyRight:
case InputKeyOk:
case InputKeyBack:
context->current_scene = SceneEntryPoint;
break;
}
}
}
}
void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context) {
UNUSED(context);
UNUSED(canvas);
}
bool flipfrid_load_protocol_from_file(FlipFridState* context) {
string_t user_file_path;
string_init(user_file_path);
string_set_str(user_file_path, LFRFID_APP_PATH_FOLDER);
// Input events and views are managed by file_select
bool res = dialog_file_browser_show(
context->dialogs,
user_file_path,
user_file_path,
LFRFID_APP_EXTENSION,
true,
&I_125_10px,
true);
if(res) {
res = flipfrid_load(context, string_get_cstr(user_file_path));
}
string_clear(user_file_path);
return res;
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_load_file_on_enter(FlipFridState* context);
void flipfrid_scene_load_file_on_exit(FlipFridState* context);
void flipfrid_scene_load_file_on_tick(FlipFridState* context);
void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context);
bool flipfrid_load_protocol_from_file(FlipFridState* context);

View File

@@ -0,0 +1,212 @@
#include "flipfrid_scene_run_attack.h"
uint8_t counter = 0;
#define TIME_BETWEEN_CARDS 5
uint8_t id_list[16][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
{0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
{0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22
{0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33
{0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44
{0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55
{0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66
{0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77
{0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
{0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
{0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID
{0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha
{0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha
{0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha
{0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha
};
void flipfrid_scene_run_attack_on_enter(FlipFridState* context) {
context->attack_step = 0;
context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
context->worker = lfrfid_worker_alloc(context->dict);
context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
}
void flipfrid_scene_run_attack_on_exit(FlipFridState* context) {
lfrfid_worker_stop(context->worker);
lfrfid_worker_stop_thread(context->worker);
lfrfid_worker_free(context->worker);
protocol_dict_free(context->dict);
notification_message(context->notify, &sequence_blink_stop);
}
void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
if(context->is_attacking) {
if(1 == counter) {
protocol_dict_set_data(context->dict, context->protocol, context->payload, 5);
lfrfid_worker_free(context->worker);
context->worker = lfrfid_worker_alloc(context->dict);
lfrfid_worker_start_thread(context->worker);
lfrfid_worker_emulate_start(context->worker, context->protocol);
} else if(0 == counter) {
lfrfid_worker_stop(context->worker);
lfrfid_worker_stop_thread(context->worker);
switch(context->attack) {
case FlipFridAttackDefaultValues:
context->payload[0] = id_list[context->attack_step][0];
context->payload[1] = id_list[context->attack_step][1];
context->payload[2] = id_list[context->attack_step][2];
context->payload[3] = id_list[context->attack_step][3];
context->payload[4] = id_list[context->attack_step][4];
if(context->attack_step == 15) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
} else {
context->attack_step++;
}
break;
case FlipFridAttackBfCustomerId:
context->payload[0] = context->attack_step;
context->payload[1] = 0x00;
context->payload[2] = 0x00;
context->payload[3] = 0x00;
context->payload[4] = 0x00;
if(context->attack_step == 255) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
} else {
context->attack_step++;
}
break;
case FlipFridAttackLoadFile:
context->payload[0] = context->data[0];
context->payload[1] = context->data[1];
context->payload[2] = context->data[2];
context->payload[3] = context->data[3];
context->payload[4] = context->data[4];
context->payload[context->key_index] = context->attack_step;
if(context->attack_step == 255) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
break;
} else {
context->attack_step++;
}
break;
case FlipFridAttackLoadFileCustomUids:
while(true) {
string_reset(context->data_str);
if(!stream_read_line(context->uids_stream, context->data_str)) {
context->attack_step = 0;
counter = 0;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
break;
};
if(string_get_char(context->data_str, 0) == '#') continue;
if(string_size(context->data_str) != 11) continue;
break;
}
FURI_LOG_D(TAG, string_get_cstr(context->data_str));
// string is valid, parse it in context->payload
for(uint8_t i = 0; i < 5; i++) {
char temp_str[3];
temp_str[0] = string_get_cstr(context->data_str)[i * 2];
temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1];
temp_str[2] = '\0';
context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16);
}
break;
}
}
if(counter > TIME_BETWEEN_CARDS) {
counter = 0;
} else {
counter++;
}
}
}
void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
switch(event.key) {
case InputKeyDown:
case InputKeyUp:
case InputKeyLeft:
case InputKeyRight:
break;
case InputKeyOk:
counter = 0;
if(!context->is_attacking) {
notification_message(context->notify, &sequence_blink_start_blue);
context->is_attacking = true;
} else {
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
}
break;
case InputKeyBack:
if(context->attack == FlipFridAttackLoadFileCustomUids) {
buffered_file_stream_close(context->uids_stream);
}
context->attack_step = 0;
context->is_attacking = false;
string_reset(context->notification_msg);
context->current_scene = SceneEntryPoint;
notification_message(context->notify, &sequence_blink_stop);
break;
}
}
}
}
void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
// Frame
canvas_draw_frame(canvas, 0, 0, 128, 64);
// Title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(
canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name));
char uid[16];
snprintf(
uid,
sizeof(uid),
"%02X:%02X:%02X:%02X:%02X",
context->payload[0],
context->payload[1],
context->payload[2],
context->payload[3],
context->payload[4]);
canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid);
canvas_set_font(canvas, FontSecondary);
char start_stop_msg[20];
if(context->is_attacking) {
snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop ");
} else {
snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to start ");
}
canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg);
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_run_attack_on_enter(FlipFridState* context);
void flipfrid_scene_run_attack_on_exit(FlipFridState* context);
void flipfrid_scene_run_attack_on_tick(FlipFridState* context);
void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context);

View File

@@ -0,0 +1,121 @@
#include "flipfrid_scene_select_field.h"
void flipfrid_center_displayed_key(FlipFridState* context, uint8_t index) {
const char* key_cstr = string_get_cstr(context->data_str);
uint8_t str_index = (index * 3);
char display_menu[17] = {
'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'};
if(index > 1) {
display_menu[0] = key_cstr[str_index - 6];
display_menu[1] = key_cstr[str_index - 5];
} else {
display_menu[0] = ' ';
display_menu[1] = ' ';
}
if(index > 0) {
display_menu[3] = key_cstr[str_index - 3];
display_menu[4] = key_cstr[str_index - 2];
} else {
display_menu[3] = ' ';
display_menu[4] = ' ';
}
display_menu[7] = key_cstr[str_index];
display_menu[8] = key_cstr[str_index + 1];
if((str_index + 4) <= (uint8_t)strlen(key_cstr)) {
display_menu[11] = key_cstr[str_index + 3];
display_menu[12] = key_cstr[str_index + 4];
} else {
display_menu[11] = ' ';
display_menu[12] = ' ';
}
if((str_index + 8) <= (uint8_t)strlen(key_cstr)) {
display_menu[14] = key_cstr[str_index + 6];
display_menu[15] = key_cstr[str_index + 7];
} else {
display_menu[14] = ' ';
display_menu[15] = ' ';
}
string_reset(context->notification_msg);
string_set_str(context->notification_msg, display_menu);
}
void flipfrid_scene_select_field_on_enter(FlipFridState* context) {
string_clear(context->notification_msg);
}
void flipfrid_scene_select_field_on_exit(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_select_field_on_tick(FlipFridState* context) {
UNUSED(context);
}
void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context) {
if(event.evt_type == EventTypeKey) {
if(event.input_type == InputTypeShort) {
const char* key_cstr = string_get_cstr(context->data_str);
// don't look, it's ugly but I'm a python dev so...
uint8_t nb_bytes = 0;
for(uint8_t i = 0; i < strlen(key_cstr); i++) {
if(' ' == key_cstr[i]) {
nb_bytes++;
}
}
switch(event.key) {
case InputKeyDown:
case InputKeyUp:
break;
case InputKeyLeft:
if(context->key_index > 0) {
context->key_index = context->key_index - 1;
}
break;
case InputKeyRight:
if(context->key_index < nb_bytes) {
context->key_index = context->key_index + 1;
}
break;
case InputKeyOk:
string_reset(context->notification_msg);
context->current_scene = SceneAttack;
break;
case InputKeyBack:
string_reset(context->notification_msg);
context->current_scene = SceneSelectFile;
break;
}
FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes);
}
}
}
void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
// Frame
canvas_draw_frame(canvas, 0, 0, 128, 64);
// Title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Use < > to select byte.");
char msg_index[18];
snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index);
canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index);
flipfrid_center_displayed_key(context, context->key_index);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 40, AlignCenter, AlignTop, string_get_cstr(context->notification_msg));
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../flipfrid.h"
void flipfrid_scene_select_field_on_enter(FlipFridState* context);
void flipfrid_scene_select_field_on_exit(FlipFridState* context);
void flipfrid_scene_select_field_on_tick(FlipFridState* context);
void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context);
void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context);
void center_displayed_key(FlipFridState* context, uint8_t index);

View File

@@ -3,6 +3,8 @@
#include <infrared.h>
#include <infrared_worker.h>
#include <furi_hal_infrared.h>
#include <flipper_format.h>
#include <toolbox/args.h>
#include "infrared_signal.h"
@@ -10,6 +12,7 @@
static void infrared_cli_start_ir_rx(Cli* cli, string_t args);
static void infrared_cli_start_ir_tx(Cli* cli, string_t args);
static void infrared_cli_process_decode(Cli* cli, string_t args);
static const struct {
const char* cmd;
@@ -17,6 +20,7 @@ static const struct {
} infrared_cli_commands[] = {
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
{.cmd = "decode", .process_function = infrared_cli_process_decode},
};
static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
@@ -86,6 +90,7 @@ static void infrared_cli_print_usage(void) {
"\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n",
INFRARED_MIN_FREQUENCY,
INFRARED_MAX_FREQUENCY);
printf("\tir decode <input_file> [<output_file>]\r\n");
}
static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
@@ -162,6 +167,160 @@ static void infrared_cli_start_ir_tx(Cli* cli, string_t args) {
infrared_signal_free(signal);
}
static bool
infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) {
bool ret = infrared_signal_save(signal, file, name);
if(!ret) {
printf("Failed to save signal: \"%s\"\r\n", name);
}
return ret;
}
static bool infrared_cli_decode_raw_signal(
InfraredRawSignal* raw_signal,
InfraredDecoderHandler* decoder,
FlipperFormat* output_file,
const char* signal_name) {
InfraredSignal* signal = infrared_signal_alloc();
bool ret = false, level = true, is_decoded = false;
size_t i;
for(i = 0; i < raw_signal->timings_size; ++i) {
// TODO: Any infrared_check_decoder_ready() magic?
const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]);
if(message) {
is_decoded = true;
printf(
"Protocol: %s address: 0x%lX command: 0x%lX %s\r\n",
infrared_get_protocol_name(message->protocol),
message->address,
message->command,
(message->repeat ? "R" : ""));
if(output_file && !message->repeat) {
infrared_signal_set_message(signal, message);
if(!infrared_cli_save_signal(signal, output_file, signal_name)) break;
}
}
level = !level;
}
if(i == raw_signal->timings_size) {
if(!is_decoded && output_file) {
infrared_signal_set_raw_signal(
signal,
raw_signal->timings,
raw_signal->timings_size,
raw_signal->frequency,
raw_signal->duty_cycle);
ret = infrared_cli_save_signal(signal, output_file, signal_name);
} else {
ret = true;
}
}
infrared_reset_decoder(decoder);
infrared_signal_free(signal);
return ret;
}
static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) {
bool ret = false;
InfraredSignal* signal = infrared_signal_alloc();
InfraredDecoderHandler* decoder = infrared_alloc_decoder();
string_t tmp;
string_init(tmp);
while(infrared_signal_read(signal, input_file, tmp)) {
ret = false;
if(!infrared_signal_is_valid(signal)) {
printf("Invalid signal\r\n");
break;
}
if(!infrared_signal_is_raw(signal)) {
if(output_file &&
!infrared_cli_save_signal(signal, output_file, string_get_cstr(tmp))) {
break;
} else {
printf("Skipping decoded signal\r\n");
continue;
}
}
InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
printf("Raw signal: %s, %u samples\r\n", string_get_cstr(tmp), raw_signal->timings_size);
if(!infrared_cli_decode_raw_signal(raw_signal, decoder, output_file, string_get_cstr(tmp)))
break;
ret = true;
}
infrared_free_decoder(decoder);
infrared_signal_free(signal);
string_clear(tmp);
return ret;
}
static void infrared_cli_process_decode(Cli* cli, string_t args) {
UNUSED(cli);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage);
FlipperFormat* output_file = NULL;
uint32_t version;
string_t tmp, header, input_path, output_path;
string_init(tmp);
string_init(header);
string_init(input_path);
string_init(output_path);
do {
if(!args_read_probably_quoted_string_and_trim(args, input_path)) {
printf("Wrong arguments.\r\n");
infrared_cli_print_usage();
break;
}
args_read_probably_quoted_string_and_trim(args, output_path);
if(!flipper_format_buffered_file_open_existing(input_file, string_get_cstr(input_path))) {
printf("Failed to open file for reading: \"%s\"\r\n", string_get_cstr(input_path));
break;
}
if(!flipper_format_read_header(input_file, header, &version) ||
(!string_start_with_str_p(header, "IR")) || version != 1) {
printf("Invalid or corrupted input file: \"%s\"\r\n", string_get_cstr(input_path));
break;
}
if(!string_empty_p(output_path)) {
printf("Writing output to file: \"%s\"\r\n", string_get_cstr(output_path));
output_file = flipper_format_file_alloc(storage);
}
if(output_file &&
!flipper_format_file_open_always(output_file, string_get_cstr(output_path))) {
printf("Failed to open file for writing: \"%s\"\r\n", string_get_cstr(output_path));
break;
}
if(output_file && !flipper_format_write_header(output_file, header, version)) {
printf("Failed to write to the output file: \"%s\"\r\n", string_get_cstr(output_path));
break;
}
if(!infrared_cli_decode_file(input_file, output_file)) {
break;
}
printf("File successfully decoded.\r\n");
} while(false);
string_clear(tmp);
string_clear(header);
string_clear(input_path);
string_clear(output_path);
flipper_format_free(input_file);
if(output_file) flipper_format_free(output_file);
furi_record_close(RECORD_STORAGE);
}
static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
UNUSED(context);
if(furi_hal_infrared_is_busy()) {
@@ -169,18 +328,15 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
return;
}
string_t command;
string_init(command);
args_read_string_and_trim(args, command);
size_t i = 0;
for(; i < COUNT_OF(infrared_cli_commands); ++i) {
size_t size = strlen(infrared_cli_commands[i].cmd);
bool cmd_found = !strncmp(string_get_cstr(args), infrared_cli_commands[i].cmd, size);
if(cmd_found) {
if(string_size(args) == size) {
break;
}
if(string_get_cstr(args)[size] == ' ') {
string_right(args, size + 1);
break;
}
size_t cmd_len = strlen(infrared_cli_commands[i].cmd);
if(!strncmp(string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) {
break;
}
}
@@ -189,6 +345,8 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
} else {
infrared_cli_print_usage();
}
string_clear(command);
}
void infrared_on_system_start() {
#ifdef SRV_CLI

View File

@@ -48,6 +48,7 @@ App(
"tetris_game",
"arkanoid_game",
"tictactoe_game",
"wav_player",
],
)
@@ -75,7 +76,6 @@ App(
"wifi_marauder",
"esp8266_deauth",
"wifi_scanner",
#"wav_player",
"multi_converter",
"flipfrid",
],

View File

@@ -8,152 +8,158 @@
#include "multi_converter_mode_select.h"
static void multi_converter_render_callback(Canvas* const canvas, void* ctx) {
const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
if(multi_converter_state == NULL) {
return;
}
const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
if(multi_converter_state == NULL) {
return;
}
if(multi_converter_state->mode == ModeDisplay) {
multi_converter_mode_display_draw(canvas, multi_converter_state);
} else {
multi_converter_mode_select_draw(canvas, multi_converter_state);
}
if (multi_converter_state->mode == ModeDisplay) {
multi_converter_mode_display_draw(canvas, multi_converter_state);
} else {
multi_converter_mode_select_draw(canvas, multi_converter_state);
}
release_mutex((ValueMutex*)ctx, multi_converter_state);
release_mutex((ValueMutex*)ctx, multi_converter_state);
}
static void multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
static void
multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event};
MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void multi_converter_init(MultiConverterState* const multi_converter_state) {
// initial default values
// initial default values
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0';
multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0';
multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators
multi_converter_state->unit_type_orig = UnitTypeDec;
multi_converter_state->unit_type_dest = UnitTypeHex;
multi_converter_state->unit_type_orig = UnitTypeDec;
multi_converter_state->unit_type_dest = UnitTypeHex;
multi_converter_state->keyboard_lock = 0;
multi_converter_state->keyboard_lock = 0;
// init the display view
multi_converter_mode_display_reset(multi_converter_state);
// init the display view
multi_converter_mode_display_reset(multi_converter_state);
// init the select view
multi_converter_mode_select_reset(multi_converter_state);
// init the select view
multi_converter_mode_select_reset(multi_converter_state);
// set ModeDisplay as the current mode
multi_converter_state->mode = ModeDisplay;
// set ModeDisplay as the current mode
multi_converter_state->mode = ModeDisplay;
}
// main entry point
int32_t multi_converter_app(void* p) {
UNUSED(p);
// get event queue
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent));
// get event queue
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent));
// allocate state
MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState));
// allocate state
MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState));
// set mutex for plugin state (different threads can access it)
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) {
FURI_LOG_E("MultiConverter", "cannot create mutex\r\n");
free(multi_converter_state);
return 255;
}
// set mutex for plugin state (different threads can access it)
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) {
FURI_LOG_E("MultiConverter", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(multi_converter_state);
return 255;
}
// register callbacks for drawing and input processing
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex);
view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue);
// register callbacks for drawing and input processing
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex);
view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue);
// open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
// open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
multi_converter_init(multi_converter_state);
multi_converter_init(multi_converter_state);
// main loop
MultiConverterEvent event;
for (bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
MultiConverterState* multi_converter_state = (MultiConverterState*)acquire_mutex_block(&state_mutex);
// main loop
MultiConverterEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
MultiConverterState* multi_converter_state =
(MultiConverterState*)acquire_mutex_block(&state_mutex);
if (event_status == FuriStatusOk) {
// press events
if (event.type == EventTypeKey && !multi_converter_state->keyboard_lock) {
if (multi_converter_state->mode == ModeDisplay) {
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey && !multi_converter_state->keyboard_lock) {
if(multi_converter_state->mode == ModeDisplay) {
if(event.input.key == InputKeyBack) {
if(event.input.type == InputTypePress) processing = false;
} else if(event.input.key == InputKeyOk) { // the "ok" press can be short or long
MultiConverterModeTrigger t = None;
if (event.input.key == InputKeyBack) {
if (event.input.type == InputTypePress) processing = false;
} else if (event.input.key == InputKeyOk) { // the "ok" press can be short or long
MultiConverterModeTrigger t = None;
if(event.input.type == InputTypeLong)
t = multi_converter_mode_display_ok(1, multi_converter_state);
else if(event.input.type == InputTypeShort)
t = multi_converter_mode_display_ok(0, multi_converter_state);
if (event.input.type == InputTypeLong) t = multi_converter_mode_display_ok(1, multi_converter_state);
else if (event.input.type == InputTypeShort) t = multi_converter_mode_display_ok(0, multi_converter_state);
if(t == Reset) {
multi_converter_mode_select_reset(multi_converter_state);
multi_converter_state->mode = ModeSelector;
}
} else {
if(event.input.type == InputTypePress)
multi_converter_mode_display_navigation(
event.input.key, multi_converter_state);
}
if (t == Reset) {
multi_converter_mode_select_reset(multi_converter_state);
multi_converter_state->mode = ModeSelector;
}
} else {
if (event.input.type == InputTypePress) multi_converter_mode_display_navigation(event.input.key, multi_converter_state);
}
} else { // ModeSelect
if (event.input.type == InputTypePress) {
switch (event.input.key) {
default:
break;
case InputKeyBack:
case InputKeyOk: {
MultiConverterModeTrigger t = multi_converter_mode_select_exit(event.input.key == InputKeyOk ? 1 : 0, multi_converter_state);
} else { // ModeSelect
if(event.input.type == InputTypePress) {
switch(event.input.key) {
default:
break;
case InputKeyBack:
case InputKeyOk: {
MultiConverterModeTrigger t = multi_converter_mode_select_exit(
event.input.key == InputKeyOk ? 1 : 0, multi_converter_state);
if (t == Reset) {
multi_converter_mode_display_reset(multi_converter_state);
} else if (t == Convert) {
multi_converter_mode_display_convert(multi_converter_state);
}
if(t == Reset) {
multi_converter_mode_display_reset(multi_converter_state);
} else if(t == Convert) {
multi_converter_mode_display_convert(multi_converter_state);
}
multi_converter_state->keyboard_lock = 1;
multi_converter_state->mode = ModeDisplay;
break;
}
case InputKeyLeft:
case InputKeyRight:
multi_converter_mode_select_switch(multi_converter_state);
break;
case InputKeyUp:
multi_converter_mode_select_change_unit(-1, multi_converter_state);
break;
case InputKeyDown:
multi_converter_mode_select_change_unit(1, multi_converter_state);
break;
}
}
}
} else if (multi_converter_state->keyboard_lock) {
multi_converter_state->keyboard_lock = 0;
}
} else {
// event timeout
}
view_port_update(view_port);
release_mutex(&state_mutex, multi_converter_state);
}
multi_converter_state->keyboard_lock = 1;
multi_converter_state->mode = ModeDisplay;
break;
}
case InputKeyLeft:
case InputKeyRight:
multi_converter_mode_select_switch(multi_converter_state);
break;
case InputKeyUp:
multi_converter_mode_select_change_unit(-1, multi_converter_state);
break;
case InputKeyDown:
multi_converter_mode_select_change_unit(1, multi_converter_state);
break;
}
}
}
} else if(multi_converter_state->keyboard_lock) {
multi_converter_state->keyboard_lock = 0;
}
} else {
// event timeout
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
view_port_update(view_port);
release_mutex(&state_mutex, multi_converter_state);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
furi_message_queue_free(event_queue);
delete_mutex(&state_mutex);
free(multi_converter_state);

View File

@@ -1,82 +1,82 @@
#pragma once
#define MULTI_CONVERTER_NUMBER_DIGITS 9
#define MULTI_CONVERTER_NUMBER_DIGITS 9
typedef enum {
EventTypeKey,
EventTypeKey,
} EventType;
typedef struct {
InputEvent input;
EventType type;
InputEvent input;
EventType type;
} MultiConverterEvent;
typedef enum {
ModeDisplay,
ModeSelector,
ModeDisplay,
ModeSelector,
} MultiConverterMode;
typedef enum {
None,
Reset,
Convert,
None,
Reset,
Convert,
} MultiConverterModeTrigger;
// new units goes here, used as index to the main multi_converter_available_units array (multi_converter_units.h)
typedef enum {
UnitTypeDec,
UnitTypeHex,
UnitTypeBin,
UnitTypeDec,
UnitTypeHex,
UnitTypeBin,
UnitTypeCelsius,
UnitTypeFahernheit,
UnitTypeKelvin,
UnitTypeCelsius,
UnitTypeFahernheit,
UnitTypeKelvin,
UnitTypeKilometers,
UnitTypeMeters,
UnitTypeCentimeters,
UnitTypeMiles,
UnitTypeFeet,
UnitTypeInches,
UnitTypeKilometers,
UnitTypeMeters,
UnitTypeCentimeters,
UnitTypeMiles,
UnitTypeFeet,
UnitTypeInches,
UnitTypeDegree,
UnitTypeRadian,
UnitTypeDegree,
UnitTypeRadian,
} MultiConverterUnitType;
typedef struct {
MultiConverterUnitType selected_unit_type_orig;
MultiConverterUnitType selected_unit_type_dest;
uint8_t select_orig;
MultiConverterUnitType selected_unit_type_orig;
MultiConverterUnitType selected_unit_type_dest;
uint8_t select_orig;
} MultiConverterModeSelect;
typedef struct {
uint8_t cursor; // cursor position when typing
int8_t key; // hover key
uint8_t comma; // comma already added? (only one comma allowed)
uint8_t negative; // is negative?
uint8_t cursor; // cursor position when typing
int8_t key; // hover key
uint8_t comma; // comma already added? (only one comma allowed)
uint8_t negative; // is negative?
} MultiConverterModeDisplay;
typedef struct MultiConverterUnit MultiConverterUnit;
typedef struct MultiConverterState MultiConverterState;
struct MultiConverterUnit {
uint8_t allow_comma;
uint8_t allow_negative;
uint8_t max_number_keys;
char mini_name[4];
char name[12];
void (*convert_function)(MultiConverterState * const);
uint8_t (*allowed_function)(MultiConverterUnitType);
uint8_t allow_comma;
uint8_t allow_negative;
uint8_t max_number_keys;
char mini_name[4];
char name[12];
void (*convert_function)(MultiConverterState* const);
uint8_t (*allowed_function)(MultiConverterUnitType);
};
struct MultiConverterState {
char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1];
char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1];
MultiConverterUnitType unit_type_orig;
MultiConverterUnitType unit_type_dest;
MultiConverterMode mode;
MultiConverterModeDisplay display;
MultiConverterModeSelect select;
uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes
// (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!)
char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1];
char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1];
MultiConverterUnitType unit_type_orig;
MultiConverterUnitType unit_type_dest;
MultiConverterMode mode;
MultiConverterModeDisplay display;
MultiConverterModeSelect select;
uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes
// (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!)
};

View File

@@ -1,284 +1,326 @@
#include "multi_converter_mode_display.h"
#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT]
#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press
#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press
#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16
#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17
#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT]
#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.'
#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-'
#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<'
#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#'
#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' '
#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press
#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press
#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16
#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17
#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3
#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8
#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.'
#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-'
#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<'
#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#'
#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' '
#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3
#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8
void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state) {
// 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...)
// just copy buffer_orig to buffer_dest and that's it
// 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...)
// just copy buffer_orig to buffer_dest and that's it
if (multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) {
memcpy(multi_converter_state->buffer_dest, multi_converter_state->buffer_orig, MULTI_CONVERTER_NUMBER_DIGITS);
return;
}
if(multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) {
memcpy(
multi_converter_state->buffer_dest,
multi_converter_state->buffer_orig,
MULTI_CONVERTER_NUMBER_DIGITS);
return;
}
// 2.- origin_buffer has not null functions
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL || multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL) return;
// 2.- origin_buffer has not null functions
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL ||
multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL)
return;
// 3.- valid destination type (using allowed_destinations function)
if (!multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function(multi_converter_state->unit_type_dest)) return;
multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function(multi_converter_state);
// 3.- valid destination type (using allowed_destinations function)
if(!multi_converter_get_unit(multi_converter_state->unit_type_orig)
.allowed_function(multi_converter_state->unit_type_dest))
return;
multi_converter_get_unit(multi_converter_state->unit_type_orig)
.convert_function(multi_converter_state);
}
void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) {
canvas_set_color(canvas, ColorBlack);
void multi_converter_mode_display_draw(
Canvas* const canvas,
const MultiConverterState* multi_converter_state) {
canvas_set_color(canvas, ColorBlack);
// ORIGIN
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name);
// ORIGIN
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(
canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig);
// DESTINATION
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 10 + 12, multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name);
// DESTINATION
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(
canvas,
2,
10 + 12,
multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest);
// SEPARATOR_LINE
canvas_draw_line(canvas, 2, 25, 128 - 3, 25);
// SEPARATOR_LINE
canvas_draw_line(canvas, 2, 25, 128 - 3, 25);
// KEYBOARD
uint8_t _x = 5;
uint8_t _y = 25 + 15; // line + 10
// KEYBOARD
uint8_t _x = 5;
uint8_t _y = 25 + 15; // line + 10
for (int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) {
for(int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) {
char g;
if(i < 10)
g = (i + '0');
else if(i < 16)
g = ((i - 10) + 'A');
else if(i == MULTI_CONVERTER_DISPLAY_KEY_DEL)
g = MULTI_CONVERTER_DISPLAY_CHAR_DEL;
else
g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT;
char g;
if (i < 10) g = (i + '0');
else if (i < 16) g = ((i - 10) + 'A');
else if (i == MULTI_CONVERTER_DISPLAY_KEY_DEL) g = MULTI_CONVERTER_DISPLAY_CHAR_DEL;
else g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT;
uint8_t g_w = canvas_glyph_width(canvas, g);
uint8_t g_w = canvas_glyph_width(canvas, g);
if (i < 16 && i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) {
// some units won't use the full [0] - [F] keyboard, in those situations just hide the char
// (won't be selectable anyway, so no worries here; this is just about drawing stuff)
g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
}
if(i < 16 &&
i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys -
1) {
// some units won't use the full [0] - [F] keyboard, in those situations just hide the char
// (won't be selectable anyway, so no worries here; this is just about drawing stuff)
g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
}
// currently hover key is highlighted
if ((multi_converter_state->display).key == i) {
canvas_draw_box(canvas,
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2
);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_draw_frame(canvas,
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2
);
}
// currently hover key is highlighted
if((multi_converter_state->display).key == i) {
canvas_draw_box(
canvas,
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_draw_frame(
canvas,
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2);
}
// draw key
canvas_draw_glyph(canvas, _x, _y, g);
// draw key
canvas_draw_glyph(canvas, _x, _y, g);
// certain keys have long_press features, draw whatever they're using there too
if (i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
canvas_draw_box(canvas,
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4,
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
4,
2
);
} else if (i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
canvas_draw_box(canvas,
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2,
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
2,
2
);
}
// certain keys have long_press features, draw whatever they're using there too
if(i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
canvas_draw_box(
canvas,
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4,
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
4,
2);
} else if(i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
canvas_draw_box(
canvas,
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2,
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
2,
2);
}
// back to black
canvas_set_color(canvas, ColorBlack);
if (i < 8) {
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2;
} else if (i == 8) {
_y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) + 3;
_x = 8; // some padding at the beginning on second line
} else {
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1;
}
}
// back to black
canvas_set_color(canvas, ColorBlack);
if(i < 8) {
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2;
} else if(i == 8) {
_y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) +
3;
_x = 8; // some padding at the beginning on second line
} else {
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1;
}
}
}
void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state) {
void multi_converter_mode_display_navigation(
InputKey key,
MultiConverterState* const multi_converter_state) {
// first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one"
switch(key) {
default:
break;
// first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one"
switch (key) {
case InputKeyUp:
case InputKeyDown:
if((multi_converter_state->display).key >= 9)
(multi_converter_state->display).key -= 9;
else
(multi_converter_state->display).key += 9;
break;
default:
break;
case InputKeyLeft:
case InputKeyRight:
case InputKeyUp:
case InputKeyDown:
if ((multi_converter_state->display).key >= 9) (multi_converter_state->display).key -= 9;
else (multi_converter_state->display).key += 9;
break;
(multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1);
case InputKeyLeft:
case InputKeyRight:
if((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS - 1)
(multi_converter_state->display).key = 0;
else if((multi_converter_state->display).key < 0)
(multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS - 1;
break;
}
(multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1);
// if destination key is disabled by max_number_keys, move to the closest one
// (this could be improved with more accurate keys movements, probably...)
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16)
return; // weird, since this means "do not show any number on the keyboard, but just in case..."
if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0;
else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1;
break;
}
// if destination key is disabled by max_number_keys, move to the closest one
// (this could be improved with more accurate keys movements, probably...)
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16) return; // weird, since this means "do not show any number on the keyboard, but just in case..."
int8_t i = -1;
if (key == InputKeyRight || key == InputKeyDown) i = 1;
while ((multi_converter_state->display).key < 16 && (multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) {
(multi_converter_state->display).key += i;
if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0;
else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1;
}
int8_t i = -1;
if(key == InputKeyRight || key == InputKeyDown) i = 1;
while((multi_converter_state->display).key < 16 &&
(multi_converter_state->display).key >
multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys -
1) {
(multi_converter_state->display).key += i;
if((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS - 1)
(multi_converter_state->display).key = 0;
else if((multi_converter_state->display).key < 0)
(multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS - 1;
}
}
void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state) {
// clean the buffers
for(int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) {
multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
}
// clean the buffers
for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) {
multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
}
// reset the display flags and index
multi_converter_state->display.cursor = 0;
multi_converter_state->display.key = 0;
multi_converter_state->display.comma = 0;
multi_converter_state->display.negative = 0;
// reset the display flags and index
multi_converter_state->display.cursor = 0;
multi_converter_state->display.key = 0;
multi_converter_state->display.comma = 0;
multi_converter_state->display.negative = 0;
}
void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state) {
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) {
if (!(multi_converter_state->display).negative) {
// shift origin buffer one to right + add the "-" sign (last digit will be lost)
for (int i = MULTI_CONVERTER_NUMBER_DIGITS-1; i > 0; i--) {
// we could avoid the blanks, but nevermind
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i-1];
}
multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE;
void multi_converter_mode_display_toggle_negative(
MultiConverterState* const multi_converter_state) {
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) {
if(!(multi_converter_state->display).negative) {
// shift origin buffer one to right + add the "-" sign (last digit will be lost)
for(int i = MULTI_CONVERTER_NUMBER_DIGITS - 1; i > 0; i--) {
// we could avoid the blanks, but nevermind
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i - 1];
}
multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE;
// only increment cursor if we're not out of bound
if ((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS) (multi_converter_state->display).cursor++;
} else {
// shift origin buffer one to left, append ' ' on the end
for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS-1; i++) {
if (multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK) break;
// only increment cursor if we're not out of bound
if((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS)
(multi_converter_state->display).cursor++;
} else {
// shift origin buffer one to left, append ' ' on the end
for(int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS - 1; i++) {
if(multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK)
break;
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i+1];
}
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS-1] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i + 1];
}
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS - 1] =
MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
(multi_converter_state->display).cursor--;
}
(multi_converter_state->display).cursor--;
}
// toggle flag
(multi_converter_state->display).negative ^= 1;
}
// toggle flag
(multi_converter_state->display).negative ^= 1;
}
}
void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state) {
if (
!multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma ||
(multi_converter_state->display).comma ||
!(multi_converter_state->display).cursor ||
((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1))
) return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars
if(!multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma ||
(multi_converter_state->display).comma || !(multi_converter_state->display).cursor ||
((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1)))
return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars
// set flag to one
(multi_converter_state->display).comma = 1;
// set flag to one
(multi_converter_state->display).comma = 1;
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_COMMA;
(multi_converter_state->display).cursor++;
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
MULTI_CONVERTER_DISPLAY_CHAR_COMMA;
(multi_converter_state->display).cursor++;
}
void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state) {
if ((multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) return;
if((multi_converter_state->display).key >
multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys - 1)
return;
if ((multi_converter_state->display).key < 10) {
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = (multi_converter_state->display).key + '0';
} else {
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = ((multi_converter_state->display).key - 10) + 'A';
}
if((multi_converter_state->display).key < 10) {
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
(multi_converter_state->display).key + '0';
} else {
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
((multi_converter_state->display).key - 10) + 'A';
}
(multi_converter_state->display).cursor++;
(multi_converter_state->display).cursor++;
}
MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state) {
MultiConverterModeTrigger multi_converter_mode_display_ok(
uint8_t long_press,
MultiConverterState* const multi_converter_state) {
if((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) {
if((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS)
return None; // limit reached, ignore
if ((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) {
if ((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS) return None; // limit reached, ignore
// long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed
if(long_press) {
if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
// toggle negative
multi_converter_mode_display_toggle_negative(multi_converter_state);
} else if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
// add comma
multi_converter_mode_display_add_comma(multi_converter_state);
}
// long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed
if (long_press) {
} else {
// regular keys
multi_converter_mode_display_add_number(multi_converter_state);
}
if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
// toggle negative
multi_converter_mode_display_toggle_negative(multi_converter_state);
} else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
// add comma
multi_converter_mode_display_add_comma(multi_converter_state);
}
multi_converter_mode_display_convert(multi_converter_state);
} else {
// regular keys
multi_converter_mode_display_add_number(multi_converter_state);
}
} else if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) {
if((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--;
multi_converter_mode_display_convert(multi_converter_state);
if(multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] ==
MULTI_CONVERTER_DISPLAY_CHAR_COMMA)
(multi_converter_state->display).comma = 0;
if(multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] ==
MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE)
(multi_converter_state->display).negative = 0;
} else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) {
if ((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--;
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_COMMA) (multi_converter_state->display).comma = 0;
if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE) (multi_converter_state->display).negative = 0;
multi_converter_mode_display_convert(multi_converter_state);
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
multi_converter_mode_display_convert(multi_converter_state);
} else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT
return Reset;
}
return None;
} else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT
return Reset;
}
return None;
}

View File

@@ -16,12 +16,16 @@ void multi_converter_mode_display_convert(MultiConverterState* const multi_conve
//
// draw the main DISPLAY view with the current multi_converter_state values
//
void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state);
void multi_converter_mode_display_draw(
Canvas* const canvas,
const MultiConverterState* multi_converter_state);
//
// keyboard navigation on DISPLAY mode (NAVIGATION only, no BACK nor OK - InputKey guaranteed to be left/right/up/down)
//
void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state);
void multi_converter_mode_display_navigation(
InputKey key,
MultiConverterState* const multi_converter_state);
//
// reset the DISPLAY mode with the current units, cleaning the buffers and different flags;
@@ -52,4 +56,6 @@ void multi_converter_mode_display_add_number(MultiConverterState* const multi_co
// handle the OK action when selecting a specific key on the keyboard (add a number, a symbol, change mode...)
// returns a ModeTrigger enum value: may or may not let to a mode change on the main loop (WON'T change the mode here)
//
MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state);
MultiConverterModeTrigger multi_converter_mode_display_ok(
uint8_t long_press,
MultiConverterState* const multi_converter_state);

View File

@@ -1,160 +1,210 @@
#include "multi_converter_mode_select.h"
#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3
#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3
#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:"
#define MULTI_CONVERTER_INFO_STRING_TO "TO:"
#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change"
#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel"
#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:"
#define MULTI_CONVERTER_INFO_STRING_TO "TO:"
#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change"
#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel"
void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state) {
int i = 1;
while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...)
int ut = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_dest, i * d);
if (
multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(ut) &&
(multi_converter_state->select).selected_unit_type_orig != ut
) {
canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name);
break;
}
i++;
}
void multi_converter_mode_select_draw_destination_offset(
uint8_t x,
uint8_t y,
int8_t d,
Canvas* const canvas,
const MultiConverterState* multi_converter_state) {
int i = 1;
while(
i <
MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...)
int ut = multi_converter_get_unit_type_offset(
(multi_converter_state->select).selected_unit_type_dest, i * d);
if(multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig]
.allowed_function(ut) &&
(multi_converter_state->select).selected_unit_type_orig != ut) {
canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name);
break;
}
i++;
}
}
void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas) {
canvas_draw_box(canvas, x - 2 , y - 10, canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4, 13);
canvas_set_color(canvas, ColorWhite);
canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name);
canvas_set_color(canvas, ColorBlack);
void multi_converter_mode_select_draw_selected_unit(
uint8_t x,
uint8_t y,
MultiConverterUnitType unit_type,
Canvas* const canvas) {
canvas_draw_box(
canvas,
x - 2,
y - 10,
canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4,
13);
canvas_set_color(canvas, ColorWhite);
canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name);
canvas_set_color(canvas, ColorBlack);
}
void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) {
void multi_converter_mode_select_draw(
Canvas* const canvas,
const MultiConverterState* multi_converter_state) {
int y = 10;
int x = 10;
int y = 10;
int x = 10;
canvas_set_color(canvas, ColorBlack);
canvas_set_color(canvas, ColorBlack);
// FROM
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM);
// FROM
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM);
canvas_set_font(canvas, FontSecondary);
canvas_set_font(canvas, FontSecondary);
// offset -1
y += 12;
// offset -1
y += 12;
canvas_draw_str(
canvas,
x,
y,
multi_converter_available_units[multi_converter_get_unit_type_offset(
(multi_converter_state->select).selected_unit_type_orig,
-1)]
.name);
canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, -1) ].name);
// current selected element
y += 12;
// current selected element
y += 12;
multi_converter_mode_select_draw_selected_unit(
x, y, (multi_converter_state->select).selected_unit_type_orig, canvas);
multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_orig, canvas);
if((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
if ((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
// offset +1
y += 12;
// offset +1
y += 12;
canvas_draw_str(
canvas,
x,
y,
multi_converter_available_units[multi_converter_get_unit_type_offset(
(multi_converter_state->select).selected_unit_type_orig,
1)]
.name);
canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, 1) ].name);
// TO
y = 10;
x = 70;
// TO
y = 10;
x = 70;
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO);
canvas_set_font(canvas, FontSecondary);
canvas_set_font(canvas, FontSecondary);
// offset -1: go back from current selected destination and find the first one valid (even if it's itself)
y += 12;
// offset -1: go back from current selected destination and find the first one valid (even if it's itself)
y += 12;
multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state);
multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state);
// current selected element
y += 12;
// current selected element
y += 12;
multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_dest, canvas);
multi_converter_mode_select_draw_selected_unit(
x, y, (multi_converter_state->select).selected_unit_type_dest, canvas);
if (!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
if(!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
// offset +1: same but on the opposite direction
y += 12;
// offset +1: same but on the opposite direction
y += 12;
multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state);
multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state);
// OK / CANCEL
// OK / CANCEL
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12);
canvas_draw_box(canvas, 128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 12, canvas_string_width(canvas, "BACK: Cancel") + 4, 12);
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(
canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12);
canvas_draw_box(
canvas,
128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK),
64 - 12,
canvas_string_width(canvas, "BACK: Cancel") + 4,
12);
canvas_set_color(canvas, ColorWhite);
canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK);
canvas_draw_str(canvas, 128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 3, MULTI_CONVERTER_INFO_STRING_BACK);
canvas_set_color(canvas, ColorWhite);
canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK);
canvas_draw_str(
canvas,
128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK),
64 - 3,
MULTI_CONVERTER_INFO_STRING_BACK);
}
void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state) {
// initial pre-selected values are equal to the current selected values
(multi_converter_state->select).selected_unit_type_orig =
multi_converter_state->unit_type_orig;
(multi_converter_state->select).selected_unit_type_dest =
multi_converter_state->unit_type_dest;
// initial pre-selected values are equal to the current selected values
(multi_converter_state->select).selected_unit_type_orig = multi_converter_state->unit_type_orig;
(multi_converter_state->select).selected_unit_type_dest = multi_converter_state->unit_type_dest;
(multi_converter_state->select).select_orig = 1;
(multi_converter_state->select).select_orig = 1;
}
MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state) {
if (save_changes) {
MultiConverterModeTrigger multi_converter_mode_select_exit(
uint8_t save_changes,
MultiConverterState* const multi_converter_state) {
if(save_changes) {
multi_converter_state->unit_type_dest =
(multi_converter_state->select).selected_unit_type_dest;
multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest;
if(multi_converter_state->unit_type_orig ==
(multi_converter_state->select).selected_unit_type_orig) {
// if the ORIGIN unit didn't changed, just trigger the convert
if (multi_converter_state->unit_type_orig == (multi_converter_state->select).selected_unit_type_orig) {
// if the ORIGIN unit didn't changed, just trigger the convert
return Convert;
} else {
multi_converter_state->unit_type_orig =
(multi_converter_state->select).selected_unit_type_orig;
multi_converter_state->unit_type_dest =
(multi_converter_state->select).selected_unit_type_dest;
return Convert;
} else {
multi_converter_state->unit_type_orig = (multi_converter_state->select).selected_unit_type_orig;
multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest;
return Reset;
}
}
return Reset;
}
}
return None;
return None;
}
void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state) {
(multi_converter_state->select).select_orig ^= 1;
(multi_converter_state->select).select_orig ^= 1;
}
void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state) {
void multi_converter_mode_select_change_unit(
int8_t direction,
MultiConverterState* const multi_converter_state) {
MultiConverterUnitType d;
if((multi_converter_state->select).select_orig) {
(multi_converter_state->select).selected_unit_type_orig =
multi_converter_get_unit_type_offset(
(multi_converter_state->select).selected_unit_type_orig, direction);
d = (multi_converter_state->select).selected_unit_type_dest;
} else {
d = ((multi_converter_state->select).selected_unit_type_dest + direction) %
MULTI_CONVERTER_AVAILABLE_UNITS;
}
MultiConverterUnitType d;
if ((multi_converter_state->select).select_orig) {
(multi_converter_state->select).selected_unit_type_orig = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, direction);
d = (multi_converter_state->select).selected_unit_type_dest;
} else {
d = ((multi_converter_state->select).selected_unit_type_dest + direction) % MULTI_CONVERTER_AVAILABLE_UNITS;
}
// check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION
// (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME);
// also notice that ORIGIN must be DIFFERENT than DESTINATION
int i = 0;
while (i < MULTI_CONVERTER_AVAILABLE_UNITS) {
if (
multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(d) &&
(multi_converter_state->select).selected_unit_type_orig != d
) {
(multi_converter_state->select).selected_unit_type_dest = d;
break;
}
d = multi_converter_get_unit_type_offset(d, direction);
i++;
}
// check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION
// (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME);
// also notice that ORIGIN must be DIFFERENT than DESTINATION
int i = 0;
while(i < MULTI_CONVERTER_AVAILABLE_UNITS) {
if(multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig]
.allowed_function(d) &&
(multi_converter_state->select).selected_unit_type_orig != d) {
(multi_converter_state->select).selected_unit_type_dest = d;
break;
}
d = multi_converter_get_unit_type_offset(d, direction);
i++;
}
}

View File

@@ -10,14 +10,25 @@
//
// aux draw function for units offsets and draw stuff
//
void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state);
void multi_converter_mode_select_draw_destination_offset(
uint8_t x,
uint8_t y,
int8_t d,
Canvas* const canvas,
const MultiConverterState* multi_converter_state);
void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas);
void multi_converter_mode_select_draw_selected_unit(
uint8_t x,
uint8_t y,
MultiConverterUnitType unit_type,
Canvas* const canvas);
//
// draw the main SELECT view with the current multi_converter_state values
//
void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state);
void multi_converter_mode_select_draw(
Canvas* const canvas,
const MultiConverterState* multi_converter_state);
//
// reset the SELECT mode view, showing as "pre-selected" the current working units
@@ -33,11 +44,13 @@ void multi_converter_mode_select_reset(MultiConverterState* const multi_converte
// prevent weird behaviours, so for now we're trusting the selected_unit_orig/dest_type values)
//
// returns an enum code MultiConverterDisplayTrigger based on doing nothing (cancel), triggering the display
// convert method or reseting the whole display mode (when fully changing the units)
// convert method or reseting the whole display mode (when fully changing the units)
//
// notice the MODE CHANGE itself is not done here but in the main loop (outside the call) via the ModeTrigger enum element
//
MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state);
MultiConverterModeTrigger multi_converter_mode_select_exit(
uint8_t save_changes,
MultiConverterState* const multi_converter_state);
//
// switch between selecting the ORIGIN or the DESTINATION unit on DISPLAY mode (since there're only
@@ -48,11 +61,13 @@ void multi_converter_mode_select_switch(MultiConverterState* const multi_convert
//
// change the selected unit on SELECTED mode, using the select_orig flag to check if we're switching the
// ORIGIN or the DESTINATION unit; the DIRECTION (up or down to travel the array) is set as a param
//
//
// when switching the ORIGIN one, reset the DESTINATION to the first valid unit (if the current one is not
// valid anymore); when switching the DESTINATION one, an allowed_function() check is performed in order to
// properly set a valid destination unit.
//
// (notice the draw step also perform which units are valid to display, so no worries about that here)
//
void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state);
void multi_converter_mode_select_change_unit(
int8_t direction,
MultiConverterState* const multi_converter_state);

View File

@@ -1,124 +1,126 @@
#include "multi_converter_units.h"
#define MULTI_CONVERTER_CHAR_OVERFLOW '#'
#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999
#define MULTI_CONVERTER_CHAR_OVERFLOW '#'
#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999
#define multi_converter_unit_set_overflow(b) for (int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW;
#define multi_converter_unit_set_overflow(b) \
for(int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) \
b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW;
//
// DEC / HEX / BIN conversion
//
//
void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state) {
char dest[MULTI_CONVERTER_NUMBER_DIGITS];
char dest[MULTI_CONVERTER_NUMBER_DIGITS];
int i = 0;
uint8_t overflow = 0;
int i = 0;
uint8_t overflow = 0;
int a = 0;
int r = 0;
uint8_t f = 1;
int a = 0;
int r = 0;
uint8_t f = 1;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeDec: {
a = atoi(multi_converter_state->buffer_orig);
f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2);
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeDec: {
a = atoi(multi_converter_state->buffer_orig);
f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2);
break;
}
case UnitTypeHex:
a = strtol(multi_converter_state->buffer_orig, NULL, 16);
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2);
break;
}
case UnitTypeHex:
a = strtol(multi_converter_state->buffer_orig, NULL, 16);
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2);
break;
case UnitTypeBin:
a = strtol(multi_converter_state->buffer_orig, NULL, 2);
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16);
break;
case UnitTypeBin:
a = strtol(multi_converter_state->buffer_orig, NULL, 2);
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16);
break;
}
break;
}
while (a > 0) {
r = a % f;
dest[i] = r + (r < 10 ? '0' : ('A' - 10) );
a /= f;
if (i++ >= MULTI_CONVERTER_NUMBER_DIGITS) {
overflow = 1;
break;
}
}
if (overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
// copy DEST (reversed) to destination and append empty chars at the end
for (int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) {
if (i >= 1) multi_converter_state->buffer_dest[j] = dest[--i];
else multi_converter_state->buffer_dest[j] = ' ';
}
}
while(a > 0) {
r = a % f;
dest[i] = r + (r < 10 ? '0' : ('A' - 10));
a /= f;
if(i++ >= MULTI_CONVERTER_NUMBER_DIGITS) {
overflow = 1;
break;
}
}
if(overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
// copy DEST (reversed) to destination and append empty chars at the end
for(int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) {
if(i >= 1)
multi_converter_state->buffer_dest[j] = dest[--i];
else
multi_converter_state->buffer_dest[j] = ' ';
}
}
}
uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType unit_type) {
return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin);
return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin);
}
//
// CEL / FAR / KEL
//
void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state) {
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeCelsius:
if(multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
// celsius to fahrenheit
a = (a * ((double)1.8)) + 32;
} else { // UnitTypeKelvin
a += ((double)273.15);
}
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeCelsius:
if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
// celsius to fahrenheit
a = (a * ((double) 1.8)) + 32;
} else { // UnitTypeKelvin
a += ((double) 273.15);
}
break;
case UnitTypeFahernheit:
// fahrenheit to celsius, always
a = (a - 32) / ((double)1.8);
if(multi_converter_state->unit_type_dest == UnitTypeKelvin) {
// if kelvin, add
a += ((double)273.15);
}
break;
case UnitTypeFahernheit:
// fahrenheit to celsius, always
a = (a - 32) / ((double) 1.8);
if (multi_converter_state->unit_type_dest == UnitTypeKelvin) {
// if kelvin, add
a += ((double) 273.15);
}
break;
case UnitTypeKelvin:
// kelvin to celsius, always
a -= ((double)273.15);
if(multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
// if fahernheit, convert
a = (a * ((double)1.8)) + 32;
}
break;
case UnitTypeKelvin:
// kelvin to celsius, always
a -= ((double) 273.15);
if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
// if fahernheit, convert
a = (a * ((double) 1.8)) + 32;
}
break;
}
break;
}
if (overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a);
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
if(overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a);
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
}
uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_type) {
return (unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit || unit_type == UnitTypeKelvin);
return (
unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit ||
unit_type == UnitTypeKelvin);
}
//
@@ -126,73 +128,102 @@ uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_typ
//
void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state) {
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeKilometers:
if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1000);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100000);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.6213711);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3280.839895013);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39370.078740157);
break;
case UnitTypeMeters:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 1000);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0006213711);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3.280839895013);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39.370078740157);
break;
case UnitTypeCentimeters:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 100000);
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a /= ((double) 100);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000006213711);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.03280839895013);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 0.39370078740157);
break;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeKilometers:
if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a *= ((double)1000);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)100000);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.6213711);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)3280.839895013);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)39370.078740157);
break;
case UnitTypeMeters:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a /= ((double)1000);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)100);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.0006213711);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)3.280839895013);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)39.370078740157);
break;
case UnitTypeCentimeters:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a /= ((double)100000);
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a /= ((double)100);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.000006213711);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)0.03280839895013);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)0.39370078740157);
break;
case UnitTypeMiles:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 1.609344);
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1609.344);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 160934.4);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 5280);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 63360);
break;
case UnitTypeFeet:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0003048);
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.3048);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 30.48);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000189393939394);
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 12);
break;
case UnitTypeInches:
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0000254);
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.0254);
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 2.54);
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0000157828282828);
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.0833333333333);
break;
case UnitTypeMiles:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a *= ((double)1.609344);
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a *= ((double)1609.344);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)160934.4);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)5280);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)63360);
break;
case UnitTypeFeet:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a *= ((double)0.0003048);
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a *= ((double)0.3048);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)30.48);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.000189393939394);
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
a *= ((double)12);
break;
case UnitTypeInches:
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
a *= ((double)0.0000254);
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
a *= ((double)0.0254);
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
a *= ((double)2.54);
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
a *= ((double)0.0000157828282828);
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
a *= ((double)0.0833333333333);
break;
}
}
if(overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
if (overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
}
uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type) {
return (
unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters || unit_type == UnitTypeCentimeters ||
unit_type == UnitTypeMiles || unit_type == UnitTypeFeet || unit_type == UnitTypeInches
);
return (
unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters ||
unit_type == UnitTypeCentimeters || unit_type == UnitTypeMiles ||
unit_type == UnitTypeFeet || unit_type == UnitTypeInches);
}
//
@@ -200,31 +231,31 @@ uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type)
//
void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state) {
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
double a = strtof(multi_converter_state->buffer_orig, NULL);
uint8_t overflow = 0;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeDegree:
if (multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double) 0.0174532925199);
break;
switch(multi_converter_state->unit_type_orig) {
default:
break;
case UnitTypeDegree:
if(multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double)0.0174532925199);
break;
case UnitTypeRadian:
if (multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double) 57.2957795131);
break;
}
case UnitTypeRadian:
if(multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double)57.2957795131);
break;
}
if (overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
if(overflow) {
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
} else {
int ret = snprintf(
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
}
}
uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type) {
return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian);
return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian);
}

View File

@@ -8,7 +8,9 @@
#define MULTI_CONVERTER_AVAILABLE_UNITS 14
#define multi_converter_get_unit(unit_type) multi_converter_available_units[unit_type]
#define multi_converter_get_unit_type_offset(unit_type, offset) (((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % MULTI_CONVERTER_AVAILABLE_UNITS)
#define multi_converter_get_unit_type_offset(unit_type, offset) \
(((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % \
MULTI_CONVERTER_AVAILABLE_UNITS)
// the modulo operation will fail with extremely large values on the units array
// DEC / HEX / BIN
@@ -31,41 +33,139 @@ uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type);
// each unit is made of comma? + negative? + keyboard_length + mini_name + name + convert function + allowed function
// (setting functions as NULL will cause convert / select options to be ignored)
//
static const MultiConverterUnit multi_converter_unit_dec = { 0, 0, 10, "DEC\0", "Decimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
static const MultiConverterUnit multi_converter_unit_hex = { 0, 0, 16, "HEX\0", "Hexadecimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
static const MultiConverterUnit multi_converter_unit_bin = { 0, 0, 2, "BIN\0", "Binary\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
static const MultiConverterUnit multi_converter_unit_dec = {
0,
0,
10,
"DEC\0",
"Decimal\0",
multi_converter_unit_dec_hex_bin_convert,
multi_converter_unit_dec_hex_bin_allowed};
static const MultiConverterUnit multi_converter_unit_hex = {
0,
0,
16,
"HEX\0",
"Hexadecimal\0",
multi_converter_unit_dec_hex_bin_convert,
multi_converter_unit_dec_hex_bin_allowed};
static const MultiConverterUnit multi_converter_unit_bin = {
0,
0,
2,
"BIN\0",
"Binary\0",
multi_converter_unit_dec_hex_bin_convert,
multi_converter_unit_dec_hex_bin_allowed};
static const MultiConverterUnit multi_converter_unit_cel = { 1, 1, 10, "CEL\0", "Celsius\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
static const MultiConverterUnit multi_converter_unit_far = { 1, 1, 10, "FAR\0", "Fahernheit\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
static const MultiConverterUnit multi_converter_unit_kel = { 1, 1, 10, "KEL\0", "Kelvin\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
static const MultiConverterUnit multi_converter_unit_cel = {
1,
1,
10,
"CEL\0",
"Celsius\0",
multi_converter_unit_temperature_convert,
multi_converter_unit_temperature_allowed};
static const MultiConverterUnit multi_converter_unit_far = {
1,
1,
10,
"FAR\0",
"Fahernheit\0",
multi_converter_unit_temperature_convert,
multi_converter_unit_temperature_allowed};
static const MultiConverterUnit multi_converter_unit_kel = {
1,
1,
10,
"KEL\0",
"Kelvin\0",
multi_converter_unit_temperature_convert,
multi_converter_unit_temperature_allowed};
static const MultiConverterUnit multi_converter_unit_km = { 1, 0, 10, "KM\0", "Kilometers\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_m = { 1, 0, 10, "M\0", "Meters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_cm = { 1, 0, 10, "CM\0", "Centimeters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_mi = { 1, 0, 10, "MI\0", "Miles\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_ft = { 1, 0, 10, "FT\0", "Feet\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_in = { 1, 0, 10, " \"\0", "Inches\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
static const MultiConverterUnit multi_converter_unit_km = {
1,
0,
10,
"KM\0",
"Kilometers\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_m = {
1,
0,
10,
"M\0",
"Meters\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_cm = {
1,
0,
10,
"CM\0",
"Centimeters\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_mi = {
1,
0,
10,
"MI\0",
"Miles\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_ft = {
1,
0,
10,
"FT\0",
"Feet\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_in = {
1,
0,
10,
" \"\0",
"Inches\0",
multi_converter_unit_distance_convert,
multi_converter_unit_distance_allowed};
static const MultiConverterUnit multi_converter_unit_deg = { 1, 0, 10, "DEG\0", "Degree\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed };
static const MultiConverterUnit multi_converter_unit_rad = { 1, 0, 10, "RAD\0", "Radian\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed };
static const MultiConverterUnit multi_converter_unit_deg = {
1,
0,
10,
"DEG\0",
"Degree\0",
multi_converter_unit_angle_convert,
multi_converter_unit_angle_allowed};
static const MultiConverterUnit multi_converter_unit_rad = {
1,
0,
10,
"RAD\0",
"Radian\0",
multi_converter_unit_angle_convert,
multi_converter_unit_angle_allowed};
// index order set by the MultiConverterUnitType enum element (multi_converter_definitions.h)
static const MultiConverterUnit multi_converter_available_units[MULTI_CONVERTER_AVAILABLE_UNITS] = {
[UnitTypeDec] = multi_converter_unit_dec,
[UnitTypeHex] = multi_converter_unit_hex,
[UnitTypeBin] = multi_converter_unit_bin,
[UnitTypeDec] = multi_converter_unit_dec,
[UnitTypeHex] = multi_converter_unit_hex,
[UnitTypeBin] = multi_converter_unit_bin,
[UnitTypeCelsius] = multi_converter_unit_cel,
[UnitTypeFahernheit] = multi_converter_unit_far,
[UnitTypeKelvin] = multi_converter_unit_kel,
[UnitTypeCelsius] = multi_converter_unit_cel,
[UnitTypeFahernheit] = multi_converter_unit_far,
[UnitTypeKelvin] = multi_converter_unit_kel,
[UnitTypeKilometers] = multi_converter_unit_km,
[UnitTypeMeters] = multi_converter_unit_m,
[UnitTypeCentimeters] = multi_converter_unit_cm,
[UnitTypeMiles] = multi_converter_unit_mi,
[UnitTypeFeet] = multi_converter_unit_ft,
[UnitTypeInches] = multi_converter_unit_in,
[UnitTypeKilometers] = multi_converter_unit_km,
[UnitTypeMeters] = multi_converter_unit_m,
[UnitTypeCentimeters] = multi_converter_unit_cm,
[UnitTypeMiles] = multi_converter_unit_mi,
[UnitTypeFeet] = multi_converter_unit_ft,
[UnitTypeInches] = multi_converter_unit_in,
[UnitTypeDegree] = multi_converter_unit_deg,
[UnitTypeRadian] = multi_converter_unit_rad,
[UnitTypeDegree] = multi_converter_unit_deg,
[UnitTypeRadian] = multi_converter_unit_rad,
};

View File

@@ -1,7 +1,7 @@
App(
appid="music_player",
name="Music Player",
apptype=FlipperAppType.PLUGIN,
apptype=FlipperAppType.GAME,
entry_point="music_player_app",
cdefines=["APP_MUSIC_PLAYER"],
requires=[
@@ -10,7 +10,7 @@ App(
],
provides=["music_player_start"],
stack_size=2 * 1024,
order=20,
order=45,
)
App(

View File

@@ -94,6 +94,11 @@ Nfc* nfc_alloc() {
view_dispatcher_add_view(
nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack));
// Detect Reader
nfc->detect_reader = detect_reader_alloc();
view_dispatcher_add_view(
nfc->view_dispatcher, NfcViewDetectReader, detect_reader_get_view(nfc->detect_reader));
// Generator
nfc->generator = NULL;
@@ -158,6 +163,10 @@ void nfc_free(Nfc* nfc) {
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack);
dict_attack_free(nfc->dict_attack);
// Detect Reader
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDetectReader);
detect_reader_free(nfc->detect_reader);
// Worker
nfc_worker_stop(nfc->worker);
nfc_worker_free(nfc->worker);

View File

@@ -28,6 +28,7 @@
#include <lib/nfc/parsers/nfc_supported_card.h>
#include "views/dict_attack.h"
#include "views/detect_reader.h"
#include <nfc/scenes/nfc_scene.h>
#include <nfc/helpers/nfc_custom_event.h>
@@ -71,6 +72,7 @@ struct Nfc {
TextBox* text_box;
Widget* widget;
DictAttack* dict_attack;
DetectReader* detect_reader;
const NfcGenerator* generator;
};
@@ -85,6 +87,7 @@ typedef enum {
NfcViewTextBox,
NfcViewWidget,
NfcViewDictAttack,
NfcViewDetectReader,
} NfcView;
Nfc* nfc_alloc();

View File

@@ -48,4 +48,6 @@ ADD_SCENE(nfc, rpc, Rpc)
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
ADD_SCENE(nfc, retry_confirm, RetryConfirm)
ADD_SCENE(nfc, detect_reader, DetectReader)
ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo)
ADD_SCENE(nfc, mfkey_complete, MfkeyComplete)
ADD_SCENE(nfc, nfc_data_info, NfcDataInfo)

View File

@@ -1,126 +1,48 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
#define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200)
enum {
NfcSceneDetectReaderStateWidget,
NfcSceneDetectReaderStateTextBox,
};
bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) {
UNUSED(event);
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
return true;
}
void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_detect_reader_textbox_callback(void* context) {
void nfc_scene_detect_reader_callback(void* context) {
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
// Add widget with device name or inform that data received
static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) {
Widget* widget = nfc->widget;
widget_reset(widget);
widget_add_icon_element(widget, 0, 14, &I_Reader_detect);
widget_add_string_element(
widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader");
widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating...");
if(data_received) {
widget_add_button_element(
widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc);
}
}
void nfc_scene_detect_reader_on_enter(void* context) {
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate);
FuriHalNfcDevData nfc_params = {
.uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34},
.uid_len = 7,
.atqa = {0x44, 0x00},
.sak = 0x08,
.type = FuriHalNfcTypeA,
};
nfc->dev->dev_data.nfc_data = nfc_params;
// Setup Widget
nfc_scene_detect_reader_widget_config(nfc, false);
// Setup TextBox
TextBox* text_box = nfc->text_box;
text_box_set_font(text_box, TextBoxFontHex);
text_box_set_focus(text_box, TextBoxFocusEnd);
string_reset(nfc->text_box_store);
// Set Widget state and view
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
// Start worker
memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData));
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
nfc_worker_start(
nfc->worker,
NfcWorkerStateUidEmulate,
NfcWorkerStateAnalyzeReader,
&nfc->dev->dev_data,
nfc_detect_reader_worker_callback,
nfc);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader);
nfc_blink_start(nfc);
}
bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data;
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader);
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) {
// Add data button to widget if data is received for the first time
if(!string_size(nfc->text_box_store)) {
nfc_scene_detect_reader_widget_config(nfc, true);
}
// Update TextBox data
if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) {
string_cat_printf(nfc->text_box_store, "R:");
for(uint16_t i = 0; i < reader_data->size; i++) {
string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]);
}
string_push_back(nfc->text_box_store, '\n');
text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store));
}
memset(reader_data, 0, sizeof(NfcReaderRequestData));
if(event.event == NfcCustomEventViewExit) {
nfc_worker_stop(nfc->worker);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo);
consumed = true;
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox);
consumed = true;
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
if(state == NfcSceneDetectReaderStateTextBox) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
} else if(event.event == NfcWorkerEventDetectReaderMfkeyCollected) {
detect_reader_inc_nonce_cnt(nfc->detect_reader);
consumed = true;
}
}
@@ -135,9 +57,7 @@ void nfc_scene_detect_reader_on_exit(void* context) {
nfc_worker_stop(nfc->worker);
// Clear view
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
string_reset(nfc->text_box_store);
detect_reader_reset(nfc->detect_reader);
nfc_blink_stop(nfc);
}

View File

@@ -0,0 +1,49 @@
#include "../nfc_i.h"
void nfc_scene_mfkey_complete_callback(GuiButtonType result, InputType type, void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_mfkey_complete_on_enter(void* context) {
Nfc* nfc = context;
widget_add_string_element(nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!");
widget_add_string_multiline_element(
nfc->widget,
64,
32,
AlignCenter,
AlignCenter,
FontSecondary,
"Now use mfkey32v2\nto extract keys");
widget_add_button_element(
nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mfkey_complete_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeCenter) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
} else if(event.event == SceneManagerEventTypeBack) {
consumed =
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
}
return consumed;
}
void nfc_scene_mfkey_complete_on_exit(void* context) {
Nfc* nfc = context;
widget_reset(nfc->widget);
}

View File

@@ -0,0 +1,55 @@
#include "../nfc_i.h"
#include <lib/nfc/helpers/mfkey32.h>
void nfc_scene_mfkey_nonces_info_callback(GuiButtonType result, InputType type, void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_mfkey_nonces_info_on_enter(void* context) {
Nfc* nfc = context;
string_t temp_str;
string_init(temp_str);
uint16_t nonces_saved = mfkey32_get_auth_sectors(temp_str);
widget_add_text_scroll_element(nfc->widget, 0, 22, 128, 42, string_get_cstr(temp_str));
string_printf(temp_str, "Nonces saved %d", nonces_saved);
widget_add_string_element(
nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, string_get_cstr(temp_str));
widget_add_string_element(
nfc->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:");
widget_add_button_element(
nfc->widget, GuiButtonTypeRight, "Next", nfc_scene_mfkey_nonces_info_callback, nfc);
string_clear(temp_str);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyComplete);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed =
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
}
return consumed;
}
void nfc_scene_mfkey_nonces_info_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
widget_reset(nfc->widget);
}

View File

@@ -49,7 +49,12 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
consumed = true;
} else if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
if(sd_exist) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
}
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
@@ -61,7 +66,6 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
consumed = true;
} else if(event.event == SubmenuIndexDebug) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
consumed = true;
}

View File

@@ -0,0 +1,115 @@
#include "detect_reader.h"
#include <gui/elements.h>
struct DetectReader {
View* view;
DetectReaderDoneCallback callback;
void* context;
};
typedef struct {
uint16_t nonces;
} DetectReaderViewModel;
static void detect_reader_draw_callback(Canvas* canvas, void* model) {
DetectReaderViewModel* m = model;
char text[32] = {};
snprintf(text, sizeof(text), "Tap the reader several times");
canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Tap the reader several times");
if(m->nonces == 0) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 52, 22, AlignLeft, AlignTop, "Emulating...");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 52, 35, AlignLeft, AlignTop, "MIFARE Classic");
canvas_draw_icon(canvas, 0, 13, &I_Tap_reader_36x38);
} else {
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 54, 22, AlignLeft, AlignTop, "Collecting...");
canvas_set_font(canvas, FontSecondary);
snprintf(text, sizeof(text), "Nonces: %d", m->nonces);
canvas_draw_str_aligned(canvas, 54, 35, AlignLeft, AlignTop, text);
elements_button_right(canvas, "Next");
canvas_draw_icon(canvas, 6, 15, &I_ArrowC_1_36x36);
}
}
static bool detect_reader_input_callback(InputEvent* event, void* context) {
DetectReader* detect_reader = context;
furi_assert(detect_reader->callback);
bool consumed = false;
uint8_t nonces = 0;
with_view_model(
detect_reader->view, (DetectReaderViewModel * model) {
nonces = model->nonces;
return false;
});
if(event->type == InputTypeShort) {
if(event->key == InputKeyRight) {
if(nonces > 0) {
detect_reader->callback(detect_reader->context);
consumed = true;
}
}
}
return consumed;
}
DetectReader* detect_reader_alloc() {
DetectReader* detect_reader = malloc(sizeof(DetectReader));
detect_reader->view = view_alloc();
view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel));
view_set_draw_callback(detect_reader->view, detect_reader_draw_callback);
view_set_input_callback(detect_reader->view, detect_reader_input_callback);
view_set_context(detect_reader->view, detect_reader);
return detect_reader;
}
void detect_reader_free(DetectReader* detect_reader) {
furi_assert(detect_reader);
view_free(detect_reader->view);
free(detect_reader);
}
void detect_reader_reset(DetectReader* detect_reader) {
furi_assert(detect_reader);
with_view_model(
detect_reader->view, (DetectReaderViewModel * model) {
model->nonces = 0;
return false;
});
}
View* detect_reader_get_view(DetectReader* detect_reader) {
furi_assert(detect_reader);
return detect_reader->view;
}
void detect_reader_set_callback(
DetectReader* detect_reader,
DetectReaderDoneCallback callback,
void* context) {
furi_assert(detect_reader);
furi_assert(callback);
detect_reader->callback = callback;
detect_reader->context = context;
}
void detect_reader_inc_nonce_cnt(DetectReader* detect_reader) {
furi_assert(detect_reader);
with_view_model(
detect_reader->view, (DetectReaderViewModel * model) {
model->nonces++;
return false;
});
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
#include <gui/view.h>
#include <gui/modules/widget.h>
typedef struct DetectReader DetectReader;
typedef void (*DetectReaderDoneCallback)(void* context);
DetectReader* detect_reader_alloc();
void detect_reader_free(DetectReader* detect_reader);
void detect_reader_reset(DetectReader* detect_reader);
View* detect_reader_get_view(DetectReader* detect_reader);
void detect_reader_set_callback(
DetectReader* detect_reader,
DetectReaderDoneCallback callback,
void* context);
void detect_reader_inc_nonce_cnt(DetectReader* detect_reader);

View File

@@ -9,7 +9,7 @@
/* When less than LFS_RESERVED_PAGES_COUNT are left free, creation &
* modification of non-dot files is restricted */
#define LFS_RESERVED_PAGES_COUNT 5
#define LFS_RESERVED_PAGES_COUNT 3
typedef struct {
const size_t start_address;

View File

@@ -13,7 +13,7 @@
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
#define TEST_RANDOM_COUNT_PARSE 208
#define TEST_RANDOM_COUNT_PARSE 232
#define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler;
@@ -427,6 +427,13 @@ MU_TEST(subghz_decoder_intertechno_v3_test) {
"Test decoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n");
}
MU_TEST(subghz_decoder_clemsa_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/clemsa_raw.sub"), SUBGHZ_PROTOCOL_CLEMSA_NAME),
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
}
//test encoders
MU_TEST(subghz_encoder_princeton_test) {
mu_assert(
@@ -542,6 +549,12 @@ MU_TEST(subghz_encoder_intertechno_v3_test) {
"Test encoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n");
}
MU_TEST(subghz_encoder_clemsa_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/clemsa.sub")),
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
}
MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
}
@@ -581,6 +594,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
MU_RUN_TEST(subghz_decoder_magellen_test);
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test);
@@ -601,6 +615,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
MU_RUN_TEST(subghz_encoder_magellen_test);
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
MU_RUN_TEST(subghz_encoder_clemsa_test);
MU_RUN_TEST(subghz_random_test);
subghz_test_deinit();

View File

@@ -1,9 +1,9 @@
App(
appid="wav_player",
name="WAV Player",
apptype=FlipperAppType.PLUGIN,
apptype=FlipperAppType.GAME,
entry_point="wav_player_app",
cdefines=["APP_WAV_PLAYER"],
stack_size=4 * 1024,
order=21,
order=46,
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -1,10 +1,11 @@
V:0
T:1661895856
T:1662385171
D:badusb
D:dolphin
D:infrared
D:music_player
D:nfc
D:rfidfuzzer
D:subghz
D:u2f
D:unirf
@@ -242,7 +243,7 @@ F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm
F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt
D:infrared/assets
F:13fe3def425723bccd05fe09c745a335:122232:infrared/assets/ac.ir
F:a3dad0916846fef907c93a65dd8e331d:52540:infrared/assets/audio.ir
F:f0569fce8a54aa74780632cb4042cc85:58026:infrared/assets/audio.ir
F:1703fea41cb6ef71553b91a1004dc936:82397:infrared/assets/fans.ir
F:08d864cf44e2557fb25aec8837740de5:6567:infrared/assets/projectors.ir
F:77bc8314d113b8618942589f21a491fc:127350:infrared/assets/tv.ir
@@ -252,6 +253,7 @@ F:81dc04c7b181f94b644079a71476dff4:4742:nfc/assets/aid.nfc
F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc
F:41b4f08774249014cb8d3dffa5f5c07d:1757:nfc/assets/currency_code.nfc
F:5f57e2ecfc850efb74c7d2677eb75a2e:51832:nfc/assets/mf_classic_dict.nfc
F:319958d37b2316a2e752ebb856c32524:126:rfidfuzzer/example_uids.txt
D:subghz/assets
F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo
F:50cf77ba8b935ee6cb3b6f111cf2d93d:286:subghz/assets/dangerous_settings

View File

@@ -1,6 +1,6 @@
Filetype: IR library file
Version: 1
# Last Updated 30th Aug, 2022
# Last Updated 3rd Sep, 2022
#
name: POWER
type: parsed
@@ -1256,3 +1256,46 @@ frequency: 38000
duty_cycle: 0.330000
data: 8435 4189 547 1557 547 1557 547 505 541 510 547 505 541 1562 542 510 547 1557 547 505 541 510 547 1557 546 1557 547 1557 547 505 541 510 547 505 541 21550 547 1558 545 1558 546 506 540 511 546 506 540 1563 541 511 546 1558 545 506 540 511 546 1558 546 1558 546 1558 546 506 540 511 546 506 540 21551 546 1558 546 1558 546 506 540 512 545 506 540 1563 540 512 545 1558 546 506 540 512 545 1558 546 1558 546 1558 546 506 540 512 545 506 540 21551 546 1559 545 1559 545 507 539 512 545 507 539 1564 539 512 545 1559 545 507 539 512 545 1559 545 1559 545 1559 545 507 539 512 545 507 539 21552 545 1559 545 1559 545 507 539 513 544 507 539 1564 540 512 545 1559 545 507 539 512 545 1559 545 1559 545 1559 545 507 539 512 545 507 539 21552 545 1559 545 1559 545 507 539 513 544 507 539 1565 538 513 544 1559 545 507 539 513 544 1559 545 1559 544 1559 545 534 512 513 544 507 539 21553 544 1560 544 1560 544 534 512 513 544 508 538 1565 539 539 518 1560 544 534 512 513 544 1560 544 1560 544 1560 544 534 512 513 544 508 538
#
#
name: POWER
type: parsed
protocol: NEC
address: 20 00 00 00
command: 41 00 00 00
#
name: VOL+
type: parsed
protocol: NEC
address: 20 00 00 00
command: 42 00 00 00
#
name: VOL-
type: parsed
protocol: NEC
address: 20 00 00 00
command: 43 00 00 00
#
name: MUTE
type: parsed
protocol: NEC
address: 20 00 00 00
command: 1B 00 00 00
#
name: VOL+
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8498 4205 651 1471 576 530 550 1572 547 535 545 536 544 1578 541 540 550 1572 547 535 545 1576 543 539 541 1580 549 1572 547 534 546 1576 543 539 541 540 550 1571 548 533 547 1574 545 537 543 539 541 541 549 532 548 1574 545 536 544 1578 541 540 550 1571 548 1572 547 1574 545 1576 543 26533 8497 4203 653 1468 569 538 542 1579 550 531 549 533 547 1573 546 535 545 1576 543 538 542 1579 550 531 549 1571 548 1573 546 536 544 1576 543 539 541 540 550 1571 548 533 547 1574 545 536 544 538 542 540 550 530 550 1572 547 534 546 1575 544 537 543 1578 541 1579 550 1570 549 1572 547 26524 8496 4207 576 1570 549 533 547 1574 545 537 543 539 541 1580 549 532 548 1573 546 536 544 1577 542 539 551 1570 549 1572 547 534 546 1575 544 538 542 540 550 1571 548 534 546 1575 544 538 542 540 550 531 549 533 547 1575 544 537 543 1579 550 531 549 1571 548 1572 547 1574 545 1576 543 26529 8491 4211 573 1573 546 535 545 1576 543 539 551 530 550 1571 548 533 547 1574 545 536 544 1578 541 540 550 1570 549 1572 547 534 546 1575 544 538 542 539 541 1580 549 532 548 1572 547 535 545 537 543 539 541 540 550 1571 548 533 547 1574 545 537 543 1578 541 1579 550 1571 548 1574 545 26522 8498 4202 571 1574 545 537 543 1578 541 541 549 532 548 1573 546 534 546 1575 544 537 543 1577 542 540 550 1570 549 1571 548 533 547 1574 545 537 543 538 542 1579 550 531 549 1572 547 534 546 536 544 537 543 538 542 1579 550 531 549 1572 547 535 545 1575 544 1577 542 1579 550 1571 548 26522 8498 4203 570 1575 544 537 543 1578 541 541 549 532 548 1573 546 535 545 1575 544 538 542 1579 550 531 549 1571 548 1572 547 535 545 1575 544 538 542 539 541 1580 549 532 548 1572 547 534 546 536 544 537 543 539 541 1579 550 531 549 1571 548 533 547 1573 546 1574 545 1575 544 1577 542
#
name: VOL-
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8490 4211 573 1573 546 535 545 1576 543 539 541 540 550 1571 548 532 548 1573 546 535 545 1576 543 539 541 1579 550 1571 548 533 547 1574 545 536 544 1576 543 1578 541 540 550 1570 549 533 547 534 546 536 544 537 543 539 541 540 550 1570 549 532 548 1572 547 1573 546 1574 545 1576 543 26528 8492 4208 648 1475 572 534 546 1575 544 537 543 539 541 1580 549 532 548 1573 546 535 545 1576 543 538 542 1579 550 1571 548 533 547 1575 544 537 543 1577 542 1579 550 532 548 1573 546 535 545 537 543 539 541 541 549 532 548 533 547 1575 544 537 543 1578 541 1579 550 1571 548 1573 546 26523 8496 4203 622 1499 569 537 543 1578 541 541 549 532 548 1572 547 534 546 1574 545 537 543 1578 541 540 550 1570 549 1571 548 534 546 1575 544 538 542 1578 541 1580 549 532 548 1573 546 536 544 538 542 540 550 532 548 533 547 535 545 1576 543 539 541 1579 550 1571 548 1573 546 1574 545 26521 8498 4201 572 1573 546 536 544 1577 542 540 550 531 549 1571 548 533 547 1573 546 536 544 1576 543 539 541 1579 550 1570 549 532 548 1573 546 535 545 1576 543 1578 541 540 550 1571 548 533 547 535 545 537 543 538 542 540 550 531 549 1572 547 534 546 1575 544 1576 543 1577 542 1579 550 26522 8498 4203 570 1575 544 538 542 1579 550 532 548 533 547 1575 544 537 543 1578 541 540 550 1570 549 533 547 1573 546 1575 544 537 543 1578 541 540 550 1571 548 1573 546 536 544 1578 551 531 549 533 547 535 545 537 543 539 541 540 550 1571 548 533 547 1575 544 1576 543 1578 541 1580 549 26521 8498 4203 570 1576 543 538 542 1580 549 532 548 533 547 1574 545 536 544 1577 542 540 550 1570 549 533 547 1573 546 1575 544 538 542 1579 550 531 549 1571 548 1573 546 536 544 1576 543 539 551 531 549 532 548 534 546 536 544 538 542 1579 550 531 549 1572 547 1573 546 1574 545 1576 543
#
name: POWER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8500 4203 653 1468 569 538 542 1579 550 532 548 534 546 1575 544 536 544 1578 541 540 550 1572 547 534 546 1575 544 1577 542 539 541 1580 550 532 548 534 546 535 545 1576 543 1577 542 1579 551 532 548 534 546 536 544 1577 542 1578 552 531 549 533 547 534 546 1574 545 1575 544 1577 542 26533 8492 4211 573 1573 546 535 545 1576 543 539 551 530 550 1571 548 533 547 1574 545 536 544 1577 542 540 550 1570 549 1572 547 534 546 1576 543 539 541 541 549 532 548 1573 546 1574 545 1576 543 539 551 531 549 533 547 1573 546 1576 543 539 541 541 549 532 548 1573 546 1574 545 1576 543 26532 8493 4209 575 1571 548 533 547 1575 544 537 543 539 541 1580 550 531 549 1572 547 534 546 1576 543 538 542 1579 551 1570 549 532 548 1574 545 537 543 538 542 540 550 1570 549 1572 547 1575 544 537 543 539 541 540 550 1571 548 1573 546 536 544 538 542 539 551 1570 549 1572 547 1574 545 26530 8496 4207 567 1579 551 531 549 1572 547 535 545 536 544 1576 543 538 542 1579 551 530 550 1571 548 534 546 1574 545 1576 543 538 542 1579 551 532 548 533 547 534 546 1576 543 1577 542 1579 551 532 548 534 546 535 545 1576 543 1578 541 540 550 532 548 533 547 1574 545 1575 544 1577 542 26531 8495 4210 574 1571 548 534 546 1575 544 538 542 539 551 1569 550 531 549 1572 547 535 545 1576 543 539 541 1579 550 1571 548 534 546 1575 544 538 542 540 550 531 549 1572 547 1549 570 1551 568 539 551 531 549 532 548 1573 546 1550 569 538 542 540 550 531 549 1571 548 1548 571 1550 569
#

View File

@@ -0,0 +1,8 @@
# Example file, P.S. keep empty line at the end!
0000000000
FE00000000
CAFE000000
00CAFE0000
0000CAFE00
000000CAFE
00000000CA

View File

@@ -0,0 +1,7 @@
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: Clemsa
Bit: 18
Key: 00 00 00 00 00 02 FC AA

View File

@@ -0,0 +1,14 @@
Filetype: Flipper SubGhz RAW File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: RAW
RAW_Data: -334 10811 -4320 65 -100 65 -698 10157 -7550 65 -200 165 -166 133 -66 531 -66 331 -102 197 -132 133 -298 99 -132 263 -200 261 -988 99 -262 131 -296 97 -132 229 -100 459 -100 131 -132 393 -100 1119 -100 8325 -6376 133 -366 131 -562 65 -1034 131 -198 563 -168 365 -66 229 -332 297 -100 231 -166 429 -132 295 -166 97 -100 195 -724 97 -132 97 -1088 163 -200 1651 -100 2885 -8520 365 -166 97 -1558 163 -198 163 -132 465 -134 131 -66 267 -198 65 -232 299 -66 165 -166 65 -498 165 -100 233 -200 133 -166 131 -68 821 -100 263 -66 7633 -7610 1259 -200 99 -98 165 -1196 99 -132 263 -266 99 -200 463 -66 627 -66 1981 -98 7801 -4004 97 -628 65 -264 133 -1088 163 -134 131 -928 297 -166 133 -134 131 -266 297 -596 229 -164 427 -564 197 -166 265 -198 65 -100 559 -6708 131 -132 131 -430 527 -200 367 -66 263 -198 233 -98 299 -68 365 -296 465 -132 855 -66 857 -98 4741 -8312 99 -364 163 -200 133 -1428 529 -132 65 -166 595 -1392 97 -100 97 -132 99 -264 199 -828 99 -398 297 -66 233 -98 861 -100 663 -100 2357 -100 5075 -4640 131 -3312 231 -100 363 -132 99 -296 99 -132 165 -132 363 -98 165 -130 65 -98 165 -132 163 -130 63 -164 297 -198 9769 -3852 133 -98 67 -1226 329 -526 99 -164 295 -496 1713 -196 1681 -130 131 -132 5497 -7230 65 -1150 133 -330 259 -66 329 -100 97 -988 165 -134 197 -166 67 -100 361 -68 461 -100 231 -132 165 -66 365 -264 231 -100 99 -98 265 -696 99 -166 199 -100 101 -962 7101 -6484 363 -760 363 -132 265 -134 431 -264 329 -66 427 -330 263 -164 593 -130 231 -130 627 -66 399 -432 329 -526 131 -100 591 -166 9305 -4044 65 -3532 361 -98 163 -66 461 -264 197 -98 391 -66 329 -132 165 -136 463 -66 529 -166 131 -100 199 -264 133 -66 361 -98 689 -66 229 -198 627 -66 297 -100 261 -66 1685 -134 7883 -3932 229 -728 133 -98 133 -862 65 -132 99 -498 297 -166 133 -332 197 -132 693 -198 97 -656 1087 -64 6209 -6164 131 -266 99 -496 165 -432 67 -100 97 -330 821 -98 361 -100 493 -164 133 -66 197 -200 431 -66 65 -66 399 -66 331 -200 199 -402 131 -664 10955 -5314 65 -262 97 -198 97 -724 99 -196 65 -592 327 -66 625 -262 131 -66 197 -64 427 -132 65 -628 265 -332 329 -368 789 -66 8809 -6238 129 -328 295 -232 363 -98 431 -100 199 -98 261 -530 561 -592 263 -132 1645 -5358 65 -764 65 -330 165 -1158 197 -432 265 -98 397 -166 463 -498 561 -398 199 -66 199 -66 3479
RAW_Data: -12124 165 -626 65 -890 229 -362 1329 -66 2187 -98 2081 -66 725 -134 3309 -9856 165 -166 263 -198 65 -960 653 -66 261 -66 821 -66 12463 -4032 97 -166 97 -924 65 -464 97 -68 163 -198 165 -100 263 -232 97 -366 633 -12244 65 -332 231 -200 197 -134 197 -234 2457 -134 399 -132 923 -198 197 -64 331 -132 295 -66 11377 -3896 163 -98 65 -194 131 -132 231 -366 131 -132 65 -1050 197 -200 299 -66 3007 -100 11685 -6172 133 -1154 491 -100 293 -200 65 -98 429 -266 463 -64 797 -98 265 -266 397 -132 1227 -66 8485 -4224 97 -166 65 -100 199 -2706 65 -66 263 -98 299 -298 231 -100 499 -66 97 -134 295 -66 431 -198 565 -66 2093 -100 533 -4056 65 -1482 229 -1160 165 -168 299 -166 459 -66 165 -134 99 -100 497 -166 397 -200 431 -200 65 -66 661 -164 529 -66 4671 -8442 131 -100 65 -66 165 -530 131 -132 597 -66 963 -488 275 -2806 2641 -438 2639 -420 2691 -412 2677 -404 2669 -416 2693 -416 361 -2730 367 -2724 2665 -422 355 -2726 2687 -394 399 -2698 2697 -390 375 -2732 2701 -388 355 -21244 2697 -416 377 -2722 2675 -416 2689 -388 2707 -396 2679 -414 2689 -392 2717 -388 375 -2706 383 -2732 2673 -396 365 -2734 2695 -386 377 -2728 2689 -384 389 -2730 2671 -386 379 -21252 2693 -416 347 -2748 2687 -396 2707 -384 2701 -388 2679 -412 2701 -418 2669 -404 363 -2726 385 -2698 2699 -416 355 -2706 2689 -416 365 -2736 2673 -386 415 -2692 2709 -378 361 -21270 2685 -452 311 -2774 2637 -442 2665 -418 2661 -448 2653 -408 2703 -384 2687 -410 365 -2738 355 -2742 2689 -382 371 -2738 2677 -384 415 -2700 2673 -410 363 -2730 2701 -386 357 -21270 2705 -398 361 -2740 2689 -386 2679 -414 2687 -410 2673 -418 2697 -386 2681 -412 383 -2702 395 -2706 2703 -380 385 -2696 2709 -418 355 -2702 2699 -418 361 -2700 2717 -386 375 -21242 2697 -418 355 -2748 2695 -382 2683 -410 2703 -380 2707 -384 2705 -404 2675 -416 383 -2700 359 -2728 2695 -382 385 -2728 2687 -416 357 -2708 2705 -386 389 -2700 2717 -388 373 -21234 2721 -418 353 -2724 2667 -416 2705 -380 2693 -386 2711 -384 2691 -422 2699 -398 365 -2726 385 -2700 2703 -378 361 -2728 2697 -420 357 -2704 2711 -388 377 -2734 2667 -406 363 -21260 2699 -418 361 -2702 2711 -380 2695 -416 2689 -402 2687 -384 2695 -416 2677 -408 361 -2732 385 -2704 2707 -414 325 -2730 2695 -418 361 -2728 2669 -414 385 -2702 2701 -414 355 -21246 2705 -378 379 -2728 2693 -384 2715 -380 2717 -386 2695 -390 2709 -388 2683 -420 355 -2730 385 -2700 2685 -416 347 -2748 2683 -396 365 -2740 2667 -416 385 -2696 2693 -414 349 -21260 2689 -452 319 -2762 2631 -476 2627 -430 2689
RAW_Data: -416 2667 -412 2671 -416 2689 -424 347 -2732 353 -2738 2669 -410 363 -2728 2689 -410 349 -2742 2677 -386 415 -2696 2705 -380 361 -21254 2717 -386 413 -2700 2693 -380 2695 -416 2691 -398 2679 -416 2699 -384 2709 -382 367 -2726 361 -2730 2707 -382 377 -2728 2675 -386 411 -2684 2713 -414 357 -2704 2707 -388 357 -21276 2699 -382 385 -2724 2689 -416 2661 -416 2685 -384 2723 -388 2703 -400 2677 -416 385 -2696 357 -2724 2713 -384 375 -2726 2673 -420 355 -2728 2685 -396 397 -2688 2697 -408 363 -21252 2667 -484 311 -2766 2635 -476 2637 -420 2665 -448 2651 -408 2701 -384 2697 -406 355 -2758 327 -2736 2685 -420 361 -2728 2683 -384 385 -2726 2685 -414 357 -2708 2711 -388 375 -21246 2689 -448 323 -2750 2695 -378 2715 -386 2687 -392 2711 -384 2683 -416 2705 -412 327 -2732 387 -2728 2679 -416 357 -2738 2659 -418 363 -2732 2677 -386 413 -2700 2695 -380 391 -21258 2679 -406 383 -2706 2695 -384 2703 -418 2679 -404 2705 -380 2687 -418 2669 -410 359 -2726 387 -2696 2707 -416 357 -2710 2693 -418 361 -2730 2691 -380 385 -2730 2677 -414 357 -21254 2711 -382 385 -2700 2713 -414 2667 -384 2705 -418 2675 -406 2699 -382 2689 -418 365 -2726 377 -2708 2701 -384 389 -2698 2709 -398 361 -2738 2669 -416 385 -2692 2687 -418 357 -77456 131 -398 197 -132 295 -330 97 -132 229 -164 459 -164 295 -264 393 -264 719 -64 427 -98 855 -134 395 -98 297 -164 263 -262 65 -100 63 -132 197 -328 1185 -66 9359 -6420 261 -664 131 -100 299 -134 301 -232 363 -232 299 -200 165 -166 427 -230 299 -164 361 -394 1025 -100 225 -820 165 -1248 491 -100 293 -66 261 -264 131 -98 589 -164 655 -132 427 -132 295 -164 129 -132 163 -328 263 -196 627 -566 129 -100 131 -98 2377 -130 1255 -3878 297 -232 195 -132 65 -98 165 -596 397 -266 99 -198 363 -98 923 -100 431 -66 1383 -3724 297 -166 165 -66 99 -398 265 -266 463 -232 133 -232 65 -230 65 -266 959 -200 99 -298 231 -68 65 -100 97 -398 363 -132 199 -134 133 -134 133 -266 593 -66 363 -66 827 -2374 65 -1724 399 -166 265 -100 331 -198 165 -398 233 -98 233 -66 165 -266 97 -66 231 -132 165 -298 395 -234 99 -132 65 -100 99 -132 131 -66 297 -264 197 -194 229 -530 2189 -166 9577 -3702 199 -98 465 -398 97 -134 395 -132 429 -100 529 -68 263 -132 265 -368 263 -860 97 -100 163 -196 427 -98 163 -166 327 -98 493 -166 327 -98 233 -5094 99 -198 97 -100 65 -1250 131 -560 855 -66 855 -262 859 -164 10219 -7528 761 -66 1121 -100 429 -298 331 -232 263 -166 261 -166 265 -100 1427 -98 9787 -6682 131 -564 429
RAW_Data: -66 529 -66 2519 -66 265 -68 10101 -1794 65 -1890 393 -562 97 -132 197 -98 493 -330 97 -164 97 -230 327 -326 99 -100 97 -164 65 -132 293 -98 297 -166 161 -130 297 -230 1391 -68 11185 -3800 229 -230 297 -66 65 -198 65 -466 99 -464 99 -430 67 -698 295 -132 165 -164 1095 -66 299 -66 1321 -264 12675 -66 99 -166 229 -134 65 -330 165 -164 65 -890 131 -830 67 -66 1157 -100 167 -168 265 -66 827 -66 2047 -100 261 -594 2279 -134 10701 -3890 163 -1384 67 -98 99 -1322 99 -98 65 -398 823 -66 65 -68 927 -100 495 -132 593 -100 165 -198 1387 -1022 131 -728 99 -662 97 -462 495 -200 829 -330 563 -100 297 -330 65 -598 165 -592 295 -166 131 -764 165 -164 565 -66 131 -166 165 -66 9675 -5052 165 -2878 199 -66 265 -432 265 -66 267 -898 163 -132 231 -198 229 -164 97 -100 4445 -66 7853 -636 199 -662 265 -298 233 -1428 331 -134 1791 -66 1649 -66 297 -100 361 -198 559 -98 363 -200 1315 -66 265 -98 1049 -132 1647 -66 265 -822 295 -526 131 -1712 199 -166 231 -200 165 -66 265 -166 97 -132 163 -164 395 -630 495 -168 297 -298 229 -266 629 -200 133 -132 133 -166 65 -132 99 -100 131 -66 67 -98 133 -496 1391 -98 1751 -164 359 -132 97 -164 263 -64 691 -66 199 -66 293 -98 589 -198 11299 -3968 65 -68 65 -2702 65 -1186 927 -166 65 -66 429 -134 197 -134 529 -200 67 -66 231 -100 2151 -4014 97 -1486 99 -464 65 -330 129 -330 331 -134 599 -66 497 -200 165 -66 661 -166 6881 -8830 295 -100 197 -232 725 -134 299 -166 229 -166 525 -198 295 -66 459 -66 329 -230 595 -98 299 -132 329 -66 99 -98 163 -134 229 -100 8345 -6726 131 -132 295 -66 1579 -66 329 -98 501 -132 231 -66 491 -298 331 -266 363 -132 1193 -168 8847 -4194 199 -828 65 -100 195 -262 197 -298 65 -898 65 -132 629 -66 229 -100 291 -100 623 -66 295 -66 461 -132 529 -632 597 -132 65 -100 97 -134 297 -100 297 -166 397 -168 527 -134 9603 -3850 99 -200 67 -896 959 -198 165 -100 229 -266 531 -64 165 -132 163 -296 3715 -11994 165 -1492 429 -68 263 -100 265 -330 199 -64 495 -132 363 -66 63 -166 297 -398 65 -100 231 -332 199 -100 7683 -4916 65 -1294 297 -1022 1325 -166 393 -132 165 -498 1255 -134 197 -198 427 -164 329 -132 631 -594 199 -196 99 -100 265 -134 1457 -100 3649 -8592 67 -268 131 -332 99 -100 65 -760 101 -198 297 -168 199 -132 369 -100 97 -132 99 -232 397 -198 99 -134 97 -100 231 -332 131 -796 329 -266 263
RAW_Data: -100 10841 -4030 163 -164 197 -398 195 -592 65 -132 63 -430 295 -298 263 -200 3517 -132 3763 -12296 99 -330 361 -98 99 -200 65 -430 165 -166 2327 -100 4051 -66 9653 -3478 197 -66 163 -198 167 -66 65 -598 165 -298 131 -666 199 -198 299 -298 165 -200 565 -66 797 -98 1125 -98 825 -100 4113 -6956 65 -5536 165 -266 99 -232 461 -198 65 -200 1989 -66 295 -66 723 -66 65 -98 329 -98 955 -66 559 -232 331 -66 10851 -1048 65 -3748 65 -498 99 -1392 99 -794 529 -98 331 -98 397 -164 363 -394 331 -266 299 -230 165 -66 3001 -568 197 -2872 2579 -468 2637 -472 2599 -488 2647 -426 2653 -448 2665 -392 379 -2734 381 -2700 2691 -388 377 -2732 2669 -420 355 -2726 2687 -394 403 -2706 2687 -388 377 -21248 2717 -388 377 -2738 2659 -408 2703 -382 2689 -416 2679 -408 2701 -382 2687 -418 365 -2736 365 -2722 2689 -384 391 -2696 2707 -386 379 -2734 2665 -410 361 -2726 2703 -418 357 -21246 2679 -466 297 -2768 2657 -448 2627 -434 2669 -450 2653 -416 2673 -408 2697 -386 383 -2728 369 -2706 2701 -382 387 -2726 2687 -418 357 -2708 2693 -418 361 -2702 2709 -396 401 -21232 2689 -406 361 -2736 2695 -386 2695 -406 2695 -382 2687 -418 2675 -410 2693 -414 375 -2692 389 -2706 2701 -404 363 -2724 2695 -388 389 -2702 2719 -358 405 -2704 2701 -402 363 -21262 2677 -414 367 -2738 2677 -386 2693 -420 2701 -400 2677 -386 2695 -420 2669 -430 369 -2718 353 -2730 2673 -412 361 -2734 2691 -420 357 -2698 2701 -394 401 -2702 2687 -424 347 -21244 2701 -418 365 -2726 2703 -382 2697 -420 2675 -400 2685 -384 2721 -398 2667 -418 355 -2744 343 -2722 2703 -420 353 -2724 2689 -396 363 -2736 2687 -390 377 -2730 2697 -386 357 -21274 2683 -414 375 -2726 2667 -420 2703 -398 2677 -388 2695 -420 2699 -398 2671 -384 415 -2698 357 -2738 2695 -382 383 -2724 2685 -416 357 -2706 2707 -384 391 -2726 2671 -384 415 -21238 2651 -476 293 -2776 2653 -462 2641 -446 2661 -422 2663 -418 2689 -412 2683 -414 357 -2706 385 -2698 2715 -378 379 -2710 2719 -388 377 -2708 2695 -406 361 -2724 2689 -416 361 -21244 2703 -386 413 -2698 2697 -414 2689 -384 2685 -386 2719 -378 2701 -416 2689 -386 377 -2728 387 -2700 2673 -410 361 -2730 2695 -420 357 -2732 2679 -386 377 -2734 2699 -378 361 -21262 2697 -392 405 -2702 2687 -406 2703 -382 2703 -418 2671 -406 2677 -416 2695 -386 387 -2700 381 -2704 2695 -418 357 -2712 2721 -388 375 -2702 2693 -408 363 -2730 2693 -420 355 -21248 2653 -494 289 -91206 131 -132 97 -232 559 -132 591 -98 691 -66 131 -130 297 -66 231 -66 331 -66 433 -100 499 -132 231 -166 197 -134 593 -100 11707 -4456 133 -200 131
RAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 -394 97 -66 99 -98 99 -328 297 -328 265 -298 3089 -132 10573 -1460 133 -432 99 -232 99 -132 333 -232 731 -164 65 -166 165 -132 131 -330 65 -98 131 -596 65 -198 133 -98 397 -568 65 -132 1157 -166 195 -130 131 -64 99 -66 63 -198 265 -98 297 -66 63 -166 295 -100 1747 -232 6099 -11348 199 -528 297 -266 97 -598 99 -198 231 -64 4433 -334 65 -298 65 -3284 67 -530 97 -432 133 -2356 493 -68 231 -168 297 -266 427 -100 559 -98 229 -460 197 -66 261 -132 65 -98 565 -132 231 -66 497 -100 3491 -12356 65 -660 197 -198 165 -132 331 -134 65 -98 2651 -134 4531 -10850 65 -1322 263 -68 431 -232 165 -134 165 -202 231 -300 5625 -66 6951 -8162 65 -398 99 -596 65 -132 461 -598 429 -132 97 -132 463 -232 229 -98 329 -100 397 -100 363 -100 231 -200 163 -200 961 -66 693 -100 397 -134 10601 -3872 263 -100 165 -100 131 -198 99 -696 233 -1524 331 -132 131 -164 229 -132 493 -98 631 -134 231 -100 595 -66 295 -66 5965 -8248 99 -296 99 -98 397 -66 65 -924 229 -398 299 -98 1425 -130 565 -198 827 -262 429 -598 725 -704 729 -12290 131 -98 99 -98 65 -100 163 -164 65 -494 231 -100 97 -100 863 -66 1751 -3948 165 -100 195 -66 165 -296 65 -2042 99 -200 495 -132 557 -100 827 -98 167 -66 433 -100 661 -164 689 -98 10803 -3906 231 -296 295 -232 99 -234 131 -332 395 -266 1283 -164 755 -466 397 -164 335 -66 1355 -14376 557 -66 331 -68 431 -134 599 -364 229 -100 763 -98 265 -132 525 -166 99 -396 495 -98 3867 -134 595 -168 865 -166 503 -200 467 -134 8145 -458 235 -794 599 -458 265 -436 231 -426 333 -368 299 -730 689 -360 327 -370 363 -326 367 -668 733 -328 363 -302 397 -328 371 -296 393 -666 725 -622 795 -634 433 -264 1023 -228 5041 -762 585 -466 233 -826 235 -470 631 -368 299 -402 299 -726 361 -328 331 -370 363 -332 701 -298 401 -692 369 -302 759 -268 461 -236 435 -622 423 -260 465 -266 719 -13608 65 -624 197 -558 921 -164 1315 -134 465 -134 263 -100 295 -132 293 -66 329 -98 197 -132 9977 -5036 197 -798 333 -828 295 -100 197 -100 165 -66 665 -100 763 -300 297 -166 165 -98 823 -8348 229 -100 427 -196 263 -624 197 -134 797 -100 263 -68 529 -132 233 -134 165 -264 131 -132 559 -66 263 -228 927 -132 731 -102 1061 -66 863 -8206 131 -332 299 -166 461 -100 99 -66 429 -66 3271 -98 465 -100 401 -232 331 -66 397 -430 10341
RAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379
RAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197
RAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671

View File

@@ -139,3 +139,9 @@ RAW_Data: 277 -244 275 -1360 275 -1340 277 -246 277 -236 261 -1380 275 -1346 277
RAW_Data: 29262 361 -68 2635 -66 24113 -66 1131 -100 4157 -66 26253 -130 621 -18438 99 -298 231 -66 197 -496 753 -230 7503 -16526 65 -396 65 -296 99 -196 293 -64 429 -132 397 -66 329 -66 37701 -66 13475 -100 54967 -64 18209 -18340 97 -462 197 -98 587 -232 97 -100 259 -98 197 -262 297 -64 557 -100 599 -100 333 -234 42493 -13212 6449 -206 173 -214 217 -176 195 -218 181 -218 181 -182 217 -182 217 -176 187 -214 215 -180 217 -182 217 -182 217 -178 185 -424 1177 -388 387 -240 381 -214 181 -398 211 -380 419 -176 217 -394 203 -394 205 -380 189 -402 421 -168 219 -398 393 -190 191 -398 205 -406 185 -402 381 -212 215 -362 241 -378 421 -176 377 -218 197 -378 427 -210 393 -172 429 -172 397 -212 217 -362 389 -228 197 -372 417 -204 395 -210 181 -398 391 -192 201 -216888 761 -200 299 -166 695 -132 15435 -66 5611 -66 21049 -66 4947 -66 2355 -66 1921 -100 2223 -100 2107 -100 397 -98 3643 -66 5301 -98 14205 -66 37371 -246 175 -216 179 -216 177 -224 149 -246 159 -228 181 -212 201 -204 159 -244 151 -254 169 -214 181 -210 197 -182 181 -454 1141 -444 357 -228 361 -246 177 -396 209 -412 367 -188 187 -434 201 -394 185 -406 193 -402 377 -238 181 -386 381 -234 153 -424 205 -412 157 -412 383 -240 181 -398 203 -392 385 -236 371 -212 179 -400 383 -240 359 -210 375 -220 381 -246 175 -394 383 -240 181 -398 363 -222 379 -246 175 -394 383 -204 217 -182856 99 -66 99 -300 133 -402 65 -198 99 -328 65 -100 491 -164 593 -100 3547 -64 361 -66 789 -68 2521 -66 22883 -66 2659 -98 3309 -130 3789 -100 9689 -17178 99 -1388 65 -266 197 -100 131 -134 99 -232 627 -130 233 -66 1949 -100 14567 -198 165 -256 181 -208 159 -214 183 -220 163 -244 149 -246 159 -236 181 -254 141 -226 151 -246 157 -228 181 -212 201 -400 1163 -428 379 -230 355 -244 177 -396 207 -412 367 -222 157 -418 189 -410 207 -412 171 -430 357 -226 165 -404 413 -204 181 -428 173 -428 169 -426 353 -236 173 -414 173 -408 381 -244 337 -222 201 -408 397 -208 393 -204 395 -208 359 -246 177 -394 387 -200 205 -380 415 -202 395 -208 181 -432 357 -226 169 -195084 65 -300 763 -66 297 -364 593 -68 2883 -66 1357 -68 363 -98 3841 -66 3119 -66 5153 -66 4023 -268 143 -246 133 -290 141 -250 139 -254 141 -226 181 -248 137 -254 143 -252 139 -252 143 -230 181 -250 139 -254 145 -436 1135 -448 349 -240 347 -254 157 -434 167 -426 377 -226 157 -434 167 -426 155 -440 163 -434 375 -206 215 -380 381 -234 153
RAW_Data: -424 205 -412 159 -412 381 -240 181 -398 203 -392 387 -236 369 -212 179 -400 383 -240 359 -244 339 -222 381 -246 175 -394 383 -240 181 -398 363 -222 381 -244 175 -392 383 -240 181 -184002 99 -360 63 -330 65 -132 129 -232 97 -198 295 -328 6031 -66 831 -132 3417 -66 2187 -64 2183 -100 6535 -66 1127 -66 2569 -66 2031 -66 2271 -66 2183 -66 3815 -66 3803 -66 493 -66 1909 -66 1627 -98 4805 -17512 67 -2164 131 -498 265 -430 163 -98 97 -64 99 -230 99 -100 229 -230 165 -196 63 -132 99 -66 927 -66 14955 -66 19621 -68 2627 -66 14305 -68 23247 -66 2891 -66 3941 -66 3021 -212 173 -242 181 -218 181 -214 181 -208 157 -250 141 -248 181 -218 179 -214 179 -210 159 -250 179 -214 181 -218 181 -404 1153 -404 389 -244 375 -192 181 -436 161 -414 383 -240 181 -398 205 -392 201 -394 205 -394 365 -246 177 -396 383 -204 217 -398 171 -426 167 -428 353 -242 173 -420 173 -408 373 -220 403 -208 175 -422 381 -194 399 -228 357 -246 355 -210 215 -400 387 -208 181 -398 391 -226 353 -246 177 -398 383 -204 217 -185098 163 -166 525 -98 293 -100 63 -66 229 -66 1183 -66 1507 -66 3089 -98 30187 -66 2847 -19112 133 -364 131 -394 97 -166 295 -66 229 -164 227 -66 263 -130 623 -98 2071 -66 493 -66 787 -98 691 -64 10249 -132 3879 -66 1949 -66 3453 -198 23157 -66 2845 -100 1193 -66 1587 -100 3797 -98 3187 -100 3319 -66 22119 -98 5513 -226 155 -244 153 -256 131 -248 151 -246 159 -262 121 -274 133 -272 127 -244 153 -254 167 -248 145 -244 133 -252 177 -398 1169 -418 381 -238 359 -242 141 -430 169 -426 357 -274 139 -422 171 -442 173 -428 167 -426 353 -236 171 -416 379 -226 149 -436 161 -438 173 -406 381 -234 153 -424 205 -380 389 -244 359 -206 215 -384 381 -246 335 -224 383 -246 355 -244 179 -404 385 -206 181 -432 359 -226 355 -246 175 -398 383 -240 181 -179760 97 -168 727 -66 97 -332 1389 -66 2793 -66 4955 -100 12453 -100 2425 -66 21965 -66 3809 -68 1683 -66 3095 -66 2153 -64 999 -208 173 -220 181 -214 191 -196 181 -212 183 -220 191 -212 181 -214 191 -198 181 -212 181 -222 191 -212 181 -214 191 -416 1167 -424 369 -220 373 -210 209 -390 207 -376 403 -190 187 -418 189 -408 209 -412 173 -428 357 -226 169 -404 399 -208 179 -412 209 -396 169 -428 355 -230 201 -378 205 -406 381 -244 339 -222 193 -400 413 -204 393 -208 347 -220 401 -210 175 -422 383 -202 217 -398 365 -222 377 -246 175 -390 385 -204 217 -179890 165 -1552 131 -164 65
RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 261 -164 5077 -272 137 -268 143 -252 141 -248 143 -246 159 -252 141 -244 143 -290 107 -276 145 -244 131 -250 179 -248 143 -252 141 -414 1165 -424 373 -236 359 -242 145 -434 169 -428 355 -230 169 -442 173 -434 157 -406 193 -402 379 -238 181 -422 335 -252 157 -434 167 -428 185 -406 381 -208 211 -390 207 -410 381 -200 373 -236 171 -414 383 -202 393 -210 379 -220 373 -208 211 -390 383 -204 217 -398 365 -220 379 -244 175 -394 381 -240 181 -161030 97 -166 167 -930 593 -2670 1091 -132 229 -98 461 -164 1649 -66 6311 -100 44723 -16832 67 -2656 131 -132 99 -132 263 -100 399 -68 893 -18950 99 -164 165 -198 525 -998 335 -66 565 -66 1057 -17880 97 -360 195 -262 131 -332 625 -98 197 -230 455 -98 9343 -16498 67 -368 131 -598 65 -1066 333 -300 789 -130 757 -66 87207 -16554 97 -3520 97 -786 591 -64 461 -98 21495 -66 24811 -18448 131 -296 491 -134 163 -760 1091 -230 893 -66 927 -68 4581 -68 32965 -64 45217 -17292 131 -1684 231 -132 327 -64 163 -330 263 -230 25751
RAW_Data: -66 529 -66 2519 -66 265 -68 10101 -1794 65 -1890 393 -562 97 -132 197 -98 493 -330 97 -164 97 -230 327 -326 99 -100 97 -164 65 -132 293 -98 297 -166 161 -130 297 -230 1391 -68 11185 -3800 229 -230 297 -66 65 -198 65 -466 99 -464 99 -430 67 -698 295 -132 165 -164 1095 -66 299 -66 1321 -264 12675 -66 99 -166 229 -134 65 -330 165 -164 65 -890 131 -830 67 -66 1157 -100 167 -168 265 -66 827 -66 2047 -100 261 -594 2279 -134 10701 -3890 163 -1384 67 -98 99 -1322 99 -98 65 -398 823 -66 65 -68 927 -100 495 -132 593 -100 165 -198 1387 -1022 131 -728 99 -662 97 -462 495 -200 829 -330 563 -100 297 -330 65 -598 165 -592 295 -166 131 -764 165 -164 565 -66 131 -166 165 -66 9675 -5052 165 -2878 199 -66 265 -432 265 -66 267 -898 163 -132 231 -198 229 -164 97 -100 4445 -66 7853 -636 199 -662 265 -298 233 -1428 331 -134 1791 -66 1649 -66 297 -100 361 -198 559 -98 363 -200 1315 -66 265 -98 1049 -132 1647 -66 265 -822 295 -526 131 -1712 199 -166 231 -200 165 -66 265 -166 97 -132 163 -164 395 -630 495 -168 297 -298 229 -266 629 -200 133 -132 133 -166 65 -132 99 -100 131 -66 67 -98 133 -496 1391 -98 1751 -164 359 -132 97 -164 263 -64 691 -66 199 -66 293 -98 589 -198 11299 -3968 65 -68 65 -2702 65 -1186 927 -166 65 -66 429 -134 197 -134 529 -200 67 -66 231 -100 2151 -4014 97 -1486 99 -464 65 -330 129 -330 331 -134 599 -66 497 -200 165 -66 661 -166 6881 -8830 295 -100 197 -232 725 -134 299 -166 229 -166 525 -198 295 -66 459 -66 329 -230 595 -98 299 -132 329 -66 99 -98 163 -134 229 -100 8345 -6726 131 -132 295 -66 1579 -66 329 -98 501 -132 231 -66 491 -298 331 -266 363 -132 1193 -168 8847 -4194 199 -828 65 -100 195 -262 197 -298 65 -898 65 -132 629 -66 229 -100 291 -100 623 -66 295 -66 461 -132 529 -632 597 -132 65 -100 97 -134 297 -100 297 -166 397 -168 527 -134 9603 -3850 99 -200 67 -896 959 -198 165 -100 229 -266 531 -64 165 -132 163 -296 3715 -11994 165 -1492 429 -68 263 -100 265 -330 199 -64 495 -132 363 -66 63 -166 297 -398 65 -100 231 -332 199 -100 7683 -4916 65 -1294 297 -1022 1325 -166 393 -132 165 -498 1255 -134 197 -198 427 -164 329 -132 631 -594 199 -196 99 -100 265 -134 1457 -100 3649 -8592 67 -268 131 -332 99 -100 65 -760 101 -198 297 -168 199 -132 369 -100 97 -132 99 -232 397 -198 99 -134 97 -100 231 -332 131 -796 329 -266 263
RAW_Data: -100 10841 -4030 163 -164 197 -398 195 -592 65 -132 63 -430 295 -298 263 -200 3517 -132 3763 -12296 99 -330 361 -98 99 -200 65 -430 165 -166 2327 -100 4051 -66 9653 -3478 197 -66 163 -198 167 -66 65 -598 165 -298 131 -666 199 -198 299 -298 165 -200 565 -66 797 -98 1125 -98 825 -100 4113 -6956 65 -5536 165 -266 99 -232 461 -198 65 -200 1989 -66 295 -66 723 -66 65 -98 329 -98 955 -66 559 -232 331 -66 10851 -1048 65 -3748 65 -498 99 -1392 99 -794 529 -98 331 -98 397 -164 363 -394 331 -266 299 -230 165 -66 3001 -568 197 -2872 2579 -468 2637 -472 2599 -488 2647 -426 2653 -448 2665 -392 379 -2734 381 -2700 2691 -388 377 -2732 2669 -420 355 -2726 2687 -394 403 -2706 2687 -388 377 -21248 2717 -388 377 -2738 2659 -408 2703 -382 2689 -416 2679 -408 2701 -382 2687 -418 365 -2736 365 -2722 2689 -384 391 -2696 2707 -386 379 -2734 2665 -410 361 -2726 2703 -418 357 -21246 2679 -466 297 -2768 2657 -448 2627 -434 2669 -450 2653 -416 2673 -408 2697 -386 383 -2728 369 -2706 2701 -382 387 -2726 2687 -418 357 -2708 2693 -418 361 -2702 2709 -396 401 -21232 2689 -406 361 -2736 2695 -386 2695 -406 2695 -382 2687 -418 2675 -410 2693 -414 375 -2692 389 -2706 2701 -404 363 -2724 2695 -388 389 -2702 2719 -358 405 -2704 2701 -402 363 -21262 2677 -414 367 -2738 2677 -386 2693 -420 2701 -400 2677 -386 2695 -420 2669 -430 369 -2718 353 -2730 2673 -412 361 -2734 2691 -420 357 -2698 2701 -394 401 -2702 2687 -424 347 -21244 2701 -418 365 -2726 2703 -382 2697 -420 2675 -400 2685 -384 2721 -398 2667 -418 355 -2744 343 -2722 2703 -420 353 -2724 2689 -396 363 -2736 2687 -390 377 -2730 2697 -386 357 -21274 2683 -414 375 -2726 2667 -420 2703 -398 2677 -388 2695 -420 2699 -398 2671 -384 415 -2698 357 -2738 2695 -382 383 -2724 2685 -416 357 -2706 2707 -384 391 -2726 2671 -384 415 -21238 2651 -476 293 -2776 2653 -462 2641 -446 2661 -422 2663 -418 2689 -412 2683 -414 357 -2706 385 -2698 2715 -378 379 -2710 2719 -388 377 -2708 2695 -406 361 -2724 2689 -416 361 -21244 2703 -386 413 -2698 2697 -414 2689 -384 2685 -386 2719 -378 2701 -416 2689 -386 377 -2728 387 -2700 2673 -410 361 -2730 2695 -420 357 -2732 2679 -386 377 -2734 2699 -378 361 -21262 2697 -392 405 -2702 2687 -406 2703 -382 2703 -418 2671 -406 2677 -416 2695 -386 387 -2700 381 -2704 2695 -418 357 -2712 2721 -388 375 -2702 2693 -408 363 -2730 2693 -420 355 -21248 2653 -494 289 -91206 131 -132 97 -232 559 -132 591 -98 691 -66 131 -130 297 -66 231 -66 331 -66 433 -100 499 -132 231 -166 197 -134 593 -100 11707 -4456 133 -200 131
RAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 -394 97 -66 99 -98 99 -328 297 -328 265 -298 3089 -132 10573 -1460 133 -432 99 -232 99 -132 333 -232 731 -164 65 -166 165 -132 131 -330 65 -98 131 -596 65 -198 133 -98 397 -568 65 -132 1157 -166 195 -130 131 -64 99 -66 63 -198 265 -98 297 -66 63 -166 295 -100 1747 -232 6099 -11348 199 -528 297 -266 97 -598 99 -198 231 -64 4433 -334 65 -298 65 -3284 67 -530 97 -432 133 -2356 493 -68 231 -168 297 -266 427 -100 559 -98 229 -460 197 -66 261 -132 65 -98 565 -132 231 -66 497 -100 3491 -12356 65 -660 197 -198 165 -132 331 -134 65 -98 2651 -134 4531 -10850 65 -1322 263 -68 431 -232 165 -134 165 -202 231 -300 5625 -66 6951 -8162 65 -398 99 -596 65 -132 461 -598 429 -132 97 -132 463 -232 229 -98 329 -100 397 -100 363 -100 231 -200 163 -200 961 -66 693 -100 397 -134 10601 -3872 263 -100 165 -100 131 -198 99 -696 233 -1524 331 -132 131 -164 229 -132 493 -98 631 -134 231 -100 595 -66 295 -66 5965 -8248 99 -296 99 -98 397 -66 65 -924 229 -398 299 -98 1425 -130 565 -198 827 -262 429 -598 725 -704 729 -12290 131 -98 99 -98 65 -100 163 -164 65 -494 231 -100 97 -100 863 -66 1751 -3948 165 -100 195 -66 165 -296 65 -2042 99 -200 495 -132 557 -100 827 -98 167 -66 433 -100 661 -164 689 -98 10803 -3906 231 -296 295 -232 99 -234 131 -332 395 -266 1283 -164 755 -466 397 -164 335 -66 1355 -14376 557 -66 331 -68 431 -134 599 -364 229 -100 763 -98 265 -132 525 -166 99 -396 495 -98 3867 -134 595 -168 865 -166 503 -200 467 -134 8145 -458 235 -794 599 -458 265 -436 231 -426 333 -368 299 -730 689 -360 327 -370 363 -326 367 -668 733 -328 363 -302 397 -328 371 -296 393 -666 725 -622 795 -634 433 -264 1023 -228 5041 -762 585 -466 233 -826 235 -470 631 -368 299 -402 299 -726 361 -328 331 -370 363 -332 701 -298 401 -692 369 -302 759 -268 461 -236 435 -622 423 -260 465 -266 719 -13608 65 -624 197 -558 921 -164 1315 -134 465 -134 263 -100 295 -132 293 -66 329 -98 197 -132 9977 -5036 197 -798 333 -828 295 -100 197 -100 165 -66 665 -100 763 -300 297 -166 165 -98 823 -8348 229 -100 427 -196 263 -624 197 -134 797 -100 263 -68 529 -132 233 -134 165 -264 131 -132 559 -66 263 -228 927 -132 731 -102 1061 -66 863 -8206 131 -332 299 -166 461 -100 99 -66 429 -66 3271 -98 465 -100 401 -232 331 -66 397 -430 10341
RAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379
RAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197
RAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671

View File

@@ -86,19 +86,19 @@ FIRMWARE_APPS = {
# Custom Games
"custom_games",
# Debug
"debug_apps",
#"debug_apps",
],
"unit_tests": [
"basic_services",
"updater_app",
"unit_tests",
],
"no_custom_apps": [
"debug_pack": [
"crypto_start",
# Svc
"basic_services",
# Apps
"basic_apps",
#"basic_apps",
"updater_app",
"storage_move_to_sd",
"archive",
@@ -107,7 +107,7 @@ FIRMWARE_APPS = {
"system_settings",
"about",
# Plugins
"basic_plugins",
#"basic_plugins",
# Debug
"debug_apps",
],

View File

@@ -217,7 +217,6 @@ bool furi_hal_nfc_listen(
}
rfalLowPowerModeStop();
rfalNfcDiscoverParam params = {
.compMode = RFAL_COMPLIANCE_MODE_NFC,
.techs2Find = RFAL_NFC_LISTEN_TECH_A,
.totalDuration = 1000,
.devLimit = 1,
@@ -230,6 +229,11 @@ bool furi_hal_nfc_listen(
.notifyCb = NULL,
.activate_after_sak = activate_after_sak,
};
if(FURI_BIT(sak, 5)) {
params.compMode = RFAL_COMPLIANCE_MODE_EMV;
} else {
params.compMode = RFAL_COMPLIANCE_MODE_NFC;
}
params.lmConfigPA.nfcidLen = uid_len;
memcpy(params.lmConfigPA.nfcid, uid, uid_len);
params.lmConfigPA.SENS_RES[0] = atqa[0];
@@ -271,6 +275,10 @@ void furi_hal_nfc_listen_sleep() {
st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SLEEP);
}
void furi_hal_nfc_stop_cmd() {
st25r3916ExecuteCommand(ST25R3916_CMD_STOP);
}
bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) {
furi_assert(tx_rx);
@@ -283,6 +291,9 @@ bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) {
if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXE)) {
furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits);
data_received = true;
if(tx_rx->sniff_rx) {
tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, false, tx_rx->sniff_context);
}
break;
}
continue;
@@ -497,14 +508,14 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA);
if(tx_rx->sniff_tx) {
tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context);
}
// Manually wait for interrupt
furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE);
if(tx_rx->sniff_tx) {
tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context);
}
uint32_t irq = 0;
uint8_t rxe = 0;
uint32_t start = DWT->CYCCNT;

View File

@@ -120,6 +120,8 @@ void furi_hal_nfc_field_off();
*/
void furi_hal_nfc_start_sleep();
void furi_hal_nfc_stop_cmd();
/** NFC stop sleep
*/
void furi_hal_nfc_exit_sleep();

View File

@@ -13,6 +13,8 @@
#include "protocol_jablotron.h"
#include "protocol_paradox.h"
#include "protocol_pac_stanley.h"
#include "protocol_keri.h"
#include "protocol_gallagher.h"
const ProtocolBase* lfrfid_protocols[] = {
[LFRFIDProtocolEM4100] = &protocol_em4100,
@@ -29,4 +31,6 @@ const ProtocolBase* lfrfid_protocols[] = {
[LFRFIDProtocolJablotron] = &protocol_jablotron,
[LFRFIDProtocolParadox] = &protocol_paradox,
[LFRFIDProtocolPACStanley] = &protocol_pac_stanley,
[LFRFIDProtocolKeri] = &protocol_keri,
[LFRFIDProtocolGallagher] = &protocol_gallagher,
};

View File

@@ -22,6 +22,8 @@ typedef enum {
LFRFIDProtocolJablotron,
LFRFIDProtocolParadox,
LFRFIDProtocolPACStanley,
LFRFIDProtocolKeri,
LFRFIDProtocolGallagher,
LFRFIDProtocolMax,
} LFRFIDProtocol;

View File

@@ -0,0 +1,300 @@
#include <furi.h>
#include <toolbox/protocols/protocol.h>
#include <toolbox/manchester_decoder.h>
#include <lfrfid/tools/bit_lib.h>
#include "lfrfid_protocols.h"
#define GALLAGHER_CLOCK_PER_BIT (32)
#define GALLAGHER_ENCODED_BIT_SIZE (96)
#define GALLAGHER_ENCODED_BYTE_SIZE ((GALLAGHER_ENCODED_BIT_SIZE) / 8)
#define GALLAGHER_PREAMBLE_BIT_SIZE (16)
#define GALLAGHER_PREAMBLE_BYTE_SIZE ((GALLAGHER_PREAMBLE_BIT_SIZE) / 8)
#define GALLAGHER_ENCODED_BYTE_FULL_SIZE \
(GALLAGHER_ENCODED_BYTE_SIZE + GALLAGHER_PREAMBLE_BYTE_SIZE)
#define GALLAGHER_DECODED_DATA_SIZE 8
#define GALLAGHER_READ_SHORT_TIME (128)
#define GALLAGHER_READ_LONG_TIME (256)
#define GALLAGHER_READ_JITTER_TIME (60)
#define GALLAGHER_READ_SHORT_TIME_LOW (GALLAGHER_READ_SHORT_TIME - GALLAGHER_READ_JITTER_TIME)
#define GALLAGHER_READ_SHORT_TIME_HIGH (GALLAGHER_READ_SHORT_TIME + GALLAGHER_READ_JITTER_TIME)
#define GALLAGHER_READ_LONG_TIME_LOW (GALLAGHER_READ_LONG_TIME - GALLAGHER_READ_JITTER_TIME)
#define GALLAGHER_READ_LONG_TIME_HIGH (GALLAGHER_READ_LONG_TIME + GALLAGHER_READ_JITTER_TIME)
typedef struct {
uint8_t data[GALLAGHER_DECODED_DATA_SIZE];
uint8_t encoded_data[GALLAGHER_ENCODED_BYTE_FULL_SIZE];
uint8_t encoded_data_index;
bool encoded_polarity;
ManchesterState decoder_manchester_state;
} ProtocolGallagher;
ProtocolGallagher* protocol_gallagher_alloc(void) {
ProtocolGallagher* proto = malloc(sizeof(ProtocolGallagher));
return (void*)proto;
};
void protocol_gallagher_free(ProtocolGallagher* protocol) {
free(protocol);
};
uint8_t* protocol_gallagher_get_data(ProtocolGallagher* protocol) {
return protocol->data;
};
static void protocol_gallagher_scramble(uint8_t* data, size_t length) {
const uint8_t lut[] = {
0xa3, 0xb0, 0x80, 0xc6, 0xb2, 0xf4, 0x5c, 0x6c, 0x81, 0xf1, 0xbb, 0xeb, 0x55, 0x67, 0x3c,
0x05, 0x1a, 0x0e, 0x61, 0xf6, 0x22, 0xce, 0xaa, 0x8f, 0xbd, 0x3b, 0x1f, 0x5e, 0x44, 0x04,
0x51, 0x2e, 0x4d, 0x9a, 0x84, 0xea, 0xf8, 0x66, 0x74, 0x29, 0x7f, 0x70, 0xd8, 0x31, 0x7a,
0x6d, 0xa4, 0x00, 0x82, 0xb9, 0x5f, 0xb4, 0x16, 0xab, 0xff, 0xc2, 0x39, 0xdc, 0x19, 0x65,
0x57, 0x7c, 0x20, 0xfa, 0x5a, 0x49, 0x13, 0xd0, 0xfb, 0xa8, 0x91, 0x73, 0xb1, 0x33, 0x18,
0xbe, 0x21, 0x72, 0x48, 0xb6, 0xdb, 0xa0, 0x5d, 0xcc, 0xe6, 0x17, 0x27, 0xe5, 0xd4, 0x53,
0x42, 0xf3, 0xdd, 0x7b, 0x24, 0xac, 0x2b, 0x58, 0x1e, 0xa7, 0xe7, 0x86, 0x40, 0xd3, 0x98,
0x97, 0x71, 0xcb, 0x3a, 0x0f, 0x01, 0x9b, 0x6e, 0x1b, 0xfc, 0x34, 0xa6, 0xda, 0x07, 0x0c,
0xae, 0x37, 0xca, 0x54, 0xfd, 0x26, 0xfe, 0x0a, 0x45, 0xa2, 0x2a, 0xc4, 0x12, 0x0d, 0xf5,
0x4f, 0x69, 0xe0, 0x8a, 0x77, 0x60, 0x3f, 0x99, 0x95, 0xd2, 0x38, 0x36, 0x62, 0xb7, 0x32,
0x7e, 0x79, 0xc0, 0x46, 0x93, 0x2f, 0xa5, 0xba, 0x5b, 0xaf, 0x52, 0x1d, 0xc3, 0x75, 0xcf,
0xd6, 0x4c, 0x83, 0xe8, 0x3d, 0x30, 0x4e, 0xbc, 0x08, 0x2d, 0x09, 0x06, 0xd9, 0x25, 0x9e,
0x89, 0xf2, 0x96, 0x88, 0xc1, 0x8c, 0x94, 0x0b, 0x28, 0xf0, 0x47, 0x63, 0xd5, 0xb3, 0x68,
0x56, 0x9c, 0xf9, 0x6f, 0x41, 0x50, 0x85, 0x8b, 0x9d, 0x59, 0xbf, 0x9f, 0xe2, 0x8e, 0x6a,
0x11, 0x23, 0xa1, 0xcd, 0xb5, 0x7d, 0xc7, 0xa9, 0xc8, 0xef, 0xdf, 0x02, 0xb8, 0x03, 0x6b,
0x35, 0x3e, 0x2c, 0x76, 0xc9, 0xde, 0x1c, 0x4b, 0xd1, 0xed, 0x14, 0xc5, 0xad, 0xe9, 0x64,
0x4a, 0xec, 0x8d, 0xf7, 0x10, 0x43, 0x78, 0x15, 0x87, 0xe4, 0xd7, 0x92, 0xe1, 0xee, 0xe3,
0x90};
for(size_t i = 0; i < length; i++) {
data[i] = lut[data[i]];
}
}
static void protocol_gallagher_descramble(uint8_t* data, size_t length) {
const uint8_t lut[] = {
0x2f, 0x6e, 0xdd, 0xdf, 0x1d, 0x0f, 0xb0, 0x76, 0xad, 0xaf, 0x7f, 0xbb, 0x77, 0x85, 0x11,
0x6d, 0xf4, 0xd2, 0x84, 0x42, 0xeb, 0xf7, 0x34, 0x55, 0x4a, 0x3a, 0x10, 0x71, 0xe7, 0xa1,
0x62, 0x1a, 0x3e, 0x4c, 0x14, 0xd3, 0x5e, 0xb2, 0x7d, 0x56, 0xbc, 0x27, 0x82, 0x60, 0xe3,
0xae, 0x1f, 0x9b, 0xaa, 0x2b, 0x95, 0x49, 0x73, 0xe1, 0x92, 0x79, 0x91, 0x38, 0x6c, 0x19,
0x0e, 0xa9, 0xe2, 0x8d, 0x66, 0xc7, 0x5a, 0xf5, 0x1c, 0x80, 0x99, 0xbe, 0x4e, 0x41, 0xf0,
0xe8, 0xa6, 0x20, 0xab, 0x87, 0xc8, 0x1e, 0xa0, 0x59, 0x7b, 0x0c, 0xc3, 0x3c, 0x61, 0xcc,
0x40, 0x9e, 0x06, 0x52, 0x1b, 0x32, 0x8c, 0x12, 0x93, 0xbf, 0xef, 0x3b, 0x25, 0x0d, 0xc2,
0x88, 0xd1, 0xe0, 0x07, 0x2d, 0x70, 0xc6, 0x29, 0x6a, 0x4d, 0x47, 0x26, 0xa3, 0xe4, 0x8b,
0xf6, 0x97, 0x2c, 0x5d, 0x3d, 0xd7, 0x96, 0x28, 0x02, 0x08, 0x30, 0xa7, 0x22, 0xc9, 0x65,
0xf8, 0xb7, 0xb4, 0x8a, 0xca, 0xb9, 0xf2, 0xd0, 0x17, 0xff, 0x46, 0xfb, 0x9a, 0xba, 0x8f,
0xb6, 0x69, 0x68, 0x8e, 0x21, 0x6f, 0xc4, 0xcb, 0xb3, 0xce, 0x51, 0xd4, 0x81, 0x00, 0x2e,
0x9c, 0x74, 0x63, 0x45, 0xd9, 0x16, 0x35, 0x5f, 0xed, 0x78, 0x9f, 0x01, 0x48, 0x04, 0xc1,
0x33, 0xd6, 0x4f, 0x94, 0xde, 0x31, 0x9d, 0x0a, 0xac, 0x18, 0x4b, 0xcd, 0x98, 0xb8, 0x37,
0xa2, 0x83, 0xec, 0x03, 0xd8, 0xda, 0xe5, 0x7a, 0x6b, 0x53, 0xd5, 0x15, 0xa4, 0x43, 0xe9,
0x90, 0x67, 0x58, 0xc0, 0xa5, 0xfa, 0x2a, 0xb1, 0x75, 0x50, 0x39, 0x5c, 0xe6, 0xdc, 0x89,
0xfc, 0xcf, 0xfe, 0xf9, 0x57, 0x54, 0x64, 0xa8, 0xee, 0x23, 0x0b, 0xf1, 0xea, 0xfd, 0xdb,
0xbd, 0x09, 0xb5, 0x5b, 0x05, 0x86, 0x13, 0xf3, 0x24, 0xc5, 0x3f, 0x44, 0x72, 0x7c, 0x7e,
0x36};
for(size_t i = 0; i < length; i++) {
data[i] = lut[data[i]];
}
}
static void protocol_gallagher_decode(ProtocolGallagher* protocol) {
bit_lib_remove_bit_every_nth(protocol->encoded_data, 16, 9 * 8, 9);
protocol_gallagher_descramble(protocol->encoded_data + 2, 8);
// Region code
bit_lib_set_bits(protocol->data, 0, (protocol->encoded_data[5] & 0x1E) >> 1, 4);
// Issue Level
bit_lib_set_bits(protocol->data, 4, (protocol->encoded_data[9] & 0x0F), 4);
// Facility Code
uint32_t fc = (protocol->encoded_data[7] & 0x0F) << 12 | protocol->encoded_data[3] << 4 |
((protocol->encoded_data[9] >> 4) & 0x0F);
protocol->data[3] = (uint8_t)fc;
protocol->data[2] = (uint8_t)(fc >>= 8);
protocol->data[1] = (uint8_t)(fc >>= 8);
// Card Number
uint32_t card = protocol->encoded_data[2] << 16 | (protocol->encoded_data[6] & 0x1F) << 11 |
protocol->encoded_data[4] << 3 | (protocol->encoded_data[5] & 0xE0) >> 5;
protocol->data[7] = (uint8_t)card;
protocol->data[6] = (uint8_t)(card >>= 8);
protocol->data[5] = (uint8_t)(card >>= 8);
protocol->data[4] = (uint8_t)(card >>= 8);
}
static bool protocol_gallagher_can_be_decoded(ProtocolGallagher* protocol) {
// check 16 bits preamble
if(bit_lib_get_bits_16(protocol->encoded_data, 0, 16) != 0b0111111111101010) return false;
// check next 16 bits preamble
if(bit_lib_get_bits_16(protocol->encoded_data, 96, 16) != 0b0111111111101010) return false;
uint8_t checksum_arr[8] = {0};
for(int i = 0, pos = 0; i < 8; i++) {
// Following the preamble, every 9th bit is a checksum-bit for the preceding byte
pos = 16 + (9 * i);
checksum_arr[i] = bit_lib_get_bits(protocol->encoded_data, pos, 8);
}
uint8_t crc = bit_lib_get_bits(protocol->encoded_data, 16 + (9 * 8), 8);
uint8_t calc_crc = bit_lib_crc8(checksum_arr, 8, 0x7, 0x2c, false, false, 0x00);
// crc
if(crc != calc_crc) return false;
return true;
}
void protocol_gallagher_decoder_start(ProtocolGallagher* protocol) {
memset(protocol->encoded_data, 0, GALLAGHER_ENCODED_BYTE_FULL_SIZE);
manchester_advance(
protocol->decoder_manchester_state,
ManchesterEventReset,
&protocol->decoder_manchester_state,
NULL);
};
bool protocol_gallagher_decoder_feed(ProtocolGallagher* protocol, bool level, uint32_t duration) {
bool result = false;
ManchesterEvent event = ManchesterEventReset;
if(duration > GALLAGHER_READ_SHORT_TIME_LOW && duration < GALLAGHER_READ_SHORT_TIME_HIGH) {
if(!level) {
event = ManchesterEventShortHigh;
} else {
event = ManchesterEventShortLow;
}
} else if(duration > GALLAGHER_READ_LONG_TIME_LOW && duration < GALLAGHER_READ_LONG_TIME_HIGH) {
if(!level) {
event = ManchesterEventLongHigh;
} else {
event = ManchesterEventLongLow;
}
}
if(event != ManchesterEventReset) {
bool data;
bool data_ok = manchester_advance(
protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data);
if(data_ok) {
bit_lib_push_bit(protocol->encoded_data, GALLAGHER_ENCODED_BYTE_FULL_SIZE, data);
if(protocol_gallagher_can_be_decoded(protocol)) {
protocol_gallagher_decode(protocol);
result = true;
}
}
}
return result;
};
bool protocol_gallagher_encoder_start(ProtocolGallagher* protocol) {
// Preamble
bit_lib_set_bits(protocol->encoded_data, 0, 0b01111111, 8);
bit_lib_set_bits(protocol->encoded_data, 8, 0b11101010, 8);
uint8_t rc = bit_lib_get_bits(protocol->data, 0, 4);
uint8_t il = bit_lib_get_bits(protocol->data, 4, 4);
uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24);
uint32_t cn = bit_lib_get_bits_32(protocol->data, 32, 32);
uint8_t payload[8] = {0};
payload[0] = (cn & 0xffffff) >> 16;
payload[1] = (fc & 0xfff) >> 4;
payload[2] = (cn & 0x7ff) >> 3;
payload[3] = (cn & 0x7) << 5 | (rc & 0xf) << 1;
payload[4] = (cn & 0xffff) >> 11;
payload[5] = (fc & 0xffff) >> 12;
payload[6] = 0;
payload[7] = (fc & 0xf) << 4 | (il & 0xf);
// Gallagher scramble
protocol_gallagher_scramble(payload, 8);
for(int i = 0; i < 8; i++) {
// data byte
bit_lib_set_bits(protocol->encoded_data, 16 + (i * 9), payload[i], 8);
// every byte is followed by a bit which is the inverse of the last bit
bit_lib_set_bit(protocol->encoded_data, 16 + (i * 9) + 8, !(payload[i] & 0x1));
}
// checksum
uint8_t crc = bit_lib_crc8(payload, 8, 0x7, 0x2c, false, false, 0x00);
bit_lib_set_bits(protocol->encoded_data, 16 + (9 * 8), crc, 8);
return true;
};
LevelDuration protocol_gallagher_encoder_yield(ProtocolGallagher* protocol) {
bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index);
uint32_t duration = GALLAGHER_CLOCK_PER_BIT / 2;
if(protocol->encoded_polarity) {
protocol->encoded_polarity = false;
} else {
level = !level;
protocol->encoded_polarity = true;
bit_lib_increment_index(protocol->encoded_data_index, GALLAGHER_ENCODED_BIT_SIZE);
}
return level_duration_make(level, duration);
};
bool protocol_gallagher_write_data(ProtocolGallagher* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
protocol_gallagher_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
request->t5577.block[0] =
(LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 |
(3 << LFRFID_T5577_MAXBLOCK_SHIFT));
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32);
request->t5577.blocks_to_write = 4;
result = true;
}
return result;
};
void protocol_gallagher_render_data(ProtocolGallagher* protocol, string_t result) {
UNUSED(protocol);
uint8_t rc = bit_lib_get_bits(protocol->data, 0, 4);
uint8_t il = bit_lib_get_bits(protocol->data, 4, 4);
uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24);
uint32_t card_id = bit_lib_get_bits_32(protocol->data, 32, 32);
string_cat_printf(result, "Region: %u, Issue Level: %u\r\n", rc, il);
string_cat_printf(result, "FC: %u, C: %lu\r\n", fc, card_id);
};
const ProtocolBase protocol_gallagher = {
.name = "Gallagher",
.manufacturer = "Gallagher",
.data_size = GALLAGHER_DECODED_DATA_SIZE,
.features = LFRFIDFeatureASK,
.validate_count = 3,
.alloc = (ProtocolAlloc)protocol_gallagher_alloc,
.free = (ProtocolFree)protocol_gallagher_free,
.get_data = (ProtocolGetData)protocol_gallagher_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_gallagher_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_gallagher_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_gallagher_encoder_start,
.yield = (ProtocolEncoderYield)protocol_gallagher_encoder_yield,
},
.render_data = (ProtocolRenderData)protocol_gallagher_render_data,
.render_brief_data = (ProtocolRenderData)protocol_gallagher_render_data,
.write_data = (ProtocolWriteData)protocol_gallagher_write_data,
};

View File

@@ -0,0 +1,4 @@
#pragma once
#include <toolbox/protocols/protocol.h>
extern const ProtocolBase protocol_gallagher;

View File

@@ -0,0 +1,264 @@
#include <furi.h>
#include <toolbox/protocols/protocol.h>
#include <lfrfid/tools/bit_lib.h>
#include "lfrfid_protocols.h"
#define KERI_PREAMBLE_BIT_SIZE (33)
#define KERI_PREAMBLE_DATA_SIZE (5)
#define KERI_ENCODED_BIT_SIZE (64)
#define KERI_ENCODED_DATA_SIZE (((KERI_ENCODED_BIT_SIZE) / 8) + KERI_PREAMBLE_DATA_SIZE)
#define KERI_ENCODED_DATA_LAST ((KERI_ENCODED_BIT_SIZE) / 8)
#define KERI_DECODED_BIT_SIZE (28)
#define KERI_DECODED_DATA_SIZE (4)
#define KERI_US_PER_BIT (255)
#define KERI_ENCODER_PULSES_PER_BIT (16)
typedef struct {
uint8_t data_index;
uint8_t bit_clock_index;
bool last_bit;
bool current_polarity;
bool pulse_phase;
} ProtocolKeriEncoder;
typedef struct {
uint8_t encoded_data[KERI_ENCODED_DATA_SIZE];
uint8_t negative_encoded_data[KERI_ENCODED_DATA_SIZE];
uint8_t corrupted_encoded_data[KERI_ENCODED_DATA_SIZE];
uint8_t corrupted_negative_encoded_data[KERI_ENCODED_DATA_SIZE];
uint8_t data[KERI_DECODED_DATA_SIZE];
ProtocolKeriEncoder encoder;
} ProtocolKeri;
ProtocolKeri* protocol_keri_alloc(void) {
ProtocolKeri* protocol = malloc(sizeof(ProtocolKeri));
return protocol;
};
void protocol_keri_free(ProtocolKeri* protocol) {
free(protocol);
};
uint8_t* protocol_keri_get_data(ProtocolKeri* protocol) {
return protocol->data;
};
void protocol_keri_decoder_start(ProtocolKeri* protocol) {
memset(protocol->encoded_data, 0, KERI_ENCODED_DATA_SIZE);
memset(protocol->negative_encoded_data, 0, KERI_ENCODED_DATA_SIZE);
memset(protocol->corrupted_encoded_data, 0, KERI_ENCODED_DATA_SIZE);
memset(protocol->corrupted_negative_encoded_data, 0, KERI_ENCODED_DATA_SIZE);
};
static bool protocol_keri_check_preamble(uint8_t* data, size_t bit_index) {
// Preamble 11100000 00000000 00000000 00000000 1
if(*(uint32_t*)&data[bit_index / 8] != 0b00000000000000000000000011100000) return false;
if(bit_lib_get_bit(data, bit_index + 32) != 1) return false;
return true;
}
static bool protocol_keri_can_be_decoded(uint8_t* data) {
if(!protocol_keri_check_preamble(data, 0)) return false;
if(!protocol_keri_check_preamble(data, 64)) return false;
///if(bit_lib_get_bit(data, 61) != 0) return false;
//if(bit_lib_get_bit(data, 60) != 0) return false;
return true;
}
static bool protocol_keri_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) {
time += (KERI_US_PER_BIT / 2);
size_t bit_count = (time / KERI_US_PER_BIT);
bool result = false;
if(bit_count < KERI_ENCODED_BIT_SIZE) {
for(size_t i = 0; i < bit_count; i++) {
bit_lib_push_bit(data, KERI_ENCODED_DATA_SIZE, polarity);
if(protocol_keri_can_be_decoded(data)) {
result = true;
break;
}
}
}
return result;
}
static void protocol_keri_descramble(uint32_t* fc, uint32_t* cn, uint32_t* internal_id) {
const uint8_t card_to_id[] = {255, 255, 255, 255, 13, 12, 20, 5, 16, 6, 21,
17, 8, 255, 0, 7, 10, 15, 255, 11, 4, 1,
255, 18, 255, 19, 2, 14, 3, 9, 255, 255};
const uint8_t card_to_fc[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 0, 255, 255, 255, 255, 2, 255, 255, 255,
3, 255, 4, 255, 255, 255, 255, 255, 1, 255};
*fc = 0;
*cn = 0;
for(uint8_t card_idx = 0; card_idx < 32; card_idx++) {
bool bit = (*internal_id >> card_idx) & 1;
// Card ID
if(card_to_id[card_idx] < 32) {
*cn = *cn | (bit << card_to_id[card_idx]);
}
// Card FC
if(card_to_fc[card_idx] < 32) {
*fc = *fc | (bit << card_to_fc[card_idx]);
}
}
}
static void protocol_keri_decoder_save(uint8_t* data_to, const uint8_t* data_from) {
uint32_t id = bit_lib_get_bits_32(data_from, 32, 32);
data_to[3] = (uint8_t)id;
data_to[2] = (uint8_t)(id >>= 8);
data_to[1] = (uint8_t)(id >>= 8);
data_to[0] = (uint8_t)(id >>= 8);
}
bool protocol_keri_decoder_feed(ProtocolKeri* protocol, bool level, uint32_t duration) {
bool result = false;
if(duration > (KERI_US_PER_BIT / 2)) {
if(protocol_keri_decoder_feed_internal(level, duration, protocol->encoded_data)) {
protocol_keri_decoder_save(protocol->data, protocol->encoded_data);
result = true;
return result;
}
if(protocol_keri_decoder_feed_internal(!level, duration, protocol->negative_encoded_data)) {
protocol_keri_decoder_save(protocol->data, protocol->negative_encoded_data);
result = true;
return result;
}
}
if(duration > (KERI_US_PER_BIT / 4)) {
// Try to decode wrong phase synced data
if(level) {
duration += 120;
} else {
if(duration > 120) {
duration -= 120;
}
}
if(protocol_keri_decoder_feed_internal(level, duration, protocol->corrupted_encoded_data)) {
protocol_keri_decoder_save(protocol->data, protocol->corrupted_encoded_data);
result = true;
return result;
}
if(protocol_keri_decoder_feed_internal(
!level, duration, protocol->corrupted_negative_encoded_data)) {
protocol_keri_decoder_save(protocol->data, protocol->corrupted_negative_encoded_data);
result = true;
return result;
}
}
return result;
};
bool protocol_keri_encoder_start(ProtocolKeri* protocol) {
memset(protocol->encoded_data, 0, KERI_ENCODED_DATA_SIZE);
*(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000011100000;
bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 0);
protocol->encoder.last_bit =
bit_lib_get_bit(protocol->encoded_data, KERI_ENCODED_BIT_SIZE - 1);
protocol->encoder.data_index = 0;
protocol->encoder.current_polarity = true;
protocol->encoder.pulse_phase = true;
protocol->encoder.bit_clock_index = 0;
return true;
};
LevelDuration protocol_keri_encoder_yield(ProtocolKeri* protocol) {
LevelDuration level_duration;
ProtocolKeriEncoder* encoder = &protocol->encoder;
if(encoder->pulse_phase) {
level_duration = level_duration_make(encoder->current_polarity, 1);
encoder->pulse_phase = false;
} else {
level_duration = level_duration_make(!encoder->current_polarity, 1);
encoder->pulse_phase = true;
encoder->bit_clock_index++;
if(encoder->bit_clock_index >= KERI_ENCODER_PULSES_PER_BIT) {
encoder->bit_clock_index = 0;
bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index);
if(current_bit != encoder->last_bit) {
encoder->current_polarity = !encoder->current_polarity;
}
encoder->last_bit = current_bit;
bit_lib_increment_index(encoder->data_index, KERI_ENCODED_BIT_SIZE);
}
}
return level_duration;
};
void protocol_keri_render_data(ProtocolKeri* protocol, string_t result) {
uint32_t data = bit_lib_get_bits_32(protocol->data, 0, 32);
uint32_t internal_id = data & 0x7FFFFFFF;
uint32_t fc = 0;
uint32_t cn = 0;
protocol_keri_descramble(&fc, &cn, &data);
string_printf(result, "Internal ID: %u\r\nFC: %u, Card: %u\r\n", internal_id, fc, cn);
}
bool protocol_keri_write_data(ProtocolKeri* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
protocol_keri_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
request->t5577.block[0] = LFRFID_T5577_TESTMODE_DISABLED | LFRFID_T5577_X_MODE |
LFRFID_T5577_MODULATION_PSK1 | LFRFID_T5577_PSKCF_RF_2 |
(2 << LFRFID_T5577_MAXBLOCK_SHIFT);
request->t5577.block[0] |= 0xF << 18;
request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32);
request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32);
request->t5577.blocks_to_write = 3;
result = true;
}
return result;
};
const ProtocolBase protocol_keri = {
.name = "Keri",
.manufacturer = "Keri",
.data_size = KERI_DECODED_DATA_SIZE,
.features = LFRFIDFeaturePSK,
.validate_count = 6,
.alloc = (ProtocolAlloc)protocol_keri_alloc,
.free = (ProtocolFree)protocol_keri_free,
.get_data = (ProtocolGetData)protocol_keri_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_keri_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_keri_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_keri_encoder_start,
.yield = (ProtocolEncoderYield)protocol_keri_encoder_yield,
},
.render_data = (ProtocolRenderData)protocol_keri_render_data,
.render_brief_data = (ProtocolRenderData)protocol_keri_render_data,
.write_data = (ProtocolWriteData)protocol_keri_write_data,
};

View File

@@ -0,0 +1,4 @@
#pragma once
#include <toolbox/protocols/protocol.h>
extern const ProtocolBase protocol_keri;

View File

@@ -5,21 +5,26 @@
#ifdef FURI_NDEBUG
#define LFS_NO_ASSERT
#define LFS_ASSERT(x)
#else
#else
#define LFS_ASSERT furi_assert
#endif
#define LFS_TAG "Lfs"
#ifdef FURI_LFS_DEBUG
#define LFS_TRACE(...) FURI_LOG_T(LFS_TAG, __VA_ARGS__);
#define LFS_DEBUG(...) FURI_LOG_D(LFS_TAG, __VA_ARGS__);
#else
#define LFS_TRACE(...)
#define LFS_DEBUG(...)
#endif // FURI_LFS_DEBUG
#define LFS_WARN(...) FURI_LOG_W(LFS_TAG, __VA_ARGS__);
#define LFS_ERROR(...) FURI_LOG_E(LFS_TAG, __VA_ARGS__);
// Because crc
#undef LFS_CONFIG
@@ -35,16 +40,13 @@
#ifndef LFS_NO_ASSERT
#include <assert.h>
#endif
#if !defined(LFS_NO_DEBUG) || \
!defined(LFS_NO_WARN) || \
!defined(LFS_NO_ERROR) || \
defined(LFS_YES_TRACE)
#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) || \
defined(LFS_YES_TRACE)
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C"
{
extern "C" {
#endif
// Builtin functions, these may be replaced by more efficient
@@ -66,21 +68,29 @@ static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
}
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
return lfs_aligndown(a + alignment-1, alignment);
return lfs_aligndown(a + alignment - 1, alignment);
}
// Find the smallest power of 2 greater than or equal to a
static inline uint32_t lfs_npw2(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a-1);
return 32 - __builtin_clz(a - 1);
#else
uint32_t r = 0;
uint32_t s;
a -= 1;
s = (a > 0xffff) << 4; a >>= s; r |= s;
s = (a > 0xff ) << 3; a >>= s; r |= s;
s = (a > 0xf ) << 2; a >>= s; r |= s;
s = (a > 0x3 ) << 1; a >>= s; r |= s;
s = (a > 0xffff) << 4;
a >>= s;
r |= s;
s = (a > 0xff) << 3;
a >>= s;
r |= s;
s = (a > 0xf) << 2;
a >>= s;
r |= s;
s = (a > 0x3) << 1;
a >>= s;
r |= s;
return (r | (a >> 1)) + 1;
#endif
}
@@ -114,20 +124,23 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) {
// Convert between 32-bit little-endian and native order
static inline uint32_t lfs_fromle32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
#if !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \
BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \
__BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
#elif !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \
__BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return __builtin_bswap32(a);
#else
return (((uint8_t*)&a)[0] << 0) |
(((uint8_t*)&a)[1] << 8) |
(((uint8_t*)&a)[2] << 16) |
return (((uint8_t*)&a)[0] << 0) | (((uint8_t*)&a)[1] << 8) | (((uint8_t*)&a)[2] << 16) |
(((uint8_t*)&a)[3] << 24);
#endif
}
@@ -138,21 +151,24 @@ static inline uint32_t lfs_tole32(uint32_t a) {
// Convert between 32-bit big-endian and native order
static inline uint32_t lfs_frombe32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
#if !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \
BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \
__BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return __builtin_bswap32(a);
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
#elif !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \
__BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return a;
#else
return (((uint8_t*)&a)[0] << 24) |
(((uint8_t*)&a)[1] << 16) |
(((uint8_t*)&a)[2] << 8) |
(((uint8_t*)&a)[3] << 0);
return (((uint8_t*)&a)[0] << 24) | (((uint8_t*)&a)[1] << 16) | (((uint8_t*)&a)[2] << 8) |
(((uint8_t*)&a)[3] << 0);
#endif
}
@@ -161,11 +177,11 @@ static inline uint32_t lfs_tobe32(uint32_t a) {
}
// Calculate CRC-32 with polynomial = 0x04c11db7
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
uint32_t lfs_crc(uint32_t crc, const void* buffer, size_t size);
// Allocate memory, only used if buffers are not provided to littlefs
// Note, memory must be 64-bit aligned
static inline void *lfs_malloc(size_t size) {
static inline void* lfs_malloc(size_t size) {
#ifndef LFS_NO_MALLOC
return malloc(size);
#else
@@ -175,7 +191,7 @@ static inline void *lfs_malloc(size_t size) {
}
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs_free(void *p) {
static inline void lfs_free(void* p) {
#ifndef LFS_NO_MALLOC
free(p);
#else
@@ -183,7 +199,6 @@ static inline void lfs_free(void *p) {
#endif
}
#ifdef __cplusplus
} /* extern "C" */
#endif

230
lib/nfc/helpers/mfkey32.c Normal file
View File

@@ -0,0 +1,230 @@
#include "mfkey32.h"
#include <furi/furi.h>
#include <storage/storage.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#include <m-array.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/nfc_util.h>
#define TAG "Mfkey32"
#define MFKEY32_LOGS_PATH EXT_PATH("nfc/.mfkey32.log")
typedef enum {
Mfkey32StateIdle,
Mfkey32StateAuthReceived,
Mfkey32StateAuthNtSent,
Mfkey32StateAuthArNrReceived,
} Mfkey32State;
typedef struct {
uint32_t cuid;
uint8_t sector;
MfClassicKey key;
uint32_t nt0;
uint32_t nr0;
uint32_t ar0;
uint32_t nt1;
uint32_t nr1;
uint32_t ar1;
} Mfkey32Params;
ARRAY_DEF(Mfkey32Params, Mfkey32Params, M_POD_OPLIST);
typedef struct {
uint8_t sector;
MfClassicKey key;
uint32_t nt;
uint32_t nr;
uint32_t ar;
} Mfkey32Nonce;
struct Mfkey32 {
Mfkey32State state;
Stream* file_stream;
Mfkey32Params_t params_arr;
Mfkey32Nonce nonce;
uint32_t cuid;
Mfkey32ParseDataCallback callback;
void* context;
};
Mfkey32* mfkey32_alloc(uint32_t cuid) {
Mfkey32* instance = malloc(sizeof(Mfkey32));
instance->cuid = cuid;
instance->state = Mfkey32StateIdle;
Storage* storage = furi_record_open(RECORD_STORAGE);
instance->file_stream = buffered_file_stream_alloc(storage);
if(!buffered_file_stream_open(
instance->file_stream, MFKEY32_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) {
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
free(instance);
instance = NULL;
} else {
Mfkey32Params_init(instance->params_arr);
}
furi_record_close(RECORD_STORAGE);
return instance;
}
void mfkey32_free(Mfkey32* instance) {
furi_assert(instance != NULL);
Mfkey32Params_clear(instance->params_arr);
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
free(instance);
}
void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static bool mfkey32_write_params(Mfkey32* instance, Mfkey32Params* params) {
string_t str;
string_init_printf(
str,
"Sector %d key %c cuid %08x nt0 %08x nr0 %08x ar0 %08x nt1 %08x nr1 %08x ar1 %08x\n",
params->sector,
params->key == MfClassicKeyA ? 'A' : 'B',
params->cuid,
params->nt0,
params->nr0,
params->ar0,
params->nt1,
params->nr1,
params->ar1);
bool write_success = stream_write_string(instance->file_stream, str);
string_clear(str);
return write_success;
}
static void mfkey32_add_params(Mfkey32* instance) {
Mfkey32Nonce* nonce = &instance->nonce;
bool nonce_added = false;
// Search if we partially collected params
if(Mfkey32Params_size(instance->params_arr)) {
Mfkey32Params_it_t it;
for(Mfkey32Params_it(it, instance->params_arr); !Mfkey32Params_end_p(it);
Mfkey32Params_next(it)) {
Mfkey32Params* params = Mfkey32Params_ref(it);
if((params->sector == nonce->sector) && (params->key == nonce->key)) {
params->nt1 = nonce->nt;
params->nr1 = nonce->nr;
params->ar1 = nonce->ar;
nonce_added = true;
FURI_LOG_I(
TAG,
"Params for sector %d key %c collected",
params->sector,
params->key == MfClassicKeyA ? 'A' : 'B');
// Write on sd card
if(mfkey32_write_params(instance, params)) {
Mfkey32Params_remove(instance->params_arr, it);
if(instance->callback) {
instance->callback(Mfkey32EventParamCollected, instance->context);
}
}
}
}
}
if(!nonce_added) {
Mfkey32Params params = {
.sector = nonce->sector,
.key = nonce->key,
.cuid = instance->cuid,
.nt0 = nonce->nt,
.nr0 = nonce->nr,
.ar0 = nonce->ar,
};
Mfkey32Params_push_back(instance->params_arr, params);
}
}
void mfkey32_process_data(
Mfkey32* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped) {
furi_assert(instance);
furi_assert(data);
Mfkey32Nonce* nonce = &instance->nonce;
uint16_t data_len = len;
if((data_len > 3) && !crc_dropped) {
data_len -= 2;
}
bool data_processed = false;
if(instance->state == Mfkey32StateIdle) {
if(reader_to_tag) {
if((data[0] == 0x60) || (data[0] == 0x61)) {
nonce->key = data[0] == 0x60 ? MfClassicKeyA : MfClassicKeyB;
nonce->sector = mf_classic_get_sector_by_block(data[1]);
instance->state = Mfkey32StateAuthReceived;
data_processed = true;
}
}
} else if(instance->state == Mfkey32StateAuthReceived) {
if(!reader_to_tag) {
if(len == 4) {
nonce->nt = nfc_util_bytes2num(data, 4);
instance->state = Mfkey32StateAuthNtSent;
data_processed = true;
}
}
} else if(instance->state == Mfkey32StateAuthNtSent) {
if(reader_to_tag) {
if(len == 8) {
nonce->nr = nfc_util_bytes2num(data, 4);
nonce->ar = nfc_util_bytes2num(&data[4], 4);
mfkey32_add_params(instance);
instance->state = Mfkey32StateIdle;
}
}
}
if(!data_processed) {
instance->state = Mfkey32StateIdle;
}
}
uint16_t mfkey32_get_auth_sectors(string_t data_str) {
furi_assert(data_str);
uint16_t nonces_num = 0;
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* file_stream = buffered_file_stream_alloc(storage);
string_t temp_str;
string_init(temp_str);
do {
if(!buffered_file_stream_open(
file_stream, MFKEY32_LOGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING))
break;
while(true) {
if(!stream_read_line(file_stream, temp_str)) break;
size_t uid_pos = string_search_str(temp_str, "cuid");
string_left(temp_str, uid_pos);
string_push_back(temp_str, '\n');
string_cat(data_str, temp_str);
nonces_num++;
}
} while(false);
buffered_file_stream_close(file_stream);
stream_free(file_stream);
string_clear(temp_str);
return nonces_num;
}

27
lib/nfc/helpers/mfkey32.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <lib/nfc/protocols/mifare_classic.h>
#include <m-string.h>
typedef struct Mfkey32 Mfkey32;
typedef enum {
Mfkey32EventParamCollected,
} Mfkey32Event;
typedef void (*Mfkey32ParseDataCallback)(Mfkey32Event event, void* context);
Mfkey32* mfkey32_alloc(uint32_t cuid);
void mfkey32_free(Mfkey32* instance);
void mfkey32_process_data(
Mfkey32* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped);
void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context);
uint16_t mfkey32_get_auth_sectors(string_t string);

View File

@@ -0,0 +1,72 @@
#include "nfc_debug_log.h"
#include <m-string.h>
#include <storage/storage.h>
#include <stream/buffered_file_stream.h>
#define TAG "NfcDebugLog"
#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.txt")
struct NfcDebugLog {
Stream* file_stream;
string_t data_str;
};
NfcDebugLog* nfc_debug_log_alloc() {
NfcDebugLog* instance = malloc(sizeof(NfcDebugLog));
Storage* storage = furi_record_open(RECORD_STORAGE);
instance->file_stream = buffered_file_stream_alloc(storage);
if(!buffered_file_stream_open(
instance->file_stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
instance->file_stream = NULL;
}
if(!instance->file_stream) {
free(instance);
instance = NULL;
} else {
string_init(instance->data_str);
}
furi_record_close(RECORD_STORAGE);
return instance;
}
void nfc_debug_log_free(NfcDebugLog* instance) {
furi_assert(instance);
furi_assert(instance->file_stream);
furi_assert(instance->data_str);
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
string_clear(instance->data_str);
free(instance);
}
void nfc_debug_log_process_data(
NfcDebugLog* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped) {
furi_assert(instance);
furi_assert(instance->file_stream);
furi_assert(instance->data_str);
furi_assert(data);
UNUSED(crc_dropped);
string_printf(instance->data_str, "%lu %c:", furi_get_tick(), reader_to_tag ? 'R' : 'T');
uint16_t data_len = len;
for(size_t i = 0; i < data_len; i++) {
string_cat_printf(instance->data_str, " %02x", data[i]);
}
string_push_back(instance->data_str, '\n');
stream_write_string(instance->file_stream, instance->data_str);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
typedef struct NfcDebugLog NfcDebugLog;
NfcDebugLog* nfc_debug_log_alloc();
void nfc_debug_log_free(NfcDebugLog* instance);
void nfc_debug_log_process_data(
NfcDebugLog* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped);

View File

@@ -1,7 +1,9 @@
#include "nfc_debug_pcap.h"
#include <storage/storage.h>
#include <stream/buffered_file_stream.h>
#include <furi_hal_nfc.h>
#include <furi_hal_rtc.h>
#include <stream_buffer.h>
#define TAG "NfcDebugPcap"
@@ -16,48 +18,94 @@
#define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA
#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap")
#define NFC_DEBUG_PCAP_BUFFER_SIZE 64
struct NfcDebugPcapWorker {
bool alive;
Storage* storage;
File* file;
StreamBufferHandle_t stream;
FuriThread* thread;
struct NfcDebugPcap {
Stream* file_stream;
};
static File* nfc_debug_pcap_open(Storage* storage) {
File* file = storage_file_alloc(storage);
if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
storage_file_free(file);
return NULL;
}
if(!storage_file_tell(file)) {
struct {
uint32_t magic;
uint16_t major, minor;
uint32_t reserved[2];
uint32_t snaplen;
uint32_t link_type;
} __attribute__((__packed__)) pcap_hdr = {
.magic = PCAP_MAGIC,
.major = PCAP_MAJOR,
.minor = PCAP_MINOR,
.snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE,
.link_type = DLT_ISO_14443,
};
if(storage_file_write(file, &pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) {
FURI_LOG_E(TAG, "Failed to write pcap header");
static Stream* nfc_debug_pcap_open(Storage* storage) {
Stream* stream = NULL;
stream = buffered_file_stream_alloc(storage);
if(!buffered_file_stream_open(stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
buffered_file_stream_close(stream);
stream_free(stream);
stream = NULL;
} else {
if(!stream_tell(stream)) {
struct {
uint32_t magic;
uint16_t major, minor;
uint32_t reserved[2];
uint32_t snaplen;
uint32_t link_type;
} __attribute__((__packed__)) pcap_hdr = {
.magic = PCAP_MAGIC,
.major = PCAP_MAJOR,
.minor = PCAP_MINOR,
.snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE,
.link_type = DLT_ISO_14443,
};
if(stream_write(stream, (uint8_t*)&pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) {
FURI_LOG_E(TAG, "Failed to write pcap header");
buffered_file_stream_close(stream);
stream_free(stream);
stream = NULL;
}
}
}
return file;
return stream;
}
static void
nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) {
NfcDebugPcap* nfc_debug_pcap_alloc() {
NfcDebugPcap* instance = malloc(sizeof(NfcDebugPcap));
Storage* storage = furi_record_open(RECORD_STORAGE);
instance->file_stream = nfc_debug_pcap_open(storage);
if(!instance->file_stream) {
free(instance);
instance = NULL;
}
furi_record_close(RECORD_STORAGE);
return instance;
}
void nfc_debug_pcap_free(NfcDebugPcap* instance) {
furi_assert(instance);
furi_assert(instance->file_stream);
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
free(instance);
}
void nfc_debug_pcap_process_data(
NfcDebugPcap* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped) {
furi_assert(instance);
furi_assert(data);
FuriHalRtcDateTime datetime;
furi_hal_rtc_get_datetime(&datetime);
uint8_t event = 0;
if(reader_to_tag) {
if(crc_dropped) {
event = DATA_PCD_TO_PICC_CRC_DROPPED;
} else {
event = DATA_PCD_TO_PICC;
}
} else {
if(crc_dropped) {
event = DATA_PICC_TO_PCD_CRC_DROPPED;
} else {
event = DATA_PICC_TO_PCD;
}
}
struct {
// https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
uint32_t ts_sec;
@@ -77,90 +125,6 @@ static void
.event = event,
.len = len << 8 | len >> 8,
};
xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever);
xStreamBufferSend(instance->stream, data, len, FuriWaitForever);
}
static void
nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC;
nfc_debug_pcap_write(instance, event, data, bits / 8);
}
static void
nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD;
nfc_debug_pcap_write(instance, event, data, bits / 8);
}
int32_t nfc_debug_pcap_thread(void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t buffer[NFC_DEBUG_PCAP_BUFFER_SIZE];
while(instance->alive) {
size_t ret =
xStreamBufferReceive(instance->stream, buffer, NFC_DEBUG_PCAP_BUFFER_SIZE, 50);
if(storage_file_write(instance->file, buffer, ret) != ret) {
FURI_LOG_E(TAG, "Failed to write pcap data");
}
}
return 0;
}
NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage) {
NfcDebugPcapWorker* instance = malloc(sizeof(NfcDebugPcapWorker));
instance->alive = true;
instance->storage = storage;
instance->file = nfc_debug_pcap_open(storage);
instance->stream = xStreamBufferCreate(4096, 1);
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "PcapWorker");
furi_thread_set_stack_size(instance->thread, 1024);
furi_thread_set_callback(instance->thread, nfc_debug_pcap_thread);
furi_thread_set_context(instance->thread, instance);
furi_thread_start(instance->thread);
return instance;
}
void nfc_debug_pcap_free(NfcDebugPcapWorker* instance) {
furi_assert(instance);
instance->alive = false;
furi_thread_join(instance->thread);
furi_thread_free(instance->thread);
vStreamBufferDelete(instance->stream);
if(instance->file) storage_file_free(instance->file);
instance->storage = NULL;
free(instance);
}
void nfc_debug_pcap_prepare_tx_rx(
NfcDebugPcapWorker* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc) {
if(!instance || !instance->file) return;
if(is_picc) {
tx_rx->sniff_tx = nfc_debug_pcap_write_rx;
tx_rx->sniff_rx = nfc_debug_pcap_write_tx;
} else {
tx_rx->sniff_tx = nfc_debug_pcap_write_tx;
tx_rx->sniff_rx = nfc_debug_pcap_write_rx;
}
tx_rx->sniff_context = instance;
stream_write(instance->file_stream, (uint8_t*)&pkt_hdr, sizeof(pkt_hdr));
stream_write(instance->file_stream, data, len);
}

View File

@@ -1,21 +1,17 @@
#pragma once
#include <furi_hal_nfc.h>
#include <storage/storage.h>
#include <stdint.h>
#include <stdbool.h>
typedef struct NfcDebugPcapWorker NfcDebugPcapWorker;
typedef struct NfcDebugPcap NfcDebugPcap;
NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage);
NfcDebugPcap* nfc_debug_pcap_alloc();
void nfc_debug_pcap_free(NfcDebugPcapWorker* instance);
void nfc_debug_pcap_free(NfcDebugPcap* instance);
/** Prepare tx/rx context for debug pcap logging, if enabled.
*
* @param instance NfcDebugPcapWorker* instance, can be NULL
* @param tx_rx TX/RX context to log
* @param is_picc if true, record Flipper as PICC, else PCD.
*/
void nfc_debug_pcap_prepare_tx_rx(
NfcDebugPcapWorker* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc);
void nfc_debug_pcap_process_data(
NfcDebugPcap* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped);

View File

@@ -0,0 +1,261 @@
#include "reader_analyzer.h"
#include <stream_buffer.h>
#include <lib/nfc/protocols/nfc_util.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <m-array.h>
#include "mfkey32.h"
#include "nfc_debug_pcap.h"
#include "nfc_debug_log.h"
#define TAG "ReaderAnalyzer"
#define READER_ANALYZER_MAX_BUFF_SIZE (1024)
typedef struct {
bool reader_to_tag;
bool crc_dropped;
uint16_t len;
} ReaderAnalyzerHeader;
typedef enum {
ReaderAnalyzerNfcDataMfClassic,
} ReaderAnalyzerNfcData;
struct ReaderAnalyzer {
FuriHalNfcDevData nfc_data;
bool alive;
StreamBufferHandle_t stream;
FuriThread* thread;
ReaderAnalyzerParseDataCallback callback;
void* context;
ReaderAnalyzerMode mode;
Mfkey32* mfkey32;
NfcDebugLog* debug_log;
NfcDebugPcap* pcap;
};
const FuriHalNfcDevData reader_analyzer_nfc_data[] = {
[ReaderAnalyzerNfcDataMfClassic] =
{.sak = 0x08,
.atqa = {0x44, 0x00},
.interface = FuriHalNfcInterfaceRf,
.type = FuriHalNfcTypeA,
.uid_len = 7,
.uid = {0x04, 0x77, 0x70, 0x2A, 0x23, 0x4F, 0x80},
.cuid = 0x2A234F80},
};
void reader_analyzer_parse(ReaderAnalyzer* instance, uint8_t* buffer, size_t size) {
if(size < sizeof(ReaderAnalyzerHeader)) return;
size_t bytes_i = 0;
while(bytes_i < size) {
ReaderAnalyzerHeader* header = (ReaderAnalyzerHeader*)&buffer[bytes_i];
uint16_t len = header->len;
if(bytes_i + len > size) break;
bytes_i += sizeof(ReaderAnalyzerHeader);
if(instance->mfkey32) {
mfkey32_process_data(
instance->mfkey32,
&buffer[bytes_i],
len,
header->reader_to_tag,
header->crc_dropped);
}
if(instance->pcap) {
nfc_debug_pcap_process_data(
instance->pcap, &buffer[bytes_i], len, header->reader_to_tag, header->crc_dropped);
}
if(instance->debug_log) {
nfc_debug_log_process_data(
instance->debug_log,
&buffer[bytes_i],
len,
header->reader_to_tag,
header->crc_dropped);
}
bytes_i += len;
}
}
int32_t reader_analyzer_thread(void* context) {
ReaderAnalyzer* reader_analyzer = context;
uint8_t buffer[READER_ANALYZER_MAX_BUFF_SIZE] = {};
while(reader_analyzer->alive || !xStreamBufferIsEmpty(reader_analyzer->stream)) {
size_t ret = xStreamBufferReceive(
reader_analyzer->stream, buffer, READER_ANALYZER_MAX_BUFF_SIZE, 50);
if(ret) {
reader_analyzer_parse(reader_analyzer, buffer, ret);
}
}
return 0;
}
ReaderAnalyzer* reader_analyzer_alloc() {
ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer));
instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic];
instance->alive = false;
instance->stream =
xStreamBufferCreate(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader));
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker");
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_callback(instance->thread, reader_analyzer_thread);
furi_thread_set_context(instance->thread, instance);
furi_thread_set_priority(instance->thread, FuriThreadPriorityLow);
return instance;
}
static void reader_analyzer_mfkey_callback(Mfkey32Event event, void* context) {
furi_assert(context);
ReaderAnalyzer* instance = context;
if(event == Mfkey32EventParamCollected) {
if(instance->callback) {
instance->callback(ReaderAnalyzerEventMfkeyCollected, instance->context);
}
}
}
void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode) {
furi_assert(instance);
xStreamBufferReset(instance->stream);
if(mode & ReaderAnalyzerModeDebugLog) {
instance->debug_log = nfc_debug_log_alloc();
}
if(mode & ReaderAnalyzerModeMfkey) {
instance->mfkey32 = mfkey32_alloc(instance->nfc_data.cuid);
if(instance->mfkey32) {
mfkey32_set_callback(instance->mfkey32, reader_analyzer_mfkey_callback, instance);
}
}
if(mode & ReaderAnalyzerModeDebugPcap) {
instance->pcap = nfc_debug_pcap_alloc();
}
instance->alive = true;
furi_thread_start(instance->thread);
}
void reader_analyzer_stop(ReaderAnalyzer* instance) {
furi_assert(instance);
instance->alive = false;
furi_thread_join(instance->thread);
if(instance->debug_log) {
nfc_debug_log_free(instance->debug_log);
instance->debug_log = NULL;
}
if(instance->mfkey32) {
mfkey32_free(instance->mfkey32);
instance->mfkey32 = NULL;
}
if(instance->pcap) {
nfc_debug_pcap_free(instance->pcap);
}
}
void reader_analyzer_free(ReaderAnalyzer* instance) {
furi_assert(instance);
reader_analyzer_stop(instance);
furi_thread_free(instance->thread);
vStreamBufferDelete(instance->stream);
free(instance);
}
void reader_analyzer_set_callback(
ReaderAnalyzer* instance,
ReaderAnalyzerParseDataCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
NfcProtocol
reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len) {
furi_assert(instance);
furi_assert(buff_rx);
UNUSED(len);
NfcProtocol protocol = NfcDeviceProtocolUnknown;
if((buff_rx[0] == 0x60) || (buff_rx[0] == 0x61)) {
protocol = NfcDeviceProtocolMifareClassic;
}
return protocol;
}
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) {
furi_assert(instance);
return &instance->nfc_data;
}
static void reader_analyzer_write(
ReaderAnalyzer* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped) {
ReaderAnalyzerHeader header = {
.reader_to_tag = reader_to_tag, .crc_dropped = crc_dropped, .len = len};
size_t data_sent = 0;
data_sent = xStreamBufferSend(
instance->stream, &header, sizeof(ReaderAnalyzerHeader), FuriWaitForever);
if(data_sent != sizeof(ReaderAnalyzerHeader)) {
FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, sizeof(ReaderAnalyzerHeader));
}
data_sent = xStreamBufferSend(instance->stream, data, len, FuriWaitForever);
if(data_sent != len) {
FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, len);
}
}
static void
reader_analyzer_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
UNUSED(crc_dropped);
ReaderAnalyzer* reader_analyzer = context;
uint16_t bytes = bits < 8 ? 1 : bits / 8;
reader_analyzer_write(reader_analyzer, data, bytes, false, crc_dropped);
}
static void
reader_analyzer_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
UNUSED(crc_dropped);
ReaderAnalyzer* reader_analyzer = context;
uint16_t bytes = bits < 8 ? 1 : bits / 8;
reader_analyzer_write(reader_analyzer, data, bytes, true, crc_dropped);
}
void reader_analyzer_prepare_tx_rx(
ReaderAnalyzer* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc) {
furi_assert(instance);
furi_assert(tx_rx);
if(is_picc) {
tx_rx->sniff_tx = reader_analyzer_write_rx;
tx_rx->sniff_rx = reader_analyzer_write_tx;
} else {
tx_rx->sniff_rx = reader_analyzer_write_rx;
tx_rx->sniff_tx = reader_analyzer_write_tx;
}
tx_rx->sniff_context = instance;
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include <stdint.h>
#include <lib/nfc/nfc_device.h>
typedef enum {
ReaderAnalyzerModeDebugLog = 0x01,
ReaderAnalyzerModeMfkey = 0x02,
ReaderAnalyzerModeDebugPcap = 0x04,
} ReaderAnalyzerMode;
typedef enum {
ReaderAnalyzerEventMfkeyCollected,
} ReaderAnalyzerEvent;
typedef struct ReaderAnalyzer ReaderAnalyzer;
typedef void (*ReaderAnalyzerParseDataCallback)(ReaderAnalyzerEvent event, void* context);
ReaderAnalyzer* reader_analyzer_alloc();
void reader_analyzer_free(ReaderAnalyzer* instance);
void reader_analyzer_set_callback(
ReaderAnalyzer* instance,
ReaderAnalyzerParseDataCallback callback,
void* context);
void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode);
void reader_analyzer_stop(ReaderAnalyzer* instance);
NfcProtocol
reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len);
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance);
void reader_analyzer_prepare_tx_rx(
ReaderAnalyzer* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc);

View File

@@ -195,6 +195,7 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) {
}
data->data_size = pages_total * 4;
data->data_read = pages_read * 4;
if(data->data_size > MF_UL_MAX_DUMP_SIZE || data->data_read > MF_UL_MAX_DUMP_SIZE) break;
bool pages_parsed = true;
for(uint16_t i = 0; i < pages_total; i++) {
string_printf(temp_str, "Page %d", i);

View File

@@ -28,9 +28,7 @@ NfcWorker* nfc_worker_alloc() {
}
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage);
}
nfc_worker->reader_analyzer = reader_analyzer_alloc(nfc_worker->storage);
return nfc_worker;
}
@@ -42,7 +40,7 @@ void nfc_worker_free(NfcWorker* nfc_worker) {
furi_record_close(RECORD_STORAGE);
if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker);
reader_analyzer_free(nfc_worker->reader_analyzer);
free(nfc_worker);
}
@@ -105,6 +103,8 @@ int32_t nfc_worker_task(void* context) {
nfc_worker_mf_ultralight_read_auth(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
nfc_worker_mf_classic_dict_attack(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
nfc_worker_analyze_reader(nfc_worker);
}
furi_hal_nfc_sleep();
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
@@ -117,7 +117,11 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC
MfUltralightReader reader = {};
MfUltralightData data = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Read card
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break;
@@ -127,6 +131,10 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -134,7 +142,11 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
furi_assert(nfc_worker->callback);
bool read_success = false;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Try to read supported card
FURI_LOG_I(TAG, "Try read supported card ...");
@@ -162,6 +174,9 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
}
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -169,13 +184,21 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
bool read_success = false;
MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
if(!mf_df_read_card(tx_rx, data)) break;
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -184,7 +207,11 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
EmvApplication emv_app = {};
EmvData* result = &nfc_worker->dev_data->emv_data;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Read card
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
@@ -211,6 +238,10 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -320,18 +351,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
// TODO add support for RATS
// Now remove bit 6 in SAK to support ISO-14443A-3 emulation
// Need to save ATS to support ISO-14443A-4 emulation
uint8_t sak = data->sak;
FURI_BIT_CLEAR(sak, 5);
while(nfc_worker->state == NfcWorkerStateUidEmulate) {
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, sak, true, 100)) {
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) {
if(furi_hal_nfc_tx_rx(&tx_rx, 100)) {
reader_data->size = tx_rx.rx_bits / 8;
if(reader_data->size > 0) {
@@ -349,7 +376,6 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData params = {
.uid = {0xCF, 0x72, 0xd4, 0x40},
.uid_len = 4,
@@ -358,6 +384,11 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
.type = FuriHalNfcTypeA,
};
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
FURI_LOG_D(TAG, "POS terminal detected");
@@ -370,6 +401,10 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
furi_hal_nfc_sleep();
furi_delay_ms(20);
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
}
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) {
@@ -484,7 +519,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
@@ -525,6 +559,11 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
MfUltralightReader reader = {};
mf_ul_reset(data);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
uint32_t key = 0;
uint16_t pack = 0;
while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) {
@@ -577,4 +616,61 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
furi_delay_ms(10);
}
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
}
static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void* context) {
furi_assert(context);
NfcWorker* nfc_worker = context;
if(event == ReaderAnalyzerEventMfkeyCollected) {
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventDetectReaderMfkeyCollected, nfc_worker->context);
}
}
}
void nfc_worker_analyze_reader(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer;
FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer);
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
.data = nfc_worker->dev_data->mf_classic_data,
.data_changed = false,
};
NfcaSignal* nfca_signal = nfca_signal_alloc();
tx_rx.nfca_signal = nfca_signal;
reader_analyzer_prepare_tx_rx(reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeMfkey);
reader_analyzer_set_callback(reader_analyzer, nfc_worker_reader_analyzer_callback, nfc_worker);
rfal_platform_spi_acquire();
FURI_LOG_D(TAG, "Start reader analyzer");
while(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
furi_hal_nfc_stop_cmd();
furi_delay_ms(5);
furi_hal_nfc_listen_start(nfc_data);
if(furi_hal_nfc_listen_rx(&tx_rx, 300)) {
NfcProtocol protocol =
reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8);
if(protocol == NfcDeviceProtocolMifareClassic) {
mf_classic_emulator(&emulator, &tx_rx);
}
} else {
FURI_LOG_D(TAG, "No data from reader");
continue;
}
}
rfal_platform_spi_release();
reader_analyzer_stop(nfc_worker->reader_analyzer);
nfca_signal_free(nfca_signal);
}

View File

@@ -16,6 +16,7 @@ typedef enum {
NfcWorkerStateMfClassicEmulate,
NfcWorkerStateReadMfUltralightReadAuth,
NfcWorkerStateMfClassicDictAttack,
NfcWorkerStateAnalyzeReader,
// Debug
NfcWorkerStateEmulateApdu,
NfcWorkerStateField,
@@ -54,8 +55,12 @@ typedef enum {
NfcWorkerEventFoundKeyA,
NfcWorkerEventFoundKeyB,
// Detect Reader events
NfcWorkerEventDetectReaderMfkeyCollected,
// Mifare Ultralight events
NfcWorkerEventMfUltralightPassKey,
} NfcWorkerEvent;
typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);

7
lib/nfc/nfc_worker_i.h Executable file → Normal file
View File

@@ -12,8 +12,7 @@
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#include <lib/nfc/protocols/nfca.h>
#include "helpers/nfc_debug_pcap.h"
#include <lib/nfc/helpers/reader_analyzer.h>
struct NfcWorker {
FuriThread* thread;
@@ -27,7 +26,7 @@ struct NfcWorker {
NfcWorkerState state;
NfcDebugPcapWorker* debug_pcap_worker;
ReaderAnalyzer* reader_analyzer;
};
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
@@ -49,3 +48,5 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker);
void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker);
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
void nfc_worker_analyze_reader(NfcWorker* nfc_worker);

View File

@@ -92,7 +92,7 @@ void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) {
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)
instance->encoder.size_upload = 52;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
@@ -233,7 +233,8 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat
case BETTDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) <
(subghz_protocol_bett_const.te_delta * 15))) {
//Found Preambula
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = BETTDecoderStepCheckDuration;
}
break;
@@ -288,20 +289,6 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_bett_check_remote_controller(SubGhzBlockGeneric* instance) {
uint32_t code_found_reverse =
subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);
instance->serial = (code_found_reverse & 0xFF) << 12 |
((code_found_reverse >> 8) & 0xFF) << 4 |
((code_found_reverse >> 20) & 0x0F);
instance->btn = ((code_found_reverse >> 16) & 0x0F);
}
uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderBETT* instance = context;
@@ -339,8 +326,7 @@ bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flip
void subghz_protocol_decoder_bett_get_string(void* context, string_t output) {
furi_assert(context);
SubGhzProtocolDecoderBETT* instance = context;
subghz_protocol_bett_check_remote_controller(&instance->generic);
uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF);
uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF);
string_cat_printf(
output,
"%s %dbit\r\n"
@@ -350,7 +336,7 @@ void subghz_protocol_decoder_bett_get_string(void* context, string_t output) {
" -: " DIP_PATTERN "\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data & 0xFFFFFF),
data,
SHOW_DIP_P(data, DIP_P),
SHOW_DIP_P(data, DIP_O),
SHOW_DIP_P(data, DIP_N));

View File

@@ -0,0 +1,365 @@
#include "clemsa.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
// protocol BERNER / ELKA / TEDSEN / TELETASTER
#define TAG "SubGhzProtocolClemsa"
#define DIP_P 0b11 //(+)
#define DIP_O 0b10 //(0)
#define DIP_N 0b00 //(-)
#define DIP_PATTERN "%c%c%c%c%c%c%c%c"
#define SHOW_DIP_P(dip, check_dip) \
((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_')
static const SubGhzBlockConst subghz_protocol_clemsa_const = {
.te_short = 385,
.te_long = 2695,
.te_delta = 150,
.min_count_bit_for_found = 18,
};
struct SubGhzProtocolDecoderClemsa {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
};
struct SubGhzProtocolEncoderClemsa {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
};
typedef enum {
ClemsaDecoderStepReset = 0,
ClemsaDecoderStepSaveDuration,
ClemsaDecoderStepCheckDuration,
} ClemsaDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder = {
.alloc = subghz_protocol_decoder_clemsa_alloc,
.free = subghz_protocol_decoder_clemsa_free,
.feed = subghz_protocol_decoder_clemsa_feed,
.reset = subghz_protocol_decoder_clemsa_reset,
.get_hash_data = subghz_protocol_decoder_clemsa_get_hash_data,
.serialize = subghz_protocol_decoder_clemsa_serialize,
.deserialize = subghz_protocol_decoder_clemsa_deserialize,
.get_string = subghz_protocol_decoder_clemsa_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder = {
.alloc = subghz_protocol_encoder_clemsa_alloc,
.free = subghz_protocol_encoder_clemsa_free,
.deserialize = subghz_protocol_encoder_clemsa_deserialize,
.stop = subghz_protocol_encoder_clemsa_stop,
.yield = subghz_protocol_encoder_clemsa_yield,
};
const SubGhzProtocol subghz_protocol_clemsa = {
.name = SUBGHZ_PROTOCOL_CLEMSA_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_clemsa_decoder,
.encoder = &subghz_protocol_clemsa_encoder,
};
void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderClemsa* instance = malloc(sizeof(SubGhzProtocolEncoderClemsa));
instance->base.protocol = &subghz_protocol_clemsa;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 52;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
}
void subghz_protocol_encoder_clemsa_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderClemsa* instance = context;
free(instance->encoder.upload);
free(instance);
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderClemsa instance
* @return true On success
*/
static bool subghz_protocol_encoder_clemsa_get_upload(SubGhzProtocolEncoderClemsa* instance) {
furi_assert(instance);
size_t index = 0;
size_t size_upload = (instance->generic.data_count_bit * 2);
if(size_upload > instance->encoder.size_upload) {
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
return false;
} else {
instance->encoder.size_upload = size_upload;
}
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
if(bit_read(instance->generic.data, i - 1)) {
//send bit 1
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_short);
} else {
//send bit 0
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_long);
}
}
if(bit_read(instance->generic.data, 0)) {
//send bit 1
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long);
instance->encoder.upload[index++] = level_duration_make(
false,
(uint32_t)subghz_protocol_clemsa_const.te_short +
subghz_protocol_clemsa_const.te_long * 7);
} else {
//send bit 0
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short);
instance->encoder.upload[index++] = level_duration_make(
false,
(uint32_t)subghz_protocol_clemsa_const.te_long +
subghz_protocol_clemsa_const.te_long * 7);
}
return true;
}
bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderClemsa* instance = context;
bool res = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
FURI_LOG_E(TAG, "Deserialize error");
break;
}
if(instance->generic.data_count_bit !=
subghz_protocol_clemsa_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
//optional parameter parameter
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_clemsa_get_upload(instance);
instance->encoder.is_running = true;
res = true;
} while(false);
return res;
}
void subghz_protocol_encoder_clemsa_stop(void* context) {
SubGhzProtocolEncoderClemsa* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_clemsa_yield(void* context) {
SubGhzProtocolEncoderClemsa* instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
}
void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderClemsa* instance = malloc(sizeof(SubGhzProtocolDecoderClemsa));
instance->base.protocol = &subghz_protocol_clemsa;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_clemsa_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
free(instance);
}
void subghz_protocol_decoder_clemsa_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
switch(instance->decoder.parser_step) {
case ClemsaDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) <
subghz_protocol_clemsa_const.te_delta * 25)) {
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
}
break;
case ClemsaDecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = ClemsaDecoderStepCheckDuration;
} else {
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
break;
case ClemsaDecoderStepCheckDuration:
if(!level) {
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) <
subghz_protocol_clemsa_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_long) <
subghz_protocol_clemsa_const.te_delta * 3)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) <
subghz_protocol_clemsa_const.te_delta * 3) &&
(DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short) <
subghz_protocol_clemsa_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
} else if(
DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) <
subghz_protocol_clemsa_const.te_delta * 25) {
if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) <
subghz_protocol_clemsa_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
} else if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) <
subghz_protocol_clemsa_const.te_delta * 3)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
} else {
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
if(instance->decoder.decode_count_bit ==
subghz_protocol_clemsa_const.min_count_bit_for_found) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
} else {
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
} else {
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
break;
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_clemsa_check_remote_controller(SubGhzBlockGeneric* instance) {
instance->serial = (instance->data >> 2) & 0xFFFF;
instance->btn = (instance->data & 0x03);
}
uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
bool subghz_protocol_decoder_clemsa_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* preset) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
bool ret = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
if(instance->generic.data_count_bit !=
subghz_protocol_clemsa_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
ret = true;
} while(false);
return ret;
}
void subghz_protocol_decoder_clemsa_get_string(void* context, string_t output) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
subghz_protocol_clemsa_check_remote_controller(&instance->generic);
//uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF);
string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%05lX Btn %X\r\n"
" +: " DIP_PATTERN "\r\n"
" o: " DIP_PATTERN "\r\n"
" -: " DIP_PATTERN "\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data & 0x3FFFF),
instance->generic.btn,
SHOW_DIP_P(instance->generic.serial, DIP_P),
SHOW_DIP_P(instance->generic.serial, DIP_O),
SHOW_DIP_P(instance->generic.serial, DIP_N));
}

View File

@@ -0,0 +1,107 @@
#pragma once
#include "base.h"
#define SUBGHZ_PROTOCOL_CLEMSA_NAME "Clemsa"
typedef struct SubGhzProtocolDecoderClemsa SubGhzProtocolDecoderClemsa;
typedef struct SubGhzProtocolEncoderClemsa SubGhzProtocolEncoderClemsa;
extern const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder;
extern const SubGhzProtocol subghz_protocol_clemsa;
/**
* Allocate SubGhzProtocolEncoderClemsa.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolEncoderClemsa* pointer to a SubGhzProtocolEncoderClemsa instance
*/
void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolEncoderClemsa.
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
*/
void subghz_protocol_encoder_clemsa_free(void* context);
/**
* Deserialize and generating an upload to send.
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Forced transmission stop.
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
*/
void subghz_protocol_encoder_clemsa_stop(void* context);
/**
* Getting the level and duration of the upload to be loaded into DMA.
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
* @return LevelDuration
*/
LevelDuration subghz_protocol_encoder_clemsa_yield(void* context);
/**
* Allocate SubGhzProtocolDecoderClemsa.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolDecoderClemsa* pointer to a SubGhzProtocolDecoderClemsa instance
*/
void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolDecoderClemsa.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
*/
void subghz_protocol_decoder_clemsa_free(void* context);
/**
* Reset decoder SubGhzProtocolDecoderClemsa.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
*/
void subghz_protocol_decoder_clemsa_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @return hash Hash sum
*/
uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context);
/**
* Serialize data SubGhzProtocolDecoderClemsa.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
* @return true On success
*/
bool subghz_protocol_decoder_clemsa_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* preset);
/**
* Deserialize data SubGhzProtocolDecoderClemsa.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @param output Resulting text
*/
void subghz_protocol_decoder_clemsa_get_string(void* context, string_t output);

View File

@@ -12,6 +12,7 @@ const SubGhzProtocol* subghz_protocol_registry[] = {
&subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec,
&subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2,
&subghz_protocol_honeywell_wdb, &subghz_protocol_magellen, &subghz_protocol_intertechno_v3,
&subghz_protocol_clemsa
};

View File

@@ -35,6 +35,7 @@
#include "honeywell_wdb.h"
#include "magellen.h"
#include "intertechno_v3.h"
#include "clemsa.h"
/**
* Registration by name SubGhzProtocol.

View File

@@ -12,7 +12,7 @@
#define UPDATE_ROOT_DIR EXT_PATH("update")
/* Need at least 4 free LFS pages before update */
#define UPDATE_MIN_INT_FREE_SPACE 4 * 4 * 1024
#define UPDATE_MIN_INT_FREE_SPACE 2 * 4 * 1024
static const char* update_prepare_result_descr[] = {
[UpdatePrepareResultOK] = "OK",

105
scripts/get_env.py Normal file
View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python3
import ssl
import json
import os
import shlex
import re
import string
import random
import argparse
import datetime
import urllib.request
def id_gen(size=5, chars=string.ascii_uppercase + string.digits):
return "".join(random.choice(chars) for _ in range(size))
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("--event_file", help="Current GitHub event file", required=True)
parser.add_argument(
"--type",
help="Event file type",
required=True,
choices=["pull", "tag", "other"],
)
args = parser.parse_args()
return args
def get_commit_json(event):
context = ssl._create_unverified_context()
with urllib.request.urlopen(
event["pull_request"]["_links"]["commits"]["href"], context=context
) as commit_file:
commit_json = json.loads(commit_file.read().decode("utf-8"))
return commit_json
def get_details(event, args):
data = {}
current_time = datetime.datetime.utcnow().date()
if args.type == "pull":
commit_json = get_commit_json(event)
data["commit_comment"] = shlex.quote(commit_json[-1]["commit"]["message"])
data["commit_hash"] = commit_json[-1]["sha"]
ref = event["pull_request"]["head"]["ref"]
data["pull_id"] = event["pull_request"]["number"]
data["pull_name"] = shlex.quote(event["pull_request"]["title"])
elif args.type == "tag":
data["commit_comment"] = shlex.quote(event["head_commit"]["message"])
data["commit_hash"] = event["head_commit"]["id"]
ref = event["ref"]
else:
data["commit_comment"] = shlex.quote(event["commits"][-1]["message"])
data["commit_hash"] = event["commits"][-1]["id"]
ref = event["ref"]
data["commit_sha"] = data["commit_hash"][:8]
data["branch_name"] = re.sub("refs/\w+/", "", ref)
data["suffix"] = (
data["branch_name"].replace("/", "_")
+ "-"
+ current_time.strftime("%d%m%Y")
+ "-"
+ data["commit_sha"]
)
if ref.startswith("refs/tags/"):
data["suffix"] = data["branch_name"].replace("/", "_")
return data
def add_env(name, value, file):
delimeter = id_gen()
print(f"{name}<<{delimeter}", file=file)
print(f"{value}", file=file)
print(f"{delimeter}", file=file)
def add_envs(data, env_file, args):
add_env("COMMIT_MSG", data["commit_comment"], env_file)
add_env("COMMIT_HASH", data["commit_hash"], env_file)
add_env("COMMIT_SHA", data["commit_sha"], env_file)
add_env("SUFFIX", data["suffix"], env_file)
add_env("BRANCH_NAME", data["branch_name"], env_file)
add_env("DIST_SUFFIX", data["suffix"], env_file)
add_env("WORKFLOW_BRANCH_OR_TAG", data["branch_name"], env_file)
if args.type == "pull":
add_env("PULL_ID", data["pull_id"], env_file)
add_env("PULL_NAME", data["pull_name"], env_file)
def main():
args = parse_args()
event_file = open(args.event_file)
event = json.load(event_file)
env_file = open(os.environ["GITHUB_ENV"], "a")
data = get_details(event, args)
add_envs(data, env_file, args)
event_file.close()
env_file.close()
if __name__ == "__main__":
main()

View File

@@ -13,7 +13,7 @@ if not [%FBT_NOENV%] == [] (
exit /b 0
)
set "FLIPPER_TOOLCHAIN_VERSION=9"
set "FLIPPER_TOOLCHAIN_VERSION=12"
set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows"

View File

@@ -5,7 +5,7 @@
# public variables
DEFAULT_SCRIPT_PATH="$(pwd -P)";
SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}";
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"8"}";
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"12"}";
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}";
fbtenv_show_usage()
@@ -64,13 +64,13 @@ fbtenv_check_sourced()
fbtenv_chck_many_source()
{
if ! echo "${PS1:-""}" | grep -q "[fbt]"; then
if ! echo "${PROMPT:-""}" | grep -q "[fbt]"; then
if ! echo "${PS1:-""}" | grep -qF "[fbt]"; then
if ! echo "${PROMPT:-""}" | grep -qF "[fbt]"; then
return 0;
fi
fi
echo "Warning! It script seen to be sourced more then once!";
echo "It may signalise what you are making some mistakes, please open a new shell!";
echo "Warning! FBT environment script sourced more than once!";
echo "This may signal that you are making mistakes, please open a new shell!";
return 1;
}