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

Merge branch 'dev' into release

This commit is contained in:
MX
2023-12-26 23:29:37 +03:00
71 changed files with 1801 additions and 855 deletions

View File

@@ -6,74 +6,38 @@
- Mifare Mini clones reading is broken (original mini working fine) (OFW) - Mifare Mini clones reading is broken (original mini working fine) (OFW)
- Mifare Classic dict attack fast skip (multiple presses on OK button) causes glitches/incorrect reading (OFW) - Mifare Classic dict attack fast skip (multiple presses on OK button) causes glitches/incorrect reading (OFW)
- EMV simple data parser was removed with protocol with refactoring (OFW) - EMV simple data parser was removed with protocol with refactoring (OFW)
- Mifare Classic Emulation slow response (unconfirmed) (OFW)
- Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW) - Option to unlock Slix-L (NFC V) with preset or custom password was removed with refactoring (OFW)
- NFC CLI was removed with refactoring (OFW) - NFC CLI was removed with refactoring (OFW)
### Some apps that was made for old nfc stack is now not compatible with the new API and require complete remake: ### Some apps that was made for old nfc stack is now not compatible with the new API and require complete remake:
**If you want to help with making this apps work again please send PR to the repo at link below** **If you want to help with making this apps work again please send PR to the repo at link below**
- Current list of affected apps: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors - Current list of affected apps: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors
- Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack <br> - Also in app **Enhanced Sub-GHz Chat** - NFC part was temporarily removed to make app usable, NFC part of the app requires remaking it with new nfc stack <br>
**API was updated to v49.x** **API was updated to v50.x**
## New changes ## New changes
* NFC: Added new parsers for transport cards - Umarsh, Kazan, Moscow, Metromoney(Tbilisi), and fixes for OFW parsers (by @assasinfil and @Leptopt1los) (special thanks for users who provided various dumps of those cards for research) * IR: Updated infrared assets (by @amec0e | PR #677)
* NFC: Added simple key name display to UI to fix regression * NFC: Fix Saflok edge case 0.5% of UIDs got wrong result (by @noproto | PR #668)
* NFC: Add keys to mf_classic_dict (by @hnlcory | PR #660) * NFC: Zolotaya Korona transport card parser added (by @Leptopt1los)
* NFC: Add Saflok and MyKey KDFs (by @noproto | PR #662) * NFC: Parsers cleanup for new api (by @Leptopt1los)
* NFC: social_moscow parser verification collisions fix (by @Leptopt1los) * SubGHz: Temp fix for subghz keyboard lock display issue (furi_timer is not working properly)
* iButton: Fix UI text - protocol name getting out of screen bounds when key name is too large, and other related issues (by @krolchonok | PR #649) * SubGHz: Added new option to delete old signals on full memory
* SubGHz: Fixed feature naming in menu * SubGHz: Faac rc/xt add manually (unverified)
* SubGHz: Added honeywell protocol [(by @htotoo)](https://github.com/Flipper-XFW/Xtreme-Firmware/commit/ceee551befa0cb8fd8514a4f8a1250fd9e0997ee) * SubGHz: Better subghz history element removal (by @Willy-JL)
* SubGHz: Add 303.9 Mhz to default frequency list * SubGHz: Fix key display newline issue in came atomo
* SubGHz: Fix Keeloq decoding order bug (random switch to HCS101 or anmotors)
* SubGHz: Fix secplus v1 key display issue
* API: Add new get function for varitemlist (by @Willy-JL)
* Misc code cleanup
* Apps: **Bluetooth Remote / USB Keyboard & Mouse** - `Movie` and `PTT` modes by @hryamzik
* Apps: **BLE Spam app** updated to latest version (New devices support, + Menu by holding Start) (by @Willy-JL) -> (app can be found in builds ` `, `e`, `n`, `r`)
* Apps: **NFC Magic** - Gen4 Actions (option to fix card with broken config) (by @Leptopt1los and @xMasterX)
* Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) * Apps: **Check out Apps updates by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev)
* OFW: NFC fixes * OFW: USART Bridge: added support for software control of DE/RE pins
* OFW: nfc: m1k-based Aime (non-AIC) card support * OFW: ufbt: changed toolchain environment invocation; updated .gitignore for app template
* OFW: SubGhz: fix count bit for detect gate_tx protocol * OFW: Keys Dict: fix PVS warnings
* OFW: Fixed a zero allocation error when reading an iso15693 nfc tag with no additional blocks. * OFW: NfcDict Refactoring
* OFW: Ntag21x write * OFW: Add AC's Carrier 42QG5A580SC and AUX YKR-H/006E
* OFW: Mifare Classic nested auth support * OFW: NFC Plugins loading rework
* OFW: ST25TB poller refining + write support * OFW: MFC emulation fix
* OFW: Libraries cleanup; u2f crypto rework to use mbedtls * OFW: nfc_util: little endian bytes2num functions added
* OFW: Add the secret door animation * OFW: Add MyKey parser
* OFW: Allows you to use UCS-2 in canvas_glyph_width * OFW: Update CLI MOTD
* OFW: Mifare Classic fixes * OFW: NFC NTAG and ISO14443-3b reading fix
* OFW: NFC: Felica UID emulation * OFW: FuriHal: RTC register reset API. New factory reset routine that wipes all RTC backup registers content.
* OFW: 64k does not enough * OFW: FuriHal: various GPIO improvements
* OFW: fbt: improvements * OFW: SubGhz: changed the name of the button when sending RAW to SubGHz
* OFW: Various Fixes for 0.95
* OFW: Add Mastercode SubGHz Protocol
* OFW: Do not remove file when renaming to itself
* OFW: Fix iButton crash on missing file
* OFW: NFC API improvements
* OFW: MF Ultralight no pwd polling adjustment
* OFW: Fix limited_credit_value having wrong value in mf_desfire_file_settings_parse
* OFW: Infrared remote button index support
* OFW: Fix NFC unit tests
* OFW: fix: invariant format of log time data
* OFW: fbt: dist improvements
* OFW: Fix crash when exiting write mode
* OFW: Dolphin: Extreme butthurt loop fix
* OFW: **Furi, FuriHal: remove FreeRTOS headers leaks**
* OFW: fbt: source collection improvements
* OFW: Rename menu items related to dummy mode and sound
* OFW: fbt: SD card resource handling speedup
* OFW: **Furi: cleanup crash use**
* OFW: Allow for larger Infrared remotes
* OFW: **fbt: reworked assets & resources handling**
* OFW: Storage: speedup write_chunk cli command
* OFW: fix crash after st25tb save
* OFW: Fix crash when reading files > 64B
* OFW: NFC RC fixes
* OFW: Fix MF DESFire record file handling
* OFW: **NFC refactoring** (new NFC stack) -> some apps still require very big changes to make them work with new system - see apps that was temporarily removed from this release here: https://github.com/xMasterX/all-the-plugins/tree/dev/apps_broken_by_last_refactors
* OFW: fbt: glob & git improvements
* OFW: FastFAP: human readable error log
---- ----

View File

@@ -100,7 +100,7 @@ Also check the [changelog in releases](https://github.com/DarkFlippers/unleashed
### Current modified and new Sub-GHz protocols list: ### Current modified and new Sub-GHz protocols list:
Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols in OFW. Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols in OFW.
Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b) Keeloq [Not ALL systems supported for decode or emulation!] - [Supported manufacturers list](https://pastes.io/raw/unuj9bhe4m)
Encoders or sending made by @xMasterX: Encoders or sending made by @xMasterX:
- Nero Radio 57bit (+ 56bit encoder improvements) - Nero Radio 57bit (+ 56bit encoder improvements)
@@ -167,7 +167,7 @@ You can support us by using links or addresses below:
## Community apps included ## Community apps included
### [🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/all-the-plugins/releases/latest) ### [🎲 Download Extra plugins for Unleashed](https://github.com/xMasterX/all-the-plugins/releases/latest)
### [List of Extra pack](https://github.com/xMasterX/all-the-plugins/tree/dev#extra-pack) | [List of Base *(Deafult)* pack](https://github.com/xMasterX/all-the-plugins/tree/dev#default-pack) ### [List of Extra pack](https://github.com/xMasterX/all-the-plugins/tree/dev#extra-pack) | [List of Base *(Default)* pack](https://github.com/xMasterX/all-the-plugins/tree/dev#default-pack)
See full list and sources here: [xMasterX/all-the-plugins](https://github.com/xMasterX/all-the-plugins/tree/dev) See full list and sources here: [xMasterX/all-the-plugins](https://github.com/xMasterX/all-the-plugins/tree/dev)

View File

@@ -12,7 +12,7 @@
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h> #include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h> #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <nfc/helpers/nfc_dict.h> #include <toolbox/keys_dict.h>
#include <nfc/nfc.h> #include <nfc/nfc.h>
#include "../minunit.h" #include "../minunit.h"
@@ -443,36 +443,36 @@ MU_TEST(mf_classic_dict_test) {
"Remove test dict failed"); "Remove test dict failed");
} }
NfcDict* dict = nfc_dict_alloc( KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
mu_assert(dict != NULL, "nfc_dict_alloc() failed"); mu_assert(dict != NULL, "keys_dict_alloc() failed");
size_t dict_keys_total = nfc_dict_get_total_keys(dict); size_t dict_keys_total = keys_dict_get_total_keys(dict);
mu_assert(dict_keys_total == 0, "nfc_dict_keys_total() failed"); mu_assert(dict_keys_total == 0, "keys_dict_keys_total() failed");
const uint32_t test_key_num = 30; const uint32_t test_key_num = 30;
MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey)); MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey));
for(size_t i = 0; i < test_key_num; i++) { for(size_t i = 0; i < test_key_num; i++) {
furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey)); furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey));
mu_assert( mu_assert(
nfc_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), "add key failed"); keys_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), "add key failed");
size_t dict_keys_total = nfc_dict_get_total_keys(dict); size_t dict_keys_total = keys_dict_get_total_keys(dict);
mu_assert(dict_keys_total == (i + 1), "nfc_dict_keys_total() failed"); mu_assert(dict_keys_total == (i + 1), "keys_dict_keys_total() failed");
} }
nfc_dict_free(dict); keys_dict_free(dict);
dict = nfc_dict_alloc( dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
mu_assert(dict != NULL, "nfc_dict_alloc() failed"); mu_assert(dict != NULL, "keys_dict_alloc() failed");
dict_keys_total = nfc_dict_get_total_keys(dict); dict_keys_total = keys_dict_get_total_keys(dict);
mu_assert(dict_keys_total == test_key_num, "nfc_dict_keys_total() failed"); mu_assert(dict_keys_total == test_key_num, "keys_dict_keys_total() failed");
MfClassicKey key_dut = {}; MfClassicKey key_dut = {};
size_t key_idx = 0; size_t key_idx = 0;
while(nfc_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) { while(keys_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) {
mu_assert( mu_assert(
memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0, memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0,
"Loaded key data mismatch"); "Loaded key data mismatch");
@@ -484,19 +484,19 @@ MU_TEST(mf_classic_dict_test) {
for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) { for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) {
MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]]; MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]];
mu_assert( mu_assert(
nfc_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)), keys_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)),
"nfc_dict_is_key_present() failed"); "keys_dict_is_key_present() failed");
mu_assert( mu_assert(
nfc_dict_delete_key(dict, key->data, sizeof(MfClassicKey)), keys_dict_delete_key(dict, key->data, sizeof(MfClassicKey)),
"nfc_dict_delete_key() failed"); "keys_dict_delete_key() failed");
} }
dict_keys_total = nfc_dict_get_total_keys(dict); dict_keys_total = keys_dict_get_total_keys(dict);
mu_assert( mu_assert(
dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx), dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx),
"nfc_dict_keys_total() failed"); "keys_dict_keys_total() failed");
nfc_dict_free(dict); keys_dict_free(dict);
free(key_arr_ref); free(key_arr_ref);
mu_assert( mu_assert(

View File

@@ -27,6 +27,7 @@ static const uint32_t baudrate_list[] = {
460800, 460800,
921600, 921600,
}; };
static const char* software_de_re[] = {"None", "4"};
bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context; GpioApp* app = context;
@@ -84,6 +85,17 @@ static void line_port_cb(VariableItem* item) {
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
static void line_software_de_re_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, software_de_re[index]);
app->usb_uart_cfg->software_de_re = index;
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
static void line_flow_cb(VariableItem* item) { static void line_flow_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app); furi_assert(app);
@@ -155,6 +167,11 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
app->var_item_flow = item; app->var_item_flow = item;
line_ensure_flow_invariant(app); line_ensure_flow_invariant(app);
item = variable_item_list_add(
var_item_list, "DE/RE Pin", COUNT_OF(software_de_re), line_software_de_re_cb, app);
variable_item_set_current_value_index(item, app->usb_uart_cfg->software_de_re);
variable_item_set_current_value_text(item, software_de_re[app->usb_uart_cfg->software_de_re]);
variable_item_list_set_selected_item( variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg));

View File

@@ -6,11 +6,16 @@
#include <furi_hal.h> #include <furi_hal.h>
#include <furi_hal_usb_cdc.h> #include <furi_hal_usb_cdc.h>
//TODO: FL-3276 port to new USART API
#include <stm32wbxx_ll_lpuart.h>
#include <stm32wbxx_ll_usart.h>
#define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_CDC_PKT_LEN CDC_DATA_SZ
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
#define USB_CDC_BIT_DTR (1 << 0) #define USB_CDC_BIT_DTR (1 << 0)
#define USB_CDC_BIT_RTS (1 << 1) #define USB_CDC_BIT_RTS (1 << 1)
#define USB_USART_DE_RE_PIN &gpio_ext_pa4
static const GpioPin* flow_pins[][2] = { static const GpioPin* flow_pins[][2] = {
{&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3
@@ -247,6 +252,17 @@ static int32_t usb_uart_worker(void* context) {
usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins; usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
events |= WorkerEvtCtrlLineSet; events |= WorkerEvtCtrlLineSet;
} }
if(usb_uart->cfg.software_de_re != usb_uart->cfg_new.software_de_re) {
usb_uart->cfg.software_de_re = usb_uart->cfg_new.software_de_re;
if(usb_uart->cfg.software_de_re != 0) {
furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);
furi_hal_gpio_init(
USB_USART_DE_RE_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedMedium);
} else {
furi_hal_gpio_init(
USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}
}
api_lock_unlock(usb_uart->cfg_lock); api_lock_unlock(usb_uart->cfg_lock);
} }
if(events & WorkerEvtLineCfgSet) { if(events & WorkerEvtLineCfgSet) {
@@ -260,6 +276,8 @@ static int32_t usb_uart_worker(void* context) {
usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch);
furi_hal_gpio_init(USB_USART_DE_RE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
if(usb_uart->cfg.flow_pins != 0) { if(usb_uart->cfg.flow_pins != 0) {
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog);
furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog);
@@ -298,7 +316,24 @@ static int32_t usb_uart_tx_thread(void* context) {
if(len > 0) { if(len > 0) {
usb_uart->st.tx_cnt += len; usb_uart->st.tx_cnt += len;
if(usb_uart->cfg.software_de_re != 0)
furi_hal_gpio_write(USB_USART_DE_RE_PIN, false);
furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len);
if(usb_uart->cfg.software_de_re != 0) {
//TODO: FL-3276 port to new USART API
if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) {
while(!LL_USART_IsActiveFlag_TC(USART1))
;
} else if(usb_uart->cfg.uart_ch == FuriHalUartIdLPUART1) {
while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
;
}
furi_hal_gpio_write(USB_USART_DE_RE_PIN, true);
}
} }
} }
} }

View File

@@ -11,6 +11,7 @@ typedef struct {
uint8_t flow_pins; uint8_t flow_pins;
uint8_t baudrate_mode; uint8_t baudrate_mode;
uint32_t baudrate; uint32_t baudrate;
uint8_t software_de_re;
} UsbUartConfig; } UsbUartConfig;
typedef struct { typedef struct {

View File

@@ -807,3 +807,79 @@ type: raw
frequency: 38000 frequency: 38000
duty_cycle: 0.330000 duty_cycle: 0.330000
data: 6175 7369 602 1570 602 1570 601 1570 573 1598 574 1598 573 1597 574 1597 574 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 529 570 530 568 1603 568 1627 544 1627 544 1627 544 1628 544 554 545 1627 544 1628 544 555 544 555 544 555 544 555 544 554 544 1627 545 555 544 554 545 1627 544 1627 544 1627 544 1627 544 1627 544 1603 568 1602 569 1603 569 530 569 529 570 529 570 529 570 529 570 529 570 529 570 528 570 1601 571 528 570 1602 570 529 570 528 570 1601 570 1600 571 1601 571 528 571 1601 570 528 570 1601 570 1602 570 529 570 529 570 528 570 1601 570 1601 570 1600 571 1601 571 528 570 1601 570 1601 571 528 571 528 571 529 570 528 571 528 570 1601 571 528 571 528 570 1603 570 529 571 1603 570 529 570 1603 570 529 570 1603 570 529 571 1602 571 1603 570 529 571 1603 570 529 571 1603 570 529 571 1603 570 530 570 7370 570 data: 6175 7369 602 1570 602 1570 601 1570 573 1598 574 1598 573 1597 574 1597 574 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 529 570 530 568 1603 568 1627 544 1627 544 1627 544 1628 544 554 545 1627 544 1628 544 555 544 555 544 555 544 555 544 554 544 1627 545 555 544 554 545 1627 544 1627 544 1627 544 1627 544 1627 544 1603 568 1602 569 1603 569 530 569 529 570 529 570 529 570 529 570 529 570 529 570 528 570 1601 571 528 570 1602 570 529 570 528 570 1601 570 1600 571 1601 571 528 571 1601 570 528 570 1601 570 1602 570 529 570 529 570 528 570 1601 570 1601 570 1600 571 1601 571 528 570 1601 570 1601 571 528 571 528 571 529 570 528 571 528 570 1601 571 528 571 528 570 1603 570 529 571 1603 570 529 570 1603 570 529 570 1603 570 529 571 1602 571 1603 570 529 571 1603 570 529 571 1603 570 529 571 1603 570 530 570 7370 570
#
# Model: AUX YKR-H/006E
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8957 4502 539 1683 538 1681 540 559 538 559 538 556 541 557 540 1683 538 1682 539 1684 537 1682 539 1681 540 1684 537 1684 537 1682 539 1684 537 558 539 558 539 558 539 558 539 559 538 558 539 1682 539 1681 540 1683 538 557 540 558 539 558 539 557 540 559 538 557 540 557 540 561 536 559 538 558 539 557 540 558 539 558 539 1683 538 558 539 1682 540 557 540 559 538 557 540 557 540 561 536 558 539 559 538 558 539 560 537 557 540 558 539 558 539 558 539 559 538 558 539 1682 539 557 540 558 539 557 540 557 540 558 539 560 537 557 540 557 540 558 539 559 538 557 540 559 538 556 541 558 539 558 539 558 539 556 541 559 538 557 540 558 539 558 539 558 539 557 540 558 539 557 541 557 540 557 540 559 538 558 539 558 539 558 539 558 539 1681 540 557 540 1683 538 558 539 559 538 557 540 559 538 559 538 1683 538 1683 538 1683 538 557 540 558 539 558 540 1683 538 560 563
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8956 4504 536 1684 537 1687 534 559 538 559 538 559 538 560 537 1683 538 1685 536 1682 539 1684 537 1683 538 1684 537 1683 538 1684 537 1683 538 558 539 562 535 559 538 558 539 562 535 560 537 1683 538 1684 537 1683 538 561 536 561 536 561 537 560 537 561 536 558 539 560 537 559 538 560 537 561 536 561 536 563 534 559 538 1684 537 559 538 1684 537 561 536 560 537 560 537 560 537 560 537 560 537 559 538 559 538 559 538 588 509 558 539 559 538 559 538 564 533 1684 537 559 538 560 537 559 538 588 509 563 534 559 538 559 538 558 539 562 535 558 539 561 536 560 537 560 537 559 538 588 509 561 536 560 537 561 536 563 534 561 536 560 537 561 536 1684 537 559 538 559 538 559 538 561 536 560 537 560 537 559 538 561 536 558 539 560 537 1684 537 559 538 1683 538 561 536 561 536 563 534 559 538 558 539 1683 538 1684 537 1684 537 560 537 560 537 1683 538 560 537 560 563
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8957 4502 538 1683 538 1684 537 562 535 560 537 559 538 559 539 1683 538 1682 539 1711 510 1685 536 1683 538 558 539 588 509 557 540 1682 539 557 540 558 539 559 538 559 538 558 539 561 536 1681 540 1682 539 1683 538 559 538 559 538 559 538 561 536 560 537 559 538 558 539 559 538 559 538 559 538 559 538 560 537 560 537 1685 536 560 537 1685 536 560 537 559 538 560 537 560 537 559 538 558 539 559 538 560 537 587 510 562 535 559 538 560 537 557 540 1685 536 559 538 560 537 587 510 588 509 559 538 562 535 560 537 557 540 559 538 557 540 560 537 587 510 560 538 558 539 559 538 559 538 561 536 560 537 560 537 558 540 560 537 559 538 560 537 1683 538 562 535 560 537 559 538 560 537 558 539 559 538 558 539 560 537 560 537 559 538 1683 538 558 539 1684 537 559 538 558 539 559 538 558 539 558 539 1682 539 1684 537 1684 537 1683 538 560 537 558 539 1683 538 1684 564
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8956 4501 539 1682 539 1682 539 559 538 557 540 587 510 558 539 1684 537 1682 539 1682 539 1682 539 1680 541 1682 539 1682 539 1682 539 1681 540 558 539 558 539 558 539 557 540 558 539 557 540 1683 538 1683 538 1683 538 558 539 558 539 558 539 557 540 560 537 560 537 557 540 558 539 557 540 558 539 557 540 560 537 558 539 1681 540 556 541 1684 537 558 539 557 540 559 538 558 539 557 540 558 539 558 539 560 537 559 538 558 539 558 539 557 540 559 538 1685 536 559 538 558 539 587 510 557 540 559 538 559 538 560 537 560 537 558 539 559 538 558 539 559 538 562 535 558 539 557 540 557 540 559 538 559 538 558 539 559 538 558 539 558 539 557 540 1682 539 557 540 558 539 559 538 557 540 558 539 560 537 559 538 557 540 561 536 558 539 1682 539 558 539 1682 539 559 538 557 540 558 539 559 538 559 538 1682 539 1684 537 1683 538 557 540 558 539 558 539 560 537 559 564
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8959 4502 539 1682 539 1682 539 556 541 559 538 558 539 559 538 1680 541 1681 540 1684 537 1683 538 1684 537 557 540 560 537 558 539 1683 538 1682 539 558 539 559 538 559 538 558 539 558 539 1683 538 1681 540 1683 538 559 538 560 537 560 537 559 538 561 536 560 537 559 538 559 538 559 538 559 538 559 538 560 537 557 540 1682 539 557 540 1682 539 559 538 558 539 560 538 558 539 558 539 558 539 558 539 558 539 559 538 559 538 557 540 558 539 558 539 559 538 560 537 1684 537 561 536 557 540 559 538 559 538 557 540 558 539 559 538 560 537 558 539 558 539 559 538 558 539 559 539 559 538 557 540 559 538 557 540 558 540 561 536 558 539 558 539 1683 538 558 539 559 538 557 540 559 538 557 540 558 539 559 538 559 538 558 539 559 538 1681 540 558 539 1684 537 562 535 560 537 559 538 559 538 560 537 1682 539 1682 539 1682 539 1686 535 559 538 1682 539 559 538 1682 565
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8961 4501 539 1681 540 1681 540 559 538 558 539 558 539 557 540 1682 539 1682 539 1681 540 1682 539 1682 539 1683 538 1682 539 1683 538 1682 539 557 540 560 537 558 539 560 537 560 537 558 539 1681 540 1681 540 1682 539 557 540 558 539 561 536 558 539 560 537 558 539 556 541 558 539 558 539 557 540 559 538 558 539 559 538 1684 537 559 538 1682 539 558 539 556 541 559 538 562 535 556 541 558 539 558 539 557 540 558 539 557 540 558 539 559 538 560 537 557 540 557 540 1682 539 558 539 557 540 558 539 559 538 559 538 557 540 558 539 558 539 558 539 558 539 557 540 561 536 558 539 586 511 558 539 557 540 586 511 559 539 556 541 557 540 557 540 1682 539 559 538 558 539 558 539 559 538 558 539 560 537 558 539 559 538 558 539 557 540 1681 540 558 539 1680 541 557 540 557 540 559 538 558 539 559 538 1682 539 1682 539 1682 539 558 539 558 539 1682 539 1682 539 558 565
#
# Model: Carrier 42QG5A580SC
#
name: Off
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8403 4308 519 1425 518 625 520 1461 485 1427 518 559 517 564 567 1386 518 1429 517 562 597 524 519 1431 517 1430 518 567 516 1426 518 565 517 1429 518 1431 518 1425 519 565 517 1457 485 562 518 1429 519 1424 520 565 516 1426 517 561 519 1430 515 1427 518 563 595 525 518 1428 517 528 489 21085 8424 4296 642 467 568 1400 569 533 569 536 647 1402 569 1395 569 535 568 534 569 1405 566 1397 567 549 568 535 569 1401 567 533 570 1399 571 537 570 534 568 537 567 1397 570 535 571 1401 568 534 567 533 567 1399 567 539 568 1402 569 565 569 532 571 1399 571 1397 569 535 566 1358 489 21085 8401 4318 512 1438 517 573 515 1476 532 1389 516 573 518 609 535 1393 568 1392 516 574 567 528 566 1393 565 1390 516 576 516 1437 517 574 517 1441 516 1445 567 1389 517 578 566 1390 516 578 568 1398 566 1392 515 575 516 1437 516 574 517 1442 567 1425 483 597 515 575 516 1446 514 539 490
#
name: Dh
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8391 4318 536 1417 518 564 516 1432 517 1428 518 563 518 562 517 1433 516 1428 518 564 519 565 513 1433 517 1430 517 562 518 1463 483 561 517 1430 516 1428 518 563 595 1393 516 559 519 565 514 1425 519 1429 517 569 514 1426 518 566 514 1466 485 562 518 559 518 561 518 1425 519 531 488 21116 8369 4318 569 534 641 1326 640 462 641 465 641 1330 641 1325 639 464 568 534 595 1403 641 1328 641 462 639 467 640 1328 568 536 642 1367 568 531 567 536 569 1398 568 537 542 1432 640 1327 567 532 568 535 567 1400 567 539 567 1399 568 536 568 1402 569 1402 568 1401 567 537 565 1359 487 21117 8392 4294 565 1388 517 575 566 1393 516 1443 514 574 516 573 517 1443 567 1391 515 574 516 577 566 1393 565 1390 566 528 567 1392 516 575 567 1395 565 1390 515 576 568 1391 515 572 566 528 565 1395 565 1393 515 572 564 1391 515 581 563 1389 516 578 513 609 483 576 566 1391 514 538 487
#
name: Cool_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8424 4292 514 1428 518 559 518 1431 514 1432 517 561 515 566 512 1433 516 1434 516 562 519 562 516 1432 517 1426 518 566 516 1429 517 566 516 1429 518 1428 517 562 516 1467 481 570 517 561 518 565 519 563 515 566 516 567 516 1433 516 1429 514 564 518 561 518 562 519 1428 519 536 562 21009 8478 4280 638 461 641 1328 641 456 642 467 637 1325 643 1326 568 597 568 537 639 1330 641 1327 568 535 640 468 639 1324 568 534 543 1432 566 535 641 494 637 1327 566 532 640 1329 567 1398 640 1327 641 1327 640 1326 642 1328 568 531 568 538 567 1402 567 1406 566 1402 568 532 569 1357 489 21085 8401 4319 565 1394 516 577 567 1396 565 1390 515 574 543 566 516 1441 568 1392 516 575 566 531 566 1393 567 1390 516 576 515 1456 515 579 562 1392 515 1440 515 575 566 1391 516 576 544 569 563 525 516 574 514 606 514 576 514 1439 567 1390 515 575 515 574 570 566 567 1390 515 540 488
#
name: Cool_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8401 4306 518 1460 483 566 518 1432 517 1430 514 561 516 564 517 1429 518 1430 519 592 485 564 517 1427 517 1432 516 561 545 1430 516 566 518 1462 483 1430 515 568 517 1427 516 566 516 564 517 1430 518 1425 518 559 518 562 518 1426 518 1425 517 593 484 561 516 573 516 1427 517 530 564 21007 8401 4322 642 465 643 1324 643 457 643 458 643 1327 643 1327 641 463 642 461 644 1325 644 1324 643 462 642 483 643 1328 641 460 645 1321 643 458 643 464 641 1325 643 459 644 1327 642 1323 643 461 643 461 642 1347 644 1331 641 463 639 462 641 1327 571 1405 542 1428 670 462 645 1282 562 21007 8402 4317 515 1436 517 606 641 1321 516 1438 517 572 515 578 516 1441 515 1440 516 572 515 575 515 1474 610 1316 515 574 644 1314 516 574 567 1394 640 1316 517 578 639 1319 516 576 517 575 513 1447 641 1315 516 576 515 577 566 1396 514 1440 516 573 518 571 638 456 565 1393 515 538 564
#
name: Heat_hi
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8399 4307 519 1426 518 560 517 1432 517 1460 485 561 518 558 517 1436 518 1427 518 555 520 561 517 1430 519 1428 516 560 519 1425 518 566 517 1430 516 1432 519 563 519 1425 518 563 515 1429 517 1433 517 1430 516 1432 518 1429 518 568 516 1432 515 1429 519 561 516 567 517 1427 519 533 487 21083 8424 4339 567 536 568 1402 641 493 608 462 545 1431 640 1326 567 532 568 538 640 1331 640 1329 641 536 565 534 641 1329 567 537 639 1329 640 462 642 467 641 1325 568 532 642 1332 640 488 568 534 565 536 566 566 536 567 641 1329 570 535 638 464 569 1401 641 1326 568 534 639 1287 490 21083 8403 4313 567 1390 516 579 514 1441 515 1438 517 575 516 576 568 1393 568 1394 515 606 534 530 515 1441 567 1425 482 576 568 1392 514 578 566 1392 569 1432 515 576 568 1389 517 577 515 1444 515 1437 569 1393 516 1444 566 1389 566 528 567 1394 566 1419 517 575 513 578 515 1440 516 539 490
#
name: Heat_lo
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 8425 4291 542 1401 544 538 542 1404 518 1427 544 534 570 540 516 1434 542 1399 544 539 544 539 541 1403 541 1402 516 570 517 1427 545 566 511 1406 517 1431 542 534 544 1406 543 536 517 567 543 1404 542 1400 595 525 544 1405 542 534 516 1433 516 1428 544 537 544 535 543 1403 542 505 488 21084 8424 4298 566 535 569 1402 567 533 568 537 570 1403 570 1403 565 534 566 537 566 1402 569 1398 569 533 569 538 592 1446 569 535 570 1400 569 567 535 535 568 1437 568 533 568 1398 569 1402 565 533 567 534 569 1403 568 536 569 1402 568 535 567 534 571 1403 568 1415 566 536 571 1362 489 21084 8403 4313 516 1439 517 574 515 1442 515 1441 518 573 516 574 567 1397 514 1440 515 573 516 575 516 1443 515 1439 518 574 516 1440 517 608 535 1396 517 1441 517 579 515 1438 515 576 517 578 568 1390 569 1391 516 575 518 1439 516 573 517 1445 566 1391 516 571 517 572 516 1441 514 543 487

View File

@@ -1,7 +1,7 @@
Filetype: IR library file Filetype: IR library file
Version: 1 Version: 1
# Last Updated 1st Sept, 2023 # Last Updated 21st Dec, 2023
# Last Checked 1st Oct, 2023 # Last Checked 21st Dec, 2023
# #
name: Power name: Power
type: parsed type: parsed
@@ -3896,3 +3896,159 @@ type: parsed
protocol: NECext protocol: NECext
address: 80 70 00 00 address: 80 70 00 00
command: C1 3E 00 00 command: C1 3E 00 00
#
name: Power
type: parsed
protocol: NECext
address: 87 7C 00 00
command: C8 37 00 00
#
name: Pause
type: parsed
protocol: NECext
address: 87 7C 00 00
command: 4A B5 00 00
#
name: Play
type: parsed
protocol: NECext
address: 87 7C 00 00
command: 01 FE 00 00
#
name: Prev
type: parsed
protocol: NECext
address: 87 7C 00 00
command: 05 FA 00 00
#
name: Next
type: parsed
protocol: NECext
address: 87 7C 00 00
command: 06 F9 00 00
#
name: Power
type: parsed
protocol: NECext
address: 12 34 00 00
command: 01 FE 00 00
#
name: Vol_up
type: parsed
protocol: NECext
address: 12 34 00 00
command: 0A F5 00 00
#
name: Vol_dn
type: parsed
protocol: NECext
address: 12 34 00 00
command: 0B F4 00 00
#
name: Mute
type: parsed
protocol: NECext
address: 12 34 00 00
command: 09 F6 00 00
#
name: Power
type: parsed
protocol: NEC
address: 01 00 00 00
command: 67 00 00 00
#
name: Next
type: parsed
protocol: NEC
address: 01 00 00 00
command: 38 00 00 00
#
name: Vol_up
type: parsed
protocol: NEC
address: 01 00 00 00
command: 3C 00 00 00
#
name: Vol_dn
type: parsed
protocol: NEC
address: 01 00 00 00
command: 3D 00 00 00
#
name: Mute
type: parsed
protocol: NEC
address: 01 00 00 00
command: 8C 00 00 00
#
name: Prev
type: parsed
protocol: NEC
address: 00 00 00 00
command: 44 00 00 00
#
name: Vol_dn
type: parsed
protocol: NEC
address: 00 00 00 00
command: 07 00 00 00
#
name: Vol_up
type: parsed
protocol: NEC
address: 00 00 00 00
command: 1C 00 00 00
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9120 4354 714 1539 660 497 633 499 631 500 630 503 628 503 628 503 629 503 628 503 628 1627 628 1627 628 1627 628 1627 628 1626 628 1627 628 1627 628 1627 628 1627 628 503 628 503 628 503 628 503 628 504 627 503 628 504 627 503 628 1627 628 1628 627 1628 627 1627 628 1627 628 1628 627 40094 9038 2194 628
#
name: Prev
type: parsed
protocol: NEC
address: 01 00 00 00
command: 5A 00 00 00
#
name: Next
type: parsed
protocol: NEC
address: 01 00 00 00
command: 5B 00 00 00
#
name: Pause
type: parsed
protocol: NEC
address: 01 00 00 00
command: B2 00 00 00
#
name: Pause
type: parsed
protocol: NECext
address: 00 EF 00 00
command: 11 EE 00 00
#
name: Pause
type: parsed
protocol: NEC
address: 01 00 00 00
command: 2C 00 00 00
#
name: Play
type: parsed
protocol: NEC
address: 01 00 00 00
command: B2 00 00 00
#
name: Play
type: parsed
protocol: NECext
address: 00 EF 00 00
command: 11 EE 00 00
#
name: Play
type: parsed
protocol: NEC
address: 01 00 00 00
command: 2C 00 00 00

View File

@@ -1,7 +1,7 @@
Filetype: IR library file Filetype: IR library file
Version: 1 Version: 1
#Last Updated 1st Oct, 2023 #Last Updated 21st Dec, 2023
#Last Checked 1st Oct, 2023 #Last Checked 21st Dec, 2023
# #
name: Power name: Power
type: raw type: raw
@@ -2103,3 +2103,39 @@ type: raw
frequency: 38000 frequency: 38000
duty_cycle: 0.33 duty_cycle: 0.33
data: 1330 376 1303 376 460 1192 1331 376 1302 376 459 1191 463 1217 462 1218 1331 376 459 1195 484 1219 460 7907 1300 379 1300 380 456 1223 1300 380 1300 380 456 1224 456 1224 456 1224 1299 380 456 1224 456 1224 456 8166 1299 380 1299 380 456 1224 1299 380 1299 380 456 1224 456 1224 455 1224 1299 380 456 1224 455 1224 456 7909 1299 380 1299 380 455 1224 1299 380 1299 380 456 1224 455 1225 455 1225 1298 381 455 1225 454 1225 455 data: 1330 376 1303 376 460 1192 1331 376 1302 376 459 1191 463 1217 462 1218 1331 376 459 1195 484 1219 460 7907 1300 379 1300 380 456 1223 1300 380 1300 380 456 1224 456 1224 456 1224 1299 380 456 1224 456 1224 456 8166 1299 380 1299 380 456 1224 1299 380 1299 380 456 1224 456 1224 455 1224 1299 380 456 1224 455 1224 456 7909 1299 380 1299 380 455 1224 1299 380 1299 380 456 1224 455 1225 455 1225 1298 381 455 1225 454 1225 455
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9213 4493 613 530 612 529 613 528 614 526 616 1631 615 1631 615 529 613 528 614 1631 615 1631 615 1632 614 1632 614 529 613 528 614 1632 614 1630 616 527 615 1631 615 528 614 1634 611 527 614 527 615 527 615 1630 616 1635 611 528 614 1632 614 527 615 1634 612 1631 615 1632 614 527 615 39730 9215 2228 617 95835 9215 2231 614
#
name: Speed_up
type: parsed
protocol: NEC
address: 30 00 00 00
command: 89 00 00 00
#
name: Timer
type: raw
frequency: 38000
duty_cycle: 0.33
data: 9279 4487 620 526 617 528 614 527 615 527 615 1633 612 1631 614 526 615 527 614 1634 611 1631 614 1633 612 1633 612 529 612 531 610 1633 612 1632 614 528 613 1630 615 1633 613 1632 613 526 615 529 613 527 615 1633 613 1632 614 526 615 526 616 527 615 1632 614 1631 614 1632 614 526 615 39731 9204 2230 614
#
name: Power
type: raw
frequency: 38000
duty_cycle: 0.33
data: 2227 839 757 1597 751 1599 749 829 757 815 750 817 748 814 751 806 749 866 751 836 750 832 754 823 753 819 757 811 754 807 758 799 756 103041 2229 837 749 1605 754 1597 751 826 750 823 753 815 750 812 753 804 751 864 753 834 752 830 756 821 755 818 758 809 756 806 749 808 757 64662 2229 774 749 1558 748 60749 2229 775 748 1559 758
#
name: Speed_up
type: raw
frequency: 38000
duty_cycle: 0.33
data: 2222 843 753 1626 732 1590 758 820 756 817 759 808 757 804 751 806 749 893 724 1603 755 827 748 1595 753 820 756 1578 759 1570 757 827 728 99745 2231 834 752 1601 757 1592 756 821 755 817 759 808 757 805 750 807 758 856 751 1630 728 827 749 1622 726 820 756 1605 732 1570 757 800 755
#
name: Speed_dn
type: raw
frequency: 38000
duty_cycle: 0.33
data: 2221 844 752 1600 758 1591 757 820 756 816 749 817 748 813 752 804 751 1632 758 1596 752 1597 751 1594 754 1585 752 1582 756 806 749 808 757 102464 2224 841 756 1597 751 1599 749 828 748 824 752 815 750 811 754 802 753 1629 750 1604 754 1595 753 1591 757 1583 755 1579 759 804 751 806 749

View File

@@ -1,7 +1,7 @@
Filetype: IR library file Filetype: IR library file
Version: 1 Version: 1
# Last Updated 1st Oct, 2023 # Last Updated 1st Oct, 2023
# Last Checked 1st Oct, 2023 # Last Checked 21st Dec, 2023
# #
# TEMP FIX FOR POWER # TEMP FIX FOR POWER
# #

View File

@@ -1,7 +1,7 @@
Filetype: IR library file Filetype: IR library file
Version: 1 Version: 1
# Last Updated 29th Oct, 2023 # Last Updated 21st Dec, 2023
# Last Checked 29th Oct, 2023 # Last Checked 21st Dec, 2023
# #
name: Power name: Power
type: parsed type: parsed
@@ -2459,3 +2459,9 @@ type: parsed
protocol: NEC protocol: NEC
address: 38 00 00 00 address: 38 00 00 00
command: 1C 00 00 00 command: 1C 00 00 00
#
name: Mute
type: parsed
protocol: NEC
address: 38 00 00 00
command: 04 00 00 00

View File

@@ -54,11 +54,11 @@ static void infrared_progress_view_draw_callback(Canvas* canvas, void* _model) {
float progress_value = (float)model->progress / model->progress_total; float progress_value = (float)model->progress / model->progress_total;
elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value); elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value);
uint8_t percent_value = 100 * model->progress / model->progress_total; char number_string[10] = {0};
char percents_string[10] = {0}; snprintf(
snprintf(percents_string, sizeof(percents_string), "%d%%", percent_value); number_string, sizeof(number_string), "%d/%d", model->progress, model->progress_total);
elements_multiline_text_aligned( elements_multiline_text_aligned(
canvas, x + 33, y + 37, AlignCenter, AlignCenter, percents_string); canvas, x + 33, y + 37, AlignCenter, AlignCenter, number_string);
canvas_draw_icon(canvas, x + 14, y + height - 14, &I_Pin_back_arrow_10x8); canvas_draw_icon(canvas, x + 14, y + height - 14, &I_Pin_back_arrow_10x8);
canvas_draw_str(canvas, x + 30, y + height - 6, "= stop"); canvas_draw_str(canvas, x + 30, y + height - 6, "= stop");

View File

@@ -137,6 +137,15 @@ App(
sources=["plugins/supported_cards/mykey.c"], sources=["plugins/supported_cards/mykey.c"],
) )
App(
appid="zolotaya_korona_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="zolotaya_korona_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/zolotaya_korona.c"],
)
App( App(
appid="nfc_start", appid="nfc_start",
targets=["f7"], targets=["f7"],

View File

@@ -1,6 +1,6 @@
#include "mf_user_dict.h" #include "mf_user_dict.h"
#include <nfc/helpers/nfc_dict.h> #include <toolbox/keys_dict.h>
#include <nfc/protocols/mf_classic/mf_classic.h> #include <nfc/protocols/mf_classic/mf_classic.h>
#include <furi/furi.h> #include <furi/furi.h>
@@ -15,22 +15,22 @@ struct MfUserDict {
MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) { MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) {
MfUserDict* instance = malloc(sizeof(MfUserDict)); MfUserDict* instance = malloc(sizeof(MfUserDict));
NfcDict* dict = nfc_dict_alloc( KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict); furi_assert(dict);
size_t dict_keys_num = nfc_dict_get_total_keys(dict); size_t dict_keys_num = keys_dict_get_total_keys(dict);
instance->keys_num = MIN(max_keys_to_load, dict_keys_num); instance->keys_num = MIN(max_keys_to_load, dict_keys_num);
if(instance->keys_num > 0) { if(instance->keys_num > 0) {
instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey)); instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey));
for(size_t i = 0; i < instance->keys_num; i++) { for(size_t i = 0; i < instance->keys_num; i++) {
bool key_loaded = bool key_loaded =
nfc_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey)); keys_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey));
furi_assert(key_loaded); furi_assert(key_loaded);
} }
} }
nfc_dict_free(dict); keys_dict_free(dict);
return instance; return instance;
} }
@@ -67,13 +67,13 @@ bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) {
furi_assert(index < instance->keys_num); furi_assert(index < instance->keys_num);
furi_assert(instance->keys_arr); furi_assert(instance->keys_arr);
NfcDict* dict = nfc_dict_alloc( KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict); furi_assert(dict);
bool key_delete_success = bool key_delete_success =
nfc_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey)); keys_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey));
nfc_dict_free(dict); keys_dict_free(dict);
if(key_delete_success) { if(key_delete_success) {
instance->keys_num--; instance->keys_num--;

View File

@@ -1,4 +1,5 @@
#include "nfc_supported_cards.h" #include "nfc_supported_cards.h"
#include "../plugins/supported_cards/nfc_supported_card_plugin.h" #include "../plugins/supported_cards/nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h> #include <flipper_application/flipper_application.h>
@@ -7,22 +8,72 @@
#include <furi.h> #include <furi.h>
#include <path.h> #include <path.h>
#include <m-array.h>
#define TAG "NfcSupportedCards" #define TAG "NfcSupportedCards"
#define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins") #define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins")
#define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal" #define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal"
typedef enum {
NfcSupportedCardsPluginFeatureHasVerify = (1U << 0),
NfcSupportedCardsPluginFeatureHasRead = (1U << 1),
NfcSupportedCardsPluginFeatureHasParse = (1U << 2),
} NfcSupportedCardsPluginFeature;
typedef struct {
FuriString* path;
NfcProtocol protocol;
NfcSupportedCardsPluginFeature feature;
} NfcSupportedCardsPluginCache;
ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST);
typedef enum {
NfcSupportedCardsLoadStateIdle,
NfcSupportedCardsLoadStateInProgress,
NfcSupportedCardsLoadStateSuccess,
NfcSupportedCardsLoadStateFail,
} NfcSupportedCardsLoadState;
typedef struct { typedef struct {
Storage* storage; Storage* storage;
File* directory; File* directory;
FuriString* file_path; FuriString* file_path;
char file_name[256]; char file_name[256];
FlipperApplication* app; FlipperApplication* app;
} NfcSupportedCards; } NfcSupportedCardsLoadContext;
static NfcSupportedCards* nfc_supported_cards_alloc() { struct NfcSupportedCards {
NfcSupportedCardsPluginCache_t plugins_cache_arr;
NfcSupportedCardsLoadState load_state;
NfcSupportedCardsLoadContext* load_context;
};
NfcSupportedCards* nfc_supported_cards_alloc() {
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards)); NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr);
return instance;
}
void nfc_supported_cards_free(NfcSupportedCards* instance) {
furi_assert(instance);
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
furi_string_free(plugin_cache->path);
}
NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr);
free(instance);
}
static NfcSupportedCardsLoadContext* nfc_supported_cards_load_context_alloc() {
NfcSupportedCardsLoadContext* instance = malloc(sizeof(NfcSupportedCardsLoadContext));
instance->storage = furi_record_open(RECORD_STORAGE); instance->storage = furi_record_open(RECORD_STORAGE);
instance->directory = storage_file_alloc(instance->storage); instance->directory = storage_file_alloc(instance->storage);
@@ -35,7 +86,7 @@ static NfcSupportedCards* nfc_supported_cards_alloc() {
return instance; return instance;
} }
static void nfc_supported_cards_free(NfcSupportedCards* instance) { static void nfc_supported_cards_load_context_free(NfcSupportedCardsLoadContext* instance) {
if(instance->app) { if(instance->app) {
flipper_application_free(instance->app); flipper_application_free(instance->app);
} }
@@ -50,7 +101,36 @@ static void nfc_supported_cards_free(NfcSupportedCards* instance) {
} }
static const NfcSupportedCardsPlugin* static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCards* instance) { nfc_supported_cards_get_plugin(NfcSupportedCardsLoadContext* instance, FuriString* path) {
furi_assert(instance);
furi_assert(path);
const NfcSupportedCardsPlugin* plugin = NULL;
do {
if(instance->app) flipper_application_free(instance->app);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
if(flipper_application_preload(instance->app, furi_string_get_cstr(path)) !=
FlipperApplicationPreloadStatusSuccess)
break;
if(!flipper_application_is_plugin(instance->app)) break;
if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
break;
const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);
if(descriptor == NULL) break;
if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) break;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) break;
plugin = descriptor->entry_point;
} while(false);
return plugin;
}
static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCardsLoadContext* instance) {
const NfcSupportedCardsPlugin* plugin = NULL; const NfcSupportedCardsPlugin* plugin = NULL;
do { do {
@@ -65,83 +145,137 @@ static const NfcSupportedCardsPlugin*
path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path); path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path);
if(instance->app) flipper_application_free(instance->app); plugin = nfc_supported_cards_get_plugin(instance, instance->file_path);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
if(flipper_application_preload(instance->app, furi_string_get_cstr(instance->file_path)) !=
FlipperApplicationPreloadStatusSuccess)
continue;
if(!flipper_application_is_plugin(instance->app)) continue;
if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
continue;
const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);
if(descriptor == NULL) continue;
if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) continue;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) continue;
plugin = descriptor->entry_point;
} while(plugin == NULL); //-V654 } while(plugin == NULL); //-V654
return plugin; return plugin;
} }
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc) { void nfc_supported_cards_load_cache(NfcSupportedCards* instance) {
furi_assert(instance);
do {
if((instance->load_state == NfcSupportedCardsLoadStateSuccess) ||
(instance->load_state == NfcSupportedCardsLoadStateFail))
break;
instance->load_context = nfc_supported_cards_load_context_alloc();
while(true) {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(instance->load_context);
if(plugin == NULL) break; //-V547
NfcSupportedCardsPluginCache plugin_cache = {}; //-V779
plugin_cache.path = furi_string_alloc_set(instance->load_context->file_path);
plugin_cache.protocol = plugin->protocol;
if(plugin->verify) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasVerify;
}
if(plugin->read) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasRead;
}
if(plugin->parse) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasParse;
}
NfcSupportedCardsPluginCache_push_back(instance->plugins_cache_arr, plugin_cache);
}
nfc_supported_cards_load_context_free(instance->load_context);
size_t plugins_loaded = NfcSupportedCardsPluginCache_size(instance->plugins_cache_arr);
if(plugins_loaded == 0) {
FURI_LOG_D(TAG, "Plugins not found");
instance->load_state = NfcSupportedCardsLoadStateFail;
} else {
FURI_LOG_D(TAG, "Loaded %zu plugins", plugins_loaded);
instance->load_state = NfcSupportedCardsLoadStateSuccess;
}
} while(false);
}
bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc) {
furi_assert(instance);
furi_assert(device); furi_assert(device);
furi_assert(nfc); furi_assert(nfc);
bool card_read = false; bool card_read = false;
NfcProtocol protocol = nfc_device_get_protocol(device);
NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
do { do {
const NfcSupportedCardsPlugin* plugin = if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547
const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779 instance->load_context = nfc_supported_cards_load_context_alloc();
if(plugin->protocol != protocol) continue;
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
if(plugin_cache->protocol != protocol) continue;
if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasRead) == 0) continue;
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path);
if(plugin == NULL) continue;
if(plugin->verify) { if(plugin->verify) {
if(!plugin->verify(nfc)) continue; if(!plugin->verify(nfc)) continue;
} }
if(plugin->read) { if(plugin->read) {
card_read = plugin->read(nfc, device); if(plugin->read(nfc, device)) {
card_read = true;
break;
}
}
} }
} while(!card_read); nfc_supported_cards_load_context_free(instance->load_context);
} while(false);
nfc_supported_cards_free(supported_cards);
return card_read; return card_read;
} }
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data) { bool nfc_supported_cards_parse(
NfcSupportedCards* instance,
NfcDevice* device,
FuriString* parsed_data) {
furi_assert(instance);
furi_assert(device); furi_assert(device);
furi_assert(parsed_data); furi_assert(parsed_data);
bool parsed = false; bool card_parsed = false;
NfcProtocol protocol = nfc_device_get_protocol(device);
NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
do { do {
const NfcSupportedCardsPlugin* plugin = if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547
const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779 instance->load_context = nfc_supported_cards_load_context_alloc();
if(plugin->protocol != protocol) continue;
NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
if(plugin_cache->protocol != protocol) continue;
if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasParse) == 0) continue;
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path);
if(plugin == NULL) continue;
if(plugin->parse) { if(plugin->parse) {
parsed = plugin->parse(device, parsed_data); if(plugin->parse(device, parsed_data)) {
card_parsed = true;
break;
}
}
} }
} while(!parsed); nfc_supported_cards_load_context_free(instance->load_context);
} while(false);
nfc_supported_cards_free(supported_cards); return card_parsed;
return parsed;
} }

View File

@@ -15,6 +15,34 @@
extern "C" { extern "C" {
#endif #endif
/**
* @brief NfcSupportedCards opaque type definition.
*/
typedef struct NfcSupportedCards NfcSupportedCards;
/**
* @brief Allocate NfcSupportedCards instance.
*
* @return pointer to allocated NfcSupportedCards instance.
*/
NfcSupportedCards* nfc_supported_cards_alloc();
/**
* @brief Delete an NfcSupportedCards instance
*
* @param[in] instance pointer to instance to be deleted.
*/
void nfc_supported_cards_free(NfcSupportedCards* instance);
/**
* @brief Load plugins information to cache.
*
* @note This function must be called before calling read and parse fanctions.
*
* @param[in, out] instance pointer to NfcSupportedCards instance.
*/
void nfc_supported_cards_load_cache(NfcSupportedCards* instance);
/** /**
* @brief Read the card using a custom procedure. * @brief Read the card using a custom procedure.
* *
@@ -22,13 +50,14 @@ extern "C" {
* try to execute the custom read procedure specified in each. Upon first success, * try to execute the custom read procedure specified in each. Upon first success,
* no further attempts will be made and the function will return. * no further attempts will be made and the function will return.
* *
* @param[in, out] instance pointer to NfcSupportedCards instance.
* @param[in,out] device pointer to a device instance to hold the read data. * @param[in,out] device pointer to a device instance to hold the read data.
* @param[in,out] nfc pointer to an Nfc instance. * @param[in,out] nfc pointer to an Nfc instance.
* @returns true if the card was successfully read, false otherwise. * @returns true if the card was successfully read, false otherwise.
* *
* @see NfcSupportedCardPluginRead for detailed description. * @see NfcSupportedCardPluginRead for detailed description.
*/ */
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc); bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc);
/** /**
* @brief Parse raw data into human-readable representation. * @brief Parse raw data into human-readable representation.
@@ -37,13 +66,17 @@ bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc);
* try to parse the data according to each implementation. Upon first success, * try to parse the data according to each implementation. Upon first success,
* no further attempts will be made and the function will return. * no further attempts will be made and the function will return.
* *
* @param[in, out] instance pointer to NfcSupportedCards instance.
* @param[in] device pointer to a device instance holding the data is to be parsed. * @param[in] device pointer to a device instance holding the data is to be parsed.
* @param[out] parsed_data pointer to the string to contain the formatted result. * @param[out] parsed_data pointer to the string to contain the formatted result.
* @returns true if the card was successfully parsed, false otherwise. * @returns true if the card was successfully parsed, false otherwise.
* *
* @see NfcSupportedCardPluginParse for detailed description. * @see NfcSupportedCardPluginParse for detailed description.
*/ */
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data); bool nfc_supported_cards_parse(
NfcSupportedCards* instance,
NfcDevice* device,
FuriString* parsed_data);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -8,7 +8,6 @@
#include "nfc_protocol_support.h" #include "nfc_protocol_support.h"
#include "nfc/nfc_app_i.h" #include "nfc/nfc_app_i.h"
#include "nfc/helpers/nfc_supported_cards.h"
#include "nfc_protocol_support_defs.h" #include "nfc_protocol_support_defs.h"
#include "nfc_protocol_support_gui_common.h" #include "nfc_protocol_support_gui_common.h"
@@ -157,9 +156,11 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
instance->protocols_detected[instance->protocols_detected_selected_idx]; instance->protocols_detected[instance->protocols_detected_selected_idx];
instance->poller = nfc_poller_alloc(instance->nfc, protocol); instance->poller = nfc_poller_alloc(instance->nfc, protocol);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
// Start poller with the appropriate callback // Start poller with the appropriate callback
nfc_protocol_support[protocol]->scene_read.on_enter(instance); nfc_protocol_support[protocol]->scene_read.on_enter(instance);
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
nfc_blink_detect_start(instance); nfc_blink_detect_start(instance);
} }
@@ -178,7 +179,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
} else if(event.event == NfcCustomEventPollerIncomplete) { } else if(event.event == NfcCustomEventPollerIncomplete) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller); nfc_poller_free(instance->poller);
bool card_read = nfc_supported_cards_read(instance->nfc_device, instance->nfc); bool card_read = nfc_supported_cards_read(
instance->nfc_supported_cards, instance->nfc_device, instance->nfc);
if(card_read) { if(card_read) {
notification_message(instance->notifications, &sequence_success); notification_message(instance->notifications, &sequence_success);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
@@ -303,7 +305,7 @@ static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) {
Widget* widget = instance->widget; Widget* widget = instance->widget;
FuriString* temp_str = furi_string_alloc(); FuriString* temp_str = furi_string_alloc();
if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) { if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {
widget_add_text_scroll_element( widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
} else { } else {

View File

@@ -51,6 +51,7 @@ NfcApp* nfc_app_alloc() {
instance->mf_ul_auth = mf_ultralight_auth_alloc(); instance->mf_ul_auth = mf_ultralight_auth_alloc();
instance->mfc_key_cache = mf_classic_key_cache_alloc(); instance->mfc_key_cache = mf_classic_key_cache_alloc();
instance->nfc_supported_cards = nfc_supported_cards_alloc();
// Nfc device // Nfc device
instance->nfc_device = nfc_device_alloc(); instance->nfc_device = nfc_device_alloc();
@@ -110,7 +111,6 @@ NfcApp* nfc_app_alloc() {
instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget)); instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget));
// Dict attack // Dict attack
instance->dict_attack = dict_attack_alloc(); instance->dict_attack = dict_attack_alloc();
view_dispatcher_add_view( view_dispatcher_add_view(
instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack)); instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack));
@@ -141,6 +141,7 @@ void nfc_app_free(NfcApp* instance) {
mf_ultralight_auth_free(instance->mf_ul_auth); mf_ultralight_auth_free(instance->mf_ul_auth);
mf_classic_key_cache_free(instance->mfc_key_cache); mf_classic_key_cache_free(instance->mfc_key_cache);
nfc_supported_cards_free(instance->nfc_supported_cards);
// Nfc device // Nfc device
nfc_device_free(instance->nfc_device); nfc_device_free(instance->nfc_device);
@@ -339,8 +340,10 @@ bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog) {
furi_assert(path); furi_assert(path);
bool result = false; bool result = false;
nfc_supported_cards_load_cache(instance->nfc_supported_cards);
FuriString* load_path = furi_string_alloc(); FuriString* load_path = furi_string_alloc();
if(nfc_has_shadow_file_internal(instance, path)) { if(nfc_has_shadow_file_internal(instance, path)) { //-V1051
nfc_set_shadow_file_path(path, load_path); nfc_set_shadow_file_path(path, load_path);
} else if(furi_string_end_with(path, NFC_APP_SHADOW_EXTENSION)) { } else if(furi_string_end_with(path, NFC_APP_SHADOW_EXTENSION)) {
size_t path_len = furi_string_size(path); size_t path_len = furi_string_size(path);

View File

@@ -31,6 +31,7 @@
#include "helpers/mf_user_dict.h" #include "helpers/mf_user_dict.h"
#include "helpers/mfkey32_logger.h" #include "helpers/mfkey32_logger.h"
#include "helpers/mf_classic_key_cache.h" #include "helpers/mf_classic_key_cache.h"
#include "helpers/nfc_supported_cards.h"
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
#include <storage/storage.h> #include <storage/storage.h>
@@ -51,7 +52,7 @@
#include <nfc/nfc_device.h> #include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_data_generator.h> #include <nfc/helpers/nfc_data_generator.h>
#include <nfc/helpers/nfc_dict.h> #include <toolbox/keys_dict.h>
#include <gui/modules/validators.h> #include <gui/modules/validators.h>
#include <toolbox/path.h> #include <toolbox/path.h>
@@ -79,7 +80,7 @@ typedef enum {
} NfcRpcState; } NfcRpcState;
typedef struct { typedef struct {
NfcDict* dict; KeysDict* dict;
uint8_t sectors_total; uint8_t sectors_total;
uint8_t sectors_read; uint8_t sectors_read;
uint8_t current_sector; uint8_t current_sector;
@@ -132,6 +133,7 @@ struct NfcApp {
Mfkey32Logger* mfkey32_logger; Mfkey32Logger* mfkey32_logger;
MfUserDict* mf_user_dict; MfUserDict* mf_user_dict;
MfClassicKeyCache* mfc_key_cache; MfClassicKeyCache* mfc_key_cache;
NfcSupportedCards* nfc_supported_cards;
NfcDevice* nfc_device; NfcDevice* nfc_device;
Iso14443_3aData* iso14443_3a_edit_data; Iso14443_3aData* iso14443_3a_edit_data;

View File

@@ -250,19 +250,18 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
last_trip.day = block_start_ptr[2]; last_trip.day = block_start_ptr[2];
last_trip.hour = block_start_ptr[3]; last_trip.hour = block_start_ptr[3];
last_trip.minute = block_start_ptr[4]; last_trip.minute = block_start_ptr[4];
bool is_last_trip_valid = (block_start_ptr[0] | block_start_ptr[1] | block_start_ptr[0]) && bool is_last_trip_valid = (block_start_ptr[0] | block_start_ptr[1] | block_start_ptr[2]) &&
(last_trip.day < 32 && last_trip.month < 12 && (last_trip.day < 32 && last_trip.month < 12 &&
last_trip.hour < 24 && last_trip.minute < 60); last_trip.hour < 24 && last_trip.minute < 60);
start_block_num = mf_classic_get_first_block_num_of_sector(balance_sector_number); start_block_num = mf_classic_get_first_block_num_of_sector(balance_sector_number);
block_start_ptr = &data->block[start_block_num].data[0]; block_start_ptr = &data->block[start_block_num].data[0];
const uint32_t trip_counter = (block_start_ptr[3] << 24) | (block_start_ptr[2] << 16) | const uint32_t trip_counter = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
(block_start_ptr[1] << 8) | (block_start_ptr[0]);
size_t uid_len = 0; size_t uid_len = 0;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len); const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
const uint32_t card_number = (uid[3] << 24) | (uid[2] << 16) | (uid[1] << 8) | (uid[0]); const uint32_t card_number = nfc_util_bytes2num_little_endian(uid, 4);
furi_string_cat_printf( furi_string_cat_printf(
parsed_data, "\e#Kazan transport card\nCard number: %lu\n", card_number); parsed_data, "\e#Kazan transport card\nCard number: %lu\n", card_number);

View File

@@ -148,15 +148,14 @@ static bool metromoney_parse(const NfcDevice* device, FuriString* parsed_data) {
const uint8_t* block_start_ptr = const uint8_t* block_start_ptr =
&data->block[start_block_num + ticket_block_number].data[0]; &data->block[start_block_num + ticket_block_number].data[0];
uint32_t balance = (block_start_ptr[3] << 24) | (block_start_ptr[2] << 16) | uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
(block_start_ptr[1] << 8) | (block_start_ptr[0]);
uint32_t balance_lari = balance / 100; uint32_t balance_lari = balance / 100;
uint8_t balance_tetri = balance % 100; uint8_t balance_tetri = balance % 100;
size_t uid_len = 0; size_t uid_len = 0;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len); const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
uint32_t card_number = (uid[3] << 24) | (uid[2] << 16) | (uid[1] << 8) | (uid[0]); uint32_t card_number = nfc_util_bytes2num_little_endian(uid, 4);
furi_string_printf( furi_string_printf(
parsed_data, parsed_data,

View File

@@ -1,119 +1,112 @@
#include "nfc_supported_card_plugin.h" #include "nfc_supported_card_plugin.h"
#include <flipper_application/flipper_application.h> #include <flipper_application/flipper_application.h>
#include <lib/nfc/protocols/st25tb/st25tb.h> #include <machine/endian.h>
#include <nfc/nfc_device.h> #include <nfc/protocols/st25tb/st25tb.h>
#include <nfc/helpers/nfc_util.h>
//Structures data of mykey card #define TAG "MyKey"
enum {
MYKEY_BLOCK_KEY_ID = 0x07,
MYKEY_BLOCK_PRODUCTION_DATE = 0x08,
MYKEY_BLOCK_VENDOR_ID_1 = 0x18,
MYKEY_BLOCK_VENDOR_ID_2 = 0x19,
MYKEY_BLOCK_CURRENT_CREDIT = 0x21,
MYKEY_BLOCK_PREVIOUS_CREDIT = 0x23,
MYKEY_DEFAULT_VENDOR_ID = 0xFEDC0123,
MYKEY_DEFAULT_VENDOR_ID_1 = 0xFEDC,
MYKEY_DEFAULT_VENDOR_ID_2 = 0x0123,
};
typedef enum { const uint32_t blankBlock18 = 0x480FCD8F, blankBlock19 = 0x070082C0;
LockIdStatusNone,
LockIdStatusActive,
} LockIdStatus;
/* Function to obtain the UID as a 32-bit */ static bool mykey_is_blank(const St25tbData* data) {
uint32_t get_uid(const uint8_t uid[8]) { return data->blocks[0x18] == blankBlock18 && data->blocks[0x19] == blankBlock19;
return (uid[7] | (uid[6] << 8) | (uid[5] << 16) | (uid[4] << 24));
} }
/* OTP calculation (reverse block 6, incremental. 1,2,3, ecc.) */ static bool mykey_has_lockid(const St25tbData* data) {
uint32_t new_get_count_down_counter(uint32_t b6) { return (data->blocks[5] & 0xFF) == 0x7F;
return ~(b6 << 24 | (b6 & 0x0000FF00) << 8 | (b6 & 0x00FF0000) >> 8 | b6 >> 24);
}
/* Function to check if the vendor is bound */
int get_is_bound(uint32_t vendor_id) {
return (vendor_id != MYKEY_DEFAULT_VENDOR_ID);
}
/* MK = UID * VENDOR */
uint32_t get_master_key(uint32_t uid, uint32_t vendor_id) {
return uid * (vendor_id + 1);
}
/* SK (Encryption key) = MK * OTP */
uint32_t get_encryption_key(uint32_t master_key, uint32_t count_down_counter) {
return master_key * (count_down_counter + 1);
}
/* Encode or decode a MyKey block */
uint32_t encode_decode_block(uint32_t input) {
/*
* Swap all values using XOR
* 32 bit: 1111222233334444
*/
input ^= (input & 0x00C00000) << 6 | (input & 0x0000C000) << 12 | (input & 0x000000C0) << 18 |
(input & 0x000C0000) >> 6 | (input & 0x00030000) >> 12 | (input & 0x00000300) >> 6;
input ^= (input & 0x30000000) >> 6 | (input & 0x0C000000) >> 12 | (input & 0x03000000) >> 18 |
(input & 0x00003000) << 6 | (input & 0x00000030) << 12 | (input & 0x0000000C) << 6;
input ^= (input & 0x00C00000) << 6 | (input & 0x0000C000) << 12 | (input & 0x000000C0) << 18 |
(input & 0x000C0000) >> 6 | (input & 0x00030000) >> 12 | (input & 0x00000300) >> 6;
return input;
}
uint32_t get_block(uint32_t block) {
return encode_decode_block(__bswap32(block));
}
uint32_t get_xored_block(uint32_t block, uint32_t key) {
return encode_decode_block(__bswap32(block) ^ key);
}
uint32_t get_vendor(uint32_t b1, uint32_t b2) {
return b1 << 16 | (b2 & 0x0000FFFF);
} }
static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device); furi_assert(device);
furi_assert(parsed_data); furi_assert(parsed_data);
bool parsed = false;
do {
//Get data
const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb); const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb);
//Calc data if(data->type != St25tbType04k && data->type != St25tbTypeX4k) {
uint32_t _uid = get_uid(data->uid); FURI_LOG_D(TAG, "bad type");
uint32_t _count_down_counter_new = new_get_count_down_counter(__bswap32(data->blocks[6])); return false;
uint32_t _vendor_id = get_vendor( }
get_block(data->blocks[MYKEY_BLOCK_VENDOR_ID_1]),
get_block(data->blocks[MYKEY_BLOCK_VENDOR_ID_2]));
uint32_t _master_key = get_master_key(_uid, _vendor_id);
uint32_t _encryption_key = get_encryption_key(_master_key, _count_down_counter_new);
uint16_t credit =
get_xored_block(data->blocks[MYKEY_BLOCK_CURRENT_CREDIT], _encryption_key);
uint16_t _previous_credit = get_block(data->blocks[MYKEY_BLOCK_PREVIOUS_CREDIT]);
bool _is_bound = get_is_bound(_vendor_id);
//parse data for(int i = 0; i < 5; i++) {
furi_string_cat_printf(parsed_data, "\e#MyKey Card\n"); if(data->blocks[i] != 0xFFFFFFFF) {
furi_string_cat_printf(parsed_data, "UID: %08lX\n", _uid); FURI_LOG_D(TAG, "bad otp block %d", i);
furi_string_cat_printf(parsed_data, "Vendor ID: %08lX\n", _vendor_id); return false;
furi_string_cat_printf( }
parsed_data, "Current Credit: %d.%02d E \n", credit / 100, credit % 100); }
if((data->blocks[8] >> 16 & 0xFF) > 0x31 || (data->blocks[8] >> 8 & 0xFF) > 0x12) {
FURI_LOG_D(TAG, "bad mfg date");
return false;
}
if(data->system_otp_block != 0xFEFFFFFF) {
FURI_LOG_D(TAG, "bad sys otp block");
return false;
}
furi_string_cat(parsed_data, "\e#MyKey\n");
if(data->blocks[6] == 0) { // Tag is actually a MyKey but it has been bricked by a reader
furi_string_cat(parsed_data, "\e#Bricked!\nBlock 6 is 0!");
return true;
}
bool is_blank = mykey_is_blank(data);
furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", (uint32_t)__bswap32(data->blocks[7]));
furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no");
furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no");
uint32_t block8 = data->blocks[8];
furi_string_cat_printf( furi_string_cat_printf(
parsed_data, parsed_data,
"Previus Credit: %d.%02d E \n", "Prod. date: %02lX/%02lX/%04lX",
_previous_credit / 100, block8 >> 16 & 0xFF,
_previous_credit % 100); block8 >> 8 & 0xFF,
furi_string_cat_printf(parsed_data, "Is Bound: %s\n", _is_bound ? "yes" : "no"); 0x2000 + (block8 & 0xFF));
parsed = true; if(!is_blank) {
} while(false); furi_string_cat_printf(
parsed_data, "\nOp. count: %zu\n", (size_t)__bswap32(data->blocks[0x12] & 0xFFFFFF00));
return parsed; uint32_t block3C = data->blocks[0x3C];
if(block3C == 0xFFFFFFFF) {
furi_string_cat(parsed_data, "No history available!");
} else {
block3C ^= data->blocks[0x07];
uint32_t startingOffset = ((block3C & 0x30000000) >> 28) |
((block3C & 0x00100000) >> 18);
furi_check(startingOffset < 8); //-V547
for(int txnOffset = 8; txnOffset > 0; txnOffset--) {
uint32_t txnBlock =
__bswap32(data->blocks[0x34 + ((startingOffset + txnOffset) % 8)]);
if(txnBlock == 0xFFFFFFFF) {
break;
}
uint8_t day = txnBlock >> 27;
uint8_t month = txnBlock >> 23 & 0xF;
uint16_t year = 2000 + (txnBlock >> 16 & 0x7F);
uint16_t credit = txnBlock & 0xFFFF;
if(txnOffset == 8) {
furi_string_cat_printf(
parsed_data, "Current credit: %d.%02d euros\n", credit / 100, credit % 100);
furi_string_cat(parsed_data, "Op. history (newest first):");
}
furi_string_cat_printf(
parsed_data,
"\n %02d/%02d/%04d %d.%02d",
day,
month,
year,
credit / 100,
credit % 100);
}
}
}
return true;
} }
/* Actual implementation of app<>plugin interface */ /* Actual implementation of app<>plugin interface */

View File

@@ -62,8 +62,8 @@ void generate_saflok_key(const uint8_t* uid, uint8_t* key) {
uint8_t carry_sum = 0; uint8_t carry_sum = 0;
for(int i = KEY_LENGTH - 1; i >= 0; i--, magickal_index--) { for(int i = KEY_LENGTH - 1; i >= 0; i--, magickal_index--) {
uint16_t keysum = temp_key[i] + magic_table[magickal_index]; uint16_t keysum = temp_key[i] + magic_table[magickal_index] + carry_sum;
temp_key[i] = (keysum & 0xFF) + carry_sum; temp_key[i] = (keysum & 0xFF);
carry_sum = keysum >> 8; carry_sum = keysum >> 8;
} }

View File

@@ -0,0 +1,254 @@
/*
* Parser for Zolotaya Korona card (Russia).
*
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
*
* More info about Zolotaya Korona cards: https://github.com/metrodroid/metrodroid/wiki/Zolotaya-Korona
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core/core_defines.h"
#include "core/string.h"
#include "furi_hal_rtc.h"
#include "nfc_supported_card_plugin.h"
#include "protocols/mf_classic/mf_classic.h"
#include <flipper_application/flipper_application.h>
#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <stdbool.h>
#include <stdint.h>
#define TAG "Zolotaya Korona"
#define TRIP_SECTOR_NUM (4)
#define PURSE_SECTOR_NUM (6)
#define INFO_SECTOR_NUM (15)
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
// Sector 15 data. Byte [11] contains the mistake. If byte [11] was 0xEF, bytes [1-18] means "ЗАО Золотая Корона"
static const uint8_t info_sector_signature[] = {0xE2, 0x87, 0x80, 0x8E, 0x20, 0x87, 0xAE,
0xAB, 0xAE, 0xF2, 0xA0, 0xEF, 0x20, 0x8A,
0xAE, 0xE0, 0xAE, 0xAD, 0xA0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
#define FURI_HAL_RTC_SECONDS_PER_MINUTE 60
#define FURI_HAL_RTC_SECONDS_PER_HOUR (FURI_HAL_RTC_SECONDS_PER_MINUTE * 60)
#define FURI_HAL_RTC_SECONDS_PER_DAY (FURI_HAL_RTC_SECONDS_PER_HOUR * 24)
#define FURI_HAL_RTC_EPOCH_START_YEAR 1970
void timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime) {
uint32_t days = timestamp / FURI_HAL_RTC_SECONDS_PER_DAY;
uint32_t seconds_in_day = timestamp % FURI_HAL_RTC_SECONDS_PER_DAY;
datetime->year = FURI_HAL_RTC_EPOCH_START_YEAR;
while(days >= furi_hal_rtc_get_days_per_year(datetime->year)) {
days -= furi_hal_rtc_get_days_per_year(datetime->year);
(datetime->year)++;
}
datetime->month = 1;
while(days >= furi_hal_rtc_get_days_per_month(
furi_hal_rtc_is_leap_year(datetime->year), datetime->month)) {
days -= furi_hal_rtc_get_days_per_month(
furi_hal_rtc_is_leap_year(datetime->year), datetime->month);
(datetime->month)++;
}
datetime->day = days + 1;
datetime->hour = seconds_in_day / FURI_HAL_RTC_SECONDS_PER_HOUR;
datetime->minute =
(seconds_in_day % FURI_HAL_RTC_SECONDS_PER_HOUR) / FURI_HAL_RTC_SECONDS_PER_MINUTE;
datetime->second = seconds_in_day % FURI_HAL_RTC_SECONDS_PER_MINUTE;
}
uint64_t bytes2num_bcd(const uint8_t* src, uint8_t len_bytes) {
furi_assert(src);
uint64_t res = 0;
for(uint8_t i = 0; i < len_bytes; i++) {
res *= 10;
res += src[i] / 16;
res *= 10;
res += src[i] % 16;
}
return res;
}
static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// Verify info sector data
const uint8_t start_info_block_number =
mf_classic_get_first_block_num_of_sector(INFO_SECTOR_NUM);
const uint8_t* block_start_ptr = &data->block[start_info_block_number].data[0];
bool verified = true;
for(uint8_t i = 0; i < sizeof(info_sector_signature); i++) {
if(i == 16) {
block_start_ptr = &data->block[start_info_block_number + 1].data[0];
}
if(block_start_ptr[i % 16] != info_sector_signature[i]) {
verified = false;
break;
}
}
if(!verified) break;
// Parse data
// INFO SECTOR
// block 1
const uint8_t region_number = bytes2num_bcd(block_start_ptr + 10, 1);
// block 2
block_start_ptr = &data->block[start_info_block_number + 2].data[4];
const uint64_t card_number =
bytes2num_bcd(block_start_ptr, 9) * 10 + bytes2num_bcd(block_start_ptr + 9, 1) / 10;
// TRIP SECTOR
const uint8_t start_trip_block_number =
mf_classic_get_first_block_num_of_sector(TRIP_SECTOR_NUM);
// block 0
block_start_ptr = &data->block[start_trip_block_number].data[7];
const uint8_t status = block_start_ptr[0] % 16;
const uint16_t sequence_number = nfc_util_bytes2num(block_start_ptr + 1, 2);
const uint8_t discount_code = nfc_util_bytes2num(block_start_ptr + 3, 1);
// block 1: refill block
block_start_ptr = &data->block[start_trip_block_number + 1].data[1];
const uint16_t refill_machine_id = nfc_util_bytes2num_little_endian(block_start_ptr, 2);
const uint32_t last_refill_timestamp =
nfc_util_bytes2num_little_endian(block_start_ptr + 2, 4);
const uint32_t last_refill_amount =
nfc_util_bytes2num_little_endian(block_start_ptr + 6, 4);
const uint32_t last_refill_amount_rub = last_refill_amount / 100;
const uint8_t last_refill_amount_kop = last_refill_amount % 100;
const uint16_t refill_counter = nfc_util_bytes2num_little_endian(block_start_ptr + 10, 2);
FuriHalRtcDateTime last_refill_datetime = {0};
timestamp_to_datetime(last_refill_timestamp, &last_refill_datetime);
// block 2: trip block
block_start_ptr = &data->block[start_trip_block_number + 2].data[0];
const char validator_first_letter =
nfc_util_bytes2num_little_endian(block_start_ptr + 1, 1);
const uint32_t validator_id = bytes2num_bcd(block_start_ptr + 2, 3);
const uint32_t last_trip_timestamp =
nfc_util_bytes2num_little_endian(block_start_ptr + 6, 4);
const uint8_t track_number = nfc_util_bytes2num_little_endian(block_start_ptr + 10, 1);
const uint32_t prev_balance = nfc_util_bytes2num_little_endian(block_start_ptr + 11, 4);
const uint32_t prev_balance_rub = prev_balance / 100;
const uint8_t prev_balance_kop = prev_balance % 100;
FuriHalRtcDateTime last_trip_datetime = {0};
timestamp_to_datetime(last_trip_timestamp, &last_trip_datetime);
// PARSE DATA FROM PURSE SECTOR
const uint8_t start_purse_block_number =
mf_classic_get_first_block_num_of_sector(PURSE_SECTOR_NUM);
block_start_ptr = &data->block[start_purse_block_number].data[0];
// block 0
uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
uint32_t balance_rub = balance / 100;
uint8_t balance_kop = balance % 100;
furi_string_cat_printf(
parsed_data,
"\e#Zolotaya korona\nCard number: %llu\nRegion: %u\nBalance: %lu.%02u RUR\nPrev. balance: %lu.%02u RUR",
card_number,
region_number,
balance_rub,
balance_kop,
prev_balance_rub,
prev_balance_kop);
furi_string_cat_printf(
parsed_data,
"\nLast refill amount: %lu.%02u RUR\nRefill counter: %u\nLast refill: %u.%02u.%02u %02u:%02u\nRefill machine id: %u",
last_refill_amount_rub,
last_refill_amount_kop,
refill_counter,
last_refill_datetime.day,
last_refill_datetime.month,
last_refill_datetime.year,
last_refill_datetime.hour,
last_refill_datetime.minute,
refill_machine_id);
furi_string_cat_printf(
parsed_data,
"\nLast trip: %u.%02u.%02u %02u:%02u\nTrack number: %u\nValidator: %c%06lu",
last_trip_datetime.day,
last_trip_datetime.month,
last_trip_datetime.year,
last_trip_datetime.hour,
last_trip_datetime.minute,
track_number,
validator_first_letter,
validator_id);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
furi_string_cat_printf(
parsed_data,
"\nStatus: %u\nSequence num: %u\nDiscount code: %u",
status,
sequence_number,
discount_code);
}
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin zolotaya_korona_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = NULL,
.read = NULL,
.parse = zolotaya_korona_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor zolotaya_korona_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &zolotaya_korona_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* zolotaya_korona_plugin_ep() {
return &zolotaya_korona_plugin_descriptor;
}

View File

@@ -3828,3 +3828,6 @@ F72CD208FDF9
555D8BBC2D3E 555D8BBC2D3E
78DF1176C8FD 78DF1176C8FD
ADC169F922CB ADC169F922CB
# Volgograd (Russia) Volna transport cards keys
2B787A063D5D
D37C8F1793F7

View File

@@ -41,7 +41,8 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) { } else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) {
MfClassicKey key = {}; MfClassicKey key = {};
if(nfc_dict_get_next_key(instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) { if(keys_dict_get_next_key(
instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) {
mfc_event->data->key_request_data.key = key; mfc_event->data->key_request_data.key = key;
mfc_event->data->key_request_data.key_provided = true; mfc_event->data->key_request_data.key_provided = true;
instance->nfc_dict_context.dict_keys_current++; instance->nfc_dict_context.dict_keys_current++;
@@ -60,7 +61,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) { } else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
nfc_dict_rewind(instance->nfc_dict_context.dict); keys_dict_rewind(instance->nfc_dict_context.dict);
instance->nfc_dict_context.dict_keys_current = 0; instance->nfc_dict_context.dict_keys_current = 0;
instance->nfc_dict_context.current_sector = instance->nfc_dict_context.current_sector =
mfc_event->data->next_sector_data.current_sector; mfc_event->data->next_sector_data.current_sector;
@@ -79,7 +80,7 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) { } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) {
nfc_dict_rewind(instance->nfc_dict_context.dict); keys_dict_rewind(instance->nfc_dict_context.dict);
instance->nfc_dict_context.is_key_attack = false; instance->nfc_dict_context.is_key_attack = false;
instance->nfc_dict_context.dict_keys_current = 0; instance->nfc_dict_context.dict_keys_current = 0;
view_dispatcher_send_custom_event( view_dispatcher_send_custom_event(
@@ -124,15 +125,15 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
if(state == DictAttackStateUserDictInProgress) { if(state == DictAttackStateUserDictInProgress) {
do { do {
if(!nfc_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) { if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
state = DictAttackStateSystemDictInProgress; state = DictAttackStateSystemDictInProgress;
break; break;
} }
instance->nfc_dict_context.dict = nfc_dict_alloc( instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
if(nfc_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) { if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
nfc_dict_free(instance->nfc_dict_context.dict); keys_dict_free(instance->nfc_dict_context.dict);
state = DictAttackStateSystemDictInProgress; state = DictAttackStateSystemDictInProgress;
break; break;
} }
@@ -141,13 +142,13 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
} while(false); } while(false);
} }
if(state == DictAttackStateSystemDictInProgress) { if(state == DictAttackStateSystemDictInProgress) {
instance->nfc_dict_context.dict = nfc_dict_alloc( instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));
dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary"); dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary");
} }
instance->nfc_dict_context.dict_keys_total = instance->nfc_dict_context.dict_keys_total =
nfc_dict_get_total_keys(instance->nfc_dict_context.dict); keys_dict_get_total_keys(instance->nfc_dict_context.dict);
dict_attack_set_total_dict_keys( dict_attack_set_total_dict_keys(
instance->dict_attack, instance->nfc_dict_context.dict_keys_total); instance->dict_attack, instance->nfc_dict_context.dict_keys_total);
instance->nfc_dict_context.dict_keys_current = 0; instance->nfc_dict_context.dict_keys_current = 0;
@@ -185,7 +186,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
if(state == DictAttackStateUserDictInProgress) { if(state == DictAttackStateUserDictInProgress) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller); nfc_poller_free(instance->poller);
nfc_dict_free(instance->nfc_dict_context.dict); keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state( scene_manager_set_scene_state(
instance->scene_manager, instance->scene_manager,
NfcSceneMfClassicDictAttack, NfcSceneMfClassicDictAttack,
@@ -215,7 +216,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
if(instance->nfc_dict_context.is_card_present) { if(instance->nfc_dict_context.is_card_present) {
nfc_poller_stop(instance->poller); nfc_poller_stop(instance->poller);
nfc_poller_free(instance->poller); nfc_poller_free(instance->poller);
nfc_dict_free(instance->nfc_dict_context.dict); keys_dict_free(instance->nfc_dict_context.dict);
scene_manager_set_scene_state( scene_manager_set_scene_state(
instance->scene_manager, instance->scene_manager,
NfcSceneMfClassicDictAttack, NfcSceneMfClassicDictAttack,
@@ -253,7 +254,7 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress);
nfc_dict_free(instance->nfc_dict_context.dict); keys_dict_free(instance->nfc_dict_context.dict);
instance->nfc_dict_context.current_sector = 0; instance->nfc_dict_context.current_sector = 0;
instance->nfc_dict_context.sectors_total = 0; instance->nfc_dict_context.sectors_total = 0;

View File

@@ -14,20 +14,20 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
// Load flipper dict keys total // Load flipper dict keys total
uint32_t flipper_dict_keys_total = 0; uint32_t flipper_dict_keys_total = 0;
NfcDict* dict = nfc_dict_alloc( KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, KeysDictModeOpenExisting, sizeof(MfClassicKey));
if(dict) { if(dict) {
flipper_dict_keys_total = nfc_dict_get_total_keys(dict); flipper_dict_keys_total = keys_dict_get_total_keys(dict);
nfc_dict_free(dict); keys_dict_free(dict);
} }
// Load user dict keys total // Load user dict keys total
uint32_t user_dict_keys_total = 0; uint32_t user_dict_keys_total = 0;
dict = nfc_dict_alloc( dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
if(dict) { if(dict) {
user_dict_keys_total = nfc_dict_get_total_keys(dict); user_dict_keys_total = keys_dict_get_total_keys(dict);
nfc_dict_free(dict); keys_dict_free(dict);
} }
FuriString* temp_str = furi_string_alloc(); FuriString* temp_str = furi_string_alloc();

View File

@@ -29,23 +29,23 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) { if(event.event == NfcCustomEventByteInputDone) {
// Add key to dict // Add key to dict
NfcDict* dict = nfc_dict_alloc( KeysDict* dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
furi_assert(dict); furi_assert(dict);
MfClassicKey key = {}; MfClassicKey key = {};
memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey)); memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey));
if(nfc_dict_is_key_present(dict, key.data, sizeof(MfClassicKey))) { if(keys_dict_is_key_present(dict, key.data, sizeof(MfClassicKey))) {
scene_manager_next_scene( scene_manager_next_scene(
instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
} else if(nfc_dict_add_key(dict, key.data, sizeof(MfClassicKey))) { } else if(keys_dict_add_key(dict, key.data, sizeof(MfClassicKey))) {
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);
dolphin_deed(DolphinDeedNfcMfcAdd); dolphin_deed(DolphinDeedNfcMfcAdd);
} else { } else {
scene_manager_previous_scene(instance->scene_manager); scene_manager_previous_scene(instance->scene_manager);
} }
nfc_dict_free(dict); keys_dict_free(dict);
consumed = true; consumed = true;
} }
} }

View File

@@ -1,6 +1,5 @@
#include "nfc/nfc_app_i.h" #include "nfc/nfc_app_i.h"
#include "nfc/helpers/nfc_supported_cards.h"
#include "nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h" #include "nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h"
void nfc_scene_supported_card_on_enter(void* context) { void nfc_scene_supported_card_on_enter(void* context) {
@@ -8,7 +7,7 @@ void nfc_scene_supported_card_on_enter(void* context) {
FuriString* temp_str = furi_string_alloc(); FuriString* temp_str = furi_string_alloc();
if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) { if(nfc_supported_cards_parse(instance->nfc_supported_cards, instance->nfc_device, temp_str)) {
widget_add_text_scroll_element( widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
widget_add_button_element( widget_add_button_element(

View File

@@ -27,6 +27,8 @@ typedef enum {
SubmenuIndexGibidi433, SubmenuIndexGibidi433,
SubmenuIndexNiceMHouse_433_92, SubmenuIndexNiceMHouse_433_92,
SubmenuIndexJCM_433_92, SubmenuIndexJCM_433_92,
SubmenuIndexFAACRCXT_433_92,
SubmenuIndexFAACRCXT_868,
SubmenuIndexNormstahl_433_92, SubmenuIndexNormstahl_433_92,
SubmenuIndexGSN, SubmenuIndexGSN,
SubmenuIndexAprimatic, SubmenuIndexAprimatic,

View File

@@ -112,6 +112,22 @@ static void subghz_scene_add_to_history_callback(
uint16_t idx = subghz_history_get_item(history); uint16_t idx = subghz_history_get_item(history);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
if(subghz->last_settings->delete_old_signals) {
if(subghz_history_get_last_index(subghz->history) >= 54) {
subghz->state_notifications = SubGhzNotificationStateRx;
subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver);
subghz_history_delete_item(subghz->history, 0);
subghz_view_receiver_delete_item(subghz->subghz_receiver, 0);
subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver);
subghz_scene_receiver_update_statusbar(subghz);
subghz->idx_menu_chosen =
subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
idx--;
}
}
if(subghz_history_add_to_history(history, decoder_base, &preset)) { if(subghz_history_add_to_history(history, decoder_base, &preset)) {
furi_string_reset(item_name); furi_string_reset(item_name);
furi_string_reset(item_time); furi_string_reset(item_time);
@@ -136,7 +152,7 @@ static void subghz_scene_add_to_history_callback(
furi_string_free(item_time); furi_string_free(item_time);
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey);
} else { } else {
FURI_LOG_I(TAG, "%s protocol ignored", decoder_base->protocol->name); FURI_LOG_D(TAG, "%s protocol ignored", decoder_base->protocol->name);
} }
} }
@@ -249,7 +265,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver);
subghz_history_delete_item(subghz->history, subghz->idx_menu_chosen); subghz_history_delete_item(subghz->history, subghz->idx_menu_chosen);
subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); subghz_view_receiver_delete_item(
subghz->subghz_receiver,
subghz_view_receiver_get_idx_menu(subghz->subghz_receiver));
subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);

View File

@@ -13,6 +13,7 @@ enum SubGhzSettingIndex {
SubGhzSettingIndexIgnoreMagellan, SubGhzSettingIndexIgnoreMagellan,
SubGhzSettingIndexIgnorePrinceton, SubGhzSettingIndexIgnorePrinceton,
SubGhzSettingIndexIgnoreNiceFlorS, SubGhzSettingIndexIgnoreNiceFlorS,
SubGhzSettingIndexDeleteOldSignals,
SubGhzSettingIndexSound, SubGhzSettingIndexSound,
SubGhzSettingIndexResetToDefault, SubGhzSettingIndexResetToDefault,
SubGhzSettingIndexLock, SubGhzSettingIndexLock,
@@ -283,6 +284,15 @@ static void subghz_scene_receiver_config_set_niceflors(VariableItem* item) {
subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_NiceFlorS); subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_NiceFlorS);
} }
static void subghz_scene_receiver_config_set_delete_old_signals(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, combobox_text[index]);
subghz->last_settings->delete_old_signals = index == 1;
}
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
@@ -314,6 +324,7 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context,
subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
subghz->last_settings->ignore_filter = subghz->ignore_filter; subghz->last_settings->ignore_filter = subghz->ignore_filter;
subghz->last_settings->filter = subghz->filter; subghz->last_settings->filter = subghz->filter;
subghz->last_settings->delete_old_signals = false;
subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]); subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[default_index]);
@@ -461,6 +472,17 @@ void subghz_scene_receiver_config_on_enter(void* context) {
subghz->ignore_filter, SubGhzProtocolFlag_NiceFlorS); subghz->ignore_filter, SubGhzProtocolFlag_NiceFlorS);
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, combobox_text[value_index]); variable_item_set_current_value_text(item, combobox_text[value_index]);
item = variable_item_list_add(
subghz->variable_item_list,
"Delete old signals when memory is full",
COMBO_BOX_COUNT,
subghz_scene_receiver_config_set_delete_old_signals,
subghz);
value_index = subghz->last_settings->delete_old_signals;
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, combobox_text[value_index]);
} }
// Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :)

View File

@@ -181,6 +181,18 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexJCM_433_92, SubmenuIndexJCM_433_92,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
submenu_add_item(
subghz->submenu,
"KL: FAAC RC,XT 433MHz",
SubmenuIndexFAACRCXT_433_92,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item(
subghz->submenu,
"KL: FAAC RC,XT 868MHz",
SubmenuIndexFAACRCXT_868,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
"KL: Nice Mhouse 433MHz", "KL: Nice Mhouse 433MHz",
@@ -744,6 +756,36 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
} }
break; break;
case SubmenuIndexFAACRCXT_433_92:
generated_protocol = subghz_txrx_gen_keeloq_protocol(
subghz->txrx,
"AM650",
433920000,
(key & 0x0000FFFF) | 0x00100000,
0x2,
0x0003,
"FAAC_RC,XT");
if(!generated_protocol) {
furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
break;
case SubmenuIndexFAACRCXT_868:
generated_protocol = subghz_txrx_gen_keeloq_protocol(
subghz->txrx,
"AM650",
868350000,
(key & 0x0000FFFF) | 0x00100000,
0x2,
0x0003,
"FAAC_RC,XT");
if(!generated_protocol) {
furi_string_set(
subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
}
break;
case SubmenuIndexNormstahl_433_92: case SubmenuIndexNormstahl_433_92:
generated_protocol = subghz_txrx_gen_keeloq_protocol( generated_protocol = subghz_txrx_gen_keeloq_protocol(
subghz->txrx, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "Normstahl"); subghz->txrx, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003, "Normstahl");

View File

@@ -89,26 +89,19 @@ void subghz_history_reset(SubGhzHistory* instance) {
instance->code_last_hash_data = 0; instance->code_last_hash_data = 0;
} }
void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id) { void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx) {
furi_assert(instance); furi_assert(instance);
SubGhzHistoryItemArray_it_t it; if(idx < SubGhzHistoryItemArray_size(instance->history->data)) {
//SubGhzHistoryItem* target_item = SubGhzHistoryItemArray_get(instance->history->data, item_id); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx);
SubGhzHistoryItemArray_it_last(it, instance->history->data);
while(!SubGhzHistoryItemArray_end_p(it)) {
SubGhzHistoryItem* item = SubGhzHistoryItemArray_ref(it);
if(it->index == (size_t)(item_id)) {
furi_string_free(item->item_str); furi_string_free(item->item_str);
furi_string_free(item->preset->name); furi_string_free(item->preset->name);
free(item->preset); free(item->preset);
flipper_format_free(item->flipper_string); flipper_format_free(item->flipper_string);
item->type = 0; item->type = 0;
SubGhzHistoryItemArray_remove(instance->history->data, it); SubGhzHistoryItemArray_remove_v(instance->history->data, idx, idx + 1);
}
SubGhzHistoryItemArray_previous(it);
}
instance->last_index_write--; instance->last_index_write--;
}
} }
uint16_t subghz_history_get_item(SubGhzHistory* instance) { uint16_t subghz_history_get_item(SubGhzHistory* instance) {

View File

@@ -27,7 +27,7 @@ void subghz_history_free(SubGhzHistory* instance);
*/ */
void subghz_history_reset(SubGhzHistory* instance); void subghz_history_reset(SubGhzHistory* instance);
void subghz_history_delete_item(SubGhzHistory* instance, uint16_t item_id); void subghz_history_delete_item(SubGhzHistory* instance, uint16_t idx);
/** Get frequency to history[idx] /** Get frequency to history[idx]
* *

View File

@@ -19,6 +19,7 @@
#define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter"
#define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter"
#define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI"
#define SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD "DelOldSignals"
SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* subghz_last_settings_alloc(void) {
SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings));
@@ -44,6 +45,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
bool temp_external_module_power_amp = false; bool temp_external_module_power_amp = false;
bool temp_timestamp_file_names = false; bool temp_timestamp_file_names = false;
bool temp_enable_hopping = false; bool temp_enable_hopping = false;
bool temp_delete_old_sig = false;
uint32_t temp_ignore_filter = 0; uint32_t temp_ignore_filter = 0;
uint32_t temp_filter = 0; uint32_t temp_filter = 0;
float temp_rssi = 0; float temp_rssi = 0;
@@ -106,6 +108,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
1); 1);
filter_was_read = flipper_format_read_uint32( filter_was_read = flipper_format_read_uint32(
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, (uint32_t*)&temp_filter, 1); fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, (uint32_t*)&temp_filter, 1);
flipper_format_read_bool(
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, (bool*)&temp_delete_old_sig, 1);
} else { } else {
FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH);
} }
@@ -156,6 +160,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
instance->timestamp_file_names = temp_timestamp_file_names; instance->timestamp_file_names = temp_timestamp_file_names;
instance->delete_old_signals = temp_delete_old_sig;
// External power amp CC1101 // External power amp CC1101
instance->external_module_power_amp = temp_external_module_power_amp; instance->external_module_power_amp = temp_external_module_power_amp;
@@ -270,6 +276,10 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) { file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) {
break; break;
} }
if(!flipper_format_insert_or_update_bool(
file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, &instance->delete_old_signals, 1)) {
break;
}
saved = true; saved = true;
} while(0); } while(0);

View File

@@ -30,6 +30,7 @@ typedef struct {
uint32_t ignore_filter; uint32_t ignore_filter;
uint32_t filter; uint32_t filter;
float rssi; float rssi;
bool delete_old_signals;
} SubGhzLastSettings; } SubGhzLastSettings;
SubGhzLastSettings* subghz_last_settings_alloc(void); SubGhzLastSettings* subghz_last_settings_alloc(void);

View File

@@ -101,6 +101,24 @@ void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) {
true); true);
} }
static void subghz_view_receiver_timer_callback(void* context) {
furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view,
SubGhzViewReceiverModel * model,
{ model->bar_show = SubGhzViewReceiverBarShowDefault; },
true);
if(subghz_receiver->lock_count < UNLOCK_CNT) {
subghz_receiver->callback(
SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);
} else {
subghz_receiver->lock = false;
subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
}
subghz_receiver->lock_count = 0;
}
void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) { void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) {
furi_assert(subghz_receiver); furi_assert(subghz_receiver);
subghz_receiver->lock_count = 0; subghz_receiver->lock_count = 0;
@@ -112,6 +130,7 @@ void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool loc
{ model->bar_show = SubGhzViewReceiverBarShowLock; }, { model->bar_show = SubGhzViewReceiverBarShowLock; },
true); true);
furi_timer_start(subghz_receiver->timer, 1000); furi_timer_start(subghz_receiver->timer, 1000);
subghz_view_receiver_timer_callback(subghz_receiver);
} else { } else {
with_view_model( with_view_model(
subghz_receiver->view, subghz_receiver->view,
@@ -424,24 +443,6 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
} }
} }
static void subghz_view_receiver_timer_callback(void* context) {
furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context;
with_view_model(
subghz_receiver->view,
SubGhzViewReceiverModel * model,
{ model->bar_show = SubGhzViewReceiverBarShowDefault; },
true);
if(subghz_receiver->lock_count < UNLOCK_CNT) {
subghz_receiver->callback(
SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);
} else {
subghz_receiver->lock = false;
subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
}
subghz_receiver->lock_count = 0;
}
bool subghz_view_receiver_input(InputEvent* event, void* context) { bool subghz_view_receiver_input(InputEvent* event, void* context) {
furi_assert(context); furi_assert(context);
SubGhzViewReceiver* subghz_receiver = context; SubGhzViewReceiver* subghz_receiver = context;
@@ -655,29 +656,20 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver)
return idx; return idx;
} }
void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver) { void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {
furi_assert(subghz_receiver); furi_assert(subghz_receiver);
with_view_model( with_view_model(
subghz_receiver->view, subghz_receiver->view,
SubGhzViewReceiverModel * model, SubGhzViewReceiverModel * model,
{ {
SubGhzReceiverMenuItemArray_it_t it; if(idx < SubGhzReceiverMenuItemArray_size(model->history->data)) {
// SubGhzReceiverMenuItem* target_item = SubGhzReceiverMenuItem* item =
// SubGhzReceiverMenuItemArray_get(model->history->data, model->idx); SubGhzReceiverMenuItemArray_get(model->history->data, idx);
SubGhzReceiverMenuItemArray_it_last(it, model->history->data);
while(!SubGhzReceiverMenuItemArray_end_p(it)) {
SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it);
if(it->index == (size_t)(model->idx)) {
furi_string_free(item->item_str); furi_string_free(item->item_str);
furi_string_free(item->time); furi_string_free(item->time);
item->type = 0; item->type = 0;
SubGhzReceiverMenuItemArray_remove(model->history->data, it); SubGhzReceiverMenuItemArray_remove_v(model->history->data, idx, idx + 1);
}
SubGhzReceiverMenuItemArray_previous(it);
}
if(model->history_item == 5) { if(model->history_item == 5) {
if(model->idx >= 2) { if(model->idx >= 2) {
@@ -686,9 +678,10 @@ void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_rec
} }
model->history_item--; model->history_item--;
if(model->idx != 0) { if(model->idx && (model->idx > idx || model->idx == model->history_item)) {
model->idx--; model->idx--;
} }
}
}, },
true); true);
} }

View File

@@ -53,7 +53,7 @@ uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver);
void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx); void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx);
void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver); void subghz_view_receiver_delete_item(SubGhzViewReceiver* subghz_receiver, uint16_t idx);
void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver); void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver);

View File

@@ -344,7 +344,7 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) {
case SubGhzReadRAWStatusLoadKeyTX: case SubGhzReadRAWStatusLoadKeyTX:
case SubGhzReadRAWStatusLoadKeyTXRepeat: case SubGhzReadRAWStatusLoadKeyTXRepeat:
graphics_mode = 0; graphics_mode = 0;
elements_button_center(canvas, "Send"); elements_button_center(canvas, "Hold to repeat");
break; break;
case SubGhzReadRAWStatusStart: case SubGhzReadRAWStatusStart:

View File

@@ -118,7 +118,8 @@ void cli_motd() {
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n" "|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n"
"\r\n" "\r\n"
"Welcome to Flipper Zero Command Line Interface!\r\n" "Welcome to Flipper Zero Command Line Interface!\r\n"
"Read Manual https://docs.flipperzero.one\r\n" "Read the manual: https://docs.flipper.net/development/cli\r\n"
"Run `help` or `?` to list available commands\r\n"
"\r\n"); "\r\n");
const Version* firmware_version = furi_hal_version_get_firmware_version(); const Version* firmware_version = furi_hal_version_get_firmware_version();

View File

@@ -179,7 +179,8 @@ static void rpc_system_system_factory_reset_process(const PB_Main* request, void
RpcSession* session = (RpcSession*)context; RpcSession* session = (RpcSession*)context;
furi_assert(session); furi_assert(session);
furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_reset_registers();
furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);
power_reboot(PowerBootModeNormal); power_reboot(PowerBootModeNormal);
(void)session; (void)session;

View File

@@ -603,7 +603,8 @@ static void storage_cli_factory_reset(Cli* cli, FuriString* args, void* context)
char c = cli_getc(cli); char c = cli_getc(cli);
if(c == 'y' || c == 'Y') { if(c == 'y' || c == 'Y') {
printf("Data will be wiped after reboot.\r\n"); printf("Data will be wiped after reboot.\r\n");
furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_reset_registers();
furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);
power_reboot(PowerBootModeNormal); power_reboot(PowerBootModeNormal);
} else { } else {
printf("Safe choice.\r\n"); printf("Safe choice.\r\n");

View File

@@ -189,7 +189,7 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) {
lfs_t* lfs = &lfs_data->lfs; lfs_t* lfs = &lfs_data->lfs;
bool was_fingerprint_outdated = storage_int_check_and_set_fingerprint(lfs_data); bool was_fingerprint_outdated = storage_int_check_and_set_fingerprint(lfs_data);
bool need_format = furi_hal_rtc_is_flag_set(FuriHalRtcFlagFactoryReset) || bool need_format = furi_hal_rtc_is_flag_set(FuriHalRtcFlagStorageFormatInternal) ||
was_fingerprint_outdated; was_fingerprint_outdated;
if(need_format) { if(need_format) {
@@ -197,7 +197,7 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) {
err = lfs_format(lfs, &lfs_data->config); err = lfs_format(lfs, &lfs_data->config);
if(err == 0) { if(err == 0) {
FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount"); FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount");
furi_hal_rtc_reset_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_reset_flag(FuriHalRtcFlagStorageFormatInternal);
err = lfs_mount(lfs, &lfs_data->config); err = lfs_mount(lfs, &lfs_data->config);
if(err == 0) { if(err == 0) {
FURI_LOG_I(TAG, "Factory reset: Mounted"); FURI_LOG_I(TAG, "Factory reset: Mounted");

View File

@@ -63,7 +63,8 @@ bool storage_settings_scene_factory_reset_on_event(void* context, SceneManagerEv
scene_manager_set_scene_state( scene_manager_set_scene_state(
app->scene_manager, StorageSettingsFactoryReset, counter); app->scene_manager, StorageSettingsFactoryReset, counter);
} else { } else {
furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_reset_registers();
furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);
power_reboot(PowerBootModeNormal); power_reboot(PowerBootModeNormal);
} }

View File

@@ -343,7 +343,7 @@ int32_t update_task_worker_flash_writer(void* context) {
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate);
// Format LFS before restoring backup on next boot // Format LFS before restoring backup on next boot
furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);
#ifdef FURI_NDEBUG #ifdef FURI_NDEBUG
// Production // Production
furi_hal_rtc_set_log_level(FuriLogLevelDefault); furi_hal_rtc_set_log_level(FuriLogLevelDefault);

View File

@@ -203,3 +203,10 @@ Max butthurt: 10
Min level: 3 Min level: 3
Max level: 3 Max level: 3
Weight: 2 Weight: 2
Name: L1_New_year_128x64
Min butthurt: 0
Max butthurt: 10
Min level: 1
Max level: 3
Weight: 7

View File

@@ -48,7 +48,6 @@ env.Append(
File("helpers/iso14443_crc.h"), File("helpers/iso14443_crc.h"),
File("helpers/iso13239_crc.h"), File("helpers/iso13239_crc.h"),
File("helpers/nfc_data_generator.h"), File("helpers/nfc_data_generator.h"),
File("helpers/nfc_dict.h"),
], ],
) )

View File

@@ -1,270 +0,0 @@
#include "nfc_dict.h"
#include <storage/storage.h>
#include <flipper_format/flipper_format.h>
#include <toolbox/stream/file_stream.h>
#include <toolbox/stream/buffered_file_stream.h>
#include <toolbox/args.h>
#include <nfc/helpers/nfc_util.h>
#define TAG "NfcDict"
struct NfcDict {
Stream* stream;
size_t key_size;
size_t key_size_symbols;
uint32_t total_keys;
};
typedef struct {
const char* path;
FS_OpenMode open_mode;
} NfcDictFile;
bool nfc_dict_check_presence(const char* path) {
furi_assert(path);
Storage* storage = furi_record_open(RECORD_STORAGE);
bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK;
furi_record_close(RECORD_STORAGE);
return dict_present;
}
NfcDict* nfc_dict_alloc(const char* path, NfcDictMode mode, size_t key_size) {
furi_assert(path);
NfcDict* instance = malloc(sizeof(NfcDict));
Storage* storage = furi_record_open(RECORD_STORAGE);
instance->stream = buffered_file_stream_alloc(storage);
furi_record_close(RECORD_STORAGE);
FS_OpenMode open_mode = FSOM_OPEN_EXISTING;
if(mode == NfcDictModeOpenAlways) {
open_mode = FSOM_OPEN_ALWAYS;
}
instance->key_size = key_size;
// Byte = 2 symbols + 1 end of line
instance->key_size_symbols = key_size * 2 + 1;
bool dict_loaded = false;
do {
if(!buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode)) {
buffered_file_stream_close(instance->stream);
break;
}
// Check for new line ending
if(!stream_eof(instance->stream)) {
if(!stream_seek(instance->stream, -1, StreamOffsetFromEnd)) break;
uint8_t last_char = 0;
if(stream_read(instance->stream, &last_char, 1) != 1) break;
if(last_char != '\n') {
FURI_LOG_D(TAG, "Adding new line ending");
if(stream_write_char(instance->stream, '\n') != 1) break;
}
if(!stream_rewind(instance->stream)) break;
}
// Read total amount of keys
FuriString* next_line;
next_line = furi_string_alloc();
while(true) {
if(!stream_read_line(instance->stream, next_line)) {
FURI_LOG_T(TAG, "No keys left in dict");
break;
}
FURI_LOG_T(
TAG,
"Read line: %s, len: %zu",
furi_string_get_cstr(next_line),
furi_string_size(next_line));
if(furi_string_get_char(next_line, 0) == '#') continue;
if(furi_string_size(next_line) != instance->key_size_symbols) continue;
instance->total_keys++;
}
furi_string_free(next_line);
stream_rewind(instance->stream);
dict_loaded = true;
FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", instance->total_keys);
} while(false);
if(!dict_loaded) {
buffered_file_stream_close(instance->stream);
free(instance);
instance = NULL;
}
return instance;
}
void nfc_dict_free(NfcDict* instance) {
furi_assert(instance);
furi_assert(instance->stream);
buffered_file_stream_close(instance->stream);
stream_free(instance->stream);
free(instance);
}
static void nfc_dict_int_to_str(NfcDict* instance, const uint8_t* key_int, FuriString* key_str) {
furi_string_reset(key_str);
for(size_t i = 0; i < instance->key_size; i++) {
furi_string_cat_printf(key_str, "%02X", key_int[i]);
}
}
static void nfc_dict_str_to_int(NfcDict* instance, FuriString* key_str, uint64_t* key_int) {
uint8_t key_byte_tmp;
*key_int = 0ULL;
for(uint8_t i = 0; i < instance->key_size * 2; i += 2) {
args_char_to_hex(
furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp);
*key_int |= (uint64_t)key_byte_tmp << (8 * (instance->key_size - 1 - i / 2));
}
}
uint32_t nfc_dict_get_total_keys(NfcDict* instance) {
furi_assert(instance);
return instance->total_keys;
}
bool nfc_dict_rewind(NfcDict* instance) {
furi_assert(instance);
furi_assert(instance->stream);
return stream_rewind(instance->stream);
}
static bool nfc_dict_get_next_key_str(NfcDict* instance, FuriString* key) {
furi_assert(instance);
furi_assert(instance->stream);
bool key_read = false;
furi_string_reset(key);
while(!key_read) {
if(!stream_read_line(instance->stream, key)) break;
if(furi_string_get_char(key, 0) == '#') continue;
if(furi_string_size(key) != instance->key_size_symbols) continue;
furi_string_left(key, instance->key_size_symbols - 1);
key_read = true;
}
return key_read;
}
bool nfc_dict_get_next_key(NfcDict* instance, uint8_t* key, size_t key_size) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(instance->key_size == key_size);
FuriString* temp_key = furi_string_alloc();
uint64_t key_int = 0;
bool key_read = nfc_dict_get_next_key_str(instance, temp_key);
if(key_read) {
nfc_dict_str_to_int(instance, temp_key, &key_int);
nfc_util_num2bytes(key_int, key_size, key);
}
furi_string_free(temp_key);
return key_read;
}
static bool nfc_dict_is_key_present_str(NfcDict* instance, FuriString* key) {
furi_assert(instance);
furi_assert(instance->stream);
FuriString* next_line;
next_line = furi_string_alloc();
bool key_found = false;
stream_rewind(instance->stream);
while(!key_found) { //-V654
if(!stream_read_line(instance->stream, next_line)) break;
if(furi_string_get_char(next_line, 0) == '#') continue;
if(furi_string_size(next_line) != instance->key_size_symbols) continue;
furi_string_left(next_line, instance->key_size_symbols - 1);
if(!furi_string_equal(key, next_line)) continue;
key_found = true;
}
furi_string_free(next_line);
return key_found;
}
bool nfc_dict_is_key_present(NfcDict* instance, const uint8_t* key, size_t key_size) {
furi_assert(instance);
furi_assert(key);
furi_assert(instance->stream);
furi_assert(instance->key_size == key_size);
FuriString* temp_key = furi_string_alloc();
nfc_dict_int_to_str(instance, key, temp_key);
bool key_found = nfc_dict_is_key_present_str(instance, temp_key);
furi_string_free(temp_key);
return key_found;
}
static bool nfc_dict_add_key_str(NfcDict* instance, FuriString* key) {
furi_assert(instance);
furi_assert(instance->stream);
furi_string_cat_printf(key, "\n");
bool key_added = false;
do {
if(!stream_seek(instance->stream, 0, StreamOffsetFromEnd)) break;
if(!stream_insert_string(instance->stream, key)) break;
instance->total_keys++;
key_added = true;
} while(false);
furi_string_left(key, instance->key_size_symbols - 1);
return key_added;
}
bool nfc_dict_add_key(NfcDict* instance, const uint8_t* key, size_t key_size) {
furi_assert(instance);
furi_assert(key);
furi_assert(instance->stream);
furi_assert(instance->key_size == key_size);
FuriString* temp_key = furi_string_alloc();
nfc_dict_int_to_str(instance, key, temp_key);
bool key_added = nfc_dict_add_key_str(instance, temp_key);
furi_string_free(temp_key);
return key_added;
}
bool nfc_dict_delete_key(NfcDict* instance, const uint8_t* key, size_t key_size) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(key);
furi_assert(instance->key_size == key_size);
bool key_removed = false;
uint8_t* temp_key = malloc(key_size);
nfc_dict_rewind(instance);
while(!key_removed) {
if(!nfc_dict_get_next_key(instance, temp_key, key_size)) break;
if(memcmp(temp_key, key, key_size) == 0) {
int32_t offset = (-1) * (instance->key_size_symbols);
stream_seek(instance->stream, offset, StreamOffsetFromCurrent);
if(!stream_delete(instance->stream, instance->key_size_symbols)) break;
instance->total_keys--;
key_removed = true;
}
}
nfc_dict_rewind(instance);
free(temp_key);
return key_removed;
}

View File

@@ -1,103 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
NfcDictModeOpenExisting,
NfcDictModeOpenAlways,
} NfcDictMode;
typedef struct NfcDict NfcDict;
/** Check dictionary presence
*
* @param path - dictionary path
*
* @return true if dictionary exists, false otherwise
*/
bool nfc_dict_check_presence(const char* path);
/** Open or create dictionary
* Depending on mode, dictionary will be opened or created.
*
* @param path - dictionary path
* @param mode - NfcDictMode value
* @param key_size - size of dictionary keys in bytes
*
* @return NfcDict dictionary instance
*/
NfcDict* nfc_dict_alloc(const char* path, NfcDictMode mode, size_t key_size);
/** Close dictionary
*
* @param instance - NfcDict dictionary instance
*/
void nfc_dict_free(NfcDict* instance);
/** Get total number of keys in dictionary
*
* @param instance - NfcDict dictionary instance
*
* @return total number of keys in dictionary
*/
uint32_t nfc_dict_get_total_keys(NfcDict* instance);
/** Rewind dictionary
*
* @param instance - NfcDict dictionary instance
*
* @return true if rewind was successful, false otherwise
*/
bool nfc_dict_rewind(NfcDict* instance);
/** Check if key is present in dictionary
*
* @param instance - NfcDict dictionary instance
* @param key - key to check
* @param key_size - size of key in bytes
*
* @return true if key is present, false otherwise
*/
bool nfc_dict_is_key_present(NfcDict* instance, const uint8_t* key, size_t key_size);
/** Get next key from dictionary
* This function will return next key from dictionary. If there are no more
* keys, it will return false, and nfc_dict_rewind() should be called.
*
* @param instance - NfcDict dictionary instance
* @param key - buffer to store key
* @param key_size - size of key in bytes
*
* @return true if key was successfully retrieved, false otherwise
*/
bool nfc_dict_get_next_key(NfcDict* instance, uint8_t* key, size_t key_size);
/** Add key to dictionary
*
* @param instance - NfcDict dictionary instance
* @param key - key to add
* @param key_size - size of key in bytes
*
* @return true if key was successfully added, false otherwise
*/
bool nfc_dict_add_key(NfcDict* instance, const uint8_t* key, size_t key_size);
/** Delete key from dictionary
*
* @param instance - NfcDict dictionary instance
* @param key - key to delete
* @param key_size - size of key in bytes
*
* @return true if key was successfully deleted, false otherwise
*/
bool nfc_dict_delete_key(NfcDict* instance, const uint8_t* key, size_t key_size);
#ifdef __cplusplus
}
#endif

View File

@@ -35,6 +35,19 @@ uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len) {
return res; return res;
} }
uint64_t nfc_util_bytes2num_little_endian(const uint8_t* src, uint8_t len) {
furi_assert(src);
furi_assert(len <= 8);
uint64_t res = 0;
uint8_t shift = 0;
while(len--) {
res |= ((uint64_t)*src) << (8 * shift++);
src++;
}
return res;
}
uint8_t nfc_util_even_parity32(uint32_t data) { uint8_t nfc_util_even_parity32(uint32_t data) {
// data ^= data >> 16; // data ^= data >> 16;
// data ^= data >> 8; // data ^= data >> 8;

View File

@@ -10,6 +10,8 @@ void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest);
uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len); uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len);
uint64_t nfc_util_bytes2num_little_endian(const uint8_t* src, uint8_t len);
uint8_t nfc_util_even_parity32(uint32_t data); uint8_t nfc_util_even_parity32(uint32_t data);
uint8_t nfc_util_odd_parity8(uint8_t data); uint8_t nfc_util_odd_parity8(uint8_t data);

View File

@@ -117,12 +117,13 @@ Iso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso144
bit_buffer_reset(instance->rx_buffer); bit_buffer_reset(instance->rx_buffer);
// Send ATTRIB // Send ATTRIB
uint8_t cid = 0;
bit_buffer_append_byte(instance->tx_buffer, 0x1d); bit_buffer_append_byte(instance->tx_buffer, 0x1d);
bit_buffer_append_bytes(instance->tx_buffer, data->uid, ISO14443_3B_UID_SIZE); bit_buffer_append_bytes(instance->tx_buffer, data->uid, ISO14443_3B_UID_SIZE);
bit_buffer_append_byte(instance->tx_buffer, 0x00); bit_buffer_append_byte(instance->tx_buffer, 0x00);
bit_buffer_append_byte(instance->tx_buffer, ISO14443_3B_ATTRIB_FRAME_SIZE_256); bit_buffer_append_byte(instance->tx_buffer, ISO14443_3B_ATTRIB_FRAME_SIZE_256);
bit_buffer_append_byte(instance->tx_buffer, 0x01); bit_buffer_append_byte(instance->tx_buffer, 0x01);
bit_buffer_append_byte(instance->tx_buffer, 0x00); bit_buffer_append_byte(instance->tx_buffer, cid);
ret = iso14443_3b_poller_frame_exchange( ret = iso14443_3b_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, iso14443_3b_get_fwt_fc_max(data)); instance, instance->tx_buffer, instance->rx_buffer, iso14443_3b_get_fwt_fc_max(data));
@@ -138,11 +139,10 @@ Iso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso144
bit_buffer_get_size_bytes(instance->rx_buffer)); bit_buffer_get_size_bytes(instance->rx_buffer));
} }
if(bit_buffer_get_byte(instance->rx_buffer, 0) != 0) { uint8_t cid_received = bit_buffer_get_byte(instance->rx_buffer, 0);
FURI_LOG_D( // 15 bit is RFU
TAG, if((cid_received & 0x7f) != cid) {
"Incorrect CID in ATTRIB response: %02X", FURI_LOG_D(TAG, "Incorrect CID in ATTRIB response: %02X", cid_received);
bit_buffer_get_byte(instance->rx_buffer, 0));
instance->state = Iso14443_3bPollerStateActivationFailed; instance->state = Iso14443_3bPollerStateActivationFailed;
ret = Iso14443_3bErrorCommunication; ret = Iso14443_3bErrorCommunication;
break; break;

View File

@@ -40,10 +40,11 @@ static void mf_classic_listener_reset_state(MfClassicListener* instance) {
static MfClassicListenerCommand static MfClassicListenerCommand
mf_classic_listener_halt_handler(MfClassicListener* instance, BitBuffer* buff) { mf_classic_listener_halt_handler(MfClassicListener* instance, BitBuffer* buff) {
UNUSED(instance);
MfClassicListenerCommand command = MfClassicListenerCommandNack; MfClassicListenerCommand command = MfClassicListenerCommandNack;
if(bit_buffer_get_byte(buff, 1) == MF_CLASSIC_CMD_HALT_LSB) { if(bit_buffer_get_byte(buff, 1) == MF_CLASSIC_CMD_HALT_LSB) {
mf_classic_listener_reset_state(instance);
command = MfClassicListenerCommandSleep; command = MfClassicListenerCommandSleep;
} }
@@ -59,10 +60,7 @@ static MfClassicListenerCommand mf_classic_listener_auth_first_part_handler(
do { do {
instance->state = MfClassicListenerStateIdle; instance->state = MfClassicListenerStateIdle;
if(block_num >= instance->total_block_num) { if(block_num >= instance->total_block_num) break;
mf_classic_listener_reset_state(instance);
break;
}
uint8_t sector_num = mf_classic_get_sector_by_block(block_num); uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
@@ -135,7 +133,7 @@ static MfClassicListenerCommand
instance->cmd_in_progress = false; instance->cmd_in_progress = false;
if(bit_buffer_get_size_bytes(buff) != (sizeof(MfClassicNr) + sizeof(MfClassicAr))) { if(bit_buffer_get_size_bytes(buff) != (sizeof(MfClassicNr) + sizeof(MfClassicAr))) {
mf_classic_listener_reset_state(instance); command = MfClassicListenerCommandSleep;
break; break;
} }
bit_buffer_write_bytes_mid(buff, instance->auth_context.nr.data, 0, sizeof(MfClassicNr)); bit_buffer_write_bytes_mid(buff, instance->auth_context.nr.data, 0, sizeof(MfClassicNr));
@@ -157,7 +155,7 @@ static MfClassicListenerCommand
if(secret_poller != prng_successor(nt_num, 64)) { if(secret_poller != prng_successor(nt_num, 64)) {
FURI_LOG_T( FURI_LOG_T(
TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64)); TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64));
mf_classic_listener_reset_state(instance); command = MfClassicListenerCommandSleep;
break; break;
} }
@@ -610,9 +608,11 @@ NfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) {
} }
mf_classic_listener_send_short_frame(instance, nack); mf_classic_listener_send_short_frame(instance, nack);
mf_classic_listener_reset_state(instance);
} else if(mfc_command == MfClassicListenerCommandSilent) { } else if(mfc_command == MfClassicListenerCommandSilent) {
command = NfcCommandReset; command = NfcCommandReset;
} else if(mfc_command == MfClassicListenerCommandSleep) { } else if(mfc_command == MfClassicListenerCommandSleep) {
mf_classic_listener_reset_state(instance);
command = NfcCommandSleep; command = NfcCommandSleep;
} }
} else if(iso3_event->type == Iso14443_3aListenerEventTypeHalted) { } else if(iso3_event->type == Iso14443_3aListenerEventTypeHalted) {

View File

@@ -371,11 +371,14 @@ static NfcCommand mf_ultralight_poller_handler_read_counters(MfUltralightPoller*
} }
static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPoller* instance) { static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPoller* instance) {
NfcCommand command = NfcCommandContinue;
if(mf_ultralight_support_feature( if(mf_ultralight_support_feature(
instance->feature_set, instance->feature_set,
MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) { MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) {
if(instance->tearing_flag_read == instance->tearing_flag_total) { if(instance->tearing_flag_read == instance->tearing_flag_total) {
instance->state = MfUltralightPollerStateTryDefaultPass; instance->state = MfUltralightPollerStateTryDefaultPass;
command = NfcCommandReset;
} else { } else {
bool single_counter = mf_ultralight_support_feature( bool single_counter = mf_ultralight_support_feature(
instance->feature_set, MfUltralightFeatureSupportSingleCounter); instance->feature_set, MfUltralightFeatureSupportSingleCounter);
@@ -391,6 +394,7 @@ static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPo
} else if(instance->error != MfUltralightErrorNone) { } else if(instance->error != MfUltralightErrorNone) {
FURI_LOG_D(TAG, "Reading tearing flag %d failed", instance->tearing_flag_read); FURI_LOG_D(TAG, "Reading tearing flag %d failed", instance->tearing_flag_read);
instance->state = MfUltralightPollerStateTryDefaultPass; instance->state = MfUltralightPollerStateTryDefaultPass;
command = NfcCommandReset;
} else { } else {
instance->tearing_flag_read++; instance->tearing_flag_read++;
} }
@@ -398,9 +402,10 @@ static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPo
} else { } else {
FURI_LOG_D(TAG, "Skip reading tearing flags"); FURI_LOG_D(TAG, "Skip reading tearing flags");
instance->state = MfUltralightPollerStateTryDefaultPass; instance->state = MfUltralightPollerStateTryDefaultPass;
command = NfcCommandReset;
} }
return NfcCommandContinue; return command;
} }
static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance) { static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance) {

View File

@@ -718,7 +718,7 @@ void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* ou
furi_string_cat_printf( furi_string_cat_printf(
output, output,
"%s %db\r\n" "%s %db\r\n"
"Key:0x%08lX%08lX\r\n" "Key:%08lX%08lX\r\n"
"Sn:0x%08lX Btn:%01X\r\n" "Sn:0x%08lX Btn:%01X\r\n"
"Pcl_Cnt:0x%04lX\r\n" "Pcl_Cnt:0x%04lX\r\n"
"Btn_Cnt:0x%02X", "Btn_Cnt:0x%02X",

View File

@@ -34,6 +34,7 @@ env.Append(
File("hex.h"), File("hex.h"),
File("simple_array.h"), File("simple_array.h"),
File("bit_buffer.h"), File("bit_buffer.h"),
File("keys_dict.h"),
], ],
) )

334
lib/toolbox/keys_dict.c Normal file
View File

@@ -0,0 +1,334 @@
#include "keys_dict.h"
#include <storage/storage.h>
#include <flipper_format/flipper_format.h>
#include <toolbox/stream/file_stream.h>
#include <toolbox/stream/buffered_file_stream.h>
#include <toolbox/args.h>
#define TAG "KeysDict"
struct KeysDict {
Stream* stream;
size_t key_size;
size_t key_size_symbols;
size_t total_keys;
};
static inline void keys_dict_add_ending_new_line(KeysDict* instance) {
if(stream_seek(instance->stream, -1, StreamOffsetFromEnd)) {
uint8_t last_char = 0;
// Check if the last char is new line or add a new line
if(stream_read(instance->stream, &last_char, 1) == 1 && last_char != '\n') {
FURI_LOG_D(TAG, "Adding new line ending");
stream_write_char(instance->stream, '\n');
}
stream_rewind(instance->stream);
}
}
static bool keys_dict_read_key_line(KeysDict* instance, FuriString* line, bool* is_endfile) {
if(stream_read_line(instance->stream, line) == false) {
*is_endfile = true;
}
else {
FURI_LOG_T(
TAG, "Read line: %s, len: %zu", furi_string_get_cstr(line), furi_string_size(line));
bool is_comment = furi_string_get_char(line, 0) == '#';
if(!is_comment) {
furi_string_left(line, instance->key_size_symbols - 1);
}
bool is_correct_size = furi_string_size(line) == instance->key_size_symbols - 1;
return !is_comment && is_correct_size;
}
return false;
}
bool keys_dict_check_presence(const char* path) {
furi_assert(path);
Storage* storage = furi_record_open(RECORD_STORAGE);
bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK;
furi_record_close(RECORD_STORAGE);
return dict_present;
}
KeysDict* keys_dict_alloc(const char* path, KeysDictMode mode, size_t key_size) {
furi_assert(path);
furi_assert(key_size > 0);
KeysDict* instance = malloc(sizeof(KeysDict));
Storage* storage = furi_record_open(RECORD_STORAGE);
furi_assert(storage);
instance->stream = buffered_file_stream_alloc(storage);
furi_assert(instance->stream);
FS_OpenMode open_mode = (mode == KeysDictModeOpenAlways) ? FSOM_OPEN_ALWAYS :
FSOM_OPEN_EXISTING;
// Byte = 2 symbols + 1 end of line
instance->key_size = key_size;
instance->key_size_symbols = key_size * 2 + 1;
instance->total_keys = 0;
bool file_exists =
buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode);
if(!file_exists) {
buffered_file_stream_close(instance->stream);
} else {
// Eventually add new line character in the last line to avoid skipping keys
keys_dict_add_ending_new_line(instance);
}
FuriString* line = furi_string_alloc();
bool is_endfile = false;
// In this loop we only count the entries in the file
// We prefer not to load the whole file in memory for space reasons
while(file_exists && !is_endfile) {
bool read_key = keys_dict_read_key_line(instance, line, &is_endfile);
if(read_key) {
instance->total_keys++;
}
}
stream_rewind(instance->stream);
FURI_LOG_I(TAG, "Loaded dictionary with %zu keys", instance->total_keys);
furi_string_free(line);
return instance;
}
void keys_dict_free(KeysDict* instance) {
furi_assert(instance);
furi_assert(instance->stream);
buffered_file_stream_close(instance->stream);
stream_free(instance->stream);
free(instance);
furi_record_close(RECORD_STORAGE);
}
static void keys_dict_int_to_str(KeysDict* instance, const uint8_t* key_int, FuriString* key_str) {
furi_assert(instance);
furi_assert(key_str);
furi_assert(key_int);
furi_string_reset(key_str);
for(size_t i = 0; i < instance->key_size; i++)
furi_string_cat_printf(key_str, "%02X", key_int[i]);
}
static void keys_dict_str_to_int(KeysDict* instance, FuriString* key_str, uint64_t* key_int) {
furi_assert(instance);
furi_assert(key_str);
furi_assert(key_int);
uint8_t key_byte_tmp;
char h, l;
*key_int = 0ULL;
for(size_t i = 0; i < instance->key_size_symbols - 1; i += 2) {
h = furi_string_get_char(key_str, i);
l = furi_string_get_char(key_str, i + 1);
args_char_to_hex(h, l, &key_byte_tmp);
*key_int |= (uint64_t)key_byte_tmp << (8 * (instance->key_size - 1 - i / 2));
}
}
size_t keys_dict_get_total_keys(KeysDict* instance) {
furi_assert(instance);
return instance->total_keys;
}
bool keys_dict_rewind(KeysDict* instance) {
furi_assert(instance);
furi_assert(instance->stream);
return stream_rewind(instance->stream);
}
static bool keys_dict_get_next_key_str(KeysDict* instance, FuriString* key) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(key);
bool key_read = false;
bool is_endfile = false;
furi_string_reset(key);
while(!key_read && !is_endfile) key_read = keys_dict_read_key_line(instance, key, &is_endfile);
return key_read;
}
bool keys_dict_get_next_key(KeysDict* instance, uint8_t* key, size_t key_size) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(instance->key_size == key_size);
furi_assert(key);
FuriString* temp_key = furi_string_alloc();
bool key_read = keys_dict_get_next_key_str(instance, temp_key);
if(key_read) {
size_t tmp_len = key_size;
uint64_t key_int = 0;
keys_dict_str_to_int(instance, temp_key, &key_int);
while(tmp_len--) {
key[tmp_len] = (uint8_t)key_int;
key_int >>= 8;
}
}
furi_string_free(temp_key);
return key_read;
}
static bool keys_dict_is_key_present_str(KeysDict* instance, FuriString* key) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(key);
FuriString* line = furi_string_alloc();
bool is_endfile = false;
bool line_found = false;
uint32_t actual_pos = stream_tell(instance->stream);
stream_rewind(instance->stream);
while(!line_found && !is_endfile)
line_found = // The line is found if the line was read and the key is equal to the line
(keys_dict_read_key_line(instance, line, &is_endfile)) &&
(furi_string_equal(key, line));
furi_string_free(line);
// Restore the position of the stream
stream_seek(instance->stream, actual_pos, StreamOffsetFromStart);
return line_found;
}
bool keys_dict_is_key_present(KeysDict* instance, const uint8_t* key, size_t key_size) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(instance->key_size == key_size);
furi_assert(key);
FuriString* temp_key = furi_string_alloc();
keys_dict_int_to_str(instance, key, temp_key);
bool key_found = keys_dict_is_key_present_str(instance, temp_key);
furi_string_free(temp_key);
return key_found;
}
static bool keys_dict_add_key_str(KeysDict* instance, FuriString* key) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(key);
furi_string_cat_str(key, "\n");
bool key_added = false;
uint32_t actual_pos = stream_tell(instance->stream);
if(stream_seek(instance->stream, 0, StreamOffsetFromEnd) &&
stream_insert_string(instance->stream, key)) {
instance->total_keys++;
key_added = true;
}
stream_seek(instance->stream, actual_pos, StreamOffsetFromStart);
return key_added;
}
bool keys_dict_add_key(KeysDict* instance, const uint8_t* key, size_t key_size) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(instance->key_size == key_size);
furi_assert(key);
FuriString* temp_key = furi_string_alloc();
furi_assert(temp_key);
keys_dict_int_to_str(instance, key, temp_key);
bool key_added = keys_dict_add_key_str(instance, temp_key);
FURI_LOG_I(TAG, "Added key %s", furi_string_get_cstr(temp_key));
furi_string_free(temp_key);
return key_added;
}
bool keys_dict_delete_key(KeysDict* instance, const uint8_t* key, size_t key_size) {
furi_assert(instance);
furi_assert(instance->stream);
furi_assert(instance->key_size == key_size);
furi_assert(key);
bool key_removed = false;
uint8_t* temp_key = malloc(key_size);
stream_rewind(instance->stream);
while(!key_removed) {
if(!keys_dict_get_next_key(instance, temp_key, key_size)) {
break;
}
if(memcmp(temp_key, key, key_size) == 0) {
stream_seek(instance->stream, -instance->key_size_symbols, StreamOffsetFromCurrent);
if(stream_delete(instance->stream, instance->key_size_symbols) == false) {
break;
}
instance->total_keys--;
key_removed = true;
}
}
FuriString* tmp = furi_string_alloc();
keys_dict_int_to_str(instance, key, tmp);
FURI_LOG_I(TAG, "Removed key %s", furi_string_get_cstr(tmp));
furi_string_free(tmp);
stream_rewind(instance->stream);
free(temp_key);
return key_removed;
}

103
lib/toolbox/keys_dict.h Normal file
View File

@@ -0,0 +1,103 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
KeysDictModeOpenExisting,
KeysDictModeOpenAlways,
} KeysDictMode;
typedef struct KeysDict KeysDict;
/** Check if the file list exists
*
* @param path - list path
*
* @return true if list exists, false otherwise
*/
bool keys_dict_check_presence(const char* path);
/** Open or create list
* Depending on mode, list will be opened or created.
*
* @param path - Path of the file that contain the list
* @param mode - ListKeysMode value
* @param key_size - Size of each key in bytes
*
* @return Returns KeysDict list instance
*/
KeysDict* keys_dict_alloc(const char* path, KeysDictMode mode, size_t key_size);
/** Close list
*
* @param instance - KeysDict list instance
*/
void keys_dict_free(KeysDict* instance);
/** Get total number of keys in list
*
* @param instance - KeysDict list instance
*
* @return Returns total number of keys in list
*/
size_t keys_dict_get_total_keys(KeysDict* instance);
/** Rewind list
*
* @param instance - KeysDict list instance
*
* @return Returns true if rewind was successful, false otherwise
*/
bool keys_dict_rewind(KeysDict* instance);
/** Check if key is present in list
*
* @param instance - KeysDict list instance
* @param key - key to check
* @param key_size - Size of the key in bytes
*
* @return Returns true if key is present, false otherwise
*/
bool keys_dict_is_key_present(KeysDict* instance, const uint8_t* key, size_t key_size);
/** Get next key from the list
* This function will return next key from list. If there are no more
* keys, it will return false, and keys_dict_rewind() should be called.
*
* @param instance - KeysDict list instance
* @param key - Array where to store key
* @param key_size - Size of key in bytes
*
* @return Returns true if key was successfully retrieved, false otherwise
*/
bool keys_dict_get_next_key(KeysDict* instance, uint8_t* key, size_t key_size);
/** Add key to list
*
* @param instance - KeysDict list instance
* @param key - Key to add
* @param key_size - Size of the key in bytes
*
* @return Returns true if key was successfully added, false otherwise
*/
bool keys_dict_add_key(KeysDict* instance, const uint8_t* key, size_t key_size);
/** Delete key from list
*
* @param instance - KeysDict list instance
* @param key - Key to delete
* @param key_size - Size of the key in bytes
*
* @return Returns true if key was successfully deleted, false otherwise
*/
bool keys_dict_delete_key(KeysDict* instance, const uint8_t* key, size_t key_size);
#ifdef __cplusplus
}
#endif

View File

@@ -11,6 +11,28 @@ WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)")
# Excludes all files ending with ~, usually created by editors as backup files # Excludes all files ending with ~, usually created by editors as backup files
GLOB_FILE_EXCLUSION = ["*~"] GLOB_FILE_EXCLUSION = ["*~"]
# List of environment variables to proxy to child processes
FORWARDED_ENV_VARIABLES = [
# CI/CD variables
"WORKFLOW_BRANCH_OR_TAG",
"DIST_SUFFIX",
"FORCE_NO_DIRTY",
# Python & other tools
"HOME",
"APPDATA",
"PYTHONHOME",
"PYTHONNOUSERSITE",
"TMP",
"TEMP",
# ccache
"CCACHE_DISABLE",
# Colors for tools
"TERM",
# Toolchain
"FBT_TOOLCHAIN_PATH",
"UFBT_HOME",
]
def tempfile_arg_esc_func(arg): def tempfile_arg_esc_func(arg):
arg = quote_spaces(arg) arg = quote_spaces(arg)

View File

@@ -25,33 +25,10 @@ forward_os_env = {
"PATH": os.environ["PATH"], "PATH": os.environ["PATH"],
} }
# Proxying environment to child processes & scripts
variables_to_forward = [
# CI/CD variables
"WORKFLOW_BRANCH_OR_TAG",
"DIST_SUFFIX",
# Python & other tools
"HOME",
"APPDATA",
"PYTHONHOME",
"PYTHONNOUSERSITE",
"TMP",
"TEMP",
# Colors for tools
"TERM",
]
if proxy_env := GetOption("proxy_env"):
variables_to_forward.extend(proxy_env.split(","))
for env_value_name in variables_to_forward:
if environ_value := os.environ.get(env_value_name, None):
forward_os_env[env_value_name] = environ_value
# Core environment init - loads SDK state, sets up paths, etc. # Core environment init - loads SDK state, sets up paths, etc.
core_env = Environment( core_env = Environment(
variables=ufbt_variables, variables=ufbt_variables,
ENV=forward_os_env,
UFBT_STATE_DIR=ufbt_state_dir, UFBT_STATE_DIR=ufbt_state_dir,
UFBT_CURRENT_SDK_DIR=ufbt_current_sdk_dir, UFBT_CURRENT_SDK_DIR=ufbt_current_sdk_dir,
UFBT_SCRIPT_DIR=ufbt_script_dir, UFBT_SCRIPT_DIR=ufbt_script_dir,
@@ -69,6 +46,7 @@ core_env.Append(CPPDEFINES=GetOption("extra_defines"))
from fbt.appmanifest import FlipperApplication, FlipperAppType from fbt.appmanifest import FlipperApplication, FlipperAppType
from fbt.sdk.cache import SdkCache from fbt.sdk.cache import SdkCache
from fbt.util import ( from fbt.util import (
FORWARDED_ENV_VARIABLES,
path_as_posix, path_as_posix,
resolve_real_dir_node, resolve_real_dir_node,
single_quote, single_quote,
@@ -76,8 +54,19 @@ from fbt.util import (
wrap_tempfile, wrap_tempfile,
) )
variables_to_forward = list(FORWARDED_ENV_VARIABLES)
if proxy_env := GetOption("proxy_env"):
variables_to_forward.extend(proxy_env.split(","))
for env_value_name in variables_to_forward:
if environ_value := os.environ.get(env_value_name, None):
forward_os_env[env_value_name] = environ_value
# Base environment with all tools loaded from SDK # Base environment with all tools loaded from SDK
env = core_env.Clone( env = core_env.Clone(
ENV=forward_os_env,
toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")], toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")],
tools=[ tools=[
"fbt_tweaks", "fbt_tweaks",
@@ -477,9 +466,12 @@ else:
dist_env.PhonyTarget("dolphin_ext", Action(missing_dolphin_folder, None)) dist_env.PhonyTarget("dolphin_ext", Action(missing_dolphin_folder, None))
# print(env.Dump())
dist_env.PhonyTarget( dist_env.PhonyTarget(
"env", "env",
"@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)", '@echo "FBT_TOOLCHAIN_PATH='
+ forward_os_env["FBT_TOOLCHAIN_PATH"]
+ '" source $( "${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh" $)',
) )
dist_env.PostConfigureUfbtEnvionment() dist_env.PostConfigureUfbtEnvionment()

View File

@@ -2,3 +2,5 @@ dist/*
.vscode .vscode
.clang-format .clang-format
.editorconfig .editorconfig
.env
.ufbt

View File

@@ -44,7 +44,11 @@ How to create a new application:
4. Run `ufbt launch` to build and upload your application. 4. Run `ufbt launch` to build and upload your application.
How to open a shell with toolchain environment and other build tools: How to open a shell with toolchain environment and other build tools:
In your shell, type "source `ufbt -s env`". You can also use "." instead of "source". In your shell, type "eval `ufbt -s env`".
How to update uFBT SDK:
Run "ufbt update" to fetch latest SDK.
You can also specify branch, target and/or channel options. See "ufbt update -h" for details.
""" """

View File

@@ -1,6 +1,5 @@
import json import json
import os import os
import pathlib
import sys import sys
from functools import reduce from functools import reduce

View File

@@ -1,13 +1,14 @@
from SCons.Platform import TempFileMunge
from fbt.util import (
tempfile_arg_esc_func,
single_quote,
wrap_tempfile,
resolve_real_dir_node,
)
import os
import multiprocessing import multiprocessing
import os
from fbt.util import (
FORWARDED_ENV_VARIABLES,
resolve_real_dir_node,
single_quote,
tempfile_arg_esc_func,
wrap_tempfile,
)
from SCons.Platform import TempFileMunge
Import("VAR_ENV") Import("VAR_ENV")
@@ -15,24 +16,9 @@ forward_os_env = {
# Import PATH from OS env - scons doesn't do that by default # Import PATH from OS env - scons doesn't do that by default
"PATH": os.environ["PATH"], "PATH": os.environ["PATH"],
} }
# Proxying CI environment to child processes & scripts
variables_to_forward = [ variables_to_forward = list(FORWARDED_ENV_VARIABLES)
# CI/CD variables
"WORKFLOW_BRANCH_OR_TAG",
"DIST_SUFFIX",
"FORCE_NO_DIRTY",
# Python & other tools
"HOME",
"APPDATA",
"PYTHONHOME",
"PYTHONNOUSERSITE",
"TMP",
"TEMP",
# ccache
"CCACHE_DISABLE",
# Colors for tools
"TERM",
]
if proxy_env := GetOption("proxy_env"): if proxy_env := GetOption("proxy_env"):
variables_to_forward.extend(proxy_env.split(",")) variables_to_forward.extend(proxy_env.split(","))

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,49.1,, Version,+,50.0,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/cli/cli_vcp.h,,
@@ -139,6 +139,7 @@ Header,+,lib/toolbox/crc32_calc.h,,
Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/dir_walk.h,,
Header,+,lib/toolbox/float_tools.h,, Header,+,lib/toolbox/float_tools.h,,
Header,+,lib/toolbox/hex.h,, Header,+,lib/toolbox/hex.h,,
Header,+,lib/toolbox/keys_dict.h,,
Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_decoder.h,,
Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/manchester_encoder.h,,
Header,+,lib/toolbox/name_generator.h,, Header,+,lib/toolbox/name_generator.h,,
@@ -1247,6 +1248,7 @@ Function,-,furi_hal_rtc_init_early,void,
Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag
Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t
Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag
Function,+,furi_hal_rtc_reset_registers,void,
Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode
Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime*
Function,+,furi_hal_rtc_set_fault_data,void,uint32_t Function,+,furi_hal_rtc_set_fault_data,void,uint32_t
@@ -1591,6 +1593,15 @@ Function,-,j1f,float,float
Function,-,jn,double,"int, double" Function,-,jn,double,"int, double"
Function,-,jnf,float,"int, float" Function,-,jnf,float,"int, float"
Function,-,jrand48,long,unsigned short[3] Function,-,jrand48,long,unsigned short[3]
Function,+,keys_dict_add_key,_Bool,"KeysDict*, const uint8_t*, size_t"
Function,+,keys_dict_alloc,KeysDict*,"const char*, KeysDictMode, size_t"
Function,+,keys_dict_check_presence,_Bool,const char*
Function,+,keys_dict_delete_key,_Bool,"KeysDict*, const uint8_t*, size_t"
Function,+,keys_dict_free,void,KeysDict*
Function,+,keys_dict_get_next_key,_Bool,"KeysDict*, uint8_t*, size_t"
Function,+,keys_dict_get_total_keys,size_t,KeysDict*
Function,+,keys_dict_is_key_present,_Bool,"KeysDict*, const uint8_t*, size_t"
Function,+,keys_dict_rewind,_Bool,KeysDict*
Function,-,l64a,char*,long Function,-,l64a,char*,long
Function,-,labs,long,long Function,-,labs,long,long
Function,-,lcong48,void,unsigned short[7] Function,-,lcong48,void,unsigned short[7]
1 entry status name type params
2 Version + 49.1 50.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
139 Header + lib/toolbox/dir_walk.h
140 Header + lib/toolbox/float_tools.h
141 Header + lib/toolbox/hex.h
142 Header + lib/toolbox/keys_dict.h
143 Header + lib/toolbox/manchester_decoder.h
144 Header + lib/toolbox/manchester_encoder.h
145 Header + lib/toolbox/name_generator.h
1248 Function + furi_hal_rtc_is_flag_set _Bool FuriHalRtcFlag
1249 Function + furi_hal_rtc_is_leap_year _Bool uint16_t
1250 Function + furi_hal_rtc_reset_flag void FuriHalRtcFlag
1251 Function + furi_hal_rtc_reset_registers void
1252 Function + furi_hal_rtc_set_boot_mode void FuriHalRtcBootMode
1253 Function + furi_hal_rtc_set_datetime void FuriHalRtcDateTime*
1254 Function + furi_hal_rtc_set_fault_data void uint32_t
1593 Function - jn double int, double
1594 Function - jnf float int, float
1595 Function - jrand48 long unsigned short[3]
1596 Function + keys_dict_add_key _Bool KeysDict*, const uint8_t*, size_t
1597 Function + keys_dict_alloc KeysDict* const char*, KeysDictMode, size_t
1598 Function + keys_dict_check_presence _Bool const char*
1599 Function + keys_dict_delete_key _Bool KeysDict*, const uint8_t*, size_t
1600 Function + keys_dict_free void KeysDict*
1601 Function + keys_dict_get_next_key _Bool KeysDict*, uint8_t*, size_t
1602 Function + keys_dict_get_total_keys size_t KeysDict*
1603 Function + keys_dict_is_key_present _Bool KeysDict*, const uint8_t*, size_t
1604 Function + keys_dict_rewind _Bool KeysDict*
1605 Function - l64a char* long
1606 Function - labs long long
1607 Function - lcong48 void unsigned short[7]

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,49.1,, Version,+,50.0,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
@@ -114,7 +114,6 @@ Header,+,lib/nanopb/pb_encode.h,,
Header,+,lib/nfc/helpers/iso13239_crc.h,, Header,+,lib/nfc/helpers/iso13239_crc.h,,
Header,+,lib/nfc/helpers/iso14443_crc.h,, Header,+,lib/nfc/helpers/iso14443_crc.h,,
Header,+,lib/nfc/helpers/nfc_data_generator.h,, Header,+,lib/nfc/helpers/nfc_data_generator.h,,
Header,+,lib/nfc/helpers/nfc_dict.h,,
Header,+,lib/nfc/helpers/nfc_util.h,, Header,+,lib/nfc/helpers/nfc_util.h,,
Header,+,lib/nfc/nfc.h,, Header,+,lib/nfc/nfc.h,,
Header,+,lib/nfc/nfc_device.h,, Header,+,lib/nfc/nfc_device.h,,
@@ -206,6 +205,7 @@ Header,+,lib/toolbox/crc32_calc.h,,
Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/dir_walk.h,,
Header,+,lib/toolbox/float_tools.h,, Header,+,lib/toolbox/float_tools.h,,
Header,+,lib/toolbox/hex.h,, Header,+,lib/toolbox/hex.h,,
Header,+,lib/toolbox/keys_dict.h,,
Header,+,lib/toolbox/manchester_decoder.h,, Header,+,lib/toolbox/manchester_decoder.h,,
Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/manchester_encoder.h,,
Header,+,lib/toolbox/name_generator.h,, Header,+,lib/toolbox/name_generator.h,,
@@ -1443,6 +1443,7 @@ Function,-,furi_hal_rtc_init_early,void,
Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag
Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t
Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag
Function,+,furi_hal_rtc_reset_registers,void,
Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode
Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime*
Function,+,furi_hal_rtc_set_fault_data,void,uint32_t Function,+,furi_hal_rtc_set_fault_data,void,uint32_t
@@ -2005,6 +2006,15 @@ Function,-,j1f,float,float
Function,-,jn,double,"int, double" Function,-,jn,double,"int, double"
Function,-,jnf,float,"int, float" Function,-,jnf,float,"int, float"
Function,-,jrand48,long,unsigned short[3] Function,-,jrand48,long,unsigned short[3]
Function,+,keys_dict_add_key,_Bool,"KeysDict*, const uint8_t*, size_t"
Function,+,keys_dict_alloc,KeysDict*,"const char*, KeysDictMode, size_t"
Function,+,keys_dict_check_presence,_Bool,const char*
Function,+,keys_dict_delete_key,_Bool,"KeysDict*, const uint8_t*, size_t"
Function,+,keys_dict_free,void,KeysDict*
Function,+,keys_dict_get_next_key,_Bool,"KeysDict*, uint8_t*, size_t"
Function,+,keys_dict_get_total_keys,size_t,KeysDict*
Function,+,keys_dict_is_key_present,_Bool,"KeysDict*, const uint8_t*, size_t"
Function,+,keys_dict_rewind,_Bool,KeysDict*
Function,-,l64a,char*,long Function,-,l64a,char*,long
Function,-,labs,long,long Function,-,labs,long,long
Function,-,lcong48,void,unsigned short[7] Function,-,lcong48,void,unsigned short[7]
@@ -2328,8 +2338,8 @@ Function,+,mf_classic_is_value_block,_Bool,"MfClassicSectorTrailer*, uint8_t"
Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t" Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t"
Function,+,mf_classic_poller_auth,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" Function,+,mf_classic_poller_auth,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*" Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_halt,MfClassicError,MfClassicPoller* Function,+,mf_classic_poller_halt,MfClassicError,MfClassicPoller*
Function,+,mf_classic_poller_read_block,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicBlock*" Function,+,mf_classic_poller_read_block,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicBlock*"
Function,+,mf_classic_poller_sync_auth,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" Function,+,mf_classic_poller_sync_auth,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
@@ -2479,15 +2489,6 @@ Function,+,nfc_device_save,_Bool,"NfcDevice*, const char*"
Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceData*" Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceData*"
Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*"
Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t" Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t"
Function,+,nfc_dict_add_key,_Bool,"NfcDict*, const uint8_t*, size_t"
Function,+,nfc_dict_alloc,NfcDict*,"const char*, NfcDictMode, size_t"
Function,+,nfc_dict_check_presence,_Bool,const char*
Function,+,nfc_dict_delete_key,_Bool,"NfcDict*, const uint8_t*, size_t"
Function,+,nfc_dict_free,void,NfcDict*
Function,+,nfc_dict_get_next_key,_Bool,"NfcDict*, uint8_t*, size_t"
Function,+,nfc_dict_get_total_keys,uint32_t,NfcDict*
Function,+,nfc_dict_is_key_present,_Bool,"NfcDict*, const uint8_t*, size_t"
Function,+,nfc_dict_rewind,_Bool,NfcDict*
Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t" Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t"
Function,+,nfc_free,void,Nfc* Function,+,nfc_free,void,Nfc*
Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t" Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t"
@@ -2526,6 +2527,7 @@ Function,+,nfc_set_mask_receive_time_fc,void,"Nfc*, uint32_t"
Function,+,nfc_start,void,"Nfc*, NfcEventCallback, void*" Function,+,nfc_start,void,"Nfc*, NfcEventCallback, void*"
Function,+,nfc_stop,void,Nfc* Function,+,nfc_stop,void,Nfc*
Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t"
Function,+,nfc_util_bytes2num_little_endian,uint64_t,"const uint8_t*, uint8_t"
Function,+,nfc_util_even_parity32,uint8_t,uint32_t Function,+,nfc_util_even_parity32,uint8_t,uint32_t
Function,+,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" Function,+,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*"
Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t"
1 entry status name type params
2 Version + 49.1 50.0
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/cli/cli.h
114 Header + lib/nfc/helpers/iso13239_crc.h
115 Header + lib/nfc/helpers/iso14443_crc.h
116 Header + lib/nfc/helpers/nfc_data_generator.h
Header + lib/nfc/helpers/nfc_dict.h
117 Header + lib/nfc/helpers/nfc_util.h
118 Header + lib/nfc/nfc.h
119 Header + lib/nfc/nfc_device.h
205 Header + lib/toolbox/dir_walk.h
206 Header + lib/toolbox/float_tools.h
207 Header + lib/toolbox/hex.h
208 Header + lib/toolbox/keys_dict.h
209 Header + lib/toolbox/manchester_decoder.h
210 Header + lib/toolbox/manchester_encoder.h
211 Header + lib/toolbox/name_generator.h
1443 Function + furi_hal_rtc_is_flag_set _Bool FuriHalRtcFlag
1444 Function + furi_hal_rtc_is_leap_year _Bool uint16_t
1445 Function + furi_hal_rtc_reset_flag void FuriHalRtcFlag
1446 Function + furi_hal_rtc_reset_registers void
1447 Function + furi_hal_rtc_set_boot_mode void FuriHalRtcBootMode
1448 Function + furi_hal_rtc_set_datetime void FuriHalRtcDateTime*
1449 Function + furi_hal_rtc_set_fault_data void uint32_t
2006 Function - jn double int, double
2007 Function - jnf float int, float
2008 Function - jrand48 long unsigned short[3]
2009 Function + keys_dict_add_key _Bool KeysDict*, const uint8_t*, size_t
2010 Function + keys_dict_alloc KeysDict* const char*, KeysDictMode, size_t
2011 Function + keys_dict_check_presence _Bool const char*
2012 Function + keys_dict_delete_key _Bool KeysDict*, const uint8_t*, size_t
2013 Function + keys_dict_free void KeysDict*
2014 Function + keys_dict_get_next_key _Bool KeysDict*, uint8_t*, size_t
2015 Function + keys_dict_get_total_keys size_t KeysDict*
2016 Function + keys_dict_is_key_present _Bool KeysDict*, const uint8_t*, size_t
2017 Function + keys_dict_rewind _Bool KeysDict*
2018 Function - l64a char* long
2019 Function - labs long long
2020 Function - lcong48 void unsigned short[7]
2338 Function + mf_classic_load _Bool MfClassicData*, FlipperFormat*, uint32_t
2339 Function + mf_classic_poller_auth MfClassicError MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*
2340 Function + mf_classic_poller_auth_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*
Function + mf_classic_poller_get_nt_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*
2341 Function + mf_classic_poller_get_nt MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*
2342 Function + mf_classic_poller_get_nt_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*
2343 Function + mf_classic_poller_halt MfClassicError MfClassicPoller*
2344 Function + mf_classic_poller_read_block MfClassicError MfClassicPoller*, uint8_t, MfClassicBlock*
2345 Function + mf_classic_poller_sync_auth MfClassicError Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*
2489 Function + nfc_device_set_data void NfcDevice*, NfcProtocol, const NfcDeviceData*
2490 Function + nfc_device_set_loading_callback void NfcDevice*, NfcLoadingCallback, void*
2491 Function + nfc_device_set_uid _Bool NfcDevice*, const uint8_t*, size_t
Function + nfc_dict_add_key _Bool NfcDict*, const uint8_t*, size_t
Function + nfc_dict_alloc NfcDict* const char*, NfcDictMode, size_t
Function + nfc_dict_check_presence _Bool const char*
Function + nfc_dict_delete_key _Bool NfcDict*, const uint8_t*, size_t
Function + nfc_dict_free void NfcDict*
Function + nfc_dict_get_next_key _Bool NfcDict*, uint8_t*, size_t
Function + nfc_dict_get_total_keys uint32_t NfcDict*
Function + nfc_dict_is_key_present _Bool NfcDict*, const uint8_t*, size_t
Function + nfc_dict_rewind _Bool NfcDict*
2492 Function + nfc_felica_listener_set_sensf_res_data NfcError Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t
2493 Function + nfc_free void Nfc*
2494 Function + nfc_iso14443a_listener_set_col_res_data NfcError Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t
2527 Function + nfc_start void Nfc*, NfcEventCallback, void*
2528 Function + nfc_stop void Nfc*
2529 Function + nfc_util_bytes2num uint64_t const uint8_t*, uint8_t
2530 Function + nfc_util_bytes2num_little_endian uint64_t const uint8_t*, uint8_t
2531 Function + nfc_util_even_parity32 uint8_t uint32_t
2532 Function + nfc_util_num2bytes void uint64_t, uint8_t, uint8_t*
2533 Function + nfc_util_odd_parity void const uint8_t*, uint8_t*, uint8_t

View File

@@ -3,14 +3,21 @@
#include <furi_hal_version.h> #include <furi_hal_version.h>
#include <furi_hal_resources.h> #include <furi_hal_resources.h>
#include <stm32wbxx_ll_comp.h> #include <stm32wbxx_ll_comp.h>
#include <stm32wbxx_ll_pwr.h>
#define GET_SYSCFG_EXTI_PORT(gpio) \ static uint32_t furi_hal_gpio_invalid_argument_crash() {
(((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \ furi_crash("Invalid argument");
((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \ return 0;
((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \ }
((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \
((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \ #define GPIO_PORT_MAP(port, prefix) \
LL_SYSCFG_EXTI_PORTH) (((port) == (GPIOA)) ? prefix##A : \
((port) == (GPIOB)) ? prefix##B : \
((port) == (GPIOC)) ? prefix##C : \
((port) == (GPIOD)) ? prefix##D : \
((port) == (GPIOE)) ? prefix##E : \
((port) == (GPIOH)) ? prefix##H : \
furi_hal_gpio_invalid_argument_crash())
#define GPIO_PIN_MAP(pin, prefix) \ #define GPIO_PIN_MAP(pin, prefix) \
(((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \
@@ -28,11 +35,16 @@
((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
prefix##15) ((pin) == (LL_GPIO_PIN_15)) ? prefix##15 : \
furi_hal_gpio_invalid_argument_crash())
#define GET_SYSCFG_EXTI_PORT(port) GPIO_PORT_MAP(port, LL_SYSCFG_EXTI_PORT)
#define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE) #define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE)
#define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_) #define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_)
#define GET_PWR_PORT(port) GPIO_PORT_MAP(port, LL_PWR_GPIO_)
#define GET_PWR_PIN(pin) GPIO_PIN_MAP(pin, LL_PWR_GPIO_BIT_)
static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER]; static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER];
static uint8_t furi_hal_gpio_get_pin_num(const GpioPin* gpio) { static uint8_t furi_hal_gpio_get_pin_num(const GpioPin* gpio) {
@@ -65,9 +77,11 @@ void furi_hal_gpio_init_ex(
const GpioPull pull, const GpioPull pull,
const GpioSpeed speed, const GpioSpeed speed,
const GpioAltFn alt_fn) { const GpioAltFn alt_fn) {
uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port); const uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port);
uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin); const uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin);
uint32_t exti_line = GET_EXTI_LINE(gpio->pin); const uint32_t exti_line = GET_EXTI_LINE(gpio->pin);
const uint32_t pwr_port = GET_PWR_PORT(gpio->port);
const uint32_t pwr_pin = GET_PWR_PIN(gpio->pin);
// Configure gpio with interrupts disabled // Configure gpio with interrupts disabled
FURI_CRITICAL_ENTER(); FURI_CRITICAL_ENTER();
@@ -92,13 +106,21 @@ void furi_hal_gpio_init_ex(
switch(pull) { switch(pull) {
case GpioPullNo: case GpioPullNo:
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO);
LL_PWR_DisableGPIOPullUp(pwr_port, pwr_pin);
LL_PWR_DisableGPIOPullDown(pwr_port, pwr_pin);
break; break;
case GpioPullUp: case GpioPullUp:
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP);
LL_PWR_DisableGPIOPullDown(pwr_port, pwr_pin);
LL_PWR_EnableGPIOPullUp(pwr_port, pwr_pin);
break; break;
case GpioPullDown: case GpioPullDown:
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN);
LL_PWR_DisableGPIOPullUp(pwr_port, pwr_pin);
LL_PWR_EnableGPIOPullDown(pwr_port, pwr_pin);
break; break;
default:
furi_crash("Incorrect GpioPull");
} }
// Set gpio mode // Set gpio mode
@@ -166,7 +188,7 @@ void furi_hal_gpio_init_ex(
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG);
break; break;
default: default:
break; furi_crash("Incorrect GpioMode");
} }
} }
FURI_CRITICAL_EXIT(); FURI_CRITICAL_EXIT();

View File

@@ -132,13 +132,7 @@ void furi_hal_rtc_init_early() {
uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterHeader); uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterHeader);
FuriHalRtcHeader* data = (FuriHalRtcHeader*)&data_reg; FuriHalRtcHeader* data = (FuriHalRtcHeader*)&data_reg;
if(data->magic != FURI_HAL_RTC_HEADER_MAGIC || data->version != FURI_HAL_RTC_HEADER_VERSION) { if(data->magic != FURI_HAL_RTC_HEADER_MAGIC || data->version != FURI_HAL_RTC_HEADER_VERSION) {
// Reset all our registers to ensure consistency furi_hal_rtc_reset_registers();
for(size_t i = 0; i < FuriHalRtcRegisterMAX; i++) {
furi_hal_rtc_set_register(i, 0);
}
data->magic = FURI_HAL_RTC_HEADER_MAGIC;
data->version = FURI_HAL_RTC_HEADER_VERSION;
furi_hal_rtc_set_register(FuriHalRtcRegisterHeader, data_reg);
} }
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
@@ -171,6 +165,18 @@ void furi_hal_rtc_sync_shadow() {
} }
} }
void furi_hal_rtc_reset_registers() {
for(size_t i = 0; i < RTC_BKP_NUMBER; i++) {
furi_hal_rtc_set_register(i, 0);
}
uint32_t data_reg = 0;
FuriHalRtcHeader* data = (FuriHalRtcHeader*)&data_reg;
data->magic = FURI_HAL_RTC_HEADER_MAGIC;
data->version = FURI_HAL_RTC_HEADER_VERSION;
furi_hal_rtc_set_register(FuriHalRtcRegisterHeader, data_reg);
}
uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) { uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) {
return LL_RTC_BAK_GetRegister(RTC, reg); return LL_RTC_BAK_GetRegister(RTC, reg);
} }

View File

@@ -56,8 +56,7 @@ void flipper_boot_recovery_exec() {
} }
if(!counter) { if(!counter) {
furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_reset_registers();
furi_hal_rtc_set_pin_fails(0); furi_hal_rtc_set_flag(FuriHalRtcFlagStorageFormatInternal);
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
} }
} }

View File

@@ -26,7 +26,7 @@ typedef struct {
typedef enum { typedef enum {
FuriHalRtcFlagDebug = (1 << 0), FuriHalRtcFlagDebug = (1 << 0),
FuriHalRtcFlagFactoryReset = (1 << 1), FuriHalRtcFlagStorageFormatInternal = (1 << 1),
FuriHalRtcFlagLock = (1 << 2), FuriHalRtcFlagLock = (1 << 2),
FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagC2Update = (1 << 3),
FuriHalRtcFlagHandOrient = (1 << 4), FuriHalRtcFlagHandOrient = (1 << 4),
@@ -91,6 +91,9 @@ void furi_hal_rtc_init();
/** Force sync shadow registers */ /** Force sync shadow registers */
void furi_hal_rtc_sync_shadow(); void furi_hal_rtc_sync_shadow();
/** Reset ALL RTC registers content */
void furi_hal_rtc_reset_registers();
/** Get RTC register content /** Get RTC register content
* *
* @param[in] reg The register identifier * @param[in] reg The register identifier