mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-14 03:28:36 -07:00
Merge branch 'dev' of https://github.com/Next-Flip/Momentum-Firmware into findmy-battery
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
#include "../minunit.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <furi.h>
|
||||
|
||||
void test_furi_memmgr(void) {
|
||||
void* ptr;
|
||||
@@ -37,3 +34,260 @@ void test_furi_memmgr(void) {
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void test_memmgr_malloc(const size_t allocation_size) {
|
||||
uint8_t* ptr = NULL;
|
||||
const char* error_message = NULL;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
ptr = malloc(allocation_size);
|
||||
|
||||
// test that we can allocate memory
|
||||
if(ptr == NULL) {
|
||||
error_message = "malloc failed";
|
||||
}
|
||||
|
||||
// test that memory is zero-initialized after allocation
|
||||
for(size_t i = 0; i < allocation_size; i++) {
|
||||
if(ptr[i] != 0) {
|
||||
error_message = "memory is not zero-initialized after malloc";
|
||||
break;
|
||||
}
|
||||
}
|
||||
memset(ptr, 0x55, allocation_size);
|
||||
free(ptr);
|
||||
|
||||
// test that memory is zero-initialized after free
|
||||
// we know that allocator can use this memory for inner purposes
|
||||
// so we check that memory at least partially zero-initialized
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
|
||||
size_t zero_count = 0;
|
||||
for(size_t i = 0; i < allocation_size; i++) {
|
||||
if(ptr[i] == 0) {
|
||||
zero_count++;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// check that at least 75% of memory is zero-initialized
|
||||
if(zero_count < (allocation_size * 0.75)) {
|
||||
error_message = "seems that memory is not zero-initialized after free (malloc)";
|
||||
}
|
||||
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
if(error_message != NULL) {
|
||||
mu_fail(error_message);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_memmgr_realloc(const size_t allocation_size) {
|
||||
uint8_t* ptr = NULL;
|
||||
const char* error_message = NULL;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
ptr = realloc(ptr, allocation_size);
|
||||
|
||||
// test that we can allocate memory
|
||||
if(ptr == NULL) {
|
||||
error_message = "realloc(NULL) failed";
|
||||
}
|
||||
|
||||
// test that memory is zero-initialized after allocation
|
||||
for(size_t i = 0; i < allocation_size; i++) {
|
||||
if(ptr[i] != 0) {
|
||||
error_message = "memory is not zero-initialized after realloc(NULL)";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memset(ptr, 0x55, allocation_size);
|
||||
|
||||
ptr = realloc(ptr, allocation_size * 2);
|
||||
|
||||
// test that we can reallocate memory
|
||||
if(ptr == NULL) {
|
||||
error_message = "realloc failed";
|
||||
}
|
||||
|
||||
// test that memory content is preserved
|
||||
for(size_t i = 0; i < allocation_size; i++) {
|
||||
if(ptr[i] != 0x55) {
|
||||
error_message = "memory is not reallocated after realloc";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// test that remaining memory is zero-initialized
|
||||
size_t non_zero_count = 0;
|
||||
for(size_t i = allocation_size; i < allocation_size * 2; i++) {
|
||||
if(ptr[i] != 0) {
|
||||
non_zero_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// check that at most of memory is zero-initialized
|
||||
// we know that allocator not always can restore content size from a pointer
|
||||
// so we check against small threshold
|
||||
if(non_zero_count > 4) {
|
||||
error_message = "seems that memory is not zero-initialized after realloc";
|
||||
}
|
||||
|
||||
uint8_t* null_ptr = realloc(ptr, 0);
|
||||
|
||||
// test that we can free memory
|
||||
if(null_ptr != NULL) {
|
||||
error_message = "realloc(0) failed";
|
||||
}
|
||||
|
||||
// test that memory is zero-initialized after realloc(0)
|
||||
// we know that allocator can use this memory for inner purposes
|
||||
// so we check that memory at least partially zero-initialized
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
|
||||
size_t zero_count = 0;
|
||||
for(size_t i = 0; i < allocation_size; i++) {
|
||||
if(ptr[i] == 0) {
|
||||
zero_count++;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// check that at least 75% of memory is zero-initialized
|
||||
if(zero_count < (allocation_size * 0.75)) {
|
||||
error_message = "seems that memory is not zero-initialized after realloc(0)";
|
||||
}
|
||||
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
if(error_message != NULL) {
|
||||
mu_fail(error_message);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_memmgr_alloc_aligned(const size_t allocation_size, const size_t alignment) {
|
||||
uint8_t* ptr = NULL;
|
||||
const char* error_message = NULL;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
ptr = aligned_alloc(alignment, allocation_size);
|
||||
|
||||
// test that we can allocate memory
|
||||
if(ptr == NULL) {
|
||||
error_message = "aligned_alloc failed";
|
||||
}
|
||||
|
||||
// test that memory is aligned
|
||||
if(((uintptr_t)ptr % alignment) != 0) {
|
||||
error_message = "memory is not aligned after aligned_alloc";
|
||||
}
|
||||
|
||||
// test that memory is zero-initialized after allocation
|
||||
for(size_t i = 0; i < allocation_size; i++) {
|
||||
if(ptr[i] != 0) {
|
||||
error_message = "memory is not zero-initialized after aligned_alloc";
|
||||
break;
|
||||
}
|
||||
}
|
||||
memset(ptr, 0x55, allocation_size);
|
||||
free(ptr);
|
||||
|
||||
// test that memory is zero-initialized after free
|
||||
// we know that allocator can use this memory for inner purposes
|
||||
// so we check that memory at least partially zero-initialized
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
|
||||
size_t zero_count = 0;
|
||||
for(size_t i = 0; i < allocation_size; i++) {
|
||||
if(ptr[i] == 0) {
|
||||
zero_count++;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// check that at least 75% of memory is zero-initialized
|
||||
if(zero_count < (allocation_size * 0.75)) {
|
||||
error_message = "seems that memory is not zero-initialized after free (aligned_alloc)";
|
||||
}
|
||||
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
if(error_message != NULL) {
|
||||
mu_fail(error_message);
|
||||
}
|
||||
}
|
||||
|
||||
void test_furi_memmgr_advanced(void) {
|
||||
const size_t sizes[] = {50, 100, 500, 1000, 5000, 10000};
|
||||
const size_t sizes_count = sizeof(sizes) / sizeof(sizes[0]);
|
||||
const size_t alignments[] = {4, 8, 16, 32, 64, 128, 256, 512, 1024};
|
||||
const size_t alignments_count = sizeof(alignments) / sizeof(alignments[0]);
|
||||
|
||||
// do test without memory fragmentation
|
||||
{
|
||||
for(size_t i = 0; i < sizes_count; i++) {
|
||||
test_memmgr_malloc(sizes[i]);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < sizes_count; i++) {
|
||||
test_memmgr_realloc(sizes[i]);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < sizes_count; i++) {
|
||||
for(size_t j = 0; j < alignments_count; j++) {
|
||||
test_memmgr_alloc_aligned(sizes[i], alignments[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do test with memory fragmentation
|
||||
{
|
||||
void* blocks[sizes_count];
|
||||
void* guards[sizes_count - 1];
|
||||
|
||||
// setup guards
|
||||
for(size_t i = 0; i < sizes_count; i++) {
|
||||
blocks[i] = malloc(sizes[i]);
|
||||
if(i < sizes_count - 1) {
|
||||
guards[i] = malloc(sizes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < sizes_count; i++) {
|
||||
free(blocks[i]);
|
||||
}
|
||||
|
||||
// do test
|
||||
for(size_t i = 0; i < sizes_count; i++) {
|
||||
test_memmgr_malloc(sizes[i]);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < sizes_count; i++) {
|
||||
test_memmgr_realloc(sizes[i]);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < sizes_count; i++) {
|
||||
for(size_t j = 0; j < alignments_count; j++) {
|
||||
test_memmgr_alloc_aligned(sizes[i], alignments[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup guards
|
||||
for(size_t i = 0; i < sizes_count - 1; i++) {
|
||||
free(guards[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ void test_furi_concurrent_access(void);
|
||||
void test_furi_pubsub(void);
|
||||
|
||||
void test_furi_memmgr(void);
|
||||
void test_furi_memmgr_advanced(void);
|
||||
|
||||
static int foo = 0;
|
||||
|
||||
@@ -37,6 +38,7 @@ MU_TEST(mu_test_furi_memmgr) {
|
||||
// this test is not accurate, but gives a basic understanding
|
||||
// that memory management is working fine
|
||||
test_furi_memmgr();
|
||||
test_furi_memmgr_advanced();
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_suite) {
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
#include <nfc/nfc_poller.h>
|
||||
#include <nfc/nfc_listener.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.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.h>
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
#include <toolbox/keys_dict.h>
|
||||
#include <nfc/nfc.h>
|
||||
@@ -22,6 +25,23 @@
|
||||
#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_device_test.nfc")
|
||||
#define NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc")
|
||||
|
||||
#define NFC_TEST_FLAG_WORKER_DONE (1)
|
||||
|
||||
typedef enum {
|
||||
NfcTestMfClassicSendFrameTestStateAuth,
|
||||
NfcTestMfClassicSendFrameTestStateReadBlock,
|
||||
|
||||
NfcTestMfClassicSendFrameTestStateFail,
|
||||
NfcTestMfClassicSendFrameTestStateSuccess,
|
||||
} NfcTestMfClassicSendFrameTestState;
|
||||
|
||||
typedef struct {
|
||||
NfcTestMfClassicSendFrameTestState state;
|
||||
BitBuffer* tx_buf;
|
||||
BitBuffer* rx_buf;
|
||||
FuriThreadId thread_id;
|
||||
} NfcTestMfClassicSendFrameTest;
|
||||
|
||||
typedef struct {
|
||||
Storage* storage;
|
||||
} NfcTest;
|
||||
@@ -435,6 +455,109 @@ static void mf_classic_value_block(void) {
|
||||
nfc_free(poller);
|
||||
}
|
||||
|
||||
NfcCommand mf_classic_poller_send_frame_callback(NfcGenericEventEx event, void* context) {
|
||||
furi_check(event.poller);
|
||||
furi_check(event.parent_event_data);
|
||||
furi_check(context);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
MfClassicPoller* instance = event.poller;
|
||||
NfcTestMfClassicSendFrameTest* frame_test = context;
|
||||
Iso14443_3aPollerEvent* iso3_event = event.parent_event_data;
|
||||
|
||||
MfClassicError error = MfClassicErrorNone;
|
||||
if(iso3_event->type == Iso14443_3aPollerEventTypeReady) {
|
||||
if(frame_test->state == NfcTestMfClassicSendFrameTestStateAuth) {
|
||||
MfClassicKey key = {
|
||||
.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
};
|
||||
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL);
|
||||
frame_test->state = (error == MfClassicErrorNone) ?
|
||||
NfcTestMfClassicSendFrameTestStateReadBlock :
|
||||
NfcTestMfClassicSendFrameTestStateFail;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateReadBlock) {
|
||||
do {
|
||||
const uint8_t read_block_cmd[] = {
|
||||
0x30,
|
||||
0x01,
|
||||
0x8b,
|
||||
0xb9,
|
||||
};
|
||||
bit_buffer_copy_bytes(frame_test->tx_buf, read_block_cmd, sizeof(read_block_cmd));
|
||||
|
||||
error = mf_classic_poller_send_encrypted_frame(
|
||||
instance, frame_test->tx_buf, frame_test->rx_buf, 200000);
|
||||
if(error != MfClassicErrorNone) break;
|
||||
if(bit_buffer_get_size_bytes(frame_test->rx_buf) != 18) {
|
||||
error = MfClassicErrorProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t* rx_data = bit_buffer_get_data(frame_test->rx_buf);
|
||||
const uint8_t rx_data_ref[16] = {0};
|
||||
if(memcmp(rx_data, rx_data_ref, sizeof(rx_data_ref)) != 0) {
|
||||
error = MfClassicErrorProtocol;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
frame_test->state = (error == MfClassicErrorNone) ?
|
||||
NfcTestMfClassicSendFrameTestStateSuccess :
|
||||
NfcTestMfClassicSendFrameTestStateFail;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateSuccess) {
|
||||
command = NfcCommandStop;
|
||||
} else if(frame_test->state == NfcTestMfClassicSendFrameTestStateFail) {
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
} else {
|
||||
frame_test->state = NfcTestMfClassicSendFrameTestStateFail;
|
||||
command = NfcCommandStop;
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
furi_thread_flags_set(frame_test->thread_id, NFC_TEST_FLAG_WORKER_DONE);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_send_frame_test) {
|
||||
Nfc* poller = nfc_alloc();
|
||||
Nfc* listener = nfc_alloc();
|
||||
|
||||
NfcDevice* nfc_device = nfc_device_alloc();
|
||||
nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device);
|
||||
NfcListener* mfc_listener = nfc_listener_alloc(
|
||||
listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic));
|
||||
nfc_listener_start(mfc_listener, NULL, NULL);
|
||||
|
||||
NfcPoller* mfc_poller = nfc_poller_alloc(poller, NfcProtocolMfClassic);
|
||||
NfcTestMfClassicSendFrameTest context = {
|
||||
.state = NfcTestMfClassicSendFrameTestStateAuth,
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
.tx_buf = bit_buffer_alloc(32),
|
||||
.rx_buf = bit_buffer_alloc(32),
|
||||
};
|
||||
nfc_poller_start_ex(mfc_poller, mf_classic_poller_send_frame_callback, &context);
|
||||
|
||||
uint32_t flag =
|
||||
furi_thread_flags_wait(NFC_TEST_FLAG_WORKER_DONE, FuriFlagWaitAny, FuriWaitForever);
|
||||
mu_assert(flag == NFC_TEST_FLAG_WORKER_DONE, "Wrong thread flag");
|
||||
nfc_poller_stop(mfc_poller);
|
||||
nfc_poller_free(mfc_poller);
|
||||
|
||||
mu_assert(
|
||||
context.state == NfcTestMfClassicSendFrameTestStateSuccess, "Wrong test state at the end");
|
||||
|
||||
bit_buffer_free(context.tx_buf);
|
||||
bit_buffer_free(context.rx_buf);
|
||||
nfc_listener_stop(mfc_listener);
|
||||
nfc_listener_free(mfc_listener);
|
||||
nfc_device_free(nfc_device);
|
||||
nfc_free(listener);
|
||||
nfc_free(poller);
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_dict_test) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_common_stat(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == FSE_OK) {
|
||||
@@ -538,11 +661,11 @@ MU_TEST_SUITE(nfc) {
|
||||
MU_RUN_TEST(mf_classic_1k_7b_file_test);
|
||||
MU_RUN_TEST(mf_classic_4k_4b_file_test);
|
||||
MU_RUN_TEST(mf_classic_4k_7b_file_test);
|
||||
MU_RUN_TEST(mf_classic_reader);
|
||||
|
||||
MU_RUN_TEST(mf_classic_reader);
|
||||
MU_RUN_TEST(mf_classic_write);
|
||||
MU_RUN_TEST(mf_classic_value_block);
|
||||
|
||||
MU_RUN_TEST(mf_classic_send_frame_test);
|
||||
MU_RUN_TEST(mf_classic_dict_test);
|
||||
|
||||
nfc_test_free();
|
||||
|
||||
@@ -238,10 +238,7 @@ bool subghz_device_cc1101_ext_alloc(SubGhzDeviceConf* conf) {
|
||||
&furi_hal_spi_bus_handle_external_extra);
|
||||
|
||||
// this is needed if multiple SPI devices are connected to the same bus but with different CS pins
|
||||
if(momentum_settings.spi_cc1101_handle == SpiDefault && !furi_hal_subghz_get_ext_power_amp()) {
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull);
|
||||
furi_hal_gpio_write(&gpio_ext_pc3, true);
|
||||
} else if(momentum_settings.spi_cc1101_handle == SpiExtra) {
|
||||
if(momentum_settings.spi_cc1101_handle == SpiExtra) {
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull);
|
||||
furi_hal_gpio_write(&gpio_ext_pa4, true);
|
||||
}
|
||||
@@ -468,6 +465,7 @@ void subghz_device_cc1101_ext_rx(void) {
|
||||
// Go GDO2 (!TX/RX) to high (RX state)
|
||||
cc1101_write_reg(
|
||||
subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV);
|
||||
|
||||
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
|
||||
if(subghz_device_cc1101_ext->power_amp) {
|
||||
furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07M20S_AMP_GPIO, 0);
|
||||
|
||||
9
applications/examples/example_adc/application.fam
Normal file
9
applications/examples/example_adc/application.fam
Normal file
@@ -0,0 +1,9 @@
|
||||
App(
|
||||
appid="example_adc",
|
||||
name="Example: ADC",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="example_adc_main",
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Examples",
|
||||
)
|
||||
176
applications/examples/example_adc/example_adc.c
Normal file
176
applications/examples/example_adc/example_adc.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @file example_adc.c
|
||||
* @brief ADC example.
|
||||
*/
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <input/input.h>
|
||||
|
||||
const uint8_t font[] =
|
||||
"`\2\3\2\3\4\1\2\4\5\11\0\376\6\376\7\377\1M\2\263\3\370 \6\315\364\371\6!\12\315"
|
||||
"\364\201\260\35\312Q\0\42\11\315tJI\316\13\0#\14\315\264\223dP*\203R'\1$\15\315\264"
|
||||
"\262A\311\266D\251l\71\0%\15\315\264\7%\61)J\42\345 \0&\14\315\264\263$\13\223\266$"
|
||||
"\7\1'\10\315\364\201\60\347\10(\10\315\364\32[\313\0)\11\315\64\322b[\35\2*\12\315\264\263"
|
||||
"(\222j\71\15+\11\315\364I\331\226\23\1,\10\315\364\271\205Y\10-\10\315\364\31t\26\0.\10"
|
||||
"\315\364\71\346(\0/\14\315\364\221\60\13\263\60\13C\0\60\13\315\264\245Jb)E:\12\61\12\315"
|
||||
"\364\201Ll\333A\0\62\12\315\264\245bV\33r\20\63\13\315\264\245Z\232D\221\216\2\64\14\315\364"
|
||||
"\201LJ\242!\313v\20\65\14\315t\207$\134\223(\322Q\0\66\13\315\264\245p\252D\221\216\2\67"
|
||||
"\12\315t\207\60+\326a\0\70\13\315\264\245\222T\211\42\35\5\71\13\315\264\245J\24\215\221\216\2:"
|
||||
"\11\315\364i\71!G\1;\12\315\364I\71!\314B\0<\11\315\364\341\254Z\7\1=\12\315\364)"
|
||||
"C<\344$\0>\11\315\364\301\264V\207\1\77\12\315\264\245Z\35\312a\0@\14\315\264\245J\242$"
|
||||
"J\272\203\0A\15\315\264\245J\224\14I\224D\71\10B\13\315t\247\312T\211\222\35\5C\12\315\264"
|
||||
"\245JX\212t\24D\15\315t\247J\224DI\224\354(\0E\14\315t\207$\234\302p\310A\0F"
|
||||
"\12\315t\207$\234\302:\1G\14\315\264\245J\230(Q\244\243\0H\17\315t\243$J\206$J\242"
|
||||
"$\312A\0I\11\315\264\267\260m\7\1J\12\315\364\221\260%\212t\24K\14\315t\243\244\244iI"
|
||||
"T\7\1L\11\315t\303\216C\16\2M\17\315t\243dH\206$J\242$\312A\0N\16\315t\243"
|
||||
"D\251(Q\22%Q\16\2O\15\315\264\245J\224DI\24\351(\0P\12\315t\247J\224LaN"
|
||||
"Q\15\315\264\245J\224DI\42\251\61\0R\14\315t\247J\224L\225(\7\1S\13\315\264\245\222\232"
|
||||
"D\221\216\2T\10\315\264\267\260;\12U\16\315t\243$J\242$J\242HG\1V\15\315t\243$"
|
||||
"J\242$Jj\71\14W\17\315t\243$J\242dH\206$\312A\0X\15\315t\243$\212\64\251\22"
|
||||
"\345 \0Y\13\315t\243$Jja\35\6Z\12\315t\207\60k\34r\20[\10\315\264\264\260G\31"
|
||||
"\134\12\315\264\303\64L\303\64\14]\10\315t\304\276\351\0^\11\315\364\201,\311\271\1_\7\315\364y"
|
||||
"\35\4`\10\315t\322\234'\0a\14\315\364IK\224$R\222\203\0b\13\315t\303p\252D\311\216"
|
||||
"\2c\12\315\364IR%\335A\0d\14\315\364\221\60Z\242$\212v\20e\12\315\364I\322\220\244;"
|
||||
"\10f\12\315\364\221,\333\302:\12g\14\315\364IK\224D\321\30I\0h\14\315t\303p\252DI"
|
||||
"\224\203\0i\12\315\364\201\34\21k;\10j\12\315\364\201\34\21\273e\0k\13\315t\303J\244%Q"
|
||||
"\35\4l\10\315\264\305n;\10m\14\315\364)CRQ\22\245\216\1n\13\315\364)%\245\224D\71"
|
||||
"\10o\12\315\364IR%\212t\24p\13\315\364)S%J\246\60\4q\13\315\364IK\224D\321X"
|
||||
"\1r\11\315\364)%\245\230\23s\12\315\364I\313\232\354(\0t\13\315\364\201\60\333\302\64\7\1u"
|
||||
"\15\315\364)Q\22%\211\224\344 \0v\13\315\364)Q\22%\265\34\6w\13\315\364)\25%Q\272"
|
||||
"\203\0x\12\315\364)Q\244Iu\20y\15\315\364)Q\22%Q\64F\22\0z\12\315\364)CV"
|
||||
"\33r\20{\12\315\364\212\265\64\254&\0|\7\315\264\302~\7}\12\315t\322\260\232\205\265\14~\11"
|
||||
"\315\364II;\13\0\177\6\315\364\371\6\0\0\0\4\377\377\0";
|
||||
|
||||
#define FONT_HEIGHT (8u)
|
||||
|
||||
typedef float (*ValueConverter)(FuriHalAdcHandle* handle, uint16_t value);
|
||||
|
||||
typedef struct {
|
||||
const GpioPinRecord* pin;
|
||||
float value;
|
||||
ValueConverter converter;
|
||||
const char* suffix;
|
||||
} DataItem;
|
||||
|
||||
typedef struct {
|
||||
size_t count;
|
||||
DataItem* items;
|
||||
} Data;
|
||||
|
||||
const GpioPinRecord item_vref = {.name = "VREF", .channel = FuriHalAdcChannelVREFINT};
|
||||
const GpioPinRecord item_temp = {.name = "TEMP", .channel = FuriHalAdcChannelTEMPSENSOR};
|
||||
const GpioPinRecord item_vbat = {.name = "VBAT", .channel = FuriHalAdcChannelVBAT};
|
||||
|
||||
static void app_draw_callback(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
Data* data = ctx;
|
||||
|
||||
canvas_set_custom_u8g2_font(canvas, font);
|
||||
char buffer[64];
|
||||
int32_t x = 0, y = FONT_HEIGHT;
|
||||
for(size_t i = 0; i < data->count; i++) {
|
||||
if(i == canvas_height(canvas) / FONT_HEIGHT) {
|
||||
x = 64;
|
||||
y = FONT_HEIGHT;
|
||||
}
|
||||
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%4s: %4.0f%s\n",
|
||||
data->items[i].pin->name,
|
||||
(double)data->items[i].value,
|
||||
data->items[i].suffix);
|
||||
canvas_draw_str(canvas, x, y, buffer);
|
||||
y += FONT_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
static void app_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t example_adc_main(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
// Data
|
||||
Data data = {};
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(gpio_pins[i].channel != FuriHalAdcChannelNone) {
|
||||
data.count++;
|
||||
}
|
||||
}
|
||||
data.count += 3; // Special channels
|
||||
data.items = malloc(data.count * sizeof(DataItem));
|
||||
size_t item_pos = 0;
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(gpio_pins[i].channel != FuriHalAdcChannelNone) {
|
||||
furi_hal_gpio_init(gpio_pins[i].pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
data.items[item_pos].pin = &gpio_pins[i];
|
||||
data.items[item_pos].converter = furi_hal_adc_convert_to_voltage;
|
||||
data.items[item_pos].suffix = "mV";
|
||||
item_pos++;
|
||||
}
|
||||
}
|
||||
data.items[item_pos].pin = &item_vref;
|
||||
data.items[item_pos].converter = furi_hal_adc_convert_vref;
|
||||
data.items[item_pos].suffix = "mV";
|
||||
item_pos++;
|
||||
data.items[item_pos].pin = &item_temp;
|
||||
data.items[item_pos].converter = furi_hal_adc_convert_temp;
|
||||
data.items[item_pos].suffix = "C";
|
||||
item_pos++;
|
||||
data.items[item_pos].pin = &item_vbat;
|
||||
data.items[item_pos].converter = furi_hal_adc_convert_vbat;
|
||||
data.items[item_pos].suffix = "mV";
|
||||
item_pos++;
|
||||
furi_assert(item_pos == data.count);
|
||||
|
||||
// Alloc message queue
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, &data);
|
||||
view_port_input_callback_set(view_port, app_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
// Initialize ADC
|
||||
FuriHalAdcHandle* adc_handle = furi_hal_adc_acquire();
|
||||
furi_hal_adc_configure(adc_handle);
|
||||
|
||||
// Process events
|
||||
InputEvent event;
|
||||
bool running = true;
|
||||
while(running) {
|
||||
if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
|
||||
if(event.type == InputTypePress && event.key == InputKeyBack) {
|
||||
running = false;
|
||||
}
|
||||
} else {
|
||||
for(size_t i = 0; i < data.count; i++) {
|
||||
data.items[i].value = data.items[i].converter(
|
||||
adc_handle, furi_hal_adc_read(adc_handle, data.items[i].pin->channel));
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_adc_release(adc_handle);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_GUI);
|
||||
free(data.items);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ int32_t example_custom_font_main(void* p) {
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, view_port);
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, NULL);
|
||||
view_port_input_callback_set(view_port, app_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
|
||||
@@ -39,7 +39,7 @@ int32_t example_images_main(void* p) {
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, view_port);
|
||||
view_port_draw_callback_set(view_port, app_draw_callback, NULL);
|
||||
view_port_input_callback_set(view_port, app_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
|
||||
Submodule applications/external updated: 5890d08342...68b1a9af7c
@@ -33,6 +33,7 @@ static const char* known_ext[] = {
|
||||
[ArchiveFileTypeSubghzRemote] = ".txt",
|
||||
[ArchiveFileTypeInfraredRemote] = ".txt",
|
||||
[ArchiveFileTypeBadKb] = ".txt",
|
||||
[ArchiveFileTypeWAV] = ".wav",
|
||||
[ArchiveFileTypeU2f] = "?",
|
||||
[ArchiveFileTypeApplication] = ".fap",
|
||||
[ArchiveFileTypeJS] = ".js",
|
||||
|
||||
@@ -36,7 +36,13 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder
|
||||
txt_path = archive_get_default_path(ArchiveTabBadKb);
|
||||
break;
|
||||
}
|
||||
if(txt_path != NULL && furi_string_start_with_str(file->path, txt_path)) {
|
||||
if(txt_path != NULL) {
|
||||
size_t len = strlen(txt_path);
|
||||
if(furi_string_size(file->path) < len) continue;
|
||||
// Compare but ignore /ext or /any, continue if different (memcmp() != 0)
|
||||
if(memcmp(furi_string_get_cstr(file->path) + 4, txt_path + 4, len - 4)) {
|
||||
continue;
|
||||
}
|
||||
file->type = i;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ typedef enum {
|
||||
ArchiveFileTypeSubghzRemote,
|
||||
ArchiveFileTypeInfraredRemote,
|
||||
ArchiveFileTypeBadKb,
|
||||
ArchiveFileTypeWAV,
|
||||
ArchiveFileTypeU2f,
|
||||
ArchiveFileTypeApplication,
|
||||
ArchiveFileTypeJS,
|
||||
|
||||
@@ -32,6 +32,8 @@ const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) {
|
||||
return EXT_PATH("apps/Infrared/ir_remote.fap");
|
||||
case ArchiveFileTypeBadKb:
|
||||
return "Bad KB";
|
||||
case ArchiveFileTypeWAV:
|
||||
return EXT_PATH("apps/Media/wav_player.fap");
|
||||
case ArchiveFileTypeU2f:
|
||||
return "U2F";
|
||||
case ArchiveFileTypeUpdateManifest:
|
||||
|
||||
@@ -34,6 +34,7 @@ static const Icon* ArchiveItemIcons[] = {
|
||||
[ArchiveFileTypeSubghzRemote] = &I_subrem_10px,
|
||||
[ArchiveFileTypeInfraredRemote] = &I_ir_scope_10px,
|
||||
[ArchiveFileTypeBadKb] = &I_badkb_10px,
|
||||
[ArchiveFileTypeWAV] = &I_music_10px,
|
||||
[ArchiveFileTypeU2f] = &I_u2f_10px,
|
||||
[ArchiveFileTypeApplication] = &I_Apps_10px,
|
||||
[ArchiveFileTypeJS] = &I_js_script_10px,
|
||||
|
||||
@@ -36,6 +36,7 @@ void bad_kb_load_settings(BadKbApp* app) {
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
if(flipper_format_file_open_existing(file, BAD_KB_SETTINGS_PATH)) {
|
||||
FuriString* tmp_str = furi_string_alloc();
|
||||
uint32_t tmp_uint = 0;
|
||||
|
||||
if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) {
|
||||
furi_string_reset(app->keyboard_layout);
|
||||
@@ -47,11 +48,17 @@ void bad_kb_load_settings(BadKbApp* app) {
|
||||
flipper_format_rewind(file);
|
||||
}
|
||||
|
||||
if(!flipper_format_read_bool(file, "Bt_Remember", &app->bt_remember, 1)) {
|
||||
app->bt_remember = false;
|
||||
if(!flipper_format_read_bool(file, "Bt_Remember", &cfg->ble.bonding, 1)) {
|
||||
cfg->ble.bonding = false;
|
||||
flipper_format_rewind(file);
|
||||
}
|
||||
|
||||
if(!flipper_format_read_uint32(file, "Bt_Pairing", &tmp_uint, 1)) {
|
||||
tmp_uint = GapPairingNone;
|
||||
flipper_format_rewind(file);
|
||||
}
|
||||
cfg->ble.pairing = tmp_uint;
|
||||
|
||||
if(flipper_format_read_string(file, "Bt_Name", tmp_str)) {
|
||||
strlcpy(cfg->ble.name, furi_string_get_cstr(tmp_str), sizeof(cfg->ble.name));
|
||||
} else {
|
||||
@@ -115,9 +122,12 @@ static void bad_kb_save_settings(BadKbApp* app) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
if(flipper_format_file_open_always(file, BAD_KB_SETTINGS_PATH)) {
|
||||
uint32_t tmp_uint = 0;
|
||||
flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout);
|
||||
flipper_format_write_bool(file, "Is_Bt", &app->is_bt, 1);
|
||||
flipper_format_write_bool(file, "Bt_Remember", &app->bt_remember, 1);
|
||||
flipper_format_write_bool(file, "Bt_Remember", &cfg->ble.bonding, 1);
|
||||
tmp_uint = cfg->ble.pairing;
|
||||
flipper_format_write_uint32(file, "Bt_Pairing", &tmp_uint, 1);
|
||||
flipper_format_write_string_cstr(file, "Bt_Name", cfg->ble.name);
|
||||
flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&cfg->ble.mac, sizeof(cfg->ble.mac));
|
||||
flipper_format_write_string_cstr(file, "Usb_Manuf", cfg->usb.manuf);
|
||||
@@ -143,22 +153,21 @@ void bad_kb_app_show_loading_popup(BadKbApp* app, bool show) {
|
||||
|
||||
int32_t bad_kb_conn_apply(BadKbApp* app) {
|
||||
if(app->is_bt) {
|
||||
// Setup profile config
|
||||
BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config;
|
||||
memcpy(&app->cur_ble_cfg, &cfg->ble, sizeof(cfg->ble));
|
||||
if(app->cur_ble_cfg.bonding) {
|
||||
// Hardcode mac for remember mode
|
||||
// Change in config copy to preserve user choice for non-remember mode
|
||||
memcpy(app->cur_ble_cfg.mac, BAD_KB_BOUND_MAC, sizeof(BAD_KB_BOUND_MAC));
|
||||
}
|
||||
|
||||
// Prepare for new profile
|
||||
bt_timeout = bt_hid_delays[LevelRssi39_0];
|
||||
bt_disconnect(app->bt);
|
||||
furi_delay_ms(200);
|
||||
bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH);
|
||||
|
||||
// Setup new config
|
||||
BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config;
|
||||
memcpy(&app->cur_ble_cfg, &cfg->ble, sizeof(cfg->ble));
|
||||
app->cur_ble_cfg.bonding = app->bt_remember;
|
||||
if(app->bt_remember) {
|
||||
app->cur_ble_cfg.pairing = GapPairingPinCodeVerifyYesNo;
|
||||
} else {
|
||||
app->cur_ble_cfg.pairing = GapPairingNone;
|
||||
memcpy(app->cur_ble_cfg.mac, BAD_KB_BOUND_MAC, sizeof(BAD_KB_BOUND_MAC));
|
||||
}
|
||||
|
||||
// Set profile
|
||||
app->ble_hid = bt_profile_start(app->bt, ble_profile_hid, &app->cur_ble_cfg);
|
||||
furi_check(app->ble_hid);
|
||||
@@ -246,7 +255,8 @@ void bad_kb_config_refresh(BadKbApp* app) {
|
||||
bad_kb_conn_reset(app);
|
||||
} else {
|
||||
BleProfileHidParams* cur = &app->cur_ble_cfg;
|
||||
apply = apply || cfg->ble.bonding != app->bt_remember;
|
||||
apply = apply || cfg->ble.bonding != cur->bonding;
|
||||
apply = apply || cfg->ble.pairing != cur->pairing;
|
||||
apply = apply || strncmp(cfg->ble.name, cur->name, sizeof(cfg->ble.name));
|
||||
apply = apply || memcmp(cfg->ble.mac, cur->mac, sizeof(cfg->ble.mac));
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ struct BadKbApp {
|
||||
|
||||
Bt* bt;
|
||||
bool is_bt;
|
||||
bool bt_remember;
|
||||
BadKbConfig config; // User options
|
||||
BadKbConfig id_config; // ID and BT_ID values
|
||||
|
||||
|
||||
@@ -344,6 +344,10 @@ static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) {
|
||||
|
||||
strlcpy(cfg->ble.name, line + mac_len, sizeof(cfg->ble.name));
|
||||
FURI_LOG_D(WORKER_TAG, "set bt id: %s", line);
|
||||
|
||||
// Can't set bonding and pairing via BT_ID, sync with user choice instead
|
||||
cfg->ble.bonding = bad_kb->app->config.ble.bonding;
|
||||
cfg->ble.pairing = bad_kb->app->config.ble.pairing;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ enum VarItemListIndex {
|
||||
|
||||
enum VarItemListIndexBt {
|
||||
VarItemListIndexBtRemember = VarItemListIndexConnection + 1,
|
||||
VarItemListIndexBtPairing,
|
||||
VarItemListIndexBtDeviceName,
|
||||
VarItemListIndexBtMacAddress,
|
||||
VarItemListIndexBtRandomizeMac,
|
||||
@@ -29,11 +30,35 @@ void bad_kb_scene_config_connection_callback(VariableItem* item) {
|
||||
|
||||
void bad_kb_scene_config_bt_remember_callback(VariableItem* item) {
|
||||
BadKbApp* bad_kb = variable_item_get_context(item);
|
||||
bad_kb->bt_remember = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF");
|
||||
bool value = variable_item_get_current_value_index(item);
|
||||
// Set user config and remember
|
||||
bad_kb->config.ble.bonding = value;
|
||||
// Apply to ID config so its temporarily overridden (currently can't set bonding with BT_ID anyway)
|
||||
if(bad_kb->set_bt_id) {
|
||||
bad_kb->id_config.ble.bonding = value;
|
||||
}
|
||||
variable_item_set_current_value_text(item, value ? "ON" : "OFF");
|
||||
view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexBtRemember);
|
||||
}
|
||||
|
||||
const char* const bt_pairing_names[GapPairingCount] = {
|
||||
"YesNo",
|
||||
"PIN Type",
|
||||
"PIN Y/N",
|
||||
};
|
||||
void bad_kb_scene_config_bt_pairing_callback(VariableItem* item) {
|
||||
BadKbApp* bad_kb = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
// Set user config and remember
|
||||
bad_kb->config.ble.pairing = index;
|
||||
// Apply to ID config so its temporarily overridden (currently can't set pairing with BT_ID anyway)
|
||||
if(bad_kb->set_bt_id) {
|
||||
bad_kb->id_config.ble.pairing = index;
|
||||
}
|
||||
variable_item_set_current_value_text(item, bt_pairing_names[index]);
|
||||
view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexBtPairing);
|
||||
}
|
||||
|
||||
void bad_kb_scene_config_var_item_list_callback(void* context, uint32_t index) {
|
||||
BadKbApp* bad_kb = context;
|
||||
view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index);
|
||||
@@ -52,20 +77,31 @@ void bad_kb_scene_config_on_enter(void* context) {
|
||||
variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB");
|
||||
|
||||
if(bad_kb->is_bt) {
|
||||
BadKbConfig* cfg = bad_kb->set_bt_id ? &bad_kb->id_config : &bad_kb->config;
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "BT Remember", 2, bad_kb_scene_config_bt_remember_callback, bad_kb);
|
||||
variable_item_set_current_value_index(item, bad_kb->bt_remember);
|
||||
variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF");
|
||||
variable_item_set_current_value_index(item, cfg->ble.bonding);
|
||||
variable_item_set_current_value_text(item, cfg->ble.bonding ? "ON" : "OFF");
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list,
|
||||
"BT Pairing",
|
||||
GapPairingCount,
|
||||
bad_kb_scene_config_bt_pairing_callback,
|
||||
bad_kb);
|
||||
variable_item_set_current_value_index(item, cfg->ble.pairing);
|
||||
variable_item_set_current_value_text(item, bt_pairing_names[cfg->ble.pairing]);
|
||||
|
||||
item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_kb);
|
||||
|
||||
item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_kb);
|
||||
if(bad_kb->bt_remember) {
|
||||
if(cfg->ble.bonding) {
|
||||
variable_item_set_locked(item, true, "Remember\nmust be Off!");
|
||||
}
|
||||
|
||||
item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_kb);
|
||||
if(bad_kb->bt_remember) {
|
||||
if(cfg->ble.bonding) {
|
||||
variable_item_set_locked(item, true, "Remember\nmust be Off!");
|
||||
}
|
||||
} else {
|
||||
@@ -109,6 +145,9 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) {
|
||||
case VarItemListIndexBtRemember:
|
||||
bad_kb_config_refresh(bad_kb);
|
||||
break;
|
||||
case VarItemListIndexBtPairing:
|
||||
bad_kb_config_refresh(bad_kb);
|
||||
break;
|
||||
case VarItemListIndexBtDeviceName:
|
||||
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtName);
|
||||
break;
|
||||
|
||||
@@ -17,5 +17,6 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm)
|
||||
ADD_SCENE(ibutton, delete_success, DeleteSuccess)
|
||||
ADD_SCENE(ibutton, retry_confirm, RetryConfirm)
|
||||
ADD_SCENE(ibutton, exit_confirm, ExitConfirm)
|
||||
ADD_SCENE(ibutton, read_exit_confirm, ReadExitConfirm)
|
||||
ADD_SCENE(ibutton, view_data, ViewData)
|
||||
ADD_SCENE(ibutton, rpc, Rpc)
|
||||
|
||||
@@ -7,23 +7,33 @@ void ibutton_scene_delete_confirm_on_enter(void* context) {
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
FuriString* uid = furi_string_alloc();
|
||||
|
||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Back", ibutton_widget_callback, context);
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context);
|
||||
|
||||
furi_string_printf(tmp, "\e#Delete %s?\e#", ibutton->key_name);
|
||||
furi_string_printf(tmp, "\e#Delete %s?\e#\n", ibutton->key_name);
|
||||
|
||||
ibutton_protocols_render_uid(ibutton->protocols, key, uid);
|
||||
|
||||
furi_string_cat_printf(
|
||||
uid,
|
||||
"\n%s %s",
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, ibutton_key_get_protocol_id(key)),
|
||||
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));
|
||||
|
||||
furi_string_cat(tmp, uid);
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), false);
|
||||
widget, 0, 0, 128, 64, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);
|
||||
|
||||
furi_string_reset(tmp);
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 128 / 2, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
|
||||
furi_string_reset(uid);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
furi_string_free(tmp);
|
||||
furi_string_free(uid);
|
||||
}
|
||||
|
||||
bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
@@ -21,17 +21,20 @@ void ibutton_scene_emulate_on_enter(void* context) {
|
||||
|
||||
widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"[%s]\n%s",
|
||||
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)),
|
||||
furi_string_empty(ibutton->file_path) ? "Unsaved Key" : ibutton->key_name);
|
||||
if(furi_string_empty(ibutton->file_path)) {
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"Unsaved\n%s",
|
||||
ibutton_protocols_get_name(ibutton->protocols, ibutton_key_get_protocol_id(key)));
|
||||
} else {
|
||||
furi_string_printf(tmp, "%s", ibutton->key_name);
|
||||
}
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 52, 30, 75, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
|
||||
widget, 52, 23, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 88, 5, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating");
|
||||
widget, 88, 10, AlignCenter, AlignTop, FontPrimary, "Emulating");
|
||||
|
||||
ibutton_worker_emulate_set_callback(ibutton->worker, ibutton_scene_emulate_callback, ibutton);
|
||||
ibutton_worker_emulate_start(ibutton->worker, key);
|
||||
|
||||
@@ -8,19 +8,24 @@ void ibutton_scene_info_on_enter(void* context) {
|
||||
const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key);
|
||||
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
FuriString* keynumber = furi_string_alloc();
|
||||
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, keynumber);
|
||||
FuriString* brief_data = furi_string_alloc();
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"\e#%s\n[%s]\e#\n%s",
|
||||
"Name:%s\n\e#%s %s\e#\n",
|
||||
ibutton->key_name,
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id),
|
||||
furi_string_get_cstr(keynumber));
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
|
||||
ibutton_protocols_render_brief_data(ibutton->protocols, key, brief_data);
|
||||
|
||||
furi_string_cat(tmp, brief_data);
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 2, 128, 64, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true);
|
||||
widget, 0, 0, 128, 64, AlignLeft, AlignTop, furi_string_get_cstr(tmp), false);
|
||||
|
||||
furi_string_reset(tmp);
|
||||
furi_string_reset(brief_data);
|
||||
|
||||
if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) &
|
||||
iButtonProtocolFeatureExtData) {
|
||||
@@ -30,7 +35,7 @@ void ibutton_scene_info_on_enter(void* context) {
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
furi_string_free(tmp);
|
||||
furi_string_free(keynumber);
|
||||
furi_string_free(brief_data);
|
||||
}
|
||||
|
||||
bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
@@ -12,9 +12,9 @@ void ibutton_scene_read_on_enter(void* context) {
|
||||
iButtonKey* key = ibutton->key;
|
||||
iButtonWorker* worker = ibutton->worker;
|
||||
|
||||
popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "Apply key to\nFlipper's back", 95, 30, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59);
|
||||
popup_set_header(popup, "Reading", 95, 26, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, "Connect key\nwith pogo pins", 95, 30, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 10, &I_DolphinWait_59x54);
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
|
||||
|
||||
|
||||
@@ -14,13 +14,10 @@ void ibutton_scene_read_error_on_enter(void* context) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context);
|
||||
|
||||
widget_add_string_element(
|
||||
widget, 128 / 2, 2, AlignCenter, AlignTop, FontPrimary, "Read Error");
|
||||
|
||||
ibutton_protocols_render_error(ibutton->protocols, key, tmp);
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp));
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 48, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageError);
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "../ibutton_i.h"
|
||||
|
||||
static void ibutton_scene_read_exit_confirm_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
iButton* ibutton = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void ibutton_scene_read_exit_confirm_on_enter(void* context) {
|
||||
iButton* ibutton = context;
|
||||
Widget* widget = ibutton->widget;
|
||||
|
||||
widget_add_button_element(
|
||||
widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Exit",
|
||||
ibutton_scene_read_exit_confirm_widget_callback,
|
||||
ibutton);
|
||||
widget_add_button_element(
|
||||
widget,
|
||||
GuiButtonTypeRight,
|
||||
"Stay",
|
||||
ibutton_scene_read_exit_confirm_widget_callback,
|
||||
ibutton);
|
||||
widget_add_string_element(
|
||||
widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?");
|
||||
widget_add_string_element(
|
||||
widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!");
|
||||
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
}
|
||||
|
||||
bool ibutton_scene_read_exit_confirm_on_event(void* context, SceneManagerEvent event) {
|
||||
iButton* ibutton = context;
|
||||
SceneManager* scene_manager = ibutton->scene_manager;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true; // Ignore Back button presses
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
scene_manager_search_and_switch_to_previous_scene(scene_manager, iButtonSceneRead);
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_previous_scene(scene_manager);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void ibutton_scene_read_exit_confirm_on_exit(void* context) {
|
||||
iButton* ibutton = context;
|
||||
widget_reset(ibutton->widget);
|
||||
}
|
||||
@@ -30,19 +30,10 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
|
||||
if(features & iButtonProtocolFeatureExtData) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"View Data",
|
||||
SubmenuIndexViewData,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteBlank) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write Blank",
|
||||
"Write ID",
|
||||
SubmenuIndexWriteBlank,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
@@ -51,12 +42,21 @@ void ibutton_scene_read_key_menu_on_enter(void* context) {
|
||||
if(features & iButtonProtocolFeatureWriteCopy) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Write Copy",
|
||||
"Full Write on Same Type",
|
||||
SubmenuIndexWriteCopy,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
if(features & iButtonProtocolFeatureExtData) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Data Info",
|
||||
SubmenuIndexViewData,
|
||||
ibutton_scene_read_key_menu_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneReadKeyMenu));
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
|
||||
|
||||
@@ -18,9 +18,9 @@ void ibutton_scene_read_success_on_enter(void* context) {
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"%s[%s]",
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id));
|
||||
"%s %s",
|
||||
ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id),
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
|
||||
widget_add_string_element(
|
||||
widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
|
||||
@@ -44,7 +44,7 @@ bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = true;
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneReadExitConfirm);
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == GuiButtonTypeRight) {
|
||||
|
||||
@@ -21,12 +21,16 @@ void ibutton_scene_saved_key_menu_on_enter(void* context) {
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteBlank) {
|
||||
submenu_add_item(
|
||||
submenu, "Write Blank", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton);
|
||||
submenu, "Write ID", SubmenuIndexWriteBlank, ibutton_submenu_callback, ibutton);
|
||||
}
|
||||
|
||||
if(features & iButtonProtocolFeatureWriteCopy) {
|
||||
submenu_add_item(
|
||||
submenu, "Write Copy", SubmenuIndexWriteCopy, ibutton_submenu_callback, ibutton);
|
||||
submenu,
|
||||
"Full Write on Same Type",
|
||||
SubmenuIndexWriteCopy,
|
||||
ibutton_submenu_callback,
|
||||
ibutton);
|
||||
}
|
||||
|
||||
submenu_add_item(submenu, "Edit", SubmenuIndexEdit, ibutton_submenu_callback, ibutton);
|
||||
|
||||
@@ -40,30 +40,29 @@ void ibutton_scene_write_on_enter(void* context) {
|
||||
|
||||
widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44);
|
||||
|
||||
furi_string_printf(
|
||||
tmp,
|
||||
"[%s]\n%s ",
|
||||
ibutton_protocols_get_name(ibutton->protocols, protocol_id),
|
||||
ibutton->key_name);
|
||||
if(furi_string_empty(ibutton->file_path)) {
|
||||
furi_string_printf(
|
||||
tmp, "Unsaved\n%s", ibutton_protocols_get_name(ibutton->protocols, protocol_id));
|
||||
} else {
|
||||
furi_string_printf(tmp, "%s", ibutton->key_name);
|
||||
}
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 52, 30, 75, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), true);
|
||||
widget, 52, 23, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(tmp), false);
|
||||
|
||||
ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
|
||||
|
||||
furi_string_set(tmp, "iButton\nwriting ");
|
||||
|
||||
if(ibutton->write_mode == iButtonWriteModeBlank) {
|
||||
furi_string_cat(tmp, "Blank");
|
||||
furi_string_set(tmp, "Writing ID");
|
||||
ibutton_worker_write_blank_start(worker, key);
|
||||
|
||||
} else if(ibutton->write_mode == iButtonWriteModeCopy) {
|
||||
furi_string_cat(tmp, "Copy");
|
||||
furi_string_set(tmp, "Full Writing");
|
||||
ibutton_worker_write_copy_start(worker, key);
|
||||
}
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
widget, 88, 5, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
|
||||
widget, 88, 10, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp));
|
||||
|
||||
ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart);
|
||||
view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
|
||||
|
||||
@@ -15,8 +15,10 @@ void lfrfid_scene_read_success_on_enter(void* context) {
|
||||
} else {
|
||||
furi_string_printf(display_text, "\e#%s\e#", protocol);
|
||||
}
|
||||
widget_add_text_box_element(
|
||||
widget, 16, 2, 112, 14, AlignLeft, AlignTop, furi_string_get_cstr(display_text), true);
|
||||
|
||||
furi_string_cat(display_text, "\nHex: ");
|
||||
furi_string_set(display_text, "Hex: ");
|
||||
|
||||
const size_t data_size = protocol_dict_get_data_size(app->dict, app->protocol_id);
|
||||
uint8_t* data = malloc(data_size);
|
||||
@@ -40,7 +42,7 @@ void lfrfid_scene_read_success_on_enter(void* context) {
|
||||
furi_string_free(rendered_data);
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 0, 0, 128, 52, AlignLeft, AlignTop, furi_string_get_cstr(display_text), true);
|
||||
widget, 0, 16, 128, 52, AlignLeft, AlignTop, furi_string_get_cstr(display_text), true);
|
||||
widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app);
|
||||
widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ void lfrfid_scene_save_name_on_enter(void* context) {
|
||||
LFRFID_TEXT_STORE_SIZE,
|
||||
key_name_is_empty);
|
||||
|
||||
FURI_LOG_I("", "%s %s", furi_string_get_cstr(folder_path), app->text_store);
|
||||
FURI_LOG_D("", "%s %s", furi_string_get_cstr(folder_path), app->text_store);
|
||||
|
||||
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||
furi_string_get_cstr(folder_path),
|
||||
|
||||
@@ -18,6 +18,7 @@ ADD_SCENE(momentum_app, misc, Misc)
|
||||
ADD_SCENE(momentum_app, misc_screen, MiscScreen)
|
||||
ADD_SCENE(momentum_app, misc_screen_color, MiscScreenColor)
|
||||
ADD_SCENE(momentum_app, misc_dolphin, MiscDolphin)
|
||||
ADD_SCENE(momentum_app, misc_spoof, MiscSpoof)
|
||||
ADD_SCENE(momentum_app, misc_spoof_name, MiscSpoofName)
|
||||
ADD_SCENE(momentum_app, misc_vgm, MiscVgm)
|
||||
ADD_SCENE(momentum_app, misc_vgm_color, MiscVgmColor)
|
||||
ADD_SCENE(momentum_app, misc_rename, MiscRename)
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
enum VarItemListIndex {
|
||||
VarItemListIndexScreen,
|
||||
VarItemListIndexDolphin,
|
||||
VarItemListIndexSpoof,
|
||||
VarItemListIndexVgm,
|
||||
VarItemListIndexChangeDeviceName,
|
||||
VarItemListIndexChargeCap,
|
||||
VarItemListIndexShowMomentumIntro,
|
||||
};
|
||||
@@ -37,10 +37,11 @@ void momentum_app_scene_misc_on_enter(void* context) {
|
||||
item = variable_item_list_add(var_item_list, "Dolphin", 0, NULL, app);
|
||||
variable_item_set_current_value_text(item, ">");
|
||||
|
||||
item = variable_item_list_add(var_item_list, "VGM Options", 0, NULL, app);
|
||||
item = variable_item_list_add(var_item_list, "Spoofing Options", 0, NULL, app);
|
||||
variable_item_set_current_value_text(item, ">");
|
||||
|
||||
variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app);
|
||||
item = variable_item_list_add(var_item_list, "VGM Options", 0, NULL, app);
|
||||
variable_item_set_current_value_text(item, ">");
|
||||
|
||||
char cap_str[6];
|
||||
value_index = momentum_settings.charge_cap / CHARGE_CAP_INTV;
|
||||
@@ -81,14 +82,14 @@ bool momentum_app_scene_misc_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_set_scene_state(app->scene_manager, MomentumAppSceneMiscDolphin, 0);
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneMiscDolphin);
|
||||
break;
|
||||
case VarItemListIndexSpoof:
|
||||
scene_manager_set_scene_state(app->scene_manager, MomentumAppSceneMiscSpoof, 0);
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneMiscSpoof);
|
||||
break;
|
||||
case VarItemListIndexVgm:
|
||||
scene_manager_set_scene_state(app->scene_manager, MomentumAppSceneMiscVgm, 0);
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneMiscVgm);
|
||||
break;
|
||||
case VarItemListIndexChangeDeviceName:
|
||||
scene_manager_set_scene_state(app->scene_manager, MomentumAppSceneMiscRename, 0);
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneMiscRename);
|
||||
break;
|
||||
case VarItemListIndexShowMomentumIntro: {
|
||||
for(int i = 0; i < 10; i++) {
|
||||
if(storage_common_copy(
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "../momentum_app.h"
|
||||
|
||||
enum VarItemListIndex {
|
||||
VarItemListIndexFlipperName, // TODO: Split into name, mac, serial
|
||||
VarItemListIndexShellColor,
|
||||
};
|
||||
|
||||
const char* const shell_color_names[FuriHalVersionColorCount] = {
|
||||
"Real",
|
||||
"Black",
|
||||
"White",
|
||||
"Transparent",
|
||||
};
|
||||
static void momentum_app_scene_misc_spoof_shell_color_changed(VariableItem* item) {
|
||||
MomentumApp* app = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(item, shell_color_names[index]);
|
||||
momentum_settings.spoof_color = index;
|
||||
app->save_settings = true;
|
||||
app->require_reboot = true;
|
||||
}
|
||||
|
||||
void momentum_app_scene_misc_spoof_var_item_list_callback(void* context, uint32_t index) {
|
||||
MomentumApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void momentum_app_scene_misc_spoof_on_enter(void* context) {
|
||||
MomentumApp* app = context;
|
||||
VariableItemList* var_item_list = app->var_item_list;
|
||||
VariableItem* item;
|
||||
|
||||
item = variable_item_list_add(var_item_list, "Flipper Name", 0, NULL, app);
|
||||
variable_item_set_current_value_text(item, app->device_name);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list,
|
||||
"Shell Color",
|
||||
FuriHalVersionColorCount,
|
||||
momentum_app_scene_misc_spoof_shell_color_changed,
|
||||
app);
|
||||
variable_item_set_current_value_index(item, momentum_settings.spoof_color);
|
||||
variable_item_set_current_value_text(item, shell_color_names[momentum_settings.spoof_color]);
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
var_item_list, momentum_app_scene_misc_spoof_var_item_list_callback, app);
|
||||
|
||||
variable_item_list_set_selected_item(
|
||||
var_item_list,
|
||||
scene_manager_get_scene_state(app->scene_manager, MomentumAppSceneMiscSpoof));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MomentumAppViewVarItemList);
|
||||
}
|
||||
|
||||
bool momentum_app_scene_misc_spoof_on_event(void* context, SceneManagerEvent event) {
|
||||
MomentumApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_set_scene_state(app->scene_manager, MomentumAppSceneMiscSpoof, event.event);
|
||||
consumed = true;
|
||||
switch(event.event) {
|
||||
case VarItemListIndexFlipperName:
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneMiscSpoofName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void momentum_app_scene_misc_spoof_on_exit(void* context) {
|
||||
MomentumApp* app = context;
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ enum TextInputIndex {
|
||||
TextInputResultOk,
|
||||
};
|
||||
|
||||
static void momentum_app_scene_misc_rename_text_input_callback(void* context) {
|
||||
static void momentum_app_scene_misc_spoof_name_text_input_callback(void* context) {
|
||||
MomentumApp* app = context;
|
||||
|
||||
app->save_name = true;
|
||||
@@ -12,8 +12,10 @@ static void momentum_app_scene_misc_rename_text_input_callback(void* context) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk);
|
||||
}
|
||||
|
||||
static bool
|
||||
momentum_app_scene_misc_rename_validator(const char* text, FuriString* error, void* context) {
|
||||
static bool momentum_app_scene_misc_spoof_name_validator(
|
||||
const char* text,
|
||||
FuriString* error,
|
||||
void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
for(; *text; ++text) {
|
||||
@@ -27,19 +29,19 @@ static bool
|
||||
return true;
|
||||
}
|
||||
|
||||
void momentum_app_scene_misc_rename_on_enter(void* context) {
|
||||
void momentum_app_scene_misc_spoof_name_on_enter(void* context) {
|
||||
MomentumApp* app = context;
|
||||
TextInput* text_input = app->text_input;
|
||||
|
||||
text_input_set_header_text(text_input, "Leave empty for default");
|
||||
text_input_set_header_text(text_input, "Leave empty for real name");
|
||||
|
||||
text_input_set_validator(text_input, momentum_app_scene_misc_rename_validator, NULL);
|
||||
text_input_set_validator(text_input, momentum_app_scene_misc_spoof_name_validator, NULL);
|
||||
|
||||
text_input_set_minimum_length(text_input, 0);
|
||||
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
momentum_app_scene_misc_rename_text_input_callback,
|
||||
momentum_app_scene_misc_spoof_name_text_input_callback,
|
||||
app,
|
||||
app->device_name,
|
||||
FURI_HAL_VERSION_ARRAY_NAME_LENGTH,
|
||||
@@ -48,7 +50,7 @@ void momentum_app_scene_misc_rename_on_enter(void* context) {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MomentumAppViewTextInput);
|
||||
}
|
||||
|
||||
bool momentum_app_scene_misc_rename_on_event(void* context, SceneManagerEvent event) {
|
||||
bool momentum_app_scene_misc_spoof_name_on_event(void* context, SceneManagerEvent event) {
|
||||
MomentumApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
@@ -66,7 +68,7 @@ bool momentum_app_scene_misc_rename_on_event(void* context, SceneManagerEvent ev
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void momentum_app_scene_misc_rename_on_exit(void* context) {
|
||||
void momentum_app_scene_misc_spoof_name_on_exit(void* context) {
|
||||
MomentumApp* app = context;
|
||||
text_input_reset(app->text_input);
|
||||
}
|
||||
21
applications/main/nfc/helpers/felica_auth.c
Normal file
21
applications/main/nfc/helpers/felica_auth.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "felica_auth.h"
|
||||
|
||||
FelicaAuthenticationContext* felica_auth_alloc() {
|
||||
FelicaAuthenticationContext* instance = malloc(sizeof(FelicaAuthenticationContext));
|
||||
memset(instance->card_key.data, 0, FELICA_DATA_BLOCK_SIZE);
|
||||
instance->skip_auth = true;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void felica_auth_free(FelicaAuthenticationContext* instance) {
|
||||
furi_assert(instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void felica_auth_reset(FelicaAuthenticationContext* instance) {
|
||||
furi_assert(instance);
|
||||
memset(instance->card_key.data, 0, FELICA_DATA_BLOCK_SIZE);
|
||||
instance->skip_auth = true;
|
||||
instance->auth_status.external = 0;
|
||||
instance->auth_status.internal = 0;
|
||||
}
|
||||
17
applications/main/nfc/helpers/felica_auth.h
Normal file
17
applications/main/nfc/helpers/felica_auth.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/felica/felica.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
FelicaAuthenticationContext* felica_auth_alloc();
|
||||
|
||||
void felica_auth_free(FelicaAuthenticationContext* instance);
|
||||
|
||||
void felica_auth_reset(FelicaAuthenticationContext* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -7,6 +7,11 @@
|
||||
|
||||
#include "../nfc_protocol_support_common.h"
|
||||
#include "../nfc_protocol_support_gui_common.h"
|
||||
#include "../nfc_protocol_support_unlock_helper.h"
|
||||
|
||||
enum {
|
||||
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||
};
|
||||
|
||||
static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
@@ -18,6 +23,35 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(temp_str));
|
||||
|
||||
widget_add_button_element(
|
||||
instance->widget,
|
||||
GuiButtonTypeRight,
|
||||
"More",
|
||||
nfc_protocol_support_common_widget_callback,
|
||||
instance);
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
nfc_render_felica_dump(data, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
|
||||
@@ -29,29 +63,75 @@ static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, v
|
||||
|
||||
NfcApp* instance = context;
|
||||
const FelicaPollerEvent* felica_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(felica_event->type == FelicaPollerEventTypeReady) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||
return NfcCommandStop;
|
||||
command = NfcCommandStop;
|
||||
} else if(
|
||||
felica_event->type == FelicaPollerEventTypeError ||
|
||||
felica_event->type == FelicaPollerEventTypeIncomplete) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(
|
||||
instance->view_dispatcher, NfcCustomEventPollerIncomplete);
|
||||
command = NfcCommandStop;
|
||||
} else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
||||
FelicaAuthenticationContext* ctx = felica_event->data->auth_context;
|
||||
ctx->skip_auth = instance->felica_auth->skip_auth;
|
||||
memcpy(ctx->card_key.data, instance->felica_auth->card_key.data, FELICA_DATA_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
return command;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_felica(NfcApp* instance) {
|
||||
nfc_unlock_helper_setup_from_state(instance);
|
||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance);
|
||||
}
|
||||
|
||||
bool nfc_scene_read_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventCardDetected) {
|
||||
nfc_unlock_helper_card_detected_handler(instance);
|
||||
} else if(event.event == NfcCustomEventPollerIncomplete) {
|
||||
notification_message(instance->notifications, &sequence_semi_success);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
|
||||
if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn)) {
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
} else {
|
||||
bool all_unlocked = data->blocks_read == data->blocks_total;
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
"\e#%s\n",
|
||||
all_unlocked ? "All Blocks Are Unlocked" : "Some Blocks Are Locked");
|
||||
nfc_render_felica_idm(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
uint8_t* ck_data = instance->felica_auth->card_key.data;
|
||||
furi_string_cat_printf(temp_str, "Key:");
|
||||
for(uint8_t i = 0; i < 7; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", ck_data[i]);
|
||||
if(i == 6) furi_string_cat_printf(temp_str, "...");
|
||||
}
|
||||
nfc_render_felica_blocks_count(data, temp_str, false);
|
||||
}
|
||||
felica_auth_reset(instance->felica_auth);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||
@@ -74,23 +154,50 @@ static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) {
|
||||
nfc_listener_start(instance->listener, NULL, NULL);
|
||||
}
|
||||
|
||||
static void nfc_scene_read_menu_on_enter_felica(NfcApp* instance) {
|
||||
const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);
|
||||
if(data->blocks_read != data->blocks_total) {
|
||||
submenu_add_item(
|
||||
instance->submenu,
|
||||
"Unlock",
|
||||
SubmenuIndexUnlock,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
instance);
|
||||
}
|
||||
}
|
||||
|
||||
static bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexUnlock) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaKeyInput);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const NfcProtocolSupportBase nfc_protocol_support_felica = {
|
||||
.features = NfcProtocolFeatureEmulateUid,
|
||||
|
||||
.scene_info =
|
||||
{
|
||||
.on_enter = nfc_scene_info_on_enter_felica,
|
||||
.on_event = nfc_scene_info_on_event_felica,
|
||||
},
|
||||
.scene_more_info =
|
||||
{
|
||||
.on_enter = nfc_scene_more_info_on_enter_felica,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read =
|
||||
{
|
||||
.on_enter = nfc_scene_read_on_enter_felica,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
.on_event = nfc_scene_read_on_event_felica,
|
||||
},
|
||||
.scene_read_menu =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
.on_enter = nfc_scene_read_menu_on_enter_felica,
|
||||
.on_event = nfc_scene_read_menu_on_event_felica,
|
||||
},
|
||||
.scene_read_success =
|
||||
{
|
||||
|
||||
@@ -1,19 +1,107 @@
|
||||
#include "felica_render.h"
|
||||
|
||||
void nfc_render_felica_blocks_count(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
bool render_auth_notification) {
|
||||
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_felica_idm(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str) {
|
||||
furi_string_cat_printf(str, (format_type == NfcProtocolFormatTypeFull) ? "IDm:\n" : "IDm:");
|
||||
|
||||
for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
(format_type == NfcProtocolFormatTypeFull) ? "%02X " : " %02X",
|
||||
data->idm.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_felica_info(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str) {
|
||||
furi_string_cat_printf(str, "IDm:");
|
||||
|
||||
for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {
|
||||
furi_string_cat_printf(str, " %02X", data->idm.data[i]);
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
|
||||
}
|
||||
|
||||
nfc_render_felica_idm(data, format_type, str);
|
||||
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
furi_string_cat_printf(str, "\nPMm:");
|
||||
furi_string_cat_printf(str, "\nPMm:\n");
|
||||
for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) {
|
||||
furi_string_cat_printf(str, " %02X", data->pmm.data[i]);
|
||||
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
|
||||
}
|
||||
}
|
||||
nfc_render_felica_blocks_count(data, str, true);
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block_name(
|
||||
const char* name,
|
||||
FuriString* str,
|
||||
uint8_t prefix_separator_cnt,
|
||||
uint8_t suffix_separator_cnt) {
|
||||
for(uint8_t i = 0; i < prefix_separator_cnt; i++) {
|
||||
furi_string_cat_printf(str, ":");
|
||||
}
|
||||
furi_string_cat_printf(str, "[ %s ]", name);
|
||||
for(uint8_t i = 0; i < suffix_separator_cnt; i++) {
|
||||
furi_string_cat_printf(str, ":");
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
|
||||
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
|
||||
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
|
||||
furi_string_cat_printf(str, "%02X ", block->data[j]);
|
||||
}
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block(
|
||||
const FelicaBlock* block,
|
||||
FuriString* str,
|
||||
const char* name,
|
||||
uint8_t prefix_separator_cnt,
|
||||
uint8_t suffix_separator_cnt) {
|
||||
nfc_render_felica_block_name(name, str, prefix_separator_cnt, suffix_separator_cnt);
|
||||
nfc_render_felica_block_data(block, str);
|
||||
}
|
||||
|
||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
||||
FuriString* name = furi_string_alloc();
|
||||
for(size_t i = 0; i < 14; i++) {
|
||||
furi_string_printf(name, "S_PAD%d", i);
|
||||
uint8_t suf_cnt = 18;
|
||||
if(i == 1) {
|
||||
suf_cnt = 19;
|
||||
} else if((i == 10) || (i == 12) || (i == 13)) {
|
||||
suf_cnt = 16;
|
||||
}
|
||||
nfc_render_felica_block(
|
||||
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
|
||||
}
|
||||
furi_string_free(name);
|
||||
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
|
||||
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
|
||||
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
|
||||
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
|
||||
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,19 @@
|
||||
|
||||
#include "../nfc_protocol_support_render_common.h"
|
||||
|
||||
void nfc_render_felica_blocks_count(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
bool render_auth_notification);
|
||||
|
||||
void nfc_render_felica_info(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
|
||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
|
||||
|
||||
void nfc_render_felica_idm(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
@@ -34,6 +34,8 @@ static void nfc_scene_more_info_on_enter_mf_desfire(NfcApp* instance) {
|
||||
static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolMfDesfire);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
NfcApp* instance = context;
|
||||
const MfDesfirePollerEvent* mf_desfire_event = event.event_data;
|
||||
|
||||
@@ -41,10 +43,12 @@ static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent even
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||
return NfcCommandStop;
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_desfire_event->type == MfDesfirePollerEventTypeReadFailed) {
|
||||
command = NfcCommandReset;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
return command;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_mf_desfire(NfcApp* instance) {
|
||||
|
||||
@@ -11,24 +11,29 @@ void nfc_render_mf_desfire_info(
|
||||
const uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1);
|
||||
const uint32_t bytes_free = data->free_memory.is_present ? data->free_memory.bytes_free : 0;
|
||||
|
||||
if(data->master_key_settings.is_free_directory_list) {
|
||||
const uint32_t app_count = simple_array_get_count(data->applications);
|
||||
uint32_t file_count = 0;
|
||||
|
||||
for(uint32_t i = 0; i < app_count; ++i) {
|
||||
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
|
||||
if(app->key_settings.is_free_directory_list) {
|
||||
file_count += simple_array_get_count(app->file_ids);
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n%lu Application%s", app_count, app_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : "");
|
||||
} else {
|
||||
furi_string_cat_printf(str, "\nAuth required to read apps!");
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n%lu", bytes_total);
|
||||
|
||||
if(data->version.sw_storage & 1) {
|
||||
furi_string_push_back(str, '+');
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, " bytes, %lu bytes free\n", bytes_free);
|
||||
|
||||
const uint32_t app_count = simple_array_get_count(data->applications);
|
||||
uint32_t file_count = 0;
|
||||
|
||||
for(uint32_t i = 0; i < app_count; ++i) {
|
||||
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
|
||||
file_count += simple_array_get_count(app->file_ids);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "%lu Application%s", app_count, app_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, " bytes, %lu bytes free", bytes_free);
|
||||
|
||||
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||
|
||||
@@ -101,17 +106,29 @@ void nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriStri
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str) {
|
||||
furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id);
|
||||
furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable);
|
||||
furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete);
|
||||
furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list);
|
||||
furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable);
|
||||
if(data->is_free_directory_list) {
|
||||
furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id);
|
||||
furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable);
|
||||
furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete);
|
||||
furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list);
|
||||
furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "changeKeyID ??\n");
|
||||
furi_string_cat_printf(str, "configChangeable ??\n");
|
||||
furi_string_cat_printf(str, "freeCreateDelete ??\n");
|
||||
furi_string_cat_printf(str, "freeDirectoryList 0\n");
|
||||
furi_string_cat_printf(str, "masterChangeable ??\n");
|
||||
}
|
||||
|
||||
if(data->flags) {
|
||||
furi_string_cat_printf(str, "flags %d\n", data->flags);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys);
|
||||
if(data->is_free_directory_list) {
|
||||
furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "maxKeys ??\n");
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_key_version(
|
||||
@@ -123,14 +140,16 @@ void nfc_render_mf_desfire_key_version(
|
||||
|
||||
void nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str) {
|
||||
const uint8_t* app_id = data->data;
|
||||
furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[0], app_id[1], app_id[2]);
|
||||
furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[2], app_id[1], app_id[0]);
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str) {
|
||||
nfc_render_mf_desfire_key_settings(&data->key_settings, str);
|
||||
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {
|
||||
nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);
|
||||
if(data->key_settings.is_free_directory_list) {
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {
|
||||
nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,13 +198,16 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "%s %s\n", type, comm);
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"r %d w %d rw %d c %d\n",
|
||||
settings->access_rights >> 12 & 0xF,
|
||||
settings->access_rights >> 8 & 0xF,
|
||||
settings->access_rights >> 4 & 0xF,
|
||||
settings->access_rights & 0xF);
|
||||
|
||||
for(size_t i = 0; i < settings->access_rights_len; i++) {
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"r %d w %d rw %d c %d\n",
|
||||
settings->access_rights[i] >> 12 & 0xF,
|
||||
settings->access_rights[i] >> 8 & 0xF,
|
||||
settings->access_rights[i] >> 4 & 0xF,
|
||||
settings->access_rights[i] & 0xF);
|
||||
}
|
||||
|
||||
uint32_t record_count = 1;
|
||||
uint32_t record_size = 0;
|
||||
@@ -217,6 +239,20 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_auth_required = true;
|
||||
for(size_t i = 0; i < settings->access_rights_len; i++) {
|
||||
uint8_t read_rights = (settings->access_rights[i] >> 12) & 0x0f;
|
||||
uint8_t read_write_rights = (settings->access_rights[i] >> 4) & 0x0f;
|
||||
if((read_rights == 0x0e) || (read_write_rights == 0x0e)) {
|
||||
is_auth_required = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(is_auth_required) {
|
||||
furi_string_cat_printf(str, "Auth required to read file data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(simple_array_get_count(data->data) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "../nfc_protocol_support_common.h"
|
||||
#include "../nfc_protocol_support_gui_common.h"
|
||||
#include "../nfc_protocol_support_unlock_helper.h"
|
||||
|
||||
enum {
|
||||
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||
@@ -157,48 +158,15 @@ static NfcCommand
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
enum {
|
||||
NfcSceneMfUltralightReadMenuStateCardSearch,
|
||||
NfcSceneMfUltralightReadMenuStateCardFound,
|
||||
};
|
||||
|
||||
static void nfc_scene_read_setup_view(NfcApp* instance) {
|
||||
Popup* popup = instance->popup;
|
||||
popup_reset(popup);
|
||||
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);
|
||||
|
||||
if(state == NfcSceneMfUltralightReadMenuStateCardSearch) {
|
||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
instance->popup, "Hold card next\nto Flipper's back", 94, 27, AlignCenter, AlignTop);
|
||||
} else {
|
||||
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
|
||||
popup_set_icon(instance->popup, 12, 20, &A_Loading_24);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
bool unlocking =
|
||||
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn);
|
||||
|
||||
uint32_t state = unlocking ? NfcSceneMfUltralightReadMenuStateCardSearch :
|
||||
NfcSceneMfUltralightReadMenuStateCardFound;
|
||||
|
||||
scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);
|
||||
|
||||
nfc_scene_read_setup_view(instance);
|
||||
nfc_unlock_helper_setup_from_state(instance);
|
||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance);
|
||||
}
|
||||
|
||||
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventCardDetected) {
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound);
|
||||
nfc_scene_read_setup_view(instance);
|
||||
nfc_unlock_helper_card_detected_handler(instance);
|
||||
} else if((event.event == NfcCustomEventPollerIncomplete)) {
|
||||
notification_message(instance->notifications, &sequence_semi_success);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||
|
||||
@@ -574,7 +574,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||
|
||||
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64);
|
||||
widget_add_icon_element(widget, 0, 0, &I_NFC_dolphin_emulation_51x64);
|
||||
|
||||
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) {
|
||||
widget_add_string_element(
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "nfc_protocol_support_unlock_helper.h"
|
||||
|
||||
static void nfc_scene_read_setup_view(NfcApp* instance) {
|
||||
Popup* popup = instance->popup;
|
||||
popup_reset(popup);
|
||||
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);
|
||||
|
||||
if(state == NfcSceneReadMenuStateCardSearch) {
|
||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
instance->popup, "Hold card next\nto Flipper's back", 94, 27, AlignCenter, AlignTop);
|
||||
} else {
|
||||
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
|
||||
popup_set_icon(instance->popup, 12, 20, &A_Loading_24);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
}
|
||||
|
||||
void nfc_unlock_helper_setup_from_state(NfcApp* instance) {
|
||||
bool unlocking =
|
||||
scene_manager_has_previous_scene(
|
||||
instance->scene_manager, NfcSceneMfUltralightUnlockWarn) ||
|
||||
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn);
|
||||
|
||||
uint32_t state = unlocking ? NfcSceneReadMenuStateCardSearch : NfcSceneReadMenuStateCardFound;
|
||||
|
||||
scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);
|
||||
|
||||
nfc_scene_read_setup_view(instance);
|
||||
}
|
||||
|
||||
void nfc_unlock_helper_card_detected_handler(NfcApp* instance) {
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneRead, NfcSceneReadMenuStateCardFound);
|
||||
nfc_scene_read_setup_view(instance);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "nfc/nfc_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
NfcSceneReadMenuStateCardSearch,
|
||||
NfcSceneReadMenuStateCardFound,
|
||||
} NfcSceneUnlockReadState;
|
||||
|
||||
void nfc_unlock_helper_setup_from_state(NfcApp* instance);
|
||||
void nfc_unlock_helper_card_detected_handler(NfcApp* instance);
|
||||
@@ -51,6 +51,7 @@ NfcApp* nfc_app_alloc(void) {
|
||||
|
||||
instance->nfc = nfc_alloc();
|
||||
|
||||
instance->felica_auth = felica_auth_alloc();
|
||||
instance->mf_ul_auth = mf_ultralight_auth_alloc();
|
||||
instance->slix_unlock = slix_unlock_alloc();
|
||||
instance->mfc_key_cache = mf_classic_key_cache_alloc();
|
||||
@@ -142,6 +143,7 @@ void nfc_app_free(NfcApp* instance) {
|
||||
|
||||
nfc_free(instance->nfc);
|
||||
|
||||
felica_auth_free(instance->felica_auth);
|
||||
mf_ultralight_auth_free(instance->mf_ul_auth);
|
||||
slix_unlock_free(instance->slix_unlock);
|
||||
mf_classic_key_cache_free(instance->mfc_key_cache);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "helpers/nfc_emv_parser.h"
|
||||
#include "helpers/mf_classic_key_cache.h"
|
||||
#include "helpers/nfc_supported_cards.h"
|
||||
#include "helpers/felica_auth.h"
|
||||
#include "helpers/slix_unlock.h"
|
||||
|
||||
#include <dialogs/dialogs.h>
|
||||
@@ -130,6 +131,7 @@ struct NfcApp {
|
||||
NfcScanner* scanner;
|
||||
NfcListener* listener;
|
||||
|
||||
FelicaAuthenticationContext* felica_auth;
|
||||
MfUltralightAuth* mf_ul_auth;
|
||||
SlixUnlock* slix_unlock;
|
||||
NfcMfClassicDictAttackContext nfc_dict_context;
|
||||
|
||||
@@ -20,8 +20,8 @@ static AllInOneLayoutType all_in_one_get_layout(const MfUltralightData* data) {
|
||||
const uint8_t layout_byte = data->page[5].data[2];
|
||||
const uint8_t layout_half_byte = data->page[5].data[2] & 0x0F;
|
||||
|
||||
FURI_LOG_I(TAG, "Layout byte: %02x", layout_byte);
|
||||
FURI_LOG_I(TAG, "Layout half-byte: %02x", layout_half_byte);
|
||||
FURI_LOG_D(TAG, "Layout byte: %02x", layout_byte);
|
||||
FURI_LOG_D(TAG, "Layout half-byte: %02x", layout_half_byte);
|
||||
|
||||
switch(layout_half_byte) {
|
||||
// If it is A, the layout type is a type A layout
|
||||
@@ -32,7 +32,7 @@ static AllInOneLayoutType all_in_one_get_layout(const MfUltralightData* data) {
|
||||
case 0x02:
|
||||
return AllInOneLayoutType2;
|
||||
default:
|
||||
FURI_LOG_I(TAG, "Unknown layout type: %d", layout_half_byte);
|
||||
FURI_LOG_E(TAG, "Unknown layout type: %d", layout_half_byte);
|
||||
return AllInOneLayoutTypeUnknown;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ static bool all_in_one_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
|
||||
do {
|
||||
if(data->page[4].data[0] != 0x45 || data->page[4].data[1] != 0xD9) {
|
||||
FURI_LOG_I(TAG, "Pass not verified");
|
||||
FURI_LOG_E(TAG, "Pass not verified");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ static bool all_in_one_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
// If the layout is D, the ride count is stored in the second byte of page 9
|
||||
ride_count = data->page[9].data[1];
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "Unknown layout: %d", layout_type);
|
||||
FURI_LOG_E(TAG, "Unknown layout: %d", layout_type);
|
||||
ride_count = 137;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -82,7 +82,7 @@ void from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t sta
|
||||
bool parse_transport_block(const MfClassicBlock* block, FuriString* result) {
|
||||
uint16_t transport_departament = bit_lib_get_bits_16(block->data, 0, 10);
|
||||
|
||||
FURI_LOG_I(TAG, "Transport departament: %x", transport_departament);
|
||||
FURI_LOG_D(TAG, "Transport departament: %x", transport_departament);
|
||||
|
||||
uint16_t layout_type = bit_lib_get_bits_16(block->data, 52, 4);
|
||||
if(layout_type == 0xE) {
|
||||
@@ -91,7 +91,7 @@ bool parse_transport_block(const MfClassicBlock* block, FuriString* result) {
|
||||
layout_type = bit_lib_get_bits_16(block->data, 52, 14);
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Layout type %x", layout_type);
|
||||
FURI_LOG_D(TAG, "Layout type %x", layout_type);
|
||||
|
||||
uint16_t card_view = 0;
|
||||
uint16_t card_type = 0;
|
||||
|
||||
@@ -33,6 +33,8 @@ ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
||||
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
||||
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
||||
ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass)
|
||||
ADD_SCENE(nfc, felica_key_input, FelicaKeyInput)
|
||||
ADD_SCENE(nfc, felica_unlock_warn, FelicaUnlockWarn)
|
||||
|
||||
ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo)
|
||||
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
|
||||
|
||||
46
applications/main/nfc/scenes/nfc_scene_felica_key_input.c
Normal file
46
applications/main/nfc/scenes/nfc_scene_felica_key_input.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
void nfc_scene_felica_key_input_byte_input_callback(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
|
||||
}
|
||||
|
||||
void nfc_scene_felica_key_input_on_enter(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = nfc->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter key in hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
nfc_scene_felica_key_input_byte_input_callback,
|
||||
NULL,
|
||||
nfc,
|
||||
nfc->felica_auth->card_key.data,
|
||||
FELICA_DATA_BLOCK_SIZE);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
|
||||
}
|
||||
|
||||
bool nfc_scene_felica_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* nfc = context;
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventByteInputDone) {
|
||||
nfc->felica_auth->skip_auth = false;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockWarn);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_felica_key_input_on_exit(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
// Clear view
|
||||
byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(nfc->byte_input, "");
|
||||
}
|
||||
59
applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c
Normal file
59
applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
void nfc_scene_felica_unlock_warn_dialog_callback(DialogExResult result, void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void nfc_scene_felica_unlock_warn_on_enter(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
const char* message = "Risky Action!";
|
||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||
dialog_ex_set_context(dialog_ex, nfc);
|
||||
dialog_ex_set_result_callback(dialog_ex, nfc_scene_felica_unlock_warn_dialog_callback);
|
||||
|
||||
dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop);
|
||||
|
||||
FuriString* str = furi_string_alloc();
|
||||
furi_string_cat_printf(str, "Unlock with key: ");
|
||||
for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++)
|
||||
furi_string_cat_printf(str, "%02X ", nfc->felica_auth->card_key.data[i]);
|
||||
furi_string_cat_printf(str, "?");
|
||||
|
||||
nfc_text_store_set(nfc, furi_string_get_cstr(str));
|
||||
furi_string_free(str);
|
||||
|
||||
dialog_ex_set_text(dialog_ex, nfc->text_store, 0, 12, AlignLeft, AlignTop);
|
||||
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Unlock");
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
|
||||
}
|
||||
|
||||
bool nfc_scene_felica_unlock_warn_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* nfc = context;
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultRight) {
|
||||
nfc->felica_auth->skip_auth = false;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExResultLeft) {
|
||||
scene_manager_previous_scene(nfc->scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_felica_unlock_warn_on_exit(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
dialog_ex_reset(nfc->dialog_ex);
|
||||
nfc_text_store_clear(nfc);
|
||||
}
|
||||
@@ -35,7 +35,7 @@ void nfc_scene_mf_desfire_more_info_on_enter(void* context) {
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {
|
||||
const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);
|
||||
furi_string_printf(
|
||||
label, "App %02x%02x%02x", app_id->data[0], app_id->data[1], app_id->data[2]);
|
||||
label, "App %02x%02x%02x", app_id->data[2], app_id->data[1], app_id->data[0]);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
furi_string_get_cstr(label),
|
||||
|
||||
@@ -60,9 +60,6 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e
|
||||
subghz_frequency_analyzer_get_frequency_to_save(subghz->subghz_frequency_analyzer);
|
||||
if(frequency > 0) {
|
||||
subghz->last_settings->frequency = frequency;
|
||||
#ifdef FURI_DEBUG
|
||||
subghz_last_settings_log(subghz->last_settings);
|
||||
#endif
|
||||
// Disable Hopping before opening the receiver scene!
|
||||
if(subghz->last_settings->enable_hopping) {
|
||||
subghz->last_settings->enable_hopping = false;
|
||||
@@ -73,9 +70,6 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e
|
||||
return true;
|
||||
} else if(event.event == SubGhzCustomEventViewFreqAnalOkLong) {
|
||||
// Don't need to save, we already saved on short event
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_W(TAG, "Goto next scene!");
|
||||
#endif
|
||||
//scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneStart, 10);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver);
|
||||
return true;
|
||||
|
||||
@@ -20,12 +20,6 @@ const char* const timestamp_names_text[TIMESTAMP_NAMES_COUNT] = {
|
||||
"ON",
|
||||
};
|
||||
|
||||
#define EXT_MOD_POWER_AMP_COUNT 2
|
||||
const char* const ext_mod_power_amp_text[EXT_MOD_POWER_AMP_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
|
||||
#define DEBUG_P_COUNT 2
|
||||
const char* const debug_pin_text[DEBUG_P_COUNT] = {
|
||||
"OFF",
|
||||
@@ -104,27 +98,6 @@ static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) {
|
||||
furi_hal_subghz_set_rolling_counter_mult(debug_counter_val[index]);
|
||||
}
|
||||
|
||||
static void subghz_scene_reciever_config_set_ext_mod_power_amp_text(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, ext_mod_power_amp_text[index]);
|
||||
|
||||
subghz->last_settings->external_module_power_amp = index == 1;
|
||||
|
||||
// Set globally in furi hal
|
||||
furi_hal_subghz_set_ext_power_amp(subghz->last_settings->external_module_power_amp);
|
||||
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
|
||||
// reinit external device
|
||||
const SubGhzRadioDeviceType current = subghz_txrx_radio_device_get(subghz->txrx);
|
||||
if(current != SubGhzRadioDeviceTypeInternal) {
|
||||
subghz_txrx_radio_device_set(subghz->txrx, SubGhzRadioDeviceTypeInternal);
|
||||
subghz_txrx_radio_device_set(subghz->txrx, current);
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_gps(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
@@ -194,16 +167,6 @@ void subghz_scene_radio_settings_on_enter(void* context) {
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, radio_device_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
variable_item_list,
|
||||
"Ext Power Amp",
|
||||
EXT_MOD_POWER_AMP_COUNT,
|
||||
subghz_scene_reciever_config_set_ext_mod_power_amp_text,
|
||||
subghz);
|
||||
value_index = subghz->last_settings->external_module_power_amp ? 1 : 0;
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, ext_mod_power_amp_text[value_index]);
|
||||
|
||||
item = variable_item_list_add(
|
||||
variable_item_list,
|
||||
"GPS Baudrate",
|
||||
|
||||
@@ -104,14 +104,13 @@ void subghz_scene_read_raw_on_enter(void* context) {
|
||||
|
||||
if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) {
|
||||
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE);
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_PRESET
|
||||
|
||||
if(furi_string_empty(file_name)) {
|
||||
subghz_txrx_set_preset_internal(
|
||||
subghz->txrx,
|
||||
subghz->last_settings->frequency,
|
||||
subghz->last_settings->preset_index);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
subghz_scene_read_raw_update_statusbar(subghz);
|
||||
|
||||
|
||||
@@ -250,12 +250,8 @@ void subghz_scene_receiver_on_enter(void* context) {
|
||||
FuriString* item_time = furi_string_alloc();
|
||||
|
||||
if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) {
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_PRESET
|
||||
subghz_txrx_set_preset_internal(
|
||||
subghz->txrx, subghz->last_settings->frequency, subghz->last_settings->preset_index);
|
||||
#else
|
||||
subghz_txrx_set_default_preset(subghz->txrx, subghz->last_settings->frequency);
|
||||
#endif
|
||||
|
||||
subghz->filter = subghz->last_settings->filter;
|
||||
subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter);
|
||||
|
||||
@@ -410,14 +410,11 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context,
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneSettingLock);
|
||||
} else if(index == SubGhzSettingIndexResetToDefault) {
|
||||
// Reset all values to default state!
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_PRESET
|
||||
subghz_txrx_set_preset_internal(
|
||||
subghz->txrx,
|
||||
SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY,
|
||||
SUBGHZ_LAST_SETTING_DEFAULT_PRESET);
|
||||
#else
|
||||
subghz_txrx_set_default_preset(subghz->txrx, SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY);
|
||||
#endif
|
||||
|
||||
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
|
||||
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
|
||||
const char* preset_name = furi_string_get_cstr(preset.name);
|
||||
@@ -449,9 +446,7 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context,
|
||||
|
||||
variable_item_list_set_selected_item(subghz->variable_item_list, default_index);
|
||||
variable_item_list_reset(subghz->variable_item_list);
|
||||
#ifdef FURI_DEBUG
|
||||
subghz_last_settings_log(subghz->last_settings);
|
||||
#endif
|
||||
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
@@ -745,9 +740,7 @@ void subghz_scene_receiver_config_on_exit(void* context) {
|
||||
|
||||
variable_item_list_set_selected_item(subghz->variable_item_list, 0);
|
||||
variable_item_list_reset(subghz->variable_item_list);
|
||||
#ifdef FURI_DEBUG
|
||||
subghz_last_settings_log(subghz->last_settings);
|
||||
#endif
|
||||
|
||||
subghz_last_settings_save(subghz->last_settings);
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);
|
||||
|
||||
@@ -18,7 +18,7 @@ void subghz_scene_set_cnt_on_enter(void* context) {
|
||||
|
||||
switch(state) {
|
||||
case SetTypeBFTClone:
|
||||
byte_input_set_header_text(byte_input, "Enter COUNTER in Hex");
|
||||
byte_input_set_header_text(byte_input, "Enter COUNTER in hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
subghz_scene_set_cnt_byte_input_callback,
|
||||
@@ -29,7 +29,7 @@ void subghz_scene_set_cnt_on_enter(void* context) {
|
||||
break;
|
||||
case SetTypeFaacSLH_Manual_433:
|
||||
case SetTypeFaacSLH_Manual_868:
|
||||
byte_input_set_header_text(byte_input, "Enter COUNTER in Hex 20 bits");
|
||||
byte_input_set_header_text(byte_input, "Enter COUNTER in hex 20 bits");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
subghz_scene_set_cnt_byte_input_callback,
|
||||
|
||||
@@ -13,7 +13,7 @@ void subghz_scene_set_fix_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = subghz->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter FIX in Hex");
|
||||
byte_input_set_header_text(byte_input, "Enter FIX in hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
subghz_scene_set_fix_byte_input_callback,
|
||||
|
||||
@@ -14,7 +14,7 @@ void subghz_scene_set_seed_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = subghz->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter SEED in Hex");
|
||||
byte_input_set_header_text(byte_input, "Enter SEED in hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
subghz_scene_set_seed_byte_input_callback,
|
||||
|
||||
@@ -445,8 +445,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
.type = GenKeeloq,
|
||||
.mod = "FM476",
|
||||
.freq = 434420000,
|
||||
.keeloq.serial = key & 0x0FFFFFFF,
|
||||
.keeloq.btn = 0x04,
|
||||
.keeloq.serial = (key & 0x0000FFFF) | 0x07150000,
|
||||
.keeloq.btn = 0x02,
|
||||
.keeloq.cnt = 0x03,
|
||||
.keeloq.manuf = "Sommer(fsk476)"};
|
||||
break;
|
||||
@@ -455,8 +455,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
|
||||
.type = GenKeeloq,
|
||||
.mod = "FM476",
|
||||
.freq = 868800000,
|
||||
.keeloq.serial = key & 0x0FFFFFFF,
|
||||
.keeloq.btn = 0x04,
|
||||
.keeloq.serial = (key & 0x0000FFFF) | 0x07150000,
|
||||
.keeloq.btn = 0x02,
|
||||
.keeloq.cnt = 0x03,
|
||||
.keeloq.manuf = "Sommer(fsk476)"};
|
||||
break;
|
||||
|
||||
@@ -207,16 +207,9 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
|
||||
subghz->last_settings = subghz_last_settings_alloc();
|
||||
size_t preset_count = subghz_setting_get_preset_count(setting);
|
||||
subghz_last_settings_load(subghz->last_settings, preset_count);
|
||||
#ifdef FURI_DEBUG
|
||||
subghz_last_settings_log(subghz->last_settings);
|
||||
#endif
|
||||
if(!alloc_for_tx_only) {
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_PRESET
|
||||
subghz_txrx_set_preset_internal(
|
||||
subghz->txrx, subghz->last_settings->frequency, subghz->last_settings->preset_index);
|
||||
#else
|
||||
subghz_txrx_set_default_preset(subghz->txrx, subghz->last_settings->frequency);
|
||||
#endif
|
||||
subghz->history = subghz_history_alloc();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,5 @@ void subghz_extended_freq() {
|
||||
SubGhzLastSettings* last_settings = subghz_last_settings_alloc();
|
||||
subghz_last_settings_load(last_settings, 0);
|
||||
|
||||
// Set globally in furi hal
|
||||
furi_hal_subghz_set_ext_power_amp(last_settings->external_module_power_amp);
|
||||
|
||||
subghz_last_settings_free(last_settings);
|
||||
}
|
||||
|
||||
@@ -4,26 +4,24 @@
|
||||
#define TAG "SubGhzLastSettings"
|
||||
|
||||
#define SUBGHZ_LAST_SETTING_FILE_TYPE "Flipper SubGhz Last Setting File"
|
||||
#define SUBGHZ_LAST_SETTING_FILE_VERSION 1
|
||||
#define SUBGHZ_LAST_SETTING_FILE_VERSION 3
|
||||
#define SUBGHZ_LAST_SETTINGS_PATH EXT_PATH("subghz/assets/last_subghz.settings")
|
||||
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY "Frequency"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset" // AKA Modulation
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER "ExtPower"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES "TimestampNames"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP "ExtPowerAmp"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_GPS "Gps"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES "ProtocolNames"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES "RemoveDuplicates"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD "DelOldSignals"
|
||||
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_GPS_BAUDRATE "GpsBaudrate"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES "RemoveDuplicates"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_REPEATER "Repeater"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND "Sound"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD "DelOldSignals"
|
||||
#define SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE "Autosave"
|
||||
|
||||
SubGhzLastSettings* subghz_last_settings_alloc(void) {
|
||||
@@ -39,202 +37,145 @@ void subghz_last_settings_free(SubGhzLastSettings* instance) {
|
||||
void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count) {
|
||||
furi_assert(instance);
|
||||
|
||||
// Default values (all others set to 0, if read from file fails these are used)
|
||||
instance->frequency = SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY;
|
||||
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
instance->frequency_analyzer_feedback_level =
|
||||
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL;
|
||||
instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
|
||||
// See bin_raw_value in scenes/subghz_scene_receiver_config.c
|
||||
instance->filter = SubGhzProtocolFlag_Decodable;
|
||||
instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
|
||||
uint32_t temp_frequency = 0;
|
||||
uint32_t temp_frequency_analyzer_feedback_level = 0;
|
||||
float temp_frequency_analyzer_trigger = 0;
|
||||
bool temp_external_module_enabled = false;
|
||||
bool temp_external_module_power_5v_disable = false;
|
||||
bool temp_external_module_power_amp = false;
|
||||
bool temp_protocol_file_names = false;
|
||||
bool temp_enable_hopping = false;
|
||||
bool temp_enable_sound = false;
|
||||
uint32_t temp_repeater_state;
|
||||
bool temp_remove_duplicates = false;
|
||||
bool temp_delete_old_sig = false;
|
||||
bool temp_autosave = false;
|
||||
uint32_t temp_ignore_filter = 0;
|
||||
uint32_t temp_filter = 0;
|
||||
float temp_rssi = 0;
|
||||
uint32_t temp_preset = 0;
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
uint32_t config_version = 0;
|
||||
|
||||
bool preset_was_read = false;
|
||||
bool rssi_was_read = false;
|
||||
bool filter_was_read = false;
|
||||
bool ignore_filter_was_read = false;
|
||||
bool remove_duplicates_was_read = false;
|
||||
bool frequency_analyzer_feedback_level_was_read = false;
|
||||
bool frequency_analyzer_trigger_was_read = false;
|
||||
bool repeater_was_read = false;
|
||||
bool enable_sound_was_read = false;
|
||||
|
||||
uint32_t temp_gps_baudrate = 0;
|
||||
|
||||
if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH &&
|
||||
if(FSE_OK == storage_sd_status(storage) &&
|
||||
flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) {
|
||||
preset_was_read = flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_PRESET, (uint32_t*)&temp_preset, 1);
|
||||
flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, (uint32_t*)&temp_frequency, 1);
|
||||
frequency_analyzer_feedback_level_was_read = flipper_format_read_uint32(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL,
|
||||
(uint32_t*)&temp_frequency_analyzer_feedback_level,
|
||||
1);
|
||||
frequency_analyzer_trigger_was_read = flipper_format_read_float(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER,
|
||||
(float*)&temp_frequency_analyzer_trigger,
|
||||
1);
|
||||
flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED,
|
||||
(bool*)&temp_external_module_enabled,
|
||||
1);
|
||||
flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER,
|
||||
(bool*)&temp_external_module_power_5v_disable,
|
||||
1);
|
||||
flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES,
|
||||
(bool*)&temp_protocol_file_names,
|
||||
1);
|
||||
flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP,
|
||||
(bool*)&temp_external_module_power_amp,
|
||||
1);
|
||||
flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_GPS, (uint32_t*)&temp_gps_baudrate, 1);
|
||||
flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE,
|
||||
(bool*)&temp_enable_hopping,
|
||||
1);
|
||||
rssi_was_read = flipper_format_read_float(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, (float*)&temp_rssi, 1);
|
||||
remove_duplicates_was_read = flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES,
|
||||
(bool*)&temp_remove_duplicates,
|
||||
1);
|
||||
ignore_filter_was_read = flipper_format_read_uint32(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER,
|
||||
(uint32_t*)&temp_ignore_filter,
|
||||
1);
|
||||
filter_was_read = flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, (uint32_t*)&temp_filter, 1);
|
||||
repeater_was_read = flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, (uint32_t*)&temp_repeater_state, 1);
|
||||
enable_sound_was_read = flipper_format_read_bool(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, (bool*)&temp_enable_sound, 1);
|
||||
flipper_format_read_bool(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, (bool*)&temp_delete_old_sig, 1);
|
||||
flipper_format_read_bool(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE, (bool*)&temp_autosave, 1);
|
||||
do {
|
||||
if(!flipper_format_read_header(fff_data_file, temp_str, &config_version)) break;
|
||||
if((strcmp(furi_string_get_cstr(temp_str), SUBGHZ_LAST_SETTING_FILE_TYPE) != 0) ||
|
||||
(config_version != SUBGHZ_LAST_SETTING_FILE_VERSION)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, &instance->frequency, 1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_PRESET, &instance->preset_index, 1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL,
|
||||
&instance->frequency_analyzer_feedback_level,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_float(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER,
|
||||
&instance->frequency_analyzer_trigger,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES,
|
||||
&instance->protocol_file_names,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE,
|
||||
&instance->enable_hopping,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER,
|
||||
&instance->ignore_filter,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_float(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, &instance->rssi, 1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD,
|
||||
&instance->delete_old_signals,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_GPS_BAUDRATE,
|
||||
&instance->gps_baudrate,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES,
|
||||
&instance->remove_duplicates,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_uint32(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_REPEATER,
|
||||
&instance->repeater_state,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_bool(
|
||||
fff_data_file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND,
|
||||
&instance->enable_sound,
|
||||
1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
if(!flipper_format_read_bool(
|
||||
fff_data_file, SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE, &instance->autosave, 1)) {
|
||||
flipper_format_rewind(fff_data_file);
|
||||
}
|
||||
} while(0);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH);
|
||||
}
|
||||
|
||||
if(temp_frequency == 0 || !furi_hal_subghz_is_tx_allowed(temp_frequency)) {
|
||||
FURI_LOG_W(TAG, "Last used frequency not found or can't be used!");
|
||||
|
||||
instance->frequency = SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY;
|
||||
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
instance->frequency_analyzer_feedback_level =
|
||||
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL;
|
||||
instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
|
||||
instance->external_module_enabled = false;
|
||||
instance->protocol_file_names = false;
|
||||
instance->external_module_power_amp = false;
|
||||
instance->gps_baudrate = 0;
|
||||
instance->enable_hopping = false;
|
||||
instance->remove_duplicates = false;
|
||||
instance->repeater_state = 0;
|
||||
instance->enable_sound = 0;
|
||||
instance->delete_old_signals = false;
|
||||
instance->autosave = false;
|
||||
instance->ignore_filter = 0x00;
|
||||
// See bin_raw_value in applications/main/subghz/scenes/subghz_scene_receiver_config.c
|
||||
instance->filter = SubGhzProtocolFlag_Decodable;
|
||||
instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN;
|
||||
} else {
|
||||
instance->frequency = temp_frequency;
|
||||
instance->frequency_analyzer_feedback_level =
|
||||
frequency_analyzer_feedback_level_was_read ?
|
||||
temp_frequency_analyzer_feedback_level :
|
||||
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL;
|
||||
|
||||
instance->frequency_analyzer_trigger = frequency_analyzer_trigger_was_read ?
|
||||
temp_frequency_analyzer_trigger :
|
||||
SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER;
|
||||
|
||||
if(!preset_was_read) {
|
||||
FURI_LOG_W(TAG, "Preset was not read. Set default");
|
||||
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
} else if(temp_preset > (uint32_t)preset_count - 1) {
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"Last used preset out of range. Preset to set: %ld, Max index: %ld. Set default",
|
||||
temp_preset,
|
||||
(uint32_t)preset_count - 1);
|
||||
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
} else {
|
||||
instance->preset_index = temp_preset;
|
||||
}
|
||||
instance->external_module_enabled = temp_external_module_enabled;
|
||||
|
||||
instance->external_module_power_5v_disable = temp_external_module_power_5v_disable;
|
||||
|
||||
instance->protocol_file_names = temp_protocol_file_names;
|
||||
|
||||
instance->delete_old_signals = temp_delete_old_sig;
|
||||
|
||||
instance->autosave = temp_autosave;
|
||||
|
||||
// External power amp CC1101
|
||||
instance->external_module_power_amp = temp_external_module_power_amp;
|
||||
|
||||
instance->rssi = rssi_was_read ? temp_rssi : SUBGHZ_RAW_THRESHOLD_MIN;
|
||||
instance->enable_hopping = temp_enable_hopping;
|
||||
instance->repeater_state = repeater_was_read ? temp_repeater_state : 0;
|
||||
instance->enable_sound = enable_sound_was_read ? temp_enable_sound : false;
|
||||
instance->remove_duplicates = remove_duplicates_was_read ? temp_remove_duplicates : false;
|
||||
instance->ignore_filter = ignore_filter_was_read ? temp_ignore_filter : 0x00;
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW
|
||||
instance->filter = filter_was_read ? temp_filter : SubGhzProtocolFlag_Decodable;
|
||||
#else
|
||||
if(filter_was_read) {
|
||||
instance->filter = temp_filter != SubGhzProtocolFlag_Decodable ?
|
||||
SubGhzProtocolFlag_Decodable :
|
||||
temp_filter;
|
||||
} else {
|
||||
instance->filter = SubGhzProtocolFlag_Decodable;
|
||||
}
|
||||
#endif
|
||||
// Set globally in furi hal
|
||||
furi_hal_subghz_set_ext_power_amp(instance->external_module_power_amp);
|
||||
|
||||
instance->gps_baudrate = temp_gps_baudrate;
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
|
||||
flipper_format_file_close(fff_data_file);
|
||||
flipper_format_free(fff_data_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
if(instance->frequency == 0 || !furi_hal_subghz_is_tx_allowed(instance->frequency)) {
|
||||
instance->frequency = SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY;
|
||||
}
|
||||
|
||||
if(instance->preset_index > (uint32_t)preset_count - 1) {
|
||||
instance->preset_index = SUBGHZ_LAST_SETTING_DEFAULT_PRESET;
|
||||
}
|
||||
}
|
||||
|
||||
bool subghz_last_settings_save(SubGhzLastSettings* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
#if SUBGHZ_LAST_SETTING_SAVE_BIN_RAW != true
|
||||
instance->filter = SubGhzProtocolFlag_Decodable;
|
||||
#endif
|
||||
bool saved = false;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
@@ -251,96 +192,76 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
|
||||
if(!flipper_format_write_header_cstr(
|
||||
file, SUBGHZ_LAST_SETTING_FILE_TYPE, SUBGHZ_LAST_SETTING_FILE_VERSION))
|
||||
break;
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_PRESET, &instance->preset_index, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
if(!flipper_format_write_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_FREQUENCY, &instance->frequency, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
if(!flipper_format_write_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_PRESET, &instance->preset_index, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_write_uint32(
|
||||
file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL,
|
||||
&instance->frequency_analyzer_feedback_level,
|
||||
1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_float(
|
||||
if(!flipper_format_write_float(
|
||||
file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER,
|
||||
&instance->frequency_analyzer_trigger,
|
||||
1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED,
|
||||
&instance->external_module_enabled,
|
||||
1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER,
|
||||
&instance->external_module_power_5v_disable,
|
||||
1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
if(!flipper_format_write_bool(
|
||||
file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES,
|
||||
&instance->protocol_file_names,
|
||||
1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP,
|
||||
&instance->external_module_power_amp,
|
||||
1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_GPS, &instance->gps_baudrate, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
if(!flipper_format_write_bool(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE, &instance->enable_hopping, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_float(
|
||||
if(!flipper_format_write_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER, &instance->ignore_filter, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_write_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_write_float(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD, &instance->rssi, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
if(!flipper_format_write_bool(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, &instance->delete_old_signals, 1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_GPS_BAUDRATE, &instance->gps_baudrate, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_write_bool(
|
||||
file,
|
||||
SUBGHZ_LAST_SETTING_FIELD_REMOVE_DUPLICATES,
|
||||
&instance->remove_duplicates,
|
||||
1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER, &instance->ignore_filter, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_FILTER, &instance->filter, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_uint32(
|
||||
if(!flipper_format_write_uint32(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_REPEATER, &instance->repeater_state, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
if(!flipper_format_write_bool(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_ENABLE_SOUND, &instance->enable_sound, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_DELETE_OLD, &instance->delete_old_signals, 1)) {
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_insert_or_update_bool(
|
||||
if(!flipper_format_write_bool(
|
||||
file, SUBGHZ_LAST_SETTING_FIELD_AUTOSAVE, &instance->autosave, 1)) {
|
||||
break;
|
||||
}
|
||||
@@ -357,54 +278,3 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
const char* LOG_ON = "ON";
|
||||
const char* LOG_OFF = "OFF";
|
||||
|
||||
static inline const char*
|
||||
subghz_last_settings_log_filter_get_index(uint32_t filter, uint32_t flag) {
|
||||
return READ_BIT(filter, flag) > 0 ? LOG_ON : LOG_OFF;
|
||||
}
|
||||
|
||||
static inline const char* bool_to_char(bool value) {
|
||||
return value ? LOG_ON : LOG_OFF;
|
||||
}
|
||||
|
||||
void subghz_last_settings_log(SubGhzLastSettings* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Frequency: %03ld.%02ld, FeedbackLevel: %ld, FATrigger: %.2f, External: %s, ExtPower: %s, TimestampNames: %s, ExtPowerAmp: %s,\n"
|
||||
"GPSBaudrate: %ld, Hopping: %s,\nPreset: %ld, RSSI: %.2f, "
|
||||
"BinRAW: %s, Repeater: %lu, Duplicates: %s, Autosave: %s, Starline: %s, Cars: %s, Magellan: %s, NiceFloR-S: %s, Weather: %s, TPMS: %s, Sound: %s",
|
||||
instance->frequency / 1000000 % 1000,
|
||||
instance->frequency / 10000 % 100,
|
||||
instance->frequency_analyzer_feedback_level,
|
||||
(double)instance->frequency_analyzer_trigger,
|
||||
bool_to_char(instance->external_module_enabled),
|
||||
bool_to_char(instance->external_module_power_5v_disable),
|
||||
bool_to_char(instance->protocol_file_names),
|
||||
bool_to_char(instance->external_module_power_amp),
|
||||
instance->gps_baudrate,
|
||||
bool_to_char(instance->enable_hopping),
|
||||
instance->preset_index,
|
||||
(double)instance->rssi,
|
||||
subghz_last_settings_log_filter_get_index(instance->filter, SubGhzProtocolFlag_BinRAW),
|
||||
instance->repeater_state,
|
||||
bool_to_char(instance->remove_duplicates),
|
||||
bool_to_char(instance->autosave),
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFilter_StarLine),
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFilter_AutoAlarms),
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFilter_Magellan),
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFilter_NiceFlorS),
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFilter_Weather),
|
||||
subghz_last_settings_log_filter_get_index(
|
||||
instance->ignore_filter, SubGhzProtocolFilter_TPMS),
|
||||
bool_to_char(instance->enable_sound));
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include <lib/subghz/types.h>
|
||||
|
||||
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER (-93.0f)
|
||||
#define SUBGHZ_LAST_SETTING_SAVE_BIN_RAW true
|
||||
#define SUBGHZ_LAST_SETTING_SAVE_PRESET true
|
||||
// 1 = "AM650"
|
||||
// "AM270", "AM650", "FM238", "FM476",
|
||||
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1
|
||||
@@ -20,21 +18,17 @@ typedef struct {
|
||||
uint32_t preset_index; // AKA Modulation
|
||||
uint32_t frequency_analyzer_feedback_level;
|
||||
float frequency_analyzer_trigger;
|
||||
// TODO not using but saved so as not to change the version
|
||||
bool external_module_enabled;
|
||||
bool external_module_power_5v_disable;
|
||||
bool external_module_power_amp;
|
||||
// saved so as not to change the version
|
||||
bool protocol_file_names;
|
||||
uint32_t gps_baudrate;
|
||||
bool enable_hopping;
|
||||
uint32_t repeater_state;
|
||||
bool enable_sound;
|
||||
bool remove_duplicates;
|
||||
uint32_t ignore_filter;
|
||||
uint32_t filter;
|
||||
float rssi;
|
||||
bool delete_old_signals;
|
||||
|
||||
uint32_t gps_baudrate;
|
||||
bool remove_duplicates;
|
||||
uint32_t repeater_state;
|
||||
bool enable_sound;
|
||||
bool autosave;
|
||||
} SubGhzLastSettings;
|
||||
|
||||
@@ -45,5 +39,3 @@ void subghz_last_settings_free(SubGhzLastSettings* instance);
|
||||
void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count);
|
||||
|
||||
bool subghz_last_settings_save(SubGhzLastSettings* instance);
|
||||
|
||||
void subghz_last_settings_log(SubGhzLastSettings* instance);
|
||||
|
||||
@@ -228,9 +228,7 @@ uint32_t subghz_frequency_find_correct(uint32_t input) {
|
||||
uint32_t prev_freq = 0;
|
||||
uint32_t current = 0;
|
||||
uint32_t result = 0;
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "input: %ld", input);
|
||||
#endif
|
||||
|
||||
for(size_t i = 0; i < sizeof(subghz_frequency_list); i++) {
|
||||
current = subghz_frequency_list[i];
|
||||
if(current == input) {
|
||||
@@ -281,7 +279,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
|
||||
break;
|
||||
}
|
||||
subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, trigger_level);
|
||||
FURI_LOG_I(TAG, "trigger = %.1f", (double)trigger_level);
|
||||
FURI_LOG_D(TAG, "trigger = %.1f", (double)trigger_level);
|
||||
need_redraw = true;
|
||||
} else if(event->type == InputTypePress && event->key == InputKeyUp) {
|
||||
if(instance->feedback_level == 0) {
|
||||
@@ -289,9 +287,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
|
||||
} else {
|
||||
instance->feedback_level--;
|
||||
}
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "feedback_level = %d", instance->feedback_level);
|
||||
#endif
|
||||
|
||||
need_redraw = true;
|
||||
} else if(
|
||||
((event->type == InputTypePress) || (event->type == InputTypeRepeat)) &&
|
||||
@@ -324,13 +320,6 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
|
||||
}
|
||||
if(frequency_candidate > 0 &&
|
||||
frequency_candidate != model->frequency_to_save) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"frequency_to_save: %ld, candidate: %ld",
|
||||
model->frequency_to_save,
|
||||
frequency_candidate);
|
||||
#endif
|
||||
model->frequency_to_save = frequency_candidate;
|
||||
updated = true;
|
||||
}
|
||||
@@ -372,24 +361,12 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
|
||||
},
|
||||
true);
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"updated: %d, long: %d, type: %d",
|
||||
updated,
|
||||
(event->type == InputTypeLong),
|
||||
event->type);
|
||||
#endif
|
||||
|
||||
if(updated) {
|
||||
instance->callback(SubGhzCustomEventViewFreqAnalOkShort, instance->context);
|
||||
}
|
||||
|
||||
// First device receive short, then when user release button we get long
|
||||
if(event->type == InputTypeLong && frequency_to_save > 0) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Long press!");
|
||||
#endif
|
||||
// Stop worker
|
||||
if(subghz_frequency_analyzer_worker_is_running(instance->worker)) {
|
||||
subghz_frequency_analyzer_worker_stop(instance->worker);
|
||||
|
||||
@@ -435,8 +435,34 @@ void cli_command_free(Cli* cli, FuriString* args, void* context) {
|
||||
printf("Minimum heap size: %zu\r\n", memmgr_get_minimum_free_heap());
|
||||
printf("Maximum heap block: %zu\r\n", memmgr_heap_get_max_free_block());
|
||||
|
||||
printf("Pool free: %zu\r\n", memmgr_pool_get_free());
|
||||
printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block());
|
||||
printf("Aux pool total free: %zu\r\n", memmgr_aux_pool_get_free());
|
||||
printf("Aux pool max free block: %zu\r\n", memmgr_pool_get_max_block());
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
void* addr;
|
||||
size_t size;
|
||||
} FreeBlockInfo;
|
||||
|
||||
#define FREE_BLOCK_INFO_MAX 128
|
||||
|
||||
typedef struct {
|
||||
FreeBlockInfo free_blocks[FREE_BLOCK_INFO_MAX];
|
||||
size_t free_blocks_count;
|
||||
} FreeBlockContext;
|
||||
|
||||
static bool free_block_walker(void* pointer, size_t size, bool used, void* context) {
|
||||
FreeBlockContext* free_blocks = (FreeBlockContext*)context;
|
||||
if(!used) {
|
||||
if(free_blocks->free_blocks_count < FREE_BLOCK_INFO_MAX) {
|
||||
free_blocks->free_blocks[free_blocks->free_blocks_count].addr = pointer;
|
||||
free_blocks->free_blocks[free_blocks->free_blocks_count].size = size;
|
||||
free_blocks->free_blocks_count++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) {
|
||||
@@ -444,7 +470,23 @@ void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) {
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
|
||||
memmgr_heap_printf_free_blocks();
|
||||
FreeBlockContext* free_blocks = malloc(sizeof(FreeBlockContext));
|
||||
free_blocks->free_blocks_count = 0;
|
||||
|
||||
memmgr_heap_walk_blocks(free_block_walker, free_blocks);
|
||||
|
||||
for(size_t i = 0; i < free_blocks->free_blocks_count; i++) {
|
||||
printf(
|
||||
"A %p S %zu\r\n",
|
||||
(void*)free_blocks->free_blocks[i].addr,
|
||||
free_blocks->free_blocks[i].size);
|
||||
}
|
||||
|
||||
if(free_blocks->free_blocks_count == FREE_BLOCK_INFO_MAX) {
|
||||
printf("... and more\r\n");
|
||||
}
|
||||
|
||||
free(free_blocks);
|
||||
}
|
||||
|
||||
void cli_command_i2c(Cli* cli, FuriString* args, void* context) {
|
||||
|
||||
@@ -34,10 +34,8 @@ static void desktop_loader_callback(const void* message, void* context) {
|
||||
const LoaderEvent* event = message;
|
||||
|
||||
if(event->type == LoaderEventTypeApplicationBeforeLoad) {
|
||||
desktop->animation_lock = api_lock_alloc_locked();
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted);
|
||||
api_lock_wait_unlock_and_free(desktop->animation_lock);
|
||||
desktop->animation_lock = NULL;
|
||||
furi_check(furi_semaphore_acquire(desktop->animation_semaphore, 3000) == FuriStatusOk);
|
||||
} else if(
|
||||
event->type == LoaderEventTypeApplicationLoadFailed ||
|
||||
event->type == LoaderEventTypeApplicationStopped) {
|
||||
@@ -125,7 +123,7 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
|
||||
animation_manager_unload_and_stall_animation(desktop->animation_manager);
|
||||
}
|
||||
desktop_auto_lock_inhibit(desktop);
|
||||
api_lock_unlock(desktop->animation_lock);
|
||||
furi_semaphore_release(desktop->animation_semaphore);
|
||||
return true;
|
||||
case DesktopGlobalAfterAppFinished:
|
||||
animation_manager_load_and_continue_animation(desktop->animation_manager);
|
||||
@@ -282,6 +280,7 @@ void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) {
|
||||
Desktop* desktop_alloc(void) {
|
||||
Desktop* desktop = malloc(sizeof(Desktop));
|
||||
|
||||
desktop->animation_semaphore = furi_semaphore_alloc(1, 0);
|
||||
desktop->animation_manager = animation_manager_alloc();
|
||||
desktop->gui = furi_record_open(RECORD_GUI);
|
||||
desktop->scene_thread = furi_thread_alloc();
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include <loader/loader.h>
|
||||
#include <notification/notification_app.h>
|
||||
#include <toolbox/api_lock.h>
|
||||
|
||||
#define STATUS_BAR_Y_SHIFT 13
|
||||
|
||||
@@ -81,7 +80,7 @@ struct Desktop {
|
||||
|
||||
bool in_transition : 1;
|
||||
|
||||
FuriApiLock animation_lock;
|
||||
FuriSemaphore* animation_semaphore;
|
||||
|
||||
Keybinds keybinds;
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ void view_dispatcher_set_navigation_event_callback(
|
||||
ViewDispatcher* view_dispatcher,
|
||||
ViewDispatcherNavigationEventCallback callback) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(callback);
|
||||
view_dispatcher->navigation_event_callback = callback;
|
||||
}
|
||||
|
||||
@@ -61,7 +60,6 @@ void view_dispatcher_set_custom_event_callback(
|
||||
ViewDispatcher* view_dispatcher,
|
||||
ViewDispatcherCustomEventCallback callback) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(callback);
|
||||
view_dispatcher->custom_event_callback = callback;
|
||||
}
|
||||
|
||||
@@ -70,7 +68,6 @@ void view_dispatcher_set_tick_event_callback(
|
||||
ViewDispatcherTickEventCallback callback,
|
||||
uint32_t tick_period) {
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(callback);
|
||||
view_dispatcher->tick_event_callback = callback;
|
||||
view_dispatcher->tick_period = tick_period;
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@ static const char*
|
||||
loader_find_external_application_by_name(const char* app_name, FlipperApplicationFlag* flags) {
|
||||
for(size_t i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) {
|
||||
if(strcmp(FLIPPER_EXTERNAL_APPS[i].name, app_name) == 0) {
|
||||
*flags = FLIPPER_EXTERNAL_APPS[i].flags;
|
||||
if(flags) *flags = FLIPPER_EXTERNAL_APPS[i].flags;
|
||||
return FLIPPER_EXTERNAL_APPS[i].path;
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
|
||||
if(strcmp(FLIPPER_SETTINGS_APPS[i].name, app_name) == 0) {
|
||||
*flags = FLIPPER_SETTINGS_APPS[i].flags;
|
||||
if(flags) *flags = FLIPPER_SETTINGS_APPS[i].flags;
|
||||
return FLIPPER_SETTINGS_APPS[i].path;
|
||||
}
|
||||
}
|
||||
@@ -61,10 +61,24 @@ LoaderStatus
|
||||
return result.value;
|
||||
}
|
||||
|
||||
static void loader_show_gui_error(LoaderStatus status, FuriString* error_message) {
|
||||
// TODO FL-3522: we have many places where we can emit a double start, ex: desktop, menu
|
||||
// so i prefer to not show LoaderStatusErrorAppStarted error message for now
|
||||
if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) {
|
||||
static void
|
||||
loader_show_gui_error(LoaderStatus status, const char* name, FuriString* error_message) {
|
||||
if(status == LoaderStatusErrorUnknownApp &&
|
||||
loader_find_external_application_by_name(name, NULL) != NULL) {
|
||||
// Special case for external apps
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop);
|
||||
dialog_message_set_buttons(message, NULL, NULL, NULL);
|
||||
dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22);
|
||||
dialog_message_set_text(
|
||||
message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop);
|
||||
dialog_message_show(dialogs, message);
|
||||
dialog_message_free(message);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
} else if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) {
|
||||
// TODO FL-3522: we have many places where we can emit a double start, ex: desktop, menu
|
||||
// so i prefer to not show LoaderStatusErrorAppStarted error message for now
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop);
|
||||
@@ -90,7 +104,7 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const
|
||||
|
||||
FuriString* error_message = furi_string_alloc();
|
||||
LoaderStatus status = loader_start(loader, name, args, error_message);
|
||||
loader_show_gui_error(status, error_message);
|
||||
loader_show_gui_error(status, name, error_message);
|
||||
furi_string_free(error_message);
|
||||
return status;
|
||||
}
|
||||
@@ -99,11 +113,11 @@ void loader_start_detached_with_gui_error(Loader* loader, const char* name, cons
|
||||
furi_check(loader);
|
||||
furi_check(name);
|
||||
|
||||
LoaderMessage message;
|
||||
|
||||
message.type = LoaderMessageTypeStartByNameDetachedWithGuiError;
|
||||
message.start.name = name ? strdup(name) : NULL;
|
||||
message.start.args = args ? strdup(args) : NULL;
|
||||
LoaderMessage message = {
|
||||
.type = LoaderMessageTypeStartByNameDetachedWithGuiError,
|
||||
.start.name = strdup(name),
|
||||
.start.args = args ? strdup(args) : NULL,
|
||||
};
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
}
|
||||
|
||||
@@ -196,11 +210,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
|
||||
|
||||
Loader* loader = context;
|
||||
|
||||
if(thread_state == FuriThreadStateRunning) {
|
||||
LoaderEvent event;
|
||||
event.type = LoaderEventTypeApplicationStarted;
|
||||
furi_pubsub_publish(loader->pubsub, &event);
|
||||
} else if(thread_state == FuriThreadStateStopped) {
|
||||
if(thread_state == FuriThreadStateStopped) {
|
||||
LoaderMessage message;
|
||||
message.type = LoaderMessageTypeAppClosed;
|
||||
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
|
||||
@@ -742,7 +752,7 @@ int32_t loader_srv(void* p) {
|
||||
FuriString* error_message = furi_string_alloc();
|
||||
LoaderStatus status = loader_do_start_by_name(
|
||||
loader, message.start.name, message.start.args, error_message);
|
||||
loader_show_gui_error(status, error_message);
|
||||
loader_show_gui_error(status, message.start.name, error_message);
|
||||
if(message.start.name) free((void*)message.start.name);
|
||||
if(message.start.args) free((void*)message.start.args);
|
||||
furi_string_free(error_message);
|
||||
|
||||
@@ -20,7 +20,6 @@ typedef enum {
|
||||
typedef enum {
|
||||
LoaderEventTypeApplicationBeforeLoad,
|
||||
LoaderEventTypeApplicationLoadFailed,
|
||||
LoaderEventTypeApplicationStarted,
|
||||
LoaderEventTypeApplicationStopped
|
||||
} LoaderEventType;
|
||||
|
||||
@@ -34,7 +33,7 @@ typedef struct {
|
||||
* @param[in] name application name or id
|
||||
* @param[in] args application arguments
|
||||
* @param[out] error_message detailed error message, can be NULL
|
||||
* @return LoaderStatus
|
||||
* @return LoaderStatus
|
||||
*/
|
||||
LoaderStatus
|
||||
loader_start(Loader* instance, const char* name, const char* args, FuriString* error_message);
|
||||
@@ -44,19 +43,19 @@ LoaderStatus
|
||||
* @param[in] instance loader instance
|
||||
* @param[in] name application name or id
|
||||
* @param[in] args application arguments
|
||||
* @return LoaderStatus
|
||||
* @return LoaderStatus
|
||||
*/
|
||||
LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args);
|
||||
|
||||
/**
|
||||
* @brief Start application detached with GUI error message
|
||||
* @param[in] instance loader instance
|
||||
* @param[in] name application name
|
||||
* @param[in] name application name or id
|
||||
* @param[in] args application arguments
|
||||
*/
|
||||
void loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Lock application start
|
||||
* @param[in] instance loader instance
|
||||
* @return true on success
|
||||
|
||||
@@ -33,8 +33,8 @@ typedef enum {
|
||||
LoaderMessageTypeLock,
|
||||
LoaderMessageTypeUnlock,
|
||||
LoaderMessageTypeIsLocked,
|
||||
|
||||
LoaderMessageTypeStartByNameDetachedWithGuiError,
|
||||
|
||||
LoaderMessageTypeShowSettings,
|
||||
} LoaderMessageType;
|
||||
|
||||
|
||||
@@ -300,9 +300,11 @@ static void power_loader_callback(const void* message, void* context) {
|
||||
Power* power = context;
|
||||
const LoaderEvent* event = message;
|
||||
|
||||
if(event->type == LoaderEventTypeApplicationStarted) {
|
||||
if(event->type == LoaderEventTypeApplicationBeforeLoad) {
|
||||
power_auto_shutdown_inhibit(power);
|
||||
} else if(event->type == LoaderEventTypeApplicationStopped) {
|
||||
} else if(
|
||||
event->type == LoaderEventTypeApplicationLoadFailed ||
|
||||
event->type == LoaderEventTypeApplicationStopped) {
|
||||
power_auto_shutdown_arm(power);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,12 @@ Storage* storage_app_alloc(void) {
|
||||
}
|
||||
|
||||
#ifndef FURI_RAM_EXEC
|
||||
storage_mnt_init(&app->storage[ST_MNT]);
|
||||
storage_int_init(&app->storage[ST_INT]);
|
||||
#endif
|
||||
storage_ext_init(&app->storage[ST_EXT]);
|
||||
#ifndef FURI_RAM_EXEC
|
||||
storage_mnt_init(&app->storage[ST_MNT]);
|
||||
#endif
|
||||
|
||||
// sd icon gui
|
||||
app->sd_gui.enabled = false;
|
||||
|
||||
@@ -156,3 +156,9 @@ size_t storage_open_files_count(StorageData* storage) {
|
||||
size_t count = StorageFileList_size(storage->files);
|
||||
return count;
|
||||
}
|
||||
|
||||
const char* storage_file_get_path(File* file, StorageData* storage) {
|
||||
StorageFile* storage_file_ref = storage_get_file(file, storage);
|
||||
if(!storage_file_ref) return "";
|
||||
return furi_string_get_cstr(storage_file_ref->path);
|
||||
}
|
||||
@@ -35,6 +35,7 @@ void storage_file_init(StorageFile* obj);
|
||||
void storage_file_init_set(StorageFile* obj, const StorageFile* src);
|
||||
void storage_file_set(StorageFile* obj, const StorageFile* src);
|
||||
void storage_file_clear(StorageFile* obj);
|
||||
const char* storage_file_get_path(File* file, StorageData* storage);
|
||||
|
||||
void storage_data_init(StorageData* storage);
|
||||
StorageStatus storage_data_status(StorageData* storage);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <furi_hal.h>
|
||||
#include "sd_notify.h"
|
||||
#include <furi_hal_sd.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
typedef FIL SDFile;
|
||||
typedef DIR SDDir;
|
||||
@@ -741,6 +742,20 @@ FS_Error storage_process_virtual_format(StorageData* storage) {
|
||||
SDError error = f_mkfs(sd_data->path, FM_ANY, 0, work, _MAX_SS);
|
||||
free(work);
|
||||
if(error != FR_OK) return FSE_INTERNAL;
|
||||
|
||||
if(storage_process_virtual_mount(storage) == FSE_OK) {
|
||||
// Image file path
|
||||
const char* img_path = storage_file_get_path(mnt_image, mnt_image_storage);
|
||||
// Image file name
|
||||
FuriString* img_name = furi_string_alloc();
|
||||
path_extract_filename_no_ext(img_path, img_name);
|
||||
// Label with drive id prefix
|
||||
char* label = storage_ext_drive_path(storage, furi_string_get_cstr(img_name));
|
||||
furi_string_free(img_name);
|
||||
f_setlabel(label);
|
||||
free(label);
|
||||
storage_process_virtual_unmount(storage);
|
||||
}
|
||||
return FSE_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3,7 +3,13 @@ let notify = require("notification");
|
||||
let flipper = require("flipper");
|
||||
let dialog = require("dialog");
|
||||
|
||||
badusb.setup({ vid: 0xAAAA, pid: 0xBBBB, mfr_name: "Flipper", prod_name: "Zero" });
|
||||
badusb.setup({
|
||||
vid: 0xAAAA,
|
||||
pid: 0xBBBB,
|
||||
mfr_name: "Flipper",
|
||||
prod_name: "Zero",
|
||||
layout_path: "/ext/badusb/assets/layouts/en-US.kl"
|
||||
});
|
||||
dialog.message("BadUSB demo", "Press OK to start");
|
||||
|
||||
if (badusb.isConnected()) {
|
||||
|
||||
@@ -1,19 +1,36 @@
|
||||
let storage = require("storage");
|
||||
let path = "/ext/storage.test";
|
||||
|
||||
function arraybuf_to_string(arraybuf) {
|
||||
let string = "";
|
||||
let data_view = Uint8Array(arraybuf);
|
||||
for (let i = 0; i < data_view.length; i++) {
|
||||
string += chr(data_view[i]);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
print("File exists:", storage.exists(path));
|
||||
|
||||
print("Writing...");
|
||||
// write(path, data, offset)
|
||||
// If offset is specified, the file is not cleared, content is kept and data is written at specified offset
|
||||
// Takes both strings and array buffers
|
||||
storage.write(path, "Hello ");
|
||||
|
||||
print("File exists:", storage.exists(path));
|
||||
|
||||
// Append will create the file even if it doesnt exist!
|
||||
// Takes both strings and array buffers
|
||||
storage.append(path, "World!");
|
||||
|
||||
print("Reading...");
|
||||
// read(path, size, offset)
|
||||
// If no size specified, total filesize is used
|
||||
// If offset is specified, size is capped at (filesize - offset)
|
||||
let data = storage.read(path);
|
||||
print(data);
|
||||
// read returns an array buffer, to allow proper usage of raw binary data
|
||||
print(arraybuf_to_string(data));
|
||||
|
||||
print("Removing...")
|
||||
storage.remove(path);
|
||||
@@ -21,6 +38,9 @@ storage.remove(path);
|
||||
print("Done")
|
||||
|
||||
// There's also:
|
||||
// storage.copy(old_path, new_path);
|
||||
// storage.move(old_path, new_path);
|
||||
// storage.mkdir(path);
|
||||
// storage.virtualInit(path);
|
||||
// storage.virtualMount();
|
||||
// storage.virtualQuit();
|
||||
@@ -2,8 +2,11 @@
|
||||
#include "../js_modules.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define ASCII_TO_KEY(layout, x) (((uint8_t)x < 128) ? (layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
|
||||
|
||||
typedef struct {
|
||||
FuriHalUsbHidConfig* hid_cfg;
|
||||
uint16_t layout[128];
|
||||
FuriHalUsbInterface* usb_if_prev;
|
||||
uint8_t key_hold_cnt;
|
||||
} JsBadusbInst;
|
||||
@@ -85,10 +88,15 @@ static void js_badusb_quit_free(JsBadusbInst* badusb) {
|
||||
}
|
||||
if(badusb->hid_cfg) {
|
||||
free(badusb->hid_cfg);
|
||||
badusb->hid_cfg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool setup_parse_params(struct mjs* mjs, mjs_val_t arg, FuriHalUsbHidConfig* hid_cfg) {
|
||||
static bool setup_parse_params(
|
||||
JsBadusbInst* badusb,
|
||||
struct mjs* mjs,
|
||||
mjs_val_t arg,
|
||||
FuriHalUsbHidConfig* hid_cfg) {
|
||||
if(!mjs_is_object(arg)) {
|
||||
return false;
|
||||
}
|
||||
@@ -96,6 +104,7 @@ static bool setup_parse_params(struct mjs* mjs, mjs_val_t arg, FuriHalUsbHidConf
|
||||
mjs_val_t pid_obj = mjs_get(mjs, arg, "pid", ~0);
|
||||
mjs_val_t mfr_obj = mjs_get(mjs, arg, "mfr_name", ~0);
|
||||
mjs_val_t prod_obj = mjs_get(mjs, arg, "prod_name", ~0);
|
||||
mjs_val_t layout_obj = mjs_get(mjs, arg, "layout_path", ~0);
|
||||
|
||||
if(mjs_is_number(vid_obj) && mjs_is_number(pid_obj)) {
|
||||
hid_cfg->vid = mjs_get_int32(mjs, vid_obj);
|
||||
@@ -122,6 +131,25 @@ static bool setup_parse_params(struct mjs* mjs, mjs_val_t arg, FuriHalUsbHidConf
|
||||
strlcpy(hid_cfg->product, str_temp, sizeof(hid_cfg->product));
|
||||
}
|
||||
|
||||
if(mjs_is_string(layout_obj)) {
|
||||
size_t str_len = 0;
|
||||
const char* str_temp = mjs_get_string(mjs, &layout_obj, &str_len);
|
||||
if((str_len == 0) || (str_temp == NULL)) {
|
||||
return false;
|
||||
}
|
||||
File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
|
||||
bool layout_loaded = storage_file_open(file, str_temp, FSAM_READ, FSOM_OPEN_EXISTING) &&
|
||||
storage_file_read(file, badusb->layout, sizeof(badusb->layout)) ==
|
||||
sizeof(badusb->layout);
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
if(!layout_loaded) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
memcpy(badusb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(badusb->layout)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -144,7 +172,7 @@ static void js_badusb_setup(struct mjs* mjs) {
|
||||
} else if(num_args == 1) {
|
||||
badusb->hid_cfg = malloc(sizeof(FuriHalUsbHidConfig));
|
||||
// Parse argument object
|
||||
args_correct = setup_parse_params(mjs, mjs_arg(mjs, 0), badusb->hid_cfg);
|
||||
args_correct = setup_parse_params(badusb, mjs, mjs_arg(mjs, 0), badusb->hid_cfg);
|
||||
}
|
||||
if(!args_correct) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
@@ -191,9 +219,9 @@ static void js_badusb_is_connected(struct mjs* mjs) {
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, is_connected));
|
||||
}
|
||||
|
||||
uint16_t get_keycode_by_name(const char* key_name, size_t name_len) {
|
||||
uint16_t get_keycode_by_name(JsBadusbInst* badusb, const char* key_name, size_t name_len) {
|
||||
if(name_len == 1) { // Single char
|
||||
return (HID_ASCII_TO_KEY(key_name[0]));
|
||||
return (ASCII_TO_KEY(badusb->layout, key_name[0]));
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(key_codes); i++) {
|
||||
@@ -210,7 +238,7 @@ uint16_t get_keycode_by_name(const char* key_name, size_t name_len) {
|
||||
return HID_KEYBOARD_NONE;
|
||||
}
|
||||
|
||||
static bool parse_keycode(struct mjs* mjs, size_t nargs, uint16_t* keycode) {
|
||||
static bool parse_keycode(JsBadusbInst* badusb, struct mjs* mjs, size_t nargs, uint16_t* keycode) {
|
||||
uint16_t key_tmp = 0;
|
||||
for(size_t i = 0; i < nargs; i++) {
|
||||
mjs_val_t arg = mjs_arg(mjs, i);
|
||||
@@ -221,7 +249,7 @@ static bool parse_keycode(struct mjs* mjs, size_t nargs, uint16_t* keycode) {
|
||||
// String error
|
||||
return false;
|
||||
}
|
||||
uint16_t str_key = get_keycode_by_name(key_name, name_len);
|
||||
uint16_t str_key = get_keycode_by_name(badusb, key_name, name_len);
|
||||
if(str_key == HID_KEYBOARD_NONE) {
|
||||
// Unknown key code
|
||||
return false;
|
||||
@@ -259,7 +287,7 @@ static void js_badusb_press(struct mjs* mjs) {
|
||||
uint16_t keycode = HID_KEYBOARD_NONE;
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
if(num_args > 0) {
|
||||
args_correct = parse_keycode(mjs, num_args, &keycode);
|
||||
args_correct = parse_keycode(badusb, mjs, num_args, &keycode);
|
||||
}
|
||||
if(!args_correct) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
@@ -285,7 +313,7 @@ static void js_badusb_hold(struct mjs* mjs) {
|
||||
uint16_t keycode = HID_KEYBOARD_NONE;
|
||||
size_t num_args = mjs_nargs(mjs);
|
||||
if(num_args > 0) {
|
||||
args_correct = parse_keycode(mjs, num_args, &keycode);
|
||||
args_correct = parse_keycode(badusb, mjs, num_args, &keycode);
|
||||
}
|
||||
if(!args_correct) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
@@ -324,7 +352,7 @@ static void js_badusb_release(struct mjs* mjs) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
} else {
|
||||
args_correct = parse_keycode(mjs, num_args, &keycode);
|
||||
args_correct = parse_keycode(badusb, mjs, num_args, &keycode);
|
||||
}
|
||||
if(!args_correct) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
|
||||
@@ -347,14 +375,14 @@ static void ducky_numlock_on() {
|
||||
}
|
||||
|
||||
// Simulate pressing a character using ALT+Numpad ASCII code
|
||||
static void ducky_altchar(const char* ascii_code) {
|
||||
static void ducky_altchar(JsBadusbInst* badusb, const char* ascii_code) {
|
||||
// Hold the ALT key
|
||||
furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT);
|
||||
|
||||
// Press the corresponding numpad key for each digit of the ASCII code
|
||||
for(size_t i = 0; ascii_code[i] != '\0'; i++) {
|
||||
char digitChar[5] = {'N', 'U', 'M', ascii_code[i], '\0'}; // Construct the numpad key name
|
||||
uint16_t numpad_keycode = get_keycode_by_name(digitChar, strlen(digitChar));
|
||||
uint16_t numpad_keycode = get_keycode_by_name(badusb, digitChar, strlen(digitChar));
|
||||
if(numpad_keycode == HID_KEYBOARD_NONE) {
|
||||
continue; // Skip if keycode not found
|
||||
}
|
||||
@@ -420,9 +448,9 @@ static void badusb_print(struct mjs* mjs, bool ln, bool alt) {
|
||||
// Convert character to ascii numeric value
|
||||
char ascii_str[4];
|
||||
snprintf(ascii_str, sizeof(ascii_str), "%u", (uint8_t)text_str[i]);
|
||||
ducky_altchar(ascii_str);
|
||||
ducky_altchar(badusb, ascii_str);
|
||||
} else {
|
||||
uint16_t keycode = HID_ASCII_TO_KEY(text_str[i]);
|
||||
uint16_t keycode = ASCII_TO_KEY(badusb->layout, text_str[i]);
|
||||
furi_hal_hid_kb_press(keycode);
|
||||
furi_hal_hid_kb_release(keycode);
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ static bool check_arg_count(struct mjs* mjs, size_t count) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_path_arg(struct mjs* mjs, const char** path) {
|
||||
mjs_val_t path_obj = mjs_arg(mjs, 0);
|
||||
static bool get_path_arg(struct mjs* mjs, const char** path, size_t index) {
|
||||
mjs_val_t path_obj = mjs_arg(mjs, index);
|
||||
if(!mjs_is_string(path_obj)) {
|
||||
ret_bad_args(mjs, "Path must be a string");
|
||||
return false;
|
||||
@@ -49,10 +49,9 @@ static bool get_path_arg(struct mjs* mjs, const char** path) {
|
||||
|
||||
static void js_storage_read(struct mjs* mjs) {
|
||||
JsStorageInst* storage = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 1)) return;
|
||||
|
||||
const char* path;
|
||||
if(!get_path_arg(mjs, &path)) return;
|
||||
if(!get_path_arg(mjs, &path, 0)) return;
|
||||
|
||||
File* file = storage_file_alloc(storage->api);
|
||||
do {
|
||||
@@ -62,15 +61,26 @@ static void js_storage_read(struct mjs* mjs) {
|
||||
}
|
||||
|
||||
uint64_t size = storage_file_size(file);
|
||||
if(size > 128 * 1024) {
|
||||
ret_int_err(mjs, "File too large");
|
||||
mjs_val_t size_arg = mjs_arg(mjs, 1);
|
||||
if(mjs_is_number(size_arg)) {
|
||||
size = mjs_get_int32(mjs, size_arg);
|
||||
}
|
||||
|
||||
mjs_val_t seek_arg = mjs_arg(mjs, 2);
|
||||
if(mjs_is_number(seek_arg)) {
|
||||
storage_file_seek(file, mjs_get_int32(mjs, seek_arg), true);
|
||||
size = MIN(size, storage_file_size(file) - storage_file_tell(file));
|
||||
}
|
||||
|
||||
if(size > memmgr_heap_get_max_free_block()) {
|
||||
ret_int_err(mjs, "Read size too large");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t* data = malloc(size);
|
||||
size_t read = storage_file_read(file, data, size);
|
||||
if(read == size) {
|
||||
mjs_return(mjs, mjs_mk_string(mjs, (const char*)data, size, true));
|
||||
mjs_return(mjs, mjs_mk_array_buf(mjs, (char*)data, size));
|
||||
} else {
|
||||
ret_int_err(mjs, "File read failed");
|
||||
}
|
||||
@@ -81,27 +91,41 @@ static void js_storage_read(struct mjs* mjs) {
|
||||
|
||||
static void js_storage_write(struct mjs* mjs) {
|
||||
JsStorageInst* storage = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 2)) return;
|
||||
|
||||
const char* path;
|
||||
if(!get_path_arg(mjs, &path)) return;
|
||||
if(!get_path_arg(mjs, &path, 0)) return;
|
||||
|
||||
mjs_val_t data_obj = mjs_arg(mjs, 1);
|
||||
if(!mjs_is_string(data_obj)) {
|
||||
ret_bad_args(mjs, "Data must be a string");
|
||||
mjs_val_t data_arg = mjs_arg(mjs, 1);
|
||||
if(!mjs_is_typed_array(data_arg) && !mjs_is_string(data_arg)) {
|
||||
ret_bad_args(mjs, "Data must be string, arraybuf or dataview");
|
||||
return;
|
||||
}
|
||||
if(mjs_is_data_view(data_arg)) {
|
||||
data_arg = mjs_dataview_get_buf(mjs, data_arg);
|
||||
}
|
||||
size_t data_len = 0;
|
||||
const char* data = mjs_get_string(mjs, &data_obj, &data_len);
|
||||
if((data_len == 0) || (data == NULL)) {
|
||||
ret_bad_args(mjs, "Bad data argument");
|
||||
return;
|
||||
const char* data = NULL;
|
||||
if(mjs_is_string(data_arg)) {
|
||||
data = mjs_get_string(mjs, &data_arg, &data_len);
|
||||
} else if(mjs_is_typed_array(data_arg)) {
|
||||
data = mjs_array_buf_get_ptr(mjs, data_arg, &data_len);
|
||||
}
|
||||
|
||||
mjs_val_t seek_arg = mjs_arg(mjs, 2);
|
||||
|
||||
File* file = storage_file_alloc(storage->api);
|
||||
if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
if(!storage_file_open(
|
||||
file,
|
||||
path,
|
||||
FSAM_WRITE,
|
||||
mjs_is_number(seek_arg) ? FSOM_OPEN_ALWAYS : FSOM_CREATE_ALWAYS)) {
|
||||
ret_int_err(mjs, storage_file_get_error_desc(file));
|
||||
|
||||
} else {
|
||||
if(mjs_is_number(seek_arg)) {
|
||||
storage_file_seek(file, mjs_get_int32(mjs, seek_arg), true);
|
||||
}
|
||||
|
||||
size_t write = storage_file_write(file, data, data_len);
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, write == data_len));
|
||||
}
|
||||
@@ -113,18 +137,22 @@ static void js_storage_append(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 2)) return;
|
||||
|
||||
const char* path;
|
||||
if(!get_path_arg(mjs, &path)) return;
|
||||
if(!get_path_arg(mjs, &path, 0)) return;
|
||||
|
||||
mjs_val_t data_obj = mjs_arg(mjs, 1);
|
||||
if(!mjs_is_string(data_obj)) {
|
||||
ret_bad_args(mjs, "Data must be a string");
|
||||
mjs_val_t data_arg = mjs_arg(mjs, 1);
|
||||
if(!mjs_is_typed_array(data_arg) && !mjs_is_string(data_arg)) {
|
||||
ret_bad_args(mjs, "Data must be string, arraybuf or dataview");
|
||||
return;
|
||||
}
|
||||
if(mjs_is_data_view(data_arg)) {
|
||||
data_arg = mjs_dataview_get_buf(mjs, data_arg);
|
||||
}
|
||||
size_t data_len = 0;
|
||||
const char* data = mjs_get_string(mjs, &data_obj, &data_len);
|
||||
if((data_len == 0) || (data == NULL)) {
|
||||
ret_bad_args(mjs, "Bad data argument");
|
||||
return;
|
||||
const char* data = NULL;
|
||||
if(mjs_is_string(data_arg)) {
|
||||
data = mjs_get_string(mjs, &data_arg, &data_len);
|
||||
} else if(mjs_is_typed_array(data_arg)) {
|
||||
data = mjs_array_buf_get_ptr(mjs, data_arg, &data_len);
|
||||
}
|
||||
|
||||
File* file = storage_file_alloc(storage->api);
|
||||
@@ -142,7 +170,7 @@ static void js_storage_exists(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1)) return;
|
||||
|
||||
const char* path;
|
||||
if(!get_path_arg(mjs, &path)) return;
|
||||
if(!get_path_arg(mjs, &path, 0)) return;
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, storage_common_exists(storage->api, path)));
|
||||
}
|
||||
@@ -152,17 +180,63 @@ static void js_storage_remove(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1)) return;
|
||||
|
||||
const char* path;
|
||||
if(!get_path_arg(mjs, &path)) return;
|
||||
if(!get_path_arg(mjs, &path, 0)) return;
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_remove(storage->api, path)));
|
||||
}
|
||||
|
||||
static void js_storage_copy(struct mjs* mjs) {
|
||||
JsStorageInst* storage = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 2)) return;
|
||||
|
||||
const char* old_path;
|
||||
if(!get_path_arg(mjs, &old_path, 0)) return;
|
||||
|
||||
const char* new_path;
|
||||
if(!get_path_arg(mjs, &new_path, 1)) return;
|
||||
|
||||
FS_Error error = storage_common_copy(storage->api, old_path, new_path);
|
||||
if(error == FSE_OK) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
} else {
|
||||
ret_int_err(mjs, storage_error_get_desc(error));
|
||||
}
|
||||
}
|
||||
|
||||
static void js_storage_move(struct mjs* mjs) {
|
||||
JsStorageInst* storage = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 2)) return;
|
||||
|
||||
const char* old_path;
|
||||
if(!get_path_arg(mjs, &old_path, 0)) return;
|
||||
|
||||
const char* new_path;
|
||||
if(!get_path_arg(mjs, &new_path, 1)) return;
|
||||
|
||||
FS_Error error = storage_common_rename(storage->api, old_path, new_path);
|
||||
if(error == FSE_OK) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
} else {
|
||||
ret_int_err(mjs, storage_error_get_desc(error));
|
||||
}
|
||||
}
|
||||
|
||||
static void js_storage_mkdir(struct mjs* mjs) {
|
||||
JsStorageInst* storage = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 1)) return;
|
||||
|
||||
const char* path;
|
||||
if(!get_path_arg(mjs, &path, 0)) return;
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, storage_simply_mkdir(storage->api, path)));
|
||||
}
|
||||
|
||||
static void js_storage_virtual_init(struct mjs* mjs) {
|
||||
JsStorageInst* storage = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 1)) return;
|
||||
|
||||
const char* path;
|
||||
if(!get_path_arg(mjs, &path)) return;
|
||||
if(!get_path_arg(mjs, &path, 0)) return;
|
||||
|
||||
if(storage->virtual) {
|
||||
ret_int_err(mjs, "Virtual already setup");
|
||||
@@ -202,11 +276,6 @@ static void js_storage_virtual_mount(struct mjs* mjs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(storage->virtual) {
|
||||
storage_file_free(storage->virtual);
|
||||
storage->virtual = NULL;
|
||||
}
|
||||
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
|
||||
@@ -219,6 +288,11 @@ static void js_storage_virtual_quit(struct mjs* mjs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(storage->virtual) {
|
||||
storage_file_free(storage->virtual);
|
||||
storage->virtual = NULL;
|
||||
}
|
||||
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
|
||||
@@ -231,6 +305,9 @@ static void* js_storage_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
mjs_set(mjs, storage_obj, "append", ~0, MJS_MK_FN(js_storage_append));
|
||||
mjs_set(mjs, storage_obj, "exists", ~0, MJS_MK_FN(js_storage_exists));
|
||||
mjs_set(mjs, storage_obj, "remove", ~0, MJS_MK_FN(js_storage_remove));
|
||||
mjs_set(mjs, storage_obj, "copy", ~0, MJS_MK_FN(js_storage_copy));
|
||||
mjs_set(mjs, storage_obj, "move", ~0, MJS_MK_FN(js_storage_move));
|
||||
mjs_set(mjs, storage_obj, "mkdir", ~0, MJS_MK_FN(js_storage_mkdir));
|
||||
mjs_set(mjs, storage_obj, "virtualInit", ~0, MJS_MK_FN(js_storage_virtual_init));
|
||||
mjs_set(mjs, storage_obj, "virtualMount", ~0, MJS_MK_FN(js_storage_virtual_mount));
|
||||
mjs_set(mjs, storage_obj, "virtualQuit", ~0, MJS_MK_FN(js_storage_virtual_quit));
|
||||
|
||||
Reference in New Issue
Block a user