mirror of
https://github.com/D4C1-Labs/Flipper-ARF.git
synced 2026-07-14 12:08:57 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 63d951dc38 | |||
| f1422cb701 | |||
| f7b04f1382 | |||
| 7286b5c529 | |||
| 6a49fd89a1 | |||
| c4378c2e20 | |||
| 6e5ba9ec22 | |||
| 502570a18b | |||
| e606c5b24f | |||
| b34a73b08a |
@@ -15,6 +15,7 @@ typedef enum {
|
||||
SubGhzCustomEventSceneReceiverInfoTxStart,
|
||||
SubGhzCustomEventSceneReceiverInfoTxStop,
|
||||
SubGhzCustomEventSceneReceiverInfoSave,
|
||||
SubGhzCustomEventSceneReceiverInfoTxFullDpad,
|
||||
SubGhzCustomEventSceneSaveName,
|
||||
SubGhzCustomEventSceneSignalSettings,
|
||||
SubGhzCustomEventSceneSaveSuccess,
|
||||
|
||||
@@ -361,6 +361,49 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat*
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subghz_txrx_rebuild_from_fff(SubGhzTxRx* instance, FlipperFormat* flipper_format) {
|
||||
furi_assert(instance);
|
||||
furi_assert(flipper_format);
|
||||
|
||||
subghz_txrx_stop(instance);
|
||||
|
||||
bool rebuilt = false;
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
SubGhzTransmitter* transmitter = NULL;
|
||||
|
||||
do {
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
transmitter =
|
||||
subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str));
|
||||
if(!transmitter) {
|
||||
FURI_LOG_E(TAG, "Protocol not found");
|
||||
break;
|
||||
}
|
||||
|
||||
rebuilt =
|
||||
subghz_transmitter_deserialize(transmitter, flipper_format) == SubGhzProtocolStatusOk;
|
||||
if(!rebuilt) {
|
||||
FURI_LOG_E(TAG, "Protocol rebuild failed");
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(transmitter) {
|
||||
subghz_transmitter_free(transmitter);
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return rebuilt;
|
||||
}
|
||||
|
||||
void subghz_txrx_rx_start(SubGhzTxRx* instance) {
|
||||
furi_assert(instance);
|
||||
subghz_txrx_stop(instance);
|
||||
|
||||
@@ -105,6 +105,15 @@ void subghz_txrx_get_frequency_and_modulation(
|
||||
*/
|
||||
SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Rebuild protocol data without starting TX.
|
||||
*
|
||||
* @param instance Pointer to a SubGhzTxRx
|
||||
* @param flipper_format Pointer to a FlipperFormat
|
||||
* @return bool True when encoder deserialization updated protocol data successfully
|
||||
*/
|
||||
bool subghz_txrx_rebuild_from_fff(SubGhzTxRx* instance, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Start RX CC1101
|
||||
*
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../subghz_i.h"
|
||||
|
||||
#include <lib/subghz/blocks/custom_btn.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#include "applications/main/subghz/helpers/subghz_txrx_i.h"
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
@@ -20,6 +21,9 @@ void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, v
|
||||
} else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave);
|
||||
} else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
|
||||
view_dispatcher_send_custom_event(
|
||||
subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxFullDpad);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +33,6 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
|
||||
if(subghz_txrx_load_decoder_by_name_protocol(
|
||||
subghz->txrx,
|
||||
subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) {
|
||||
// we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal
|
||||
subghz_protocol_decoder_base_deserialize(
|
||||
subghz_txrx_get_decoder(subghz->txrx),
|
||||
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen));
|
||||
@@ -37,7 +40,6 @@ static bool subghz_scene_receiver_info_update_parser(void* context) {
|
||||
SubGhzRadioPreset* preset =
|
||||
subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen);
|
||||
|
||||
//Edit TX power, if necessary.
|
||||
subghz_txrx_set_tx_power(preset->data, preset->data_size, subghz->tx_power);
|
||||
|
||||
subghz_txrx_set_preset(
|
||||
@@ -93,7 +95,7 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
|
||||
subghz_scene_receiver_info_callback,
|
||||
subghz);
|
||||
}
|
||||
// Removed static check
|
||||
|
||||
if(subghz_txrx_protocol_is_transmittable(subghz->txrx, false)) {
|
||||
widget_add_button_element(
|
||||
subghz->widget,
|
||||
@@ -101,9 +103,14 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) {
|
||||
"Send",
|
||||
subghz_scene_receiver_info_callback,
|
||||
subghz);
|
||||
widget_add_button_element(
|
||||
subghz->widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Full",
|
||||
subghz_scene_receiver_info_callback,
|
||||
subghz);
|
||||
}
|
||||
} else {
|
||||
// [NO_DOLPHIN] widget_add_icon_element(subghz->widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||
widget_add_string_element(
|
||||
subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse.");
|
||||
}
|
||||
@@ -131,12 +138,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
return false;
|
||||
}
|
||||
//CC1101 Stop RX -> Start TX
|
||||
subghz_txrx_hopper_pause(subghz->txrx);
|
||||
// key concept: we start endless TX until user release OK button, and after this we send last
|
||||
// protocols repeats - this guarantee that one press OK will
|
||||
// be guarantee send the required minimum protocol data packets
|
||||
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
|
||||
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||
subghz_block_generic_global.endless_tx = true;
|
||||
if(!subghz_tx_start(
|
||||
@@ -146,37 +148,51 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
subghz_txrx_hopper_unpause(subghz->txrx);
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) {
|
||||
//CC1101 Stop Tx -> next tick event Start RX
|
||||
// user release OK
|
||||
// we switch off endless_tx - that mean protocols yield finish endless transmission,
|
||||
// send upload "repeat=xx" times, and after will be stoped by the tick event down in this code
|
||||
subghz->state_notifications = SubGhzNotificationStateTxWait;
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
|
||||
return true;
|
||||
|
||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) {
|
||||
//CC1101 Stop RX -> Save
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
|
||||
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(subghz_txrx_protocol_is_serializable(subghz->txrx)) {
|
||||
subghz_file_name_clear(subghz);
|
||||
|
||||
subghz->save_datetime =
|
||||
subghz_history_get_datetime(subghz->history, subghz->idx_menu_chosen);
|
||||
subghz->save_datetime_set = true;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxFullDpad) {
|
||||
if(!subghz_scene_receiver_info_update_parser(subghz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FlipperFormat* fff_history =
|
||||
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen);
|
||||
FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
|
||||
Stream* src = flipper_format_get_raw_stream(fff_history);
|
||||
Stream* dst = flipper_format_get_raw_stream(fff_data);
|
||||
|
||||
stream_seek(src, 0, StreamOffsetFromStart);
|
||||
stream_clean(dst);
|
||||
stream_copy_full(src, dst);
|
||||
stream_seek(dst, 0, StreamOffsetFromStart);
|
||||
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) {
|
||||
subghz_txrx_hopper_update(subghz->txrx, subghz->last_settings->hopping_threshold);
|
||||
@@ -193,18 +209,15 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
break;
|
||||
case SubGhzNotificationStateTxWait:
|
||||
// we wait until hardware TX finished and after stop TX and start RX, else just blink led
|
||||
if(!subghz_devices_is_async_complete_tx(subghz->txrx->radio_device)) {
|
||||
notification_message(subghz->notifications, &sequence_blink_magenta_10);
|
||||
} else {
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
// update screen
|
||||
widget_reset(subghz->widget);
|
||||
subghz_scene_receiver_info_draw_widget(subghz);
|
||||
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
|
||||
if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) {
|
||||
if(!scene_manager_has_previous_scene(
|
||||
subghz->scene_manager, SubGhzSceneDecodeRAW)) {
|
||||
subghz_txrx_rx_start(subghz->txrx);
|
||||
subghz_txrx_hopper_unpause(subghz->txrx);
|
||||
if(!subghz_history_get_text_space_left(subghz->history, NULL)) {
|
||||
@@ -222,7 +235,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
|
||||
void subghz_scene_receiver_info_on_exit(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
widget_reset(subghz->widget);
|
||||
subghz_txrx_reset_dynamic_and_custom_btns(subghz->txrx);
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubGhzCustomEventSceneSaveName) {
|
||||
if(strcmp(subghz->file_name_tmp, "") != 0) {
|
||||
furi_string_reset(subghz->error_str);
|
||||
furi_string_cat_printf(
|
||||
subghz->file_path,
|
||||
"/%s%s",
|
||||
|
||||
@@ -12,8 +12,14 @@ void subghz_scene_save_success_on_enter(void* context) {
|
||||
// Setup view
|
||||
Popup* popup = subghz->popup;
|
||||
// [NO_DOLPHIN] popup_set_icon(popup, 36, 5, &I_DolphinSaved_92x58);
|
||||
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
|
||||
popup_set_timeout(popup, 1500);
|
||||
if(furi_string_size(subghz->error_str)) {
|
||||
popup_set_header(popup, "Saved", 15, 4, AlignLeft, AlignTop);
|
||||
popup_set_text(popup, furi_string_get_cstr(subghz->error_str), 4, 18, AlignLeft, AlignTop);
|
||||
popup_set_timeout(popup, 2500);
|
||||
} else {
|
||||
popup_set_header(popup, "Saved", 15, 19, AlignLeft, AlignBottom);
|
||||
popup_set_timeout(popup, 1500);
|
||||
}
|
||||
popup_set_context(popup, subghz);
|
||||
popup_set_callback(popup, subghz_scene_save_success_popup_callback);
|
||||
popup_enable_timeout(popup);
|
||||
@@ -72,4 +78,5 @@ void subghz_scene_save_success_on_exit(void* context) {
|
||||
Popup* popup = subghz->popup;
|
||||
|
||||
popup_reset(popup);
|
||||
furi_string_reset(subghz->error_str);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexEmulate,
|
||||
SubmenuIndexSignalSettings,
|
||||
SubmenuIndexPsaDecrypt,
|
||||
SubmenuIndexEdit,
|
||||
SubmenuIndexDelete,
|
||||
SubmenuIndexSignalSettings,
|
||||
SubmenuIndexCounterBf, /* <-- comma was missing here */
|
||||
SubmenuIndexCarEmulateSettings,
|
||||
};
|
||||
@@ -20,17 +20,20 @@ void subghz_scene_saved_menu_on_enter(void* context) {
|
||||
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
bool is_psa_encrypted = false;
|
||||
bool has_signal_editor = false;
|
||||
bool has_counter = false;
|
||||
if(fff) {
|
||||
FuriString* proto = furi_string_alloc();
|
||||
flipper_format_rewind(fff);
|
||||
if(flipper_format_read_string(fff, "Protocol", proto)) {
|
||||
has_signal_editor = !furi_string_equal_str(proto, "RAW");
|
||||
if(furi_string_equal_str(proto, "PSA GROUP")) {
|
||||
FuriString* type_str = furi_string_alloc();
|
||||
flipper_format_rewind(fff);
|
||||
if(!flipper_format_read_string(fff, "Type", type_str) ||
|
||||
furi_string_equal_str(type_str, "00")) {
|
||||
is_psa_encrypted = true;
|
||||
has_signal_editor = false;
|
||||
}
|
||||
furi_string_free(type_str);
|
||||
}
|
||||
@@ -53,6 +56,15 @@ void subghz_scene_saved_menu_on_enter(void* context) {
|
||||
SubmenuIndexEmulate,
|
||||
subghz_scene_saved_menu_submenu_callback,
|
||||
subghz);
|
||||
|
||||
if(has_signal_editor) {
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"Signal Editor",
|
||||
SubmenuIndexSignalSettings,
|
||||
subghz_scene_saved_menu_submenu_callback,
|
||||
subghz);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_psa_encrypted) {
|
||||
@@ -85,15 +97,6 @@ void subghz_scene_saved_menu_on_enter(void* context) {
|
||||
subghz_scene_saved_menu_submenu_callback,
|
||||
subghz);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
"Signal Settings",
|
||||
SubmenuIndexSignalSettings,
|
||||
subghz_scene_saved_menu_submenu_callback,
|
||||
subghz);
|
||||
}
|
||||
|
||||
if(has_counter) {
|
||||
submenu_add_item(
|
||||
subghz->submenu,
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <machine/endian.h>
|
||||
#include <toolbox/strint.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/custom_btn_i.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "SubGhzSceneSignalSettings"
|
||||
|
||||
@@ -21,6 +23,14 @@ static uint8_t btn_byte_count = 1;
|
||||
static uint8_t* btn_byte_ptr = NULL;
|
||||
|
||||
static uint8_t submenu_called = 0;
|
||||
static bool button_uses_custom_btn = false;
|
||||
static uint8_t button_custom_id = SUBGHZ_CUSTOM_BTN_OK;
|
||||
|
||||
enum {
|
||||
SignalSettingsIndexCounterMode,
|
||||
SignalSettingsIndexCounter,
|
||||
SignalSettingsIndexButton,
|
||||
};
|
||||
|
||||
#define COUNTER_MODE_COUNT 8
|
||||
static const char* const counter_mode_text[COUNTER_MODE_COUNT] = {
|
||||
@@ -45,6 +55,65 @@ static const int32_t counter_mode_value[COUNTER_MODE_COUNT] = {
|
||||
7,
|
||||
};
|
||||
|
||||
static const uint8_t button_value[] = {
|
||||
SUBGHZ_CUSTOM_BTN_OK,
|
||||
SUBGHZ_CUSTOM_BTN_UP,
|
||||
SUBGHZ_CUSTOM_BTN_DOWN,
|
||||
SUBGHZ_CUSTOM_BTN_LEFT,
|
||||
SUBGHZ_CUSTOM_BTN_RIGHT,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
};
|
||||
|
||||
#define BUTTON_VALUE_COUNT (sizeof(button_value) / sizeof(button_value[0]))
|
||||
|
||||
static const char* const button_default_labels[BUTTON_VALUE_COUNT] = {
|
||||
"Original",
|
||||
"Up",
|
||||
"Down",
|
||||
"Left",
|
||||
"Right",
|
||||
"Button 5",
|
||||
"Button 6",
|
||||
"Button 7",
|
||||
};
|
||||
|
||||
static const char* button_labels[BUTTON_VALUE_COUNT] = {
|
||||
"Original",
|
||||
"Up",
|
||||
"Down",
|
||||
"Left",
|
||||
"Right",
|
||||
"Button 5",
|
||||
"Button 6",
|
||||
"Button 7",
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* protocol;
|
||||
const char* labels[BUTTON_VALUE_COUNT];
|
||||
} ProtocolButtonLabels;
|
||||
|
||||
static const ProtocolButtonLabels protocol_button_labels[] = {
|
||||
{"VAG GROUP", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"Porsche AG", {"Original", "Lock", "Unlock", "Trunk", "Open"}},
|
||||
{"FORD V0", {"Original", "Lock", "Unlock", "Trunk"}},
|
||||
{"Ford V2", {"Unlock", "Lock", "Trunk", "Panic", "Remote Start"}},
|
||||
{"PSA GROUP", {"Original", "Lock", "Unlock", "Trunk", "Trunk"}},
|
||||
{"KIA/HYU V0", {"Original", "Lock", "Unlock", "Trunk", "Horn"}},
|
||||
{"KIA/HYU V1", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"KIA/HYU V2", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"KIA/HYU V3/V4", {"Original", "Lock", "Unlock", "Trunk", "Panic", "Horn"}},
|
||||
{"KIA/HYU V5", {"Original", "Unlock", "Lock", "Trunk", "Horn"}},
|
||||
{"KIA/HYU V6", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"SUBARU", {"Original", "Lock", "Unlock", "Trunk", "Panic", "Extra"}},
|
||||
{"SUZUKI", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
{"Star Line", {"Original", "Lock", "Unlock", "Trunk", "Start"}},
|
||||
{"Scher-Khan", {"Original", "Lock", "Unlock", "Trunk", "Start"}},
|
||||
{"Sheriff CFM", {"Original", "Lock", "Unlock", "Trunk", "Panic"}},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
uint8_t mode_count;
|
||||
@@ -59,7 +128,171 @@ static Protocols protocols[] = {
|
||||
{"Phoenix_V2", 3},
|
||||
};
|
||||
|
||||
#define PROTOCOLS_COUNT (sizeof(protocols) / sizeof(Protocols));
|
||||
#define PROTOCOLS_COUNT (sizeof(protocols) / sizeof(Protocols))
|
||||
|
||||
static void subghz_scene_signal_settings_reset_button_labels(void) {
|
||||
for(uint8_t i = 0; i < BUTTON_VALUE_COUNT; i++) {
|
||||
button_labels[i] = button_default_labels[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_apply_button_labels(const char* protocol) {
|
||||
for(uint8_t i = 0; i < COUNT_OF(protocol_button_labels); i++) {
|
||||
if(strcmp(protocol, protocol_button_labels[i].protocol) == 0) {
|
||||
for(uint8_t btn = 0; btn < BUTTON_VALUE_COUNT; btn++) {
|
||||
if(protocol_button_labels[i].labels[btn]) {
|
||||
button_labels[btn] = protocol_button_labels[i].labels[btn];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char* subghz_scene_signal_settings_get_button_label(uint8_t custom_btn_id) {
|
||||
if(custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) {
|
||||
uint8_t original = subghz_custom_btn_get_original();
|
||||
if((original != SUBGHZ_CUSTOM_BTN_OK) && (original < BUTTON_VALUE_COUNT)) {
|
||||
return button_labels[original];
|
||||
}
|
||||
}
|
||||
if(custom_btn_id < BUTTON_VALUE_COUNT) {
|
||||
return button_labels[custom_btn_id];
|
||||
}
|
||||
return "Button";
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_update_uint32_field(
|
||||
FlipperFormat* fff,
|
||||
const char* key,
|
||||
uint32_t value) {
|
||||
flipper_format_rewind(fff);
|
||||
return flipper_format_insert_or_update_uint32(fff, key, &value, 1);
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_hex_digit(char c, uint8_t* value) {
|
||||
if(c >= '0' && c <= '9') {
|
||||
*value = c - '0';
|
||||
return true;
|
||||
} else if(c >= 'a' && c <= 'f') {
|
||||
*value = c - 'a' + 10;
|
||||
return true;
|
||||
} else if(c >= 'A' && c <= 'F') {
|
||||
*value = c - 'A' + 10;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_parse_hex_field(
|
||||
const char* text,
|
||||
const char* marker,
|
||||
uint32_t* value,
|
||||
uint8_t* length_bit) {
|
||||
const char* field = strstr(text, marker);
|
||||
if(!field) return false;
|
||||
|
||||
field += strlen(marker);
|
||||
while(*field == ' ' || *field == '\t') {
|
||||
field++;
|
||||
}
|
||||
|
||||
uint32_t parsed = 0;
|
||||
uint8_t digits = 0;
|
||||
uint8_t digit_value = 0;
|
||||
while(digits < 8 && subghz_scene_signal_settings_hex_digit(*field, &digit_value)) {
|
||||
parsed = (parsed << 4) | digit_value;
|
||||
digits++;
|
||||
field++;
|
||||
}
|
||||
|
||||
if(digits == 0) return false;
|
||||
|
||||
*value = parsed;
|
||||
*length_bit = digits * 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_apply_text_fallback(FuriString* decoded_text) {
|
||||
uint32_t value = 0;
|
||||
uint8_t length_bit = 0;
|
||||
const char* text = furi_string_get_cstr(decoded_text);
|
||||
|
||||
if(!subghz_block_generic_global.cnt_is_available &&
|
||||
subghz_scene_signal_settings_parse_hex_field(text, "Cnt:", &value, &length_bit)) {
|
||||
subghz_block_generic_global.cnt_is_available = true;
|
||||
subghz_block_generic_global.current_cnt = value;
|
||||
subghz_block_generic_global.cnt_length_bit = length_bit;
|
||||
}
|
||||
|
||||
if(!subghz_block_generic_global.btn_is_available &&
|
||||
subghz_scene_signal_settings_parse_hex_field(text, "Btn:", &value, &length_bit)) {
|
||||
subghz_block_generic_global.btn_is_available = true;
|
||||
subghz_block_generic_global.current_btn = value & 0xFF;
|
||||
subghz_block_generic_global.btn_length_bit = length_bit > 8 ? 8 : length_bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void subghz_scene_signal_settings_apply_file_fallback(FlipperFormat* fff) {
|
||||
uint32_t value = 0;
|
||||
|
||||
if(!subghz_block_generic_global.cnt_is_available) {
|
||||
flipper_format_rewind(fff);
|
||||
if(flipper_format_read_uint32(fff, "Cnt", &value, 1)) {
|
||||
subghz_block_generic_global.cnt_is_available = true;
|
||||
subghz_block_generic_global.current_cnt = value;
|
||||
subghz_block_generic_global.cnt_length_bit = 32;
|
||||
}
|
||||
}
|
||||
|
||||
if(!subghz_block_generic_global.btn_is_available) {
|
||||
flipper_format_rewind(fff);
|
||||
if(flipper_format_read_uint32(fff, "Btn", &value, 1)) {
|
||||
subghz_block_generic_global.btn_is_available = true;
|
||||
subghz_block_generic_global.current_btn = value & 0xFF;
|
||||
subghz_block_generic_global.btn_length_bit = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool subghz_scene_signal_settings_rebuild_save_reload(
|
||||
SubGhz* subghz,
|
||||
bool use_custom_btn,
|
||||
uint8_t custom_btn_id) {
|
||||
const char* file_path = furi_string_get_cstr(subghz->file_path);
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
|
||||
bool updated = false;
|
||||
int32_t counter_mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
furi_hal_subghz_set_rolling_counter_mult(0);
|
||||
|
||||
if(use_custom_btn) {
|
||||
subghz_custom_btn_set(custom_btn_id);
|
||||
}
|
||||
|
||||
do {
|
||||
if(!subghz_txrx_rebuild_from_fff(subghz->txrx, fff)) {
|
||||
FURI_LOG_E(TAG, "Error rebuilding protocol data");
|
||||
break;
|
||||
}
|
||||
if(!subghz_save_protocol_to_file(subghz, fff, file_path)) {
|
||||
FURI_LOG_E(TAG, "Error saving edited signal");
|
||||
break;
|
||||
}
|
||||
if(!subghz_key_load(subghz, file_path, false)) {
|
||||
FURI_LOG_E(TAG, "Error reloading edited signal");
|
||||
break;
|
||||
}
|
||||
updated = true;
|
||||
} while(false);
|
||||
|
||||
furi_hal_subghz_set_rolling_counter_mult(counter_mult);
|
||||
|
||||
if(!updated) {
|
||||
dialog_message_show_storage_error(subghz->dialogs, "Cannot save\nsignal");
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
void subghz_scene_signal_settings_counter_mode_changed(VariableItem* item) {
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
@@ -99,6 +332,22 @@ void subghz_scene_signal_settings_counter_mode_changed(VariableItem* item) {
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_scene_signal_settings_button_changed(VariableItem* item) {
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
if(index >= BUTTON_VALUE_COUNT) index = 0;
|
||||
|
||||
button_custom_id = button_value[index];
|
||||
variable_item_set_current_value_text(
|
||||
item, subghz_scene_signal_settings_get_button_label(button_custom_id));
|
||||
|
||||
if(!button_uses_custom_btn) return;
|
||||
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
furi_assert(subghz);
|
||||
|
||||
subghz_scene_signal_settings_rebuild_save_reload(subghz, true, button_custom_id);
|
||||
}
|
||||
|
||||
void subghz_scene_signal_settings_byte_input_callback(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone);
|
||||
@@ -108,8 +357,10 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
|
||||
SubGhz* subghz = context;
|
||||
|
||||
// when we click OK on "Edit counter" item
|
||||
if(index == 1) {
|
||||
if(index == SignalSettingsIndexCounter) {
|
||||
if(!cnt_byte_ptr || cnt_byte_count == 0) return;
|
||||
submenu_called = 1;
|
||||
furi_string_set_str(byte_input_text, "Enter ");
|
||||
furi_string_cat_printf(byte_input_text, "%i", subghz_block_generic_global.cnt_length_bit);
|
||||
furi_string_cat_str(byte_input_text, "-bits counter in HEX");
|
||||
|
||||
@@ -127,8 +378,10 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput);
|
||||
}
|
||||
// when we click OK on "Edit button" item
|
||||
if(index == 2) {
|
||||
if(index == SignalSettingsIndexButton && !button_uses_custom_btn) {
|
||||
if(!btn_byte_ptr || btn_byte_count == 0) return;
|
||||
submenu_called = 2;
|
||||
furi_string_set_str(byte_input_text, "Enter ");
|
||||
furi_string_cat_printf(byte_input_text, "%i", subghz_block_generic_global.btn_length_bit);
|
||||
furi_string_cat_str(byte_input_text, "-bits button in HEX");
|
||||
|
||||
@@ -150,6 +403,20 @@ void subghz_scene_signal_settings_variable_item_list_enter_callback(void* contex
|
||||
void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
counter32 = 0;
|
||||
counter16 = 0;
|
||||
cnt_byte_count = 0;
|
||||
cnt_byte_ptr = NULL;
|
||||
button = 0;
|
||||
btn_byte_count = 1;
|
||||
btn_byte_ptr = NULL;
|
||||
submenu_called = 0;
|
||||
button_uses_custom_btn = false;
|
||||
button_custom_id = SUBGHZ_CUSTOM_BTN_OK;
|
||||
subghz_block_generic_global_reset(NULL);
|
||||
subghz_custom_btns_reset();
|
||||
subghz_scene_signal_settings_reset_button_labels();
|
||||
|
||||
// ### Counter mode section ###
|
||||
|
||||
// When we open saved file we do some check and fill up subghz->file_path.
|
||||
@@ -162,6 +429,7 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
FuriString* tmp_text = furi_string_alloc_set_str("");
|
||||
FuriString* protocol_name = furi_string_alloc();
|
||||
|
||||
uint32_t tmp_counter_mode = 0;
|
||||
counter_mode = 0xff;
|
||||
@@ -174,8 +442,9 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
FURI_LOG_E(TAG, "Error open file %s", file_path);
|
||||
} else {
|
||||
flipper_format_read_string(fff_data_file, "Protocol", tmp_text);
|
||||
furi_string_set(protocol_name, tmp_text);
|
||||
// compare available protocols names, load CounterMode value from file and setup variable_item_list values_count
|
||||
for(uint8_t i = 0; i < PROTOCOLS_COUNT i++) {
|
||||
for(uint8_t i = 0; i < PROTOCOLS_COUNT; i++) {
|
||||
if(!strcmp(furi_string_get_cstr(tmp_text), protocols[i].name)) {
|
||||
mode_count = protocols[i].mode_count;
|
||||
if(flipper_format_read_uint32(fff_data_file, "CounterMode", &tmp_counter_mode, 1)) {
|
||||
@@ -223,7 +492,10 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
// deserialaze and decode loaded sugbhz file and push data to subghz_block_generic_global variable
|
||||
if(subghz_protocol_decoder_base_deserialize(decoder, subghz_txrx_get_fff_data(subghz->txrx)) ==
|
||||
SubGhzProtocolStatusOk) {
|
||||
subghz_scene_signal_settings_apply_button_labels(furi_string_get_cstr(protocol_name));
|
||||
subghz_protocol_decoder_base_get_string(decoder, tmp_text);
|
||||
subghz_scene_signal_settings_apply_text_fallback(tmp_text);
|
||||
subghz_scene_signal_settings_apply_file_fallback(subghz_txrx_get_fff_data(subghz->txrx));
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Cant deserialize this subghz file");
|
||||
}
|
||||
@@ -232,6 +504,7 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
|
||||
if(!subghz_block_generic_global.cnt_is_available) {
|
||||
counter_mode = 0xff;
|
||||
furi_string_set_str(tmp_text, "-");
|
||||
FURI_LOG_D(TAG, "Counter mode and edit not available for this protocol");
|
||||
} else {
|
||||
counter_not_available = false;
|
||||
@@ -261,26 +534,45 @@ void subghz_scene_signal_settings_on_enter(void* context) {
|
||||
|
||||
// ### Button edit section ###
|
||||
|
||||
if(!subghz_block_generic_global.btn_is_available) {
|
||||
if(subghz_custom_btn_is_allowed()) {
|
||||
uint8_t max_custom_btn = subghz_custom_btn_get_max();
|
||||
uint8_t custom_button_count = max_custom_btn + 1;
|
||||
if(custom_button_count > BUTTON_VALUE_COUNT) custom_button_count = BUTTON_VALUE_COUNT;
|
||||
button_uses_custom_btn = custom_button_count > 1;
|
||||
}
|
||||
|
||||
if(button_uses_custom_btn) {
|
||||
button_not_available = false;
|
||||
furi_string_set_str(
|
||||
tmp_text, subghz_scene_signal_settings_get_button_label(SUBGHZ_CUSTOM_BTN_OK));
|
||||
} else if(!subghz_block_generic_global.btn_is_available) {
|
||||
furi_string_set_str(tmp_text, "-");
|
||||
FURI_LOG_D(TAG, "Button edit not available for this protocol");
|
||||
} else {
|
||||
button_not_available = false;
|
||||
button = subghz_block_generic_global.current_btn;
|
||||
furi_string_printf(tmp_text, "%X", button);
|
||||
btn_byte_ptr = (uint8_t*)&button;
|
||||
furi_string_printf(tmp_text, "%X", button);
|
||||
}
|
||||
|
||||
item = variable_item_list_add(variable_item_list, "Edit Button", 1, NULL, subghz);
|
||||
uint8_t button_count = 1;
|
||||
if(button_uses_custom_btn) {
|
||||
button_count = subghz_custom_btn_get_max() + 1;
|
||||
if(button_count > BUTTON_VALUE_COUNT) button_count = BUTTON_VALUE_COUNT;
|
||||
}
|
||||
item = variable_item_list_add(
|
||||
variable_item_list,
|
||||
button_uses_custom_btn ? "Button" : "Edit Button",
|
||||
button_count,
|
||||
button_uses_custom_btn ? subghz_scene_signal_settings_button_changed : NULL,
|
||||
subghz);
|
||||
variable_item_set_current_value_index(item, 0);
|
||||
variable_item_set_current_value_text(item, furi_string_get_cstr(tmp_text));
|
||||
variable_item_set_locked(item, (button_not_available), "Not available\nfor this\nprotocol !");
|
||||
//
|
||||
|
||||
furi_assert(cnt_byte_ptr);
|
||||
furi_assert(cnt_byte_count > 0);
|
||||
furi_assert(btn_byte_ptr);
|
||||
|
||||
furi_string_free(tmp_text);
|
||||
furi_string_free(protocol_name);
|
||||
|
||||
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList);
|
||||
}
|
||||
@@ -290,23 +582,25 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubGhzCustomEventByteInputDone) {
|
||||
FlipperFormat* fff = subghz_txrx_get_fff_data(subghz->txrx);
|
||||
|
||||
switch(submenu_called) {
|
||||
// edit counter
|
||||
case 1:
|
||||
switch(cnt_byte_count) {
|
||||
case 2:
|
||||
// set new cnt value and override_flag to global variable and call transmit to generate and save subghz signal
|
||||
counter16 = __bswap16(counter16);
|
||||
subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter16);
|
||||
subghz_block_generic_global_counter_override_set(counter16);
|
||||
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
subghz_scene_signal_settings_rebuild_save_reload(
|
||||
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
|
||||
break;
|
||||
case 4:
|
||||
// the same for 32 bit Counter
|
||||
counter32 = __bswap32(counter32);
|
||||
subghz_scene_signal_settings_update_uint32_field(fff, "Cnt", counter32);
|
||||
subghz_block_generic_global_counter_override_set(counter32);
|
||||
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
subghz_scene_signal_settings_rebuild_save_reload(
|
||||
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -314,14 +608,10 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
|
||||
break;
|
||||
// edit button
|
||||
case 2:
|
||||
subghz_scene_signal_settings_update_uint32_field(fff, "Btn", button);
|
||||
subghz_block_generic_global_button_override_set(button);
|
||||
// save counter mult to rewrite subghz singnal without changing counter
|
||||
int32_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
|
||||
furi_hal_subghz_set_rolling_counter_mult(0);
|
||||
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
|
||||
subghz_txrx_stop(subghz->txrx);
|
||||
// restore counter mult
|
||||
furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
|
||||
subghz_scene_signal_settings_rebuild_save_reload(
|
||||
subghz, false, SUBGHZ_CUSTOM_BTN_OK);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -330,13 +620,10 @@ bool subghz_scene_signal_settings_on_event(void* context, SceneManagerEvent even
|
||||
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_previous_scene(subghz->scene_manager);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -352,4 +639,5 @@ void subghz_scene_signal_settings_on_exit(void* context) {
|
||||
byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(subghz->byte_input, "");
|
||||
furi_string_free(byte_input_text);
|
||||
subghz_custom_btns_reset();
|
||||
}
|
||||
|
||||
@@ -13,12 +13,15 @@ def ProtoPirateDefineEnabled(name, app_manifest_path=app_manifest_path):
|
||||
|
||||
|
||||
_ENABLE_TIMING_TUNER = ProtoPirateDefineEnabled("ENABLE_TIMING_TUNER_SCENE")
|
||||
_ENABLE_EMULATE = ProtoPirateDefineEnabled("ENABLE_EMULATE_FEATURE")
|
||||
|
||||
_MAIN_APP_SOURCES = [
|
||||
"*.c*",
|
||||
"!protocols/plugins",
|
||||
"!scenes/plugins",
|
||||
"!protocols",
|
||||
"!protopirate_emulate_plugin.c",
|
||||
"!protopirate_psa_bf_plugin.c",
|
||||
"protocols/protocol_items.c",
|
||||
"protocols/protocols_common.c",
|
||||
"!raw_file_reader.c",
|
||||
@@ -30,6 +33,11 @@ if not _ENABLE_TIMING_TUNER:
|
||||
"!protocol_timings.c",
|
||||
]
|
||||
|
||||
if not _ENABLE_EMULATE:
|
||||
_MAIN_APP_SOURCES += [
|
||||
"!protopirate_scene_emulate.c",
|
||||
]
|
||||
|
||||
App(
|
||||
appid="proto_pirate",
|
||||
name="ProtoPirate",
|
||||
@@ -39,7 +47,7 @@ App(
|
||||
requires=["gui"],
|
||||
stack_size=8 * 1024,
|
||||
fap_description="Decode car key fob signals from Sub-GHz",
|
||||
fap_version="3.0",
|
||||
fap_version="3.2",
|
||||
fap_icon="images/protopirate_10px.png",
|
||||
fap_category="Sub-GHz",
|
||||
fap_icon_assets="images",
|
||||
@@ -165,7 +173,11 @@ def ProtoPirateTxProtocolPlugin(
|
||||
App=App,
|
||||
FlipperAppType=FlipperAppType,
|
||||
tx_plugin_sources=_TX_PLUGIN_SOURCES,
|
||||
enable_emulate=_ENABLE_EMULATE,
|
||||
):
|
||||
if not enable_emulate:
|
||||
return
|
||||
|
||||
App(
|
||||
appid=f"protopirate_tx_{key}_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
@@ -370,17 +382,18 @@ if _ENABLE_TIMING_TUNER:
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="protopirate_emulate_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_emulate_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
sources=[
|
||||
"scenes/plugins/protopirate_emulate_plugin.c",
|
||||
"protocols/protocol_items.c",
|
||||
],
|
||||
fal_embedded=True,
|
||||
)
|
||||
if _ENABLE_EMULATE:
|
||||
App(
|
||||
appid="protopirate_emulate_plugin",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="protopirate_emulate_plugin_ep",
|
||||
requires=["proto_pirate"],
|
||||
sources=[
|
||||
"scenes/plugins/protopirate_emulate_plugin.c",
|
||||
"protocols/protocol_items.c",
|
||||
],
|
||||
fal_embedded=True,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="protopirate_psa_bf_plugin",
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#define TAG "ProtoPirateProtocolPlugin"
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#define PROTOPIRATE_TX_PLUGIN_PATH_MAX 160U
|
||||
#endif
|
||||
|
||||
static const char* protopirate_get_registry_plugin_path(ProtoPirateProtocolRegistryRoute route) {
|
||||
switch(route) {
|
||||
@@ -25,6 +27,7 @@ static const char* protopirate_get_registry_plugin_path(ProtoPirateProtocolRegis
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
static bool protopirate_build_tx_protocol_plugin_path(
|
||||
const char* tx_key,
|
||||
char* plugin_path,
|
||||
@@ -40,6 +43,7 @@ static bool protopirate_build_tx_protocol_plugin_path(
|
||||
tx_key);
|
||||
return (written > 0) && ((size_t)written < plugin_path_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const SubGhzProtocolRegistry protopirate_empty_protocol_registry = {
|
||||
.items = NULL,
|
||||
@@ -165,6 +169,7 @@ static bool protopirate_ensure_protocol_registry_plugin(
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
static bool protopirate_ensure_tx_protocol_plugin(
|
||||
ProtoPirateApp* app,
|
||||
const char* protocol_name,
|
||||
@@ -264,6 +269,7 @@ static bool protopirate_ensure_tx_protocol_plugin(
|
||||
*registry = plugin->registry;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool protopirate_refresh_protocol_registry(ProtoPirateApp* app, bool ensure_receiver_ready) {
|
||||
furi_check(app);
|
||||
@@ -378,6 +384,7 @@ bool protopirate_apply_protocol_registry_for_context(
|
||||
}
|
||||
|
||||
if(protocol_name && protocol_name[0] != '\0') {
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
const char* registry_name = protopirate_protocol_catalog_canonical_name(protocol_name);
|
||||
if(!registry_name || !protopirate_protocol_catalog_can_tx(protocol_name)) {
|
||||
FURI_LOG_E(TAG, "No TX protocol plugin for %s", protocol_name);
|
||||
@@ -410,6 +417,9 @@ bool protopirate_apply_protocol_registry_for_context(
|
||||
subghz_environment_set_protocol_registry(app->txrx->environment, tx_registry);
|
||||
app->txrx->protocol_registry = tx_registry;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
ProtoPirateProtocolRegistryRoute route = protopirate_get_protocol_registry_route(
|
||||
|
||||
@@ -103,12 +103,14 @@ void protopirate_settings_load(ProtoPirateSettings* settings) {
|
||||
}
|
||||
settings->hopping_enabled = (hopping_temp == 1);
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
uint32_t emulate_temp = 0;
|
||||
if(!flipper_format_read_uint32(ff, "EmulateFeature", &emulate_temp, 1)) {
|
||||
FURI_LOG_I(TAG, "EmulateFeature key missing, defaulting to disabled");
|
||||
emulate_temp = 0;
|
||||
}
|
||||
settings->emulate_feature_enabled = (emulate_temp == 1);
|
||||
#endif
|
||||
|
||||
uint32_t check_saved_temp = 0;
|
||||
if(!flipper_format_read_uint32(ff, "CheckSaved", &check_saved_temp, 1)) {
|
||||
@@ -185,11 +187,13 @@ void protopirate_settings_save(ProtoPirateSettings* settings) {
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
uint32_t emulate_temp = settings->emulate_feature_enabled ? 1 : 0;
|
||||
if(!flipper_format_write_uint32(ff, "EmulateFeature", &emulate_temp, 1)) {
|
||||
FURI_LOG_E(TAG, "Failed to write emulate feature flag");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t check_saved_temp = settings->check_saved ? 1 : 0;
|
||||
if(!flipper_format_write_uint32(ff, "CheckSaved", &check_saved_temp, 1)) {
|
||||
|
||||
@@ -513,6 +513,20 @@ static bool protopirate_storage_copy_key_2(
|
||||
protopirate_storage_copy_u32_optional(save_file, flipper_format, "Key_2");
|
||||
}
|
||||
|
||||
static bool protopirate_storage_copy_key2(
|
||||
FlipperFormat* save_file,
|
||||
FlipperFormat* flipper_format) {
|
||||
bool copied = false;
|
||||
if(!protopirate_storage_copy_hex_fixed(save_file, flipper_format, "Key2", 8, &copied)) {
|
||||
return false;
|
||||
}
|
||||
if(copied) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "Key2", 4);
|
||||
}
|
||||
|
||||
static bool protopirate_storage_write_capture_data(
|
||||
FlipperFormat* save_file,
|
||||
FlipperFormat* flipper_format) {
|
||||
@@ -548,7 +562,7 @@ static bool protopirate_storage_write_capture_data(
|
||||
protopirate_storage_base_u32_fields,
|
||||
COUNT_OF(protopirate_storage_base_u32_fields)))
|
||||
break;
|
||||
if(!protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "Key2", 4)) break;
|
||||
if(!protopirate_storage_copy_key2(save_file, flipper_format)) break;
|
||||
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "KeyIdx")) break;
|
||||
if(!protopirate_storage_copy_u32_optional(save_file, flipper_format, "Seed")) break;
|
||||
if(!protopirate_storage_copy_hex_or_u32(save_file, flipper_format, "ValidationField", 2))
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <furi.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../defines.h"
|
||||
|
||||
#define TAG "ProtoPirateCatalog"
|
||||
|
||||
#define PROTOPIRATE_CC1101_REG_MDMCFG2 0x12U
|
||||
@@ -17,6 +19,12 @@
|
||||
|
||||
#define PROTOPIRATE_COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
#define PROTOPIRATE_TX_KEY(key) key
|
||||
#else
|
||||
#define PROTOPIRATE_TX_KEY(key) NULL
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ProtoPirateProtocolCatalogModulationAM = 0,
|
||||
ProtoPirateProtocolCatalogModulationFM,
|
||||
@@ -28,33 +36,33 @@ typedef struct {
|
||||
} ProtoPirateProtocolCatalogAlias;
|
||||
|
||||
static const ProtoPirateProtocolCatalogEntry protopirate_protocol_catalog[] = {
|
||||
{"Chrysler V0", ProtoPirateProtocolCatalogRouteAMDefault, "chrysler_v0"},
|
||||
{"Fiat V0", ProtoPirateProtocolCatalogRouteAMDefault, "fiat_v0"},
|
||||
{"Fiat V1", ProtoPirateProtocolCatalogRouteAMDefault, "fiat_v1"},
|
||||
{"Chrysler V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("chrysler_v0")},
|
||||
{"Fiat V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("fiat_v0")},
|
||||
{"Fiat V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("fiat_v1")},
|
||||
{"Fiat V2", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
|
||||
{"Ford V0", ProtoPirateProtocolCatalogRouteAMDefault, "ford_v0"},
|
||||
{"Ford V1", ProtoPirateProtocolCatalogRouteFMF4, "ford_v1"},
|
||||
{"Ford V2", ProtoPirateProtocolCatalogRouteFMF4, "ford_v2"},
|
||||
{"Ford V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("ford_v0")},
|
||||
{"Ford V1", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("ford_v1")},
|
||||
{"Ford V2", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("ford_v2")},
|
||||
{"Ford V3", ProtoPirateProtocolCatalogRouteFMF4, NULL},
|
||||
{"Honda Static", ProtoPirateProtocolCatalogRouteFMHonda1, "honda_static"},
|
||||
{"Honda V1", ProtoPirateProtocolCatalogRouteAMDefault, "honda_v1"},
|
||||
{"Kia V0", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v0"},
|
||||
{"Kia V1", ProtoPirateProtocolCatalogRouteAMDefault, "kia_v1"},
|
||||
{"Kia V2", ProtoPirateProtocolCatalogRouteAMDefault, "kia_v2"},
|
||||
{"Kia V3/V4", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v3_v4"},
|
||||
{"Kia V5", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v5"},
|
||||
{"Kia V6", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v6"},
|
||||
{"Kia V7", ProtoPirateProtocolCatalogRouteFMDefault, "kia_v7"},
|
||||
{"Honda V2", ProtoPirateProtocolCatalogRouteFMF4, "honda_v2"},
|
||||
{"Mazda V0", ProtoPirateProtocolCatalogRouteByModulation, "mazda_v0"},
|
||||
{"Honda Static", ProtoPirateProtocolCatalogRouteFMHonda1, PROTOPIRATE_TX_KEY("honda_static")},
|
||||
{"Honda V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("honda_v1")},
|
||||
{"Kia V0", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v0")},
|
||||
{"Kia V1", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("kia_v1")},
|
||||
{"Kia V2", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("kia_v2")},
|
||||
{"Kia V3/V4", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v3_v4")},
|
||||
{"Kia V5", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v5")},
|
||||
{"Kia V6", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v6")},
|
||||
{"Kia V7", ProtoPirateProtocolCatalogRouteFMDefault, PROTOPIRATE_TX_KEY("kia_v7")},
|
||||
{"Honda V2", ProtoPirateProtocolCatalogRouteFMF4, PROTOPIRATE_TX_KEY("honda_v2")},
|
||||
{"Mazda V0", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("mazda_v0")},
|
||||
{"Mitsubishi V0", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
|
||||
{"Porsche Touareg", ProtoPirateProtocolCatalogRouteAMDefault, NULL},
|
||||
{"PSA", ProtoPirateProtocolCatalogRouteByModulation, "psa"},
|
||||
{"Renault V0", ProtoPirateProtocolCatalogRouteAMDefault, "renault_v0"},
|
||||
{"PSA", ProtoPirateProtocolCatalogRouteByModulation, PROTOPIRATE_TX_KEY("psa")},
|
||||
{"Renault V0", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("renault_v0")},
|
||||
{"Scher-Khan", ProtoPirateProtocolCatalogRouteFMDefault, NULL},
|
||||
{"Star Line", ProtoPirateProtocolCatalogRouteAMDefault, "star_line"},
|
||||
{"Subaru", ProtoPirateProtocolCatalogRouteAMDefault, "subaru"},
|
||||
{"VAG", ProtoPirateProtocolCatalogRouteAMVag, "vag"},
|
||||
{"Star Line", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("star_line")},
|
||||
{"Subaru", ProtoPirateProtocolCatalogRouteAMDefault, PROTOPIRATE_TX_KEY("subaru")},
|
||||
{"VAG", ProtoPirateProtocolCatalogRouteAMVag, PROTOPIRATE_TX_KEY("vag")},
|
||||
};
|
||||
|
||||
static const ProtoPirateProtocolCatalogAlias protopirate_protocol_catalog_aliases[] = {
|
||||
|
||||
@@ -90,7 +90,11 @@ ProtoPirateApp* protopirate_app_alloc() {
|
||||
app->auto_save = settings.auto_save;
|
||||
app->check_saved = settings.check_saved;
|
||||
app->tx_power = settings.tx_power;
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
app->emulate_feature_enabled = settings.emulate_feature_enabled;
|
||||
#else
|
||||
app->emulate_feature_enabled = false;
|
||||
#endif
|
||||
|
||||
// Init setting - KEEP THIS, it's small
|
||||
app->setting = subghz_setting_alloc();
|
||||
@@ -175,7 +179,11 @@ void protopirate_app_free(ProtoPirateApp* app) {
|
||||
settings.check_saved = app->check_saved;
|
||||
settings.tx_power = app->tx_power;
|
||||
settings.hopping_enabled = (app->txrx->hopper_state != ProtoPirateHopperStateOFF);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
settings.emulate_feature_enabled = app->emulate_feature_enabled;
|
||||
#else
|
||||
settings.emulate_feature_enabled = false;
|
||||
#endif
|
||||
|
||||
// Find current preset index
|
||||
settings.preset_index = 0;
|
||||
@@ -273,14 +281,18 @@ int32_t protopirate_app(char* p) {
|
||||
|
||||
//We now jump straight to emulate scene from Browser. If the user wanted the key to look at, just click back.
|
||||
if(load_saved) {
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
if(protopirate_app->emulate_feature_enabled) {
|
||||
view_dispatcher_send_custom_event(
|
||||
protopirate_app->view_dispatcher, ProtoPirateCustomEventSavedInfoEmulate);
|
||||
notification_message(protopirate_app->notifications, &sequence_success);
|
||||
} else {
|
||||
#endif
|
||||
view_dispatcher_send_custom_event(
|
||||
protopirate_app->view_dispatcher, ProtoPirateCustomEventReceiverInfoSave);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
view_dispatcher_run(protopirate_app->view_dispatcher);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#define CREDIT_LINE_HEIGHT 10
|
||||
#define SCROLL_SPEED 1
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
static const InputKey EMULATE_TOGGLE_COMBO[] = {
|
||||
InputKeyUp,
|
||||
InputKeyUp,
|
||||
@@ -23,6 +24,7 @@ static const InputKey EMULATE_TOGGLE_COMBO[] = {
|
||||
InputKeyRight,
|
||||
};
|
||||
#define EMULATE_TOGGLE_COMBO_LEN (sizeof(EMULATE_TOGGLE_COMBO) / sizeof(EMULATE_TOGGLE_COMBO[0]))
|
||||
#endif
|
||||
|
||||
static const char* credits[] = {
|
||||
"",
|
||||
@@ -62,7 +64,9 @@ typedef struct {
|
||||
uint8_t frame;
|
||||
uint8_t seed;
|
||||
int16_t scroll_offset;
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
uint8_t combo_progress;
|
||||
#endif
|
||||
} GlitchState;
|
||||
|
||||
static GlitchState g_state = {0};
|
||||
@@ -159,6 +163,7 @@ static void about_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
static bool about_input_callback(InputEvent* event, void* context) {
|
||||
furi_check(context);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
ProtoPirateApp* app = context;
|
||||
|
||||
if(event->type != InputTypePress) {
|
||||
@@ -184,8 +189,13 @@ static bool about_input_callback(InputEvent* event, void* context) {
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
UNUSED(event);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
static void about_show_emulate_toggle_popup(ProtoPirateApp* app) {
|
||||
const bool now_enabled = app->emulate_feature_enabled;
|
||||
|
||||
@@ -204,6 +214,7 @@ static void about_show_emulate_toggle_popup(ProtoPirateApp* app) {
|
||||
dialog_message_show(app->dialogs, message);
|
||||
dialog_message_free(message);
|
||||
}
|
||||
#endif
|
||||
|
||||
void protopirate_scene_about_on_enter(void* context) {
|
||||
furi_check(context);
|
||||
@@ -218,7 +229,9 @@ void protopirate_scene_about_on_enter(void* context) {
|
||||
g_state.frame = 0;
|
||||
g_state.seed = furi_get_tick() & 0xFF;
|
||||
g_state.scroll_offset = 0;
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
g_state.combo_progress = 0;
|
||||
#endif
|
||||
|
||||
view_set_draw_callback(app->view_about, about_draw_callback);
|
||||
view_set_input_callback(app->view_about, about_input_callback);
|
||||
@@ -245,7 +258,9 @@ bool protopirate_scene_about_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
view_commit_model(app->view_about, true);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom) {
|
||||
}
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
else if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == ProtoPirateCustomEventAboutToggleEmulate) {
|
||||
app->emulate_feature_enabled = !app->emulate_feature_enabled;
|
||||
|
||||
@@ -262,6 +277,7 @@ bool protopirate_scene_about_on_event(void* context, SceneManagerEvent event) {
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
@@ -1205,7 +1205,9 @@ bool protopirate_scene_sub_decode_on_event(void* context, SceneManagerEvent even
|
||||
app);
|
||||
|
||||
ctx->signal_info_left_is_emulate = false;
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
bool left_button_used = false;
|
||||
#endif
|
||||
app->emulate_disabled_for_loaded = true;
|
||||
|
||||
// Store reference to history item's flipper format for saving
|
||||
@@ -1239,7 +1241,9 @@ bool protopirate_scene_sub_decode_on_event(void* context, SceneManagerEvent even
|
||||
"Brute force",
|
||||
protopirate_scene_sub_decode_widget_callback,
|
||||
app);
|
||||
#ifdef ENABLE_EMULATE_FEATURE
|
||||
left_button_used = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@ uint8_t subghz_custom_btn_get(void) {
|
||||
return custom_btn_id;
|
||||
}
|
||||
|
||||
uint8_t subghz_custom_btn_get_max(void) {
|
||||
return custom_btn_max_btns;
|
||||
}
|
||||
|
||||
void subghz_custom_btn_set_original(uint8_t btn_code) {
|
||||
custom_btn_original = btn_code;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ void subghz_custom_btn_set_original(uint8_t btn_code);
|
||||
|
||||
void subghz_custom_btn_set_max(uint8_t b);
|
||||
|
||||
uint8_t subghz_custom_btn_get_max(void);
|
||||
|
||||
void subghz_custom_btn_set_prog_mode(ProgMode prog_mode);
|
||||
|
||||
ProgMode subghz_custom_btn_get_prog_mode(void);
|
||||
|
||||
@@ -407,7 +407,7 @@ LevelDuration subghz_protocol_encoder_fiat_spa_yield(void* context) {
|
||||
}
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -364,7 +364,7 @@ LevelDuration subghz_protocol_encoder_ford_v0_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
@@ -438,12 +438,18 @@ SubGhzProtocolStatus subghz_protocol_encoder_ford_v0_deserialize(void* context,
|
||||
}
|
||||
subghz_custom_btn_set_max(3);
|
||||
|
||||
instance->count = (instance->count + furi_hal_subghz_get_rolling_counter_mult()) & 0xFFFFF;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->count = override_cnt & 0xFFFFF;
|
||||
} else {
|
||||
instance->count = (instance->count + furi_hal_subghz_get_rolling_counter_mult()) & 0xFFFFF;
|
||||
}
|
||||
|
||||
uint8_t btn = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ?
|
||||
subghz_custom_btn_get_original() :
|
||||
subghz_custom_btn_get();
|
||||
instance->button = ford_v0_get_button_code(btn);
|
||||
subghz_block_generic_global_button_override_get(&instance->button);
|
||||
|
||||
uint8_t new_chk = 0;
|
||||
encode_ford_v0(instance->generic.data, instance->serial, instance->button, instance->count, &new_chk, &instance->key1);
|
||||
@@ -459,6 +465,8 @@ SubGhzProtocolStatus subghz_protocol_encoder_ford_v0_deserialize(void* context,
|
||||
flipper_format_update_hex(flipper_format, "Key", key_data, 8);
|
||||
flipper_format_update_uint32(flipper_format, "CheckSum", (uint32_t[]){new_chk}, 1);
|
||||
flipper_format_update_uint32(flipper_format, "CRC", (uint32_t[]){new_crc}, 1);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", (uint32_t[]){instance->button}, 1);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->count, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
return SubGhzProtocolStatusOk;
|
||||
|
||||
@@ -1278,8 +1278,9 @@ LevelDuration subghz_protocol_encoder_ford_v1_yield(void* context) {
|
||||
"Encoder yield: finished one full %lu-word frame (all %u bursts); repeats_left=%u",
|
||||
(unsigned long)instance->encoder.size_upload,
|
||||
(unsigned)FORD_V1_ENC_BURST_COUNT,
|
||||
(unsigned)instance->encoder.repeat - 1U);
|
||||
instance->encoder.repeat--;
|
||||
subghz_block_generic_global.endless_tx ? (unsigned)instance->encoder.repeat :
|
||||
(unsigned)instance->encoder.repeat - 1U);
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
} else if(instance->encoder.front <= 4U) {
|
||||
uint32_t raw_word;
|
||||
|
||||
@@ -399,6 +399,62 @@ static void ford_v2_encoder_refresh_data_from_raw(SubGhzProtocolEncoderFordV2* i
|
||||
}
|
||||
}
|
||||
|
||||
static void ford_v2_encoder_set_button_code(
|
||||
SubGhzProtocolEncoderFordV2* instance,
|
||||
uint8_t new_code) {
|
||||
if(!ford_v2_button_is_valid(new_code)) return;
|
||||
|
||||
instance->raw_bytes[6] = new_code;
|
||||
instance->raw_bytes[7] =
|
||||
(instance->raw_bytes[7] & 0x7FU) | (uint8_t)(ford_v2_uint8_parity(new_code) << 7);
|
||||
ford_v2_encoder_refresh_data_from_raw(instance);
|
||||
instance->generic.btn = new_code;
|
||||
}
|
||||
|
||||
static void ford_v2_encoder_set_counter(
|
||||
SubGhzProtocolEncoderFordV2* instance,
|
||||
uint16_t cnt) {
|
||||
cnt &= 0x7FFFU;
|
||||
instance->raw_bytes[7] =
|
||||
(instance->raw_bytes[7] & 0x80U) | (uint8_t)((cnt >> 9) & 0x7FU);
|
||||
instance->raw_bytes[8] = (uint8_t)((cnt >> 1) & 0xFFU);
|
||||
instance->raw_bytes[9] =
|
||||
(instance->raw_bytes[9] & 0x7FU) | (uint8_t)((cnt & 1U) << 7);
|
||||
ford_v2_encoder_refresh_data_from_raw(instance);
|
||||
instance->generic.cnt = cnt;
|
||||
}
|
||||
|
||||
static void ford_v2_encoder_update_flipper_format(
|
||||
SubGhzProtocolEncoderFordV2* instance,
|
||||
FlipperFormat* flipper_format) {
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_hex(
|
||||
flipper_format, "Key", instance->raw_bytes, FORD_V2_KEY_BYTE_COUNT);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(
|
||||
flipper_format, "Serial", &instance->generic.serial, 1);
|
||||
|
||||
uint32_t btn = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn, 1);
|
||||
|
||||
uint32_t cnt = instance->generic.cnt;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &cnt, 1);
|
||||
|
||||
uint32_t tail31 = (((uint32_t)(instance->raw_bytes[9] & 0x7FU)) << 24) |
|
||||
((uint32_t)instance->raw_bytes[10] << 16) |
|
||||
((uint32_t)instance->raw_bytes[11] << 8) |
|
||||
(uint32_t)instance->raw_bytes[12];
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Tail31", &tail31, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_hex(
|
||||
flipper_format, "TailRaw", &instance->raw_bytes[8], FORD_V2_TAIL_RAW_BYTE_COUNT);
|
||||
}
|
||||
|
||||
static inline void ford_v2_encoder_emit_manchester_bit(
|
||||
SubGhzProtocolEncoderFordV2* instance,
|
||||
bool bit) {
|
||||
@@ -490,17 +546,14 @@ static SubGhzProtocolStatus ford_v2_encoder_deserialize_validate_and_pack(
|
||||
(((uint16_t)instance->raw_bytes[8]) << 1) |
|
||||
((uint16_t)(instance->raw_bytes[9] >> 7)));
|
||||
|
||||
cnt = (cnt + 1U) & 0x7FFFU;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
cnt = override_cnt & 0x7FFFU;
|
||||
} else {
|
||||
cnt = (cnt + furi_hal_subghz_get_rolling_counter_mult()) & 0x7FFFU;
|
||||
}
|
||||
|
||||
// raw_bytes[7] bits [6:0] = cnt[14:8], bit[7] = parity(btn)
|
||||
instance->raw_bytes[7] = (instance->raw_bytes[7] & 0x80U) |
|
||||
(uint8_t)((cnt >> 9) & 0x7FU);
|
||||
instance->raw_bytes[8] = (uint8_t)((cnt >> 1) & 0xFFU);
|
||||
// raw_bytes[9] bit[7] = cnt[0], bits[6:0] = tail
|
||||
instance->raw_bytes[9] = (instance->raw_bytes[9] & 0x7FU) |
|
||||
(uint8_t)((cnt & 1U) << 7);
|
||||
|
||||
ford_v2_encoder_refresh_data_from_raw(instance);
|
||||
ford_v2_encoder_set_counter(instance, cnt);
|
||||
|
||||
instance->generic.btn = instance->raw_bytes[6];
|
||||
instance->generic.serial =
|
||||
@@ -566,17 +619,13 @@ SubGhzProtocolStatus
|
||||
if(ret == SubGhzProtocolStatusOk) {
|
||||
ford_v2_custom_btn_init(instance->raw_bytes[6]);
|
||||
|
||||
uint8_t btn_sel = subghz_custom_btn_get();
|
||||
if(btn_sel != SUBGHZ_CUSTOM_BTN_OK) {
|
||||
uint8_t new_code = ford_v2_custom_btn_to_code(btn_sel);
|
||||
if(ford_v2_button_is_valid(new_code)) {
|
||||
instance->raw_bytes[6] = new_code;
|
||||
const uint8_t k7_msb =
|
||||
(uint8_t)(ford_v2_uint8_parity(new_code) << 7);
|
||||
instance->raw_bytes[7] =
|
||||
(instance->raw_bytes[7] & 0x7FU) | k7_msb;
|
||||
ford_v2_encoder_refresh_data_from_raw(instance);
|
||||
instance->generic.btn = new_code;
|
||||
uint8_t new_code = 0;
|
||||
if(subghz_block_generic_global_button_override_get(&new_code)) {
|
||||
ford_v2_encoder_set_button_code(instance, new_code);
|
||||
} else {
|
||||
uint8_t btn_sel = subghz_custom_btn_get();
|
||||
if(btn_sel != SUBGHZ_CUSTOM_BTN_OK) {
|
||||
ford_v2_encoder_set_button_code(instance, ford_v2_custom_btn_to_code(btn_sel));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,6 +635,7 @@ SubGhzProtocolStatus
|
||||
(instance->extra_data << 8) | (uint64_t)instance->raw_bytes[8U + i];
|
||||
}
|
||||
|
||||
ford_v2_encoder_update_flipper_format(instance, flipper_format);
|
||||
ford_v2_encoder_deserialize_apply_repeat(instance, flipper_format);
|
||||
ford_v2_encoder_build_upload(instance);
|
||||
instance->encoder.is_running = true;
|
||||
@@ -615,7 +665,7 @@ LevelDuration subghz_protocol_encoder_ford_v2_yield(void* context) {
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.front = 0U;
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -154,7 +154,7 @@ LevelDuration subghz_protocol_encoder_kia_yield(void* context) {
|
||||
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
@@ -193,8 +193,10 @@ static bool subghz_protocol_encoder_kia_get_upload(SubGhzProtocolEncoderKIA* ins
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
// Counter increment logic
|
||||
if(instance->generic.cnt < 0xFFFF) {
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFFF;
|
||||
} else if(instance->generic.cnt < 0xFFFF) {
|
||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
} else {
|
||||
@@ -209,6 +211,7 @@ static bool subghz_protocol_encoder_kia_get_upload(SubGhzProtocolEncoderKIA* ins
|
||||
uint8_t btn = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ?
|
||||
subghz_custom_btn_get_original() :
|
||||
subghz_custom_btn_get();
|
||||
subghz_block_generic_global_button_override_get(&btn);
|
||||
|
||||
// Update the generic button value for potential button changes
|
||||
instance->generic.btn = btn;
|
||||
@@ -325,6 +328,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_deserialize(void* context, Flip
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp_btn = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
} while(false);
|
||||
|
||||
@@ -594,4 +604,3 @@ void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) {
|
||||
received_crc,
|
||||
crc_valid ? "(OK)" : "(FAIL)");
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ LevelDuration subghz_protocol_encoder_kia_v1_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
@@ -289,8 +289,10 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v1_deserialize(void* context, F
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
// Incrementa counter
|
||||
if(instance->generic.cnt < 0xFFF) {
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFF;
|
||||
} else if(instance->generic.cnt < 0xFFF) {
|
||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||
if(instance->generic.cnt > 0xFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
@@ -301,7 +303,9 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v1_deserialize(void* context, F
|
||||
|
||||
// Gestione bottoni custom
|
||||
uint8_t btn = subghz_custom_btn_get();
|
||||
if(btn != SUBGHZ_CUSTOM_BTN_OK) {
|
||||
if(subghz_block_generic_global_button_override_get(&btn)) {
|
||||
instance->generic.btn = btn;
|
||||
} else if(btn != SUBGHZ_CUSTOM_BTN_OK) {
|
||||
instance->generic.btn = btn;
|
||||
}
|
||||
|
||||
@@ -324,6 +328,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v1_deserialize(void* context, F
|
||||
ret = SubGhzProtocolStatusErrorParserKey;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp_btn = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
|
||||
@@ -183,7 +183,7 @@ LevelDuration subghz_protocol_encoder_kia_v2_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
@@ -214,7 +214,10 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v2_deserialize(void* context, F
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
if(instance->generic.cnt < 0xFFF) {
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFF;
|
||||
} else if(instance->generic.cnt < 0xFFF) {
|
||||
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
|
||||
if(instance->generic.cnt > 0xFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
@@ -224,7 +227,9 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v2_deserialize(void* context, F
|
||||
}
|
||||
|
||||
uint8_t btn = subghz_custom_btn_get();
|
||||
if(btn != SUBGHZ_CUSTOM_BTN_OK) {
|
||||
if(subghz_block_generic_global_button_override_get(&btn)) {
|
||||
instance->generic.btn = btn;
|
||||
} else if(btn != SUBGHZ_CUSTOM_BTN_OK) {
|
||||
instance->generic.btn = btn;
|
||||
}
|
||||
|
||||
@@ -257,6 +262,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v2_deserialize(void* context, F
|
||||
ret = SubGhzProtocolStatusErrorParserKey;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp_btn = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
|
||||
@@ -124,6 +124,8 @@ static uint8_t kia_v3_v4_calculate_crc(uint8_t* bytes) {
|
||||
return crc & 0x0F;
|
||||
}
|
||||
|
||||
static uint8_t kia_v3_v4_btn_to_custom(uint8_t btn);
|
||||
|
||||
static bool kia_v3_v4_process_buffer(SubGhzProtocolDecoderKiaV3V4* instance) {
|
||||
if(instance->raw_bit_count < 68) {
|
||||
return false;
|
||||
@@ -175,7 +177,7 @@ static bool kia_v3_v4_process_buffer(SubGhzProtocolDecoderKiaV3V4* instance) {
|
||||
instance->decoder.decode_count_bit = 68;
|
||||
|
||||
if(subghz_custom_btn_get_original() == 0) {
|
||||
subghz_custom_btn_set_original(instance->generic.btn);
|
||||
subghz_custom_btn_set_original(kia_v3_v4_btn_to_custom(instance->generic.btn));
|
||||
}
|
||||
subghz_custom_btn_set_max(5);
|
||||
|
||||
@@ -222,6 +224,17 @@ static const char* subghz_protocol_kia_v3_v4_get_name_button(uint8_t btn) {
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t kia_v3_v4_btn_to_custom(uint8_t btn) {
|
||||
switch(btn) {
|
||||
case 0x1: return 1;
|
||||
case 0x2: return 2;
|
||||
case 0x3: return 3;
|
||||
case 0x4: return 4;
|
||||
case 0x8: return 5;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void* subghz_protocol_encoder_kia_v3_v4_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderKiaV3V4* instance = malloc(sizeof(SubGhzProtocolEncoderKiaV3V4));
|
||||
@@ -520,7 +533,7 @@ SubGhzProtocolStatus
|
||||
}
|
||||
|
||||
if(subghz_custom_btn_get_original() == 0) {
|
||||
subghz_custom_btn_set_original(instance->btn);
|
||||
subghz_custom_btn_set_original(kia_v3_v4_btn_to_custom(instance->btn));
|
||||
}
|
||||
subghz_custom_btn_set_max(5);
|
||||
|
||||
@@ -531,14 +544,21 @@ SubGhzProtocolStatus
|
||||
selected_btn = subghz_custom_btn_get();
|
||||
}
|
||||
|
||||
if(selected_btn == 5) {
|
||||
if(subghz_block_generic_global_button_override_get(&selected_btn)) {
|
||||
instance->btn = selected_btn;
|
||||
} else if(selected_btn == 5) {
|
||||
instance->btn = 0x8;
|
||||
} else if(selected_btn >= 1 && selected_btn <= 4) {
|
||||
instance->btn = selected_btn;
|
||||
}
|
||||
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->cnt = (instance->cnt + mult) & 0xFFFF;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->cnt = override_cnt & 0xFFFF;
|
||||
} else {
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->cnt = (instance->cnt + mult) & 0xFFFF;
|
||||
}
|
||||
|
||||
instance->generic.btn = instance->btn;
|
||||
instance->generic.cnt = instance->cnt;
|
||||
@@ -583,6 +603,13 @@ SubGhzProtocolStatus
|
||||
uint32_t crc_to_write = instance->crc;
|
||||
flipper_format_update_uint32(flipper_format, "CRC", &crc_to_write, 1);
|
||||
|
||||
uint32_t btn_to_write = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn_to_write, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
instance->encoder.front = 0;
|
||||
|
||||
@@ -648,7 +675,7 @@ LevelDuration subghz_protocol_encoder_kia_v3_v4_yield(void* context) {
|
||||
instance->crc_iter = (uint8_t)((instance->crc_iter + 1U) & 0x0FU);
|
||||
subghz_protocol_encoder_kia_v3_v4_patch_crc(instance);
|
||||
instance->encoder.front = 0;
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
if(instance->bursts_sent < 16U) {
|
||||
instance->bursts_sent++;
|
||||
}
|
||||
@@ -931,7 +958,7 @@ SubGhzProtocolStatus
|
||||
}
|
||||
|
||||
if(subghz_custom_btn_get_original() == 0) {
|
||||
subghz_custom_btn_set_original(instance->generic.btn);
|
||||
subghz_custom_btn_set_original(kia_v3_v4_btn_to_custom(instance->generic.btn));
|
||||
}
|
||||
subghz_custom_btn_set_max(5);
|
||||
|
||||
|
||||
@@ -166,6 +166,26 @@ static uint64_t kia_v5_encode_data(uint32_t serial, uint16_t counter, uint8_t bu
|
||||
return bit_reverse_64(yek);
|
||||
}
|
||||
|
||||
static uint8_t kia_v5_custom_to_btn(uint8_t custom_btn) {
|
||||
switch(custom_btn) {
|
||||
case 1: return 0x01; // Unlock
|
||||
case 2: return 0x02; // Lock
|
||||
case 3: return 0x04; // Trunk
|
||||
case 4: return 0x08; // Horn
|
||||
default: return 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t kia_v5_btn_to_custom(uint8_t btn) {
|
||||
switch(btn) {
|
||||
case 0x01: return 1; // Unlock
|
||||
case 0x02: return 2; // Lock
|
||||
case 0x04: return 3; // Trunk
|
||||
case 0x08: return 4; // Horn
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct SubGhzProtocolDecoderKiaV5 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
@@ -327,15 +347,29 @@ SubGhzProtocolStatus
|
||||
uint32_t encrypted = (uint32_t)(yek & 0xFFFFFFFF);
|
||||
instance->generic.cnt = mixer_decode(encrypted);
|
||||
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFF;
|
||||
FURI_LOG_I(TAG, "deserialize #%lu, cnt after=%04lX", call_count, (uint32_t)instance->generic.cnt);
|
||||
|
||||
if(subghz_custom_btn_get_original() == 0) {
|
||||
subghz_custom_btn_set_original(instance->generic.btn);
|
||||
subghz_custom_btn_set_original(kia_v5_btn_to_custom(instance->generic.btn));
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFFF;
|
||||
} else {
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFF;
|
||||
}
|
||||
FURI_LOG_I(
|
||||
TAG, "deserialize #%lu, cnt after=%04lX", call_count, (uint32_t)instance->generic.cnt);
|
||||
|
||||
uint8_t btn = kia_v5_custom_to_btn(
|
||||
subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ? subghz_custom_btn_get_original() :
|
||||
subghz_custom_btn_get());
|
||||
if(subghz_block_generic_global_button_override_get(&btn)) {
|
||||
FURI_LOG_D(TAG, "Button changed to 0x%X", btn);
|
||||
}
|
||||
instance->generic.btn = btn & 0x0F;
|
||||
|
||||
instance->generic.data = kia_v5_encode_data(
|
||||
instance->generic.serial, instance->generic.cnt, instance->generic.btn);
|
||||
instance->replay_data = instance->generic.data;
|
||||
@@ -361,12 +395,12 @@ SubGhzProtocolStatus
|
||||
}
|
||||
|
||||
uint32_t temp_btn = instance->generic.btn;
|
||||
if(!flipper_format_update_uint32(flipper_format, "Btn", &temp_btn, 1)) {
|
||||
if(!flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1)) {
|
||||
ret = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1)) {
|
||||
if(!flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1)) {
|
||||
ret = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
@@ -374,18 +408,18 @@ SubGhzProtocolStatus
|
||||
yek = bit_reverse_64(instance->generic.data);
|
||||
yek_high = (uint32_t)(yek >> 32);
|
||||
yek_low = (uint32_t)(yek & 0xFFFFFFFF);
|
||||
if(!flipper_format_update_uint32(flipper_format, "YekHi", &yek_high, 1)) {
|
||||
if(!flipper_format_insert_or_update_uint32(flipper_format, "YekHi", &yek_high, 1)) {
|
||||
ret = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_update_uint32(flipper_format, "YekLo", &yek_low, 1)) {
|
||||
if(!flipper_format_insert_or_update_uint32(flipper_format, "YekLo", &yek_low, 1)) {
|
||||
ret = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t crc = kia_v5_calculate_crc(yek);
|
||||
uint32_t crc_temp = crc;
|
||||
if(!flipper_format_update_uint32(flipper_format, "CRC", &crc_temp, 1)) {
|
||||
if(!flipper_format_insert_or_update_uint32(flipper_format, "CRC", &crc_temp, 1)) {
|
||||
ret = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
@@ -456,7 +490,7 @@ LevelDuration subghz_protocol_encoder_kia_v5_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
@@ -596,7 +630,7 @@ void subghz_protocol_decoder_kia_v5_feed(void* context, bool level, uint32_t dur
|
||||
instance->decoder.decode_count_bit = instance->generic.data_count_bit;
|
||||
|
||||
if(subghz_custom_btn_get_original() == 0) {
|
||||
subghz_custom_btn_set_original(instance->generic.btn);
|
||||
subghz_custom_btn_set_original(kia_v5_btn_to_custom(instance->generic.btn));
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
@@ -730,7 +764,7 @@ SubGhzProtocolStatus
|
||||
instance->generic.cnt = mixer_decode(encrypted);
|
||||
|
||||
if(subghz_custom_btn_get_original() == 0) {
|
||||
subghz_custom_btn_set_original(instance->generic.btn);
|
||||
subghz_custom_btn_set_original(kia_v5_btn_to_custom(instance->generic.btn));
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
}
|
||||
|
||||
@@ -717,8 +717,10 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v6_deserialize(void* context, F
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
// Incrementa cnt
|
||||
if(instance->generic.cnt < 0xFFFFFFFF) {
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt;
|
||||
} else if(instance->generic.cnt < 0xFFFFFFFF) {
|
||||
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFFFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
} else {
|
||||
@@ -728,11 +730,15 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v6_deserialize(void* context, F
|
||||
instance->generic.cnt = 0;
|
||||
}
|
||||
|
||||
// Risolvi btn dal tasto premuto (identico a Suzuki)
|
||||
instance->generic.btn = kia_v6_custom_to_btn(
|
||||
subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK
|
||||
? subghz_custom_btn_get_original()
|
||||
: subghz_custom_btn_get());
|
||||
uint8_t override_btn = 0;
|
||||
if(subghz_block_generic_global_button_override_get(&override_btn)) {
|
||||
instance->generic.btn = override_btn & 0x0F;
|
||||
} else {
|
||||
instance->generic.btn = kia_v6_custom_to_btn(
|
||||
subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK
|
||||
? subghz_custom_btn_get_original()
|
||||
: subghz_custom_btn_get());
|
||||
}
|
||||
|
||||
kia_v6_encoder_build_upload(instance);
|
||||
|
||||
@@ -766,6 +772,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_kia_v6_deserialize(void* context, F
|
||||
uint32_t file_key3 = file_part3;
|
||||
flipper_format_update_uint32(flipper_format, "Key_4", &file_key3, 1);
|
||||
|
||||
uint32_t btn_to_write = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn_to_write, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
} while(false);
|
||||
@@ -791,7 +804,7 @@ LevelDuration subghz_protocol_encoder_kia_v6_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
instance->encoder.front++;
|
||||
if(instance->encoder.front >= instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -431,7 +431,7 @@ LevelDuration kia_protocol_encoder_v7_yield(void* context) {
|
||||
LevelDuration duration = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -969,7 +969,7 @@ LevelDuration subghz_protocol_encoder_land_rover_v0_yield(void* context) {
|
||||
|
||||
if(instance->encoder.front >= instance->encoder.size_upload) {
|
||||
/* One full repetition done; count it down */
|
||||
if(instance->encoder.repeat > 0) {
|
||||
if(instance->encoder.repeat > 0 && !subghz_block_generic_global.endless_tx) {
|
||||
instance->encoder.repeat--;
|
||||
}
|
||||
instance->encoder.front = 0;
|
||||
|
||||
@@ -499,7 +499,7 @@ LevelDuration subghz_protocol_encoder_mazda_v0_yield(void* context) {
|
||||
LevelDuration out = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ LevelDuration subghz_protocol_encoder_mitsubishi_v0_yield(void* context) {
|
||||
if(!instance->encoder.is_running || instance->encoder.repeat == 0) return level_duration_reset();
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -422,12 +422,14 @@ SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_deserialize(
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint8_t porsche_cayenne_btn_to_custom(uint8_t btn);
|
||||
|
||||
void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriString* output) {
|
||||
furi_check(context);
|
||||
SubGhzProtocolDecoderPorscheCayenne* instance = context;
|
||||
|
||||
if(subghz_custom_btn_get_original() == 0) {
|
||||
subghz_custom_btn_set_original(instance->generic.btn);
|
||||
subghz_custom_btn_set_original(porsche_cayenne_btn_to_custom(instance->generic.btn));
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
@@ -456,16 +458,37 @@ void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriStrin
|
||||
// ENCODER
|
||||
// =============================================================================
|
||||
|
||||
static uint8_t porsche_cayenne_custom_to_btn(uint8_t custom_btn, uint8_t fallback_btn) {
|
||||
switch(custom_btn) {
|
||||
case SUBGHZ_CUSTOM_BTN_UP: return 0x01; // Lock
|
||||
case SUBGHZ_CUSTOM_BTN_DOWN: return 0x02; // Unlock
|
||||
case SUBGHZ_CUSTOM_BTN_LEFT: return 0x04; // Trunk
|
||||
case SUBGHZ_CUSTOM_BTN_RIGHT: return 0x08; // Open
|
||||
default: return fallback_btn;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t porsche_cayenne_btn_to_custom(uint8_t btn) {
|
||||
switch(btn) {
|
||||
case 0x01: return SUBGHZ_CUSTOM_BTN_UP;
|
||||
case 0x02: return SUBGHZ_CUSTOM_BTN_DOWN;
|
||||
case 0x04: return SUBGHZ_CUSTOM_BTN_LEFT;
|
||||
case 0x08: return SUBGHZ_CUSTOM_BTN_RIGHT;
|
||||
default: return SUBGHZ_CUSTOM_BTN_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t porsche_cayenne_get_btn_code(void) {
|
||||
uint8_t override_btn = 0;
|
||||
if(subghz_block_generic_global_button_override_get(&override_btn)) {
|
||||
return override_btn;
|
||||
}
|
||||
|
||||
uint8_t custom_btn = subghz_custom_btn_get();
|
||||
uint8_t original_btn = subghz_custom_btn_get_original();
|
||||
uint8_t original_btn = porsche_cayenne_custom_to_btn(
|
||||
subghz_custom_btn_get_original(), 0x01);
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn;
|
||||
// Map d-pad buttons to common VAG key codes
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_UP) return 0x01; // Lock
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_DOWN) return 0x02; // Unlock
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_LEFT) return 0x04; // Trunk
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_RIGHT) return 0x08; // Open
|
||||
return original_btn;
|
||||
return porsche_cayenne_custom_to_btn(custom_btn, original_btn);
|
||||
}
|
||||
|
||||
static void porsche_cayenne_build_upload(SubGhzProtocolEncoderPorscheCayenne* instance) {
|
||||
@@ -483,6 +506,7 @@ static void porsche_cayenne_build_upload(SubGhzProtocolEncoderPorscheCayenne* in
|
||||
uint32_t serial = instance->generic.serial & 0xFFFFFF;
|
||||
uint8_t btn = porsche_cayenne_get_btn_code();
|
||||
uint16_t cnt = (uint16_t)instance->generic.cnt;
|
||||
instance->generic.btn = btn;
|
||||
|
||||
size_t idx = 0;
|
||||
LevelDuration* up = instance->encoder.upload;
|
||||
@@ -522,8 +546,9 @@ static void porsche_cayenne_build_upload(SubGhzProtocolEncoderPorscheCayenne* in
|
||||
instance->encoder.size_upload = idx;
|
||||
instance->encoder.front = 0;
|
||||
|
||||
// Advance stored counter by 4 (one per frame)
|
||||
instance->generic.cnt = (uint16_t)(cnt + 4);
|
||||
if(furi_hal_subghz_get_rolling_counter_mult() != 0) {
|
||||
instance->generic.cnt = (uint16_t)(cnt + 4);
|
||||
}
|
||||
}
|
||||
|
||||
void* subghz_protocol_encoder_porsche_cayenne_alloc(SubGhzEnvironment* environment) {
|
||||
@@ -582,9 +607,14 @@ SubGhzProtocolStatus subghz_protocol_encoder_porsche_cayenne_deserialize(
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFFF;
|
||||
}
|
||||
|
||||
// Apply custom button if set
|
||||
if(subghz_custom_btn_get_original() == 0) {
|
||||
subghz_custom_btn_set_original(instance->generic.btn);
|
||||
subghz_custom_btn_set_original(porsche_cayenne_btn_to_custom(instance->generic.btn));
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
@@ -596,6 +626,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_porsche_cayenne_deserialize(
|
||||
uint32_t new_cnt = instance->generic.cnt;
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Counter", &new_cnt, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &new_cnt, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
uint32_t new_btn = instance->generic.btn;
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &new_btn, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
} while(false);
|
||||
@@ -621,7 +658,7 @@ LevelDuration subghz_protocol_encoder_porsche_cayenne_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
|
||||
+62
-19
@@ -70,17 +70,38 @@ static const char* psa_button_name(uint8_t btn) {
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t psa_btn_to_custom(uint8_t btn) {
|
||||
switch(btn) {
|
||||
case 0x0:
|
||||
return SUBGHZ_CUSTOM_BTN_UP;
|
||||
case 0x1:
|
||||
return SUBGHZ_CUSTOM_BTN_DOWN;
|
||||
case 0x2:
|
||||
return SUBGHZ_CUSTOM_BTN_LEFT;
|
||||
default:
|
||||
return SUBGHZ_CUSTOM_BTN_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t psa_custom_to_btn(uint8_t custom_btn, uint8_t fallback_btn) {
|
||||
switch(custom_btn) {
|
||||
case SUBGHZ_CUSTOM_BTN_UP:
|
||||
return 0x0; // Lock
|
||||
case SUBGHZ_CUSTOM_BTN_DOWN:
|
||||
return 0x1; // Unlock
|
||||
case SUBGHZ_CUSTOM_BTN_LEFT:
|
||||
case SUBGHZ_CUSTOM_BTN_RIGHT:
|
||||
return 0x2; // Trunk
|
||||
default:
|
||||
return fallback_btn;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t psa_get_btn_code(void) {
|
||||
uint8_t custom_btn = subghz_custom_btn_get();
|
||||
uint8_t original_raw = subghz_custom_btn_get_original();
|
||||
// 0xFF is sentinel for PSA btn 0x0 (Lock)
|
||||
uint8_t original_btn = (original_raw == 0xFF) ? 0x0 : original_raw;
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn;
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_UP) return 0x0; // Lock
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_DOWN) return 0x1; // Unlock
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_LEFT) return 0x2; // Trunk
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_RIGHT) return 0x2; // Trunk
|
||||
return original_btn;
|
||||
uint8_t original_btn = psa_custom_to_btn(subghz_custom_btn_get_original(), 0x0);
|
||||
if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn;
|
||||
return psa_custom_to_btn(custom_btn, original_btn);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
@@ -1390,7 +1411,7 @@ void subghz_protocol_decoder_psa_get_string(void* context, FuriString* output) {
|
||||
|
||||
if(instance->decrypted == 0x50 && instance->decrypted_type != 0) {
|
||||
// Always update original button when loading a new file
|
||||
subghz_custom_btn_set_original(instance->generic.btn == 0 ? 0xFF : instance->generic.btn);
|
||||
subghz_custom_btn_set_original(psa_btn_to_custom(instance->generic.btn));
|
||||
subghz_custom_btn_set_max(4);
|
||||
uint8_t display_btn = psa_get_btn_code();
|
||||
if(instance->decrypted_type == 0x23) {
|
||||
@@ -1762,7 +1783,10 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
|
||||
counter = (counter << 4) | nibble;
|
||||
}
|
||||
} else {
|
||||
has_decrypted_data = false;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_uint32(flipper_format, "Cnt", &counter, 1)) {
|
||||
has_decrypted_data = false;
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
@@ -1779,7 +1803,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
|
||||
}
|
||||
button = (uint8_t)(btn_val & 0xFF);
|
||||
} else {
|
||||
has_decrypted_data = false;
|
||||
uint32_t btn_val = 0;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Btn", &btn_val, 1)) {
|
||||
button = (uint8_t)(btn_val & 0xFF);
|
||||
} else {
|
||||
has_decrypted_data = false;
|
||||
}
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
@@ -1849,14 +1879,20 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
|
||||
// Setup custom button system
|
||||
// Save original button from FILE before any d-pad remapping
|
||||
uint8_t file_btn = button; // 'button' was read directly from file above
|
||||
subghz_custom_btn_set_original(file_btn == 0 ? 0xFF : file_btn);
|
||||
subghz_custom_btn_set_original(psa_btn_to_custom(file_btn));
|
||||
subghz_custom_btn_set_max(4);
|
||||
instance->button = psa_get_btn_code();
|
||||
if(!subghz_block_generic_global_button_override_get(&instance->button)) {
|
||||
instance->button = psa_get_btn_code();
|
||||
}
|
||||
FURI_LOG_I("PSA_ENC", "file_btn=%02X custom=%02X result=%02X orig=%02X", file_btn, subghz_custom_btn_get(), instance->button, subghz_custom_btn_get_original());
|
||||
|
||||
// Increment counter
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->counter = (instance->counter + mult) & 0xFFFFFFFF;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->counter = override_cnt;
|
||||
} else {
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->counter = (instance->counter + mult) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
psa_encoder_build_upload(instance);
|
||||
|
||||
@@ -1879,7 +1915,12 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
|
||||
(unsigned int)((instance->counter >> 8) & 0xFF),
|
||||
(unsigned int)(instance->counter & 0xFF));
|
||||
}
|
||||
flipper_format_update_string_cstr(flipper_format, "Cnt", cnt_str);
|
||||
flipper_format_insert_or_update_string_cstr(flipper_format, "Cnt", cnt_str);
|
||||
|
||||
char btn_str[8];
|
||||
snprintf(btn_str, sizeof(btn_str), "%02X", (unsigned int)instance->button);
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_string_cstr(flipper_format, "Btn", btn_str);
|
||||
|
||||
char key_str[32];
|
||||
uint64_t key1 = ((uint64_t)instance->key1_high << 32) | instance->key1_low;
|
||||
@@ -1892,6 +1933,7 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
|
||||
(unsigned int)((key1 >> 16) & 0xFF),
|
||||
(unsigned int)((key1 >> 8) & 0xFF),
|
||||
(unsigned int)(key1 & 0xFF));
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_update_string_cstr(flipper_format, "Key", key_str);
|
||||
|
||||
char key2_str[32];
|
||||
@@ -1899,6 +1941,7 @@ SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, Flip
|
||||
0, 0, 0, 0, 0, 0,
|
||||
(unsigned int)((instance->key2_low >> 8) & 0xFF),
|
||||
(unsigned int)(instance->key2_low & 0xFF));
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_update_string_cstr(flipper_format, "Key_2", key2_str);
|
||||
|
||||
|
||||
@@ -1932,7 +1975,7 @@ LevelDuration subghz_protocol_encoder_psa_yield(void* context) {
|
||||
instance->encoder.front++;
|
||||
if(instance->encoder.front >= instance->encoder.size_upload) {
|
||||
instance->encoder.front = 0;
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
if(instance->encoder.repeat <= 0) instance->is_running = false;
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -307,10 +307,16 @@ static bool subghz_protocol_encoder_scher_khan_get_upload(
|
||||
|
||||
// For 51-bit dynamic: rebuild data with new button and incremented counter
|
||||
if(instance->generic.data_count_bit == 51) {
|
||||
if((instance->generic.cnt + 1) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFFF;
|
||||
} else {
|
||||
instance->generic.cnt += 1;
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
if((instance->generic.cnt + mult) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
} else {
|
||||
instance->generic.cnt += mult;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t upper = instance->generic.data & 0x7FFFFFFF0000ULL;
|
||||
@@ -320,10 +326,16 @@ static bool subghz_protocol_encoder_scher_khan_get_upload(
|
||||
}
|
||||
|
||||
if(instance->generic.data_count_bit == 57) {
|
||||
if((instance->generic.cnt + 1) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFFF;
|
||||
} else {
|
||||
instance->generic.cnt += 1;
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
if((instance->generic.cnt + mult) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
} else {
|
||||
instance->generic.cnt += mult;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t kb[7];
|
||||
@@ -581,6 +593,7 @@ SubGhzProtocolStatus
|
||||
instance->protocol_name = pname;
|
||||
|
||||
uint8_t selected_btn = scher_khan_get_btn_code(instance->generic.btn);
|
||||
subghz_block_generic_global_button_override_get(&selected_btn);
|
||||
|
||||
FURI_LOG_I(TAG,
|
||||
"Building upload: original_btn=0x%02X, selected_btn=0x%02X, bits=%d",
|
||||
@@ -622,7 +635,7 @@ LevelDuration subghz_protocol_encoder_scher_khan_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,10 +282,16 @@ static bool subghz_protocol_encoder_sheriff_cfm_get_upload(
|
||||
uint8_t btn) {
|
||||
furi_check(instance);
|
||||
|
||||
if((instance->generic.cnt + 1) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFFF;
|
||||
} else {
|
||||
instance->generic.cnt += 1;
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
if((instance->generic.cnt + mult) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
} else {
|
||||
instance->generic.cnt += mult;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t fix = (uint32_t)(instance->generic.data >> 32);
|
||||
@@ -388,11 +394,30 @@ SubGhzProtocolStatus
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
uint8_t selected_btn = cfm_get_btn_code(instance->generic.btn);
|
||||
subghz_block_generic_global_button_override_get(&selected_btn);
|
||||
|
||||
if(!subghz_protocol_encoder_sheriff_cfm_get_upload(instance, selected_btn)) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF;
|
||||
}
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t));
|
||||
|
||||
uint32_t model = (uint32_t)instance->model;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Model", &model, 1);
|
||||
|
||||
uint32_t btn = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
instance->encoder.front = 0;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
@@ -418,7 +443,7 @@ LevelDuration subghz_protocol_encoder_sheriff_cfm_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -206,11 +206,18 @@ void subghz_protocol_encoder_star_line_free(void* context) {
|
||||
static bool
|
||||
subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) {
|
||||
|
||||
if((instance->generic.cnt + 1) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFFF;
|
||||
} else {
|
||||
instance->generic.cnt += 1;
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
if((instance->generic.cnt + mult) > 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
} else {
|
||||
instance->generic.cnt += mult;
|
||||
}
|
||||
}
|
||||
instance->generic.btn = btn;
|
||||
|
||||
uint32_t fix = btn << 24 | instance->generic.serial;
|
||||
uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt;
|
||||
@@ -520,6 +527,7 @@ SubGhzProtocolStatus
|
||||
&instance->generic, instance->keystore, &instance->manufacture_name);
|
||||
|
||||
uint8_t selected_btn = star_line_get_btn_code(instance->generic.btn);
|
||||
subghz_block_generic_global_button_override_get(&selected_btn);
|
||||
subghz_protocol_encoder_star_line_get_upload(instance, selected_btn);
|
||||
|
||||
subghz_protocol_encoder_star_line_serialize(instance, flipper_format);
|
||||
@@ -556,7 +564,7 @@ LevelDuration subghz_protocol_encoder_star_line_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ LevelDuration subghz_protocol_encoder_subaru_yield(void* context) {
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
@@ -609,10 +609,16 @@ SubGhzProtocolStatus subghz_protocol_encoder_subaru_deserialize(void* context, F
|
||||
}
|
||||
|
||||
uint8_t new_button = subaru_get_button_code(selected_custom_btn);
|
||||
subghz_block_generic_global_button_override_get(&new_button);
|
||||
instance->button = new_button;
|
||||
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->count = (instance->count + mult) & 0xFFFF;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->count = override_cnt & 0xFFFF;
|
||||
} else {
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->count = (instance->count + mult) & 0xFFFF;
|
||||
}
|
||||
|
||||
b[0] = (b[0] & 0xF0) | (instance->button & 0x0F);
|
||||
|
||||
@@ -643,6 +649,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_subaru_deserialize(void* context, F
|
||||
ret = SubGhzProtocolStatusErrorParserKey;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp_btn = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
|
||||
@@ -400,13 +400,19 @@ SubGhzProtocolStatus subghz_protocol_encoder_suzuki_deserialize(void *context, F
|
||||
}
|
||||
subghz_custom_btn_set_max(4);
|
||||
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFFF;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->generic.cnt = override_cnt & 0xFFFFF;
|
||||
} else {
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFFF;
|
||||
}
|
||||
|
||||
uint8_t selected = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ?
|
||||
subghz_custom_btn_get_original() :
|
||||
subghz_custom_btn_get();
|
||||
uint8_t btn = suzuki_custom_to_btn(selected);
|
||||
subghz_block_generic_global_button_override_get(&btn);
|
||||
instance->generic.btn = btn;
|
||||
|
||||
uint64_t new_data = 0;
|
||||
@@ -434,6 +440,13 @@ SubGhzProtocolStatus subghz_protocol_encoder_suzuki_deserialize(void *context, F
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp_btn = instance->generic.btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &temp_btn, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
||||
|
||||
size_t preamble_count = SUZUKI_ENCODER_PREAMBLE_COUNT;
|
||||
size_t bit_count = 64;
|
||||
instance->encoder.size_upload = (preamble_count * 2) + (bit_count * 2) + 1;
|
||||
@@ -494,7 +507,7 @@ LevelDuration subghz_protocol_encoder_suzuki_yield(void *context)
|
||||
|
||||
if (++instance->encoder.front == instance->encoder.size_upload)
|
||||
{
|
||||
instance->encoder.repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1358,7 +1358,7 @@ LevelDuration subghz_protocol_encoder_vag_yield(void* context) {
|
||||
|
||||
if(instance->front >= instance->size_upload) {
|
||||
instance->front = 0;
|
||||
instance->repeat--;
|
||||
if(!subghz_block_generic_global.endless_tx) instance->repeat--;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1443,12 +1443,18 @@ SubGhzProtocolStatus
|
||||
}
|
||||
|
||||
uint8_t new_btn = vag_custom_to_btn(selected_custom, instance->btn);
|
||||
subghz_block_generic_global_button_override_get(&new_btn);
|
||||
instance->btn = new_btn;
|
||||
|
||||
|
||||
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->cnt = (instance->cnt + mult) & 0xFFFFFF;
|
||||
uint32_t override_cnt = 0;
|
||||
if(subghz_block_generic_global_counter_override_get(&override_cnt)) {
|
||||
instance->cnt = override_cnt & 0xFFFFFF;
|
||||
} else {
|
||||
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
|
||||
instance->cnt = (instance->cnt + mult) & 0xFFFFFF;
|
||||
}
|
||||
|
||||
uint8_t type_byte = (uint8_t)(instance->key1_high >> 24);
|
||||
if(instance->vag_type == 1 && type_byte == 0x00) {
|
||||
@@ -1518,6 +1524,12 @@ SubGhzProtocolStatus
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Type", &type32, 1);
|
||||
}
|
||||
|
||||
uint32_t btn32 = instance->btn;
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn32, 1);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &instance->cnt, 1);
|
||||
|
||||
|
||||
instance->repeat = 1;
|
||||
|
||||
+32
-4
@@ -3,7 +3,9 @@ import hashlib
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
|
||||
@@ -16,6 +18,21 @@ from flipper.app import App
|
||||
VERSION = 1
|
||||
|
||||
|
||||
def replace_file_with_retry(source: str, target: str, attempts: int = 50) -> None:
|
||||
for attempt in range(attempts):
|
||||
try:
|
||||
os.replace(source, target)
|
||||
return
|
||||
except PermissionError:
|
||||
if attempt == attempts - 1:
|
||||
raise
|
||||
time.sleep(0.1)
|
||||
except OSError:
|
||||
shutil.copy2(source, target)
|
||||
os.remove(source)
|
||||
return
|
||||
|
||||
|
||||
@dataclass
|
||||
class RelData:
|
||||
section: int
|
||||
@@ -135,11 +152,16 @@ class Main(App):
|
||||
RelSection(section_name, section.name, unique_relocations)
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
for section in sections:
|
||||
with tempfile.TemporaryDirectory(dir=os.path.dirname(os.path.abspath(fap_path))) as temp_dir:
|
||||
current_fap_path = fap_path
|
||||
|
||||
for section_index, section in enumerate(sections):
|
||||
data = serialize_relsection_data(section.data)
|
||||
hash_name = hashlib.md5(section.name.encode()).hexdigest()
|
||||
filename = f"{temp_dir}/{hash_name}.bin"
|
||||
filename = os.path.join(temp_dir, f"{section_index}_{hash_name}.bin")
|
||||
patched_fap_path = os.path.join(
|
||||
temp_dir, f"{section_index}_{hash_name}.fap"
|
||||
)
|
||||
|
||||
if os.path.isfile(filename):
|
||||
self.logger.error(f"File {filename} already exists")
|
||||
@@ -153,7 +175,8 @@ class Main(App):
|
||||
objcopy_path,
|
||||
"--add-section",
|
||||
f"{section.name}={filename}",
|
||||
fap_path,
|
||||
current_fap_path,
|
||||
patched_fap_path,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
@@ -162,6 +185,11 @@ class Main(App):
|
||||
self.logger.error("objcopy failed")
|
||||
return 1
|
||||
|
||||
current_fap_path = patched_fap_path
|
||||
|
||||
if current_fap_path != fap_path:
|
||||
replace_file_with_retry(current_fap_path, fap_path)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user