Files
ProtoPirate/protocols/protocols_common.c
T
0mega 3612385fcc New AM/FM protocol registry plugin split
More shared helpers in protocols_commons
Storage and history improvements
Add Chrysler V0, Ford V2 (simple replay encoder), Land Rover V0
Fix Fiat V0, Subaru & Kia V5
2026-05-05 20:13:02 +02:00

343 lines
11 KiB
C

#include "protocols_common.h"
#include <string.h>
const char FF_BIT[] = "Bit";
const char FF_KEY[] = "Key";
const char FF_SERIAL[] = "Serial";
const char FF_BTN[] = "Btn";
const char FF_CNT[] = "Cnt";
const char FF_REPEAT[] = "Repeat";
const char FF_PROTOCOL[] = "Protocol";
const char FF_PRESET[] = "Preset";
const char FF_FREQUENCY[] = "Frequency";
const char FF_MANUFACTURE[] = "Manufacture";
const char FF_TYPE[] = "Type";
uint8_t pp_reverse_bits8(uint8_t value) {
value = (uint8_t)(((value >> 4U) | (value << 4U)) & 0xFFU);
value = (uint8_t)(((value & 0x33U) << 2U) | ((value >> 2U) & 0x33U));
value = (uint8_t)(((value & 0x55U) << 1U) | ((value >> 1U) & 0x55U));
return value;
}
void pp_u64_to_bytes_be(uint64_t data, uint8_t bytes[8]) {
for(size_t i = 0; i < 8; i++) {
bytes[i] = (uint8_t)((data >> ((7U - i) * 8U)) & 0xFFU);
}
}
uint64_t pp_bytes_to_u64_be(const uint8_t bytes[8]) {
uint64_t data = 0;
for(size_t i = 0; i < 8; i++) {
data = (data << 8U) | bytes[i];
}
return data;
}
static bool pp_hex_nibble(char c, uint8_t* nibble) {
if(c >= '0' && c <= '9') {
*nibble = (uint8_t)(c - '0');
} else if(c >= 'A' && c <= 'F') {
*nibble = (uint8_t)(c - 'A' + 10);
} else if(c >= 'a' && c <= 'f') {
*nibble = (uint8_t)(c - 'a' + 10);
} else {
return false;
}
return true;
}
bool pp_parse_hex_u64_strict(const char* str, uint64_t* out_key) {
if(!str || !out_key) {
return false;
}
uint64_t key = 0;
uint8_t hex_pos = 0;
for(size_t i = 0; str[i] != '\0' && hex_pos < 16; i++) {
if(str[i] == ' ') {
continue;
}
uint8_t nibble = 0;
if(!pp_hex_nibble(str[i], &nibble)) {
return false;
}
key = (key << 4) | nibble;
hex_pos++;
}
if(hex_pos != 16) {
return false;
}
*out_key = key;
return true;
}
bool pp_flipper_read_hex_u64(FlipperFormat* flipper_format, const char* key, uint64_t* out_key) {
FuriString* value = furi_string_alloc();
if(!value) {
return false;
}
bool ok = false;
if(flipper_format_rewind(flipper_format) &&
flipper_format_read_string(flipper_format, key, value)) {
ok = pp_parse_hex_u64_strict(furi_string_get_cstr(value), out_key);
}
furi_string_free(value);
return ok;
}
void pp_flipper_update_or_insert_u32(
FlipperFormat* flipper_format,
const char* key,
uint32_t value) {
flipper_format_rewind(flipper_format);
if(!flipper_format_update_uint32(flipper_format, key, &value, 1)) {
flipper_format_rewind(flipper_format);
flipper_format_insert_or_update_uint32(flipper_format, key, &value, 1);
}
}
SubGhzProtocolStatus pp_verify_protocol_name(FlipperFormat* ff, const char* expected_name) {
if(!ff || !expected_name) {
return SubGhzProtocolStatusError;
}
FuriString* tmp = furi_string_alloc();
if(!tmp) {
return SubGhzProtocolStatusError;
}
SubGhzProtocolStatus result = SubGhzProtocolStatusError;
if(!flipper_format_read_string(ff, FF_PROTOCOL, tmp)) {
result = SubGhzProtocolStatusErrorParserOthers;
} else if(furi_string_equal(tmp, expected_name)) {
result = SubGhzProtocolStatusOk;
}
furi_string_free(tmp);
return result;
}
SubGhzProtocolStatus pp_encoder_read_bit(
FlipperFormat* ff,
const uint16_t* allowed_bits,
size_t allowed_bits_count,
uint32_t* out_bit) {
if(!ff || !out_bit) return SubGhzProtocolStatusError;
flipper_format_rewind(ff);
uint32_t bit = 0;
if(!flipper_format_read_uint32(ff, FF_BIT, &bit, 1)) {
return SubGhzProtocolStatusErrorValueBitCount;
}
if(allowed_bits && allowed_bits_count) {
bool ok = false;
for(size_t i = 0; i < allowed_bits_count; i++) {
if((uint32_t)allowed_bits[i] == bit) {
ok = true;
break;
}
}
if(!ok) return SubGhzProtocolStatusError;
}
*out_bit = bit;
return SubGhzProtocolStatusOk;
}
void pp_encoder_read_fields(
FlipperFormat* ff,
uint32_t* serial_out,
uint32_t* btn_out,
uint32_t* cnt_out,
uint32_t* type_out) {
if(!ff) return;
uint32_t tmp = 0;
if(serial_out) {
flipper_format_rewind(ff);
if(flipper_format_read_uint32(ff, FF_SERIAL, &tmp, 1)) *serial_out = tmp;
}
if(btn_out) {
flipper_format_rewind(ff);
if(flipper_format_read_uint32(ff, FF_BTN, &tmp, 1)) *btn_out = tmp;
}
if(cnt_out) {
flipper_format_rewind(ff);
if(flipper_format_read_uint32(ff, FF_CNT, &tmp, 1)) *cnt_out = tmp;
}
if(type_out) {
flipper_format_rewind(ff);
if(flipper_format_read_uint32(ff, FF_TYPE, &tmp, 1)) *type_out = tmp;
}
}
uint32_t pp_encoder_read_repeat(FlipperFormat* ff, uint32_t default_repeat) {
if(!ff) return default_repeat;
flipper_format_rewind(ff);
uint32_t tmp = 0;
return flipper_format_read_uint32(ff, FF_REPEAT, &tmp, 1) ? tmp : default_repeat;
}
SubGhzProtocolStatus pp_serialize_fields(
FlipperFormat* ff,
uint32_t field_mask,
uint32_t serial,
uint32_t btn,
uint32_t cnt,
uint32_t type) {
if(!ff) return SubGhzProtocolStatusError;
if((field_mask & PP_FIELD_SERIAL) && !flipper_format_write_uint32(ff, FF_SERIAL, &serial, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
if((field_mask & PP_FIELD_BTN) && !flipper_format_write_uint32(ff, FF_BTN, &btn, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
if((field_mask & PP_FIELD_CNT) && !flipper_format_write_uint32(ff, FF_CNT, &cnt, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
if((field_mask & PP_FIELD_TYPE) && !flipper_format_write_uint32(ff, FF_TYPE, &type, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
return SubGhzProtocolStatusOk;
}
SubGhzProtocolStatus
pp_write_display(FlipperFormat* ff, const char* protocol_name, const char* suffix) {
if(!ff || !protocol_name || !suffix) {
return SubGhzProtocolStatusError;
}
FuriString* display = furi_string_alloc();
if(!display) {
return SubGhzProtocolStatusError;
}
furi_string_printf(display, "%s - %s", protocol_name, suffix);
SubGhzProtocolStatus status =
flipper_format_write_string_cstr(ff, "Disp", furi_string_get_cstr(display)) ?
SubGhzProtocolStatusOk :
SubGhzProtocolStatusErrorParserOthers;
furi_string_free(display);
return status;
}
size_t pp_emit_merge(LevelDuration* up, size_t i, size_t cap, bool level, uint32_t us) {
if(i > 0 && level_duration_get_level(up[i - 1]) == level) {
uint32_t prev = level_duration_get_duration(up[i - 1]);
up[i - 1] = level_duration_make(level, prev + us);
return i;
}
if(i < cap) up[i++] = level_duration_make(level, us);
return i;
}
size_t
pp_emit_byte_manchester(LevelDuration* up, size_t i, size_t cap, uint8_t value, uint32_t te) {
for(int8_t bit = 7; bit >= 0; bit--) {
bool bit_value = ((value >> bit) & 1) != 0;
i = pp_emit_manchester_bit(up, i, cap, bit_value, te);
}
return i;
}
size_t
pp_emit_short_pairs(LevelDuration* up, size_t i, size_t cap, uint32_t te, size_t pair_count) {
for(size_t p = 0; p < pair_count; p++) {
i = pp_emit(up, i, cap, true, te);
i = pp_emit(up, i, cap, false, te);
}
return i;
}
void pp_encoder_free(void* context) {
furi_check(context);
ProtoPirateEncoderHeader* hdr = context;
hdr->encoder.upload = NULL;
hdr->encoder.size_upload = 0;
free(hdr);
}
void pp_encoder_stop(void* context) {
furi_check(context);
ProtoPirateEncoderHeader* hdr = context;
hdr->encoder.is_running = false;
hdr->encoder.front = 0;
}
LevelDuration pp_encoder_yield(void* context) {
furi_check(context);
ProtoPirateEncoderHeader* hdr = context;
if(hdr->encoder.repeat == 0 || !hdr->encoder.is_running || hdr->encoder.size_upload == 0) {
hdr->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = hdr->encoder.upload[hdr->encoder.front];
if(++hdr->encoder.front == hdr->encoder.size_upload) {
hdr->encoder.repeat--;
hdr->encoder.front = 0;
}
return ret;
}
static LevelDuration* pp_shared_upload_buf = NULL;
LevelDuration* pp_shared_upload_buffer(void) {
if(pp_shared_upload_buf == NULL) {
pp_shared_upload_buf = malloc(PP_SHARED_UPLOAD_CAPACITY * sizeof(LevelDuration));
furi_check(pp_shared_upload_buf);
}
return pp_shared_upload_buf;
}
size_t pp_shared_upload_capacity(void) {
return PP_SHARED_UPLOAD_CAPACITY;
}
void pp_shared_upload_release(void) {
free(pp_shared_upload_buf);
pp_shared_upload_buf = NULL;
}
void pp_encoder_buffer_ensure(void* context, size_t capacity) {
furi_check(context);
ProtoPirateEncoderHeader* hdr = context;
furi_check(capacity <= PP_SHARED_UPLOAD_CAPACITY);
hdr->encoder.upload = pp_shared_upload_buffer();
hdr->encoder.size_upload = capacity;
}
uint8_t pp_decoder_hash_blocks(void* context) {
furi_check(context);
ProtoPirateDecoderHeader* hdr = context;
return subghz_protocol_blocks_get_hash_data(
&hdr->decoder, (hdr->decoder.decode_count_bit / 8U) + 1U);
}
void pp_decoder_free_default(void* context) {
furi_check(context);
free(context);
}
bool pp_preset_name_is_custom_marker(const char* preset_name) {
return preset_name && (!strcmp(preset_name, "Custom") || !strcmp(preset_name, "CUSTOM") ||
!strcmp(preset_name, "FuriHalSubGhzPresetCustom") ||
strstr(preset_name, "PresetCustom"));
}
const char* pp_get_short_preset_name(const char* preset_name) {
if(!preset_name || preset_name[0] == '\0') return "AM650";
if(strstr(preset_name, "Ook650") || strstr(preset_name, "OOK650")) return "AM650";
if(strstr(preset_name, "Ook270") || strstr(preset_name, "OOK270")) return "AM270";
if(strstr(preset_name, "2FSKDev238") || strstr(preset_name, "Dev238")) return "FM238";
if(strstr(preset_name, "2FSKDev12K") || strstr(preset_name, "Dev12K")) return "FM12K";
if(strstr(preset_name, "2FSKDev476") || strstr(preset_name, "Dev476")) return "FM476";
if(pp_preset_name_is_custom_marker(preset_name)) return "Custom";
if(!strcmp(preset_name, "AM650")) return "AM650";
if(!strcmp(preset_name, "AM270")) return "AM270";
if(!strcmp(preset_name, "FM238")) return "FM238";
if(!strcmp(preset_name, "FM12K")) return "FM12K";
if(!strcmp(preset_name, "FM476")) return "FM476";
return preset_name;
}