mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-05-14 08:55:05 +00:00
3612385fcc
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
343 lines
11 KiB
C
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;
|
|
}
|