mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-16 00:18:11 -07:00
Merge branch 'dev' into zlo/tlsf-and-a-temple-of-memcorrupt
This commit is contained in:
@@ -411,6 +411,7 @@ bool protocol_electra_write_data(ProtocolElectra* protocol, void* data) {
|
||||
};
|
||||
|
||||
void protocol_electra_render_data(ProtocolElectra* protocol, FuriString* result) {
|
||||
protocol_electra_encoder_start(protocol);
|
||||
furi_string_printf(result, "Epilogue: %016llX", protocol->encoded_epilogue);
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -280,7 +280,7 @@ static void mjs_append_stack_trace_line(struct mjs* mjs, size_t offset) {
|
||||
const char* filename = mjs_get_bcode_filename_by_offset(mjs, offset);
|
||||
int line_no = mjs_get_lineno_by_offset(mjs, offset);
|
||||
char* new_line = NULL;
|
||||
const char* fmt = "at %s:%d\n";
|
||||
const char* fmt = "\tat %s:%d\r\n";
|
||||
if(filename == NULL) {
|
||||
// fprintf(
|
||||
// stderr,
|
||||
|
||||
@@ -33,6 +33,7 @@ env.Append(
|
||||
File("protocols/mf_ultralight/mf_ultralight_poller.h"),
|
||||
File("protocols/mf_classic/mf_classic_poller.h"),
|
||||
File("protocols/mf_desfire/mf_desfire_poller.h"),
|
||||
File("protocols/slix/slix_poller.h"),
|
||||
File("protocols/st25tb/st25tb_poller.h"),
|
||||
File("protocols/felica/felica_poller.h"),
|
||||
# Listeners
|
||||
|
||||
+1
-1
@@ -660,4 +660,4 @@ NfcError nfc_felica_listener_set_sensf_res_data(
|
||||
return nfc_process_hal_error(error);
|
||||
}
|
||||
|
||||
#endif // APP_UNIT_TESTS
|
||||
#endif // FW_CFG_unit_tests
|
||||
|
||||
@@ -0,0 +1,476 @@
|
||||
#ifdef FW_CFG_unit_tests
|
||||
|
||||
#include <lib/nfc/nfc.h>
|
||||
#include <lib/nfc/helpers/iso14443_crc.h>
|
||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
|
||||
#include <furi/furi.h>
|
||||
|
||||
#define NFC_MAX_BUFFER_SIZE (256)
|
||||
|
||||
typedef enum {
|
||||
NfcTransportLogLevelWarning,
|
||||
NfcTransportLogLevelInfo,
|
||||
} NfcTransportLogLevel;
|
||||
|
||||
FuriMessageQueue* poller_queue = NULL;
|
||||
FuriMessageQueue* listener_queue = NULL;
|
||||
|
||||
typedef enum {
|
||||
NfcMessageTypeTx,
|
||||
NfcMessageTypeTimeout,
|
||||
NfcMessageTypeAbort,
|
||||
} NfcMessageType;
|
||||
|
||||
typedef struct {
|
||||
uint16_t data_bits;
|
||||
uint8_t data[NFC_MAX_BUFFER_SIZE];
|
||||
} NfcMessageData;
|
||||
|
||||
typedef struct {
|
||||
NfcMessageType type;
|
||||
NfcMessageData data;
|
||||
} NfcMessage;
|
||||
|
||||
typedef enum {
|
||||
NfcStateIdle,
|
||||
NfcStateReady,
|
||||
NfcStateReset,
|
||||
} NfcState;
|
||||
|
||||
typedef enum {
|
||||
Iso14443_3aColResStatusIdle,
|
||||
Iso14443_3aColResStatusInProgress,
|
||||
Iso14443_3aColResStatusDone,
|
||||
} Iso14443_3aColResStatus;
|
||||
|
||||
typedef struct {
|
||||
Iso14443_3aSensResp sens_resp;
|
||||
Iso14443_3aSddResp sdd_resp[2];
|
||||
Iso14443_3aSelResp sel_resp[2];
|
||||
} Iso14443_3aColResData;
|
||||
|
||||
struct Nfc {
|
||||
NfcState state;
|
||||
|
||||
Iso14443_3aColResStatus col_res_status;
|
||||
Iso14443_3aColResData col_res_data;
|
||||
bool software_col_res_required;
|
||||
|
||||
NfcEventCallback callback;
|
||||
void* context;
|
||||
|
||||
NfcMode mode;
|
||||
|
||||
FuriThread* worker_thread;
|
||||
};
|
||||
|
||||
static void nfc_test_print(
|
||||
NfcTransportLogLevel log_level,
|
||||
const char* message,
|
||||
uint8_t* buffer,
|
||||
uint16_t bits) {
|
||||
FuriString* str = furi_string_alloc();
|
||||
size_t bytes = (bits + 7) / 8;
|
||||
|
||||
for(size_t i = 0; i < bytes; i++) {
|
||||
furi_string_cat_printf(str, " %02X", buffer[i]);
|
||||
}
|
||||
if(log_level == NfcTransportLogLevelWarning) {
|
||||
FURI_LOG_W(message, "%s", furi_string_get_cstr(str));
|
||||
} else {
|
||||
FURI_LOG_I(message, "%s", furi_string_get_cstr(str));
|
||||
}
|
||||
|
||||
furi_string_free(str);
|
||||
}
|
||||
|
||||
static void nfc_prepare_col_res_data(
|
||||
Nfc* instance,
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak) {
|
||||
memcpy(instance->col_res_data.sens_resp.sens_resp, atqa, 2);
|
||||
|
||||
if(uid_len == 7) {
|
||||
instance->col_res_data.sdd_resp[0].nfcid[0] = 0x88;
|
||||
memcpy(&instance->col_res_data.sdd_resp[0].nfcid[1], uid, 3);
|
||||
uint8_t bss = 0;
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
bss ^= instance->col_res_data.sdd_resp[0].nfcid[i];
|
||||
}
|
||||
instance->col_res_data.sdd_resp[0].bss = bss;
|
||||
instance->col_res_data.sel_resp[0].sak = 0x04;
|
||||
|
||||
memcpy(instance->col_res_data.sdd_resp[1].nfcid, &uid[3], 4);
|
||||
bss = 0;
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
bss ^= instance->col_res_data.sdd_resp[1].nfcid[i];
|
||||
}
|
||||
instance->col_res_data.sdd_resp[1].bss = bss;
|
||||
instance->col_res_data.sel_resp[1].sak = sak;
|
||||
|
||||
} else {
|
||||
furi_crash("Not supporting not 7 bytes");
|
||||
}
|
||||
}
|
||||
|
||||
Nfc* nfc_alloc(void) {
|
||||
Nfc* instance = malloc(sizeof(Nfc));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_free(Nfc* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) {
|
||||
UNUSED(instance);
|
||||
UNUSED(tech);
|
||||
|
||||
instance->mode = mode;
|
||||
}
|
||||
|
||||
void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) {
|
||||
UNUSED(instance);
|
||||
UNUSED(fdt_poll_fc);
|
||||
}
|
||||
|
||||
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {
|
||||
UNUSED(instance);
|
||||
UNUSED(fdt_listen_fc);
|
||||
}
|
||||
|
||||
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {
|
||||
UNUSED(instance);
|
||||
UNUSED(mask_rx_time_fc);
|
||||
}
|
||||
|
||||
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {
|
||||
UNUSED(instance);
|
||||
UNUSED(fdt_poll_poll_us);
|
||||
}
|
||||
|
||||
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {
|
||||
UNUSED(instance);
|
||||
UNUSED(guard_time_us);
|
||||
}
|
||||
|
||||
NfcError nfc_iso14443a_listener_set_col_res_data(
|
||||
Nfc* instance,
|
||||
uint8_t* uid,
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak) {
|
||||
furi_check(instance);
|
||||
furi_check(uid);
|
||||
furi_check(atqa);
|
||||
|
||||
nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak);
|
||||
instance->software_col_res_required = true;
|
||||
|
||||
return NfcErrorNone;
|
||||
}
|
||||
|
||||
static int32_t nfc_worker_poller(void* context) {
|
||||
Nfc* instance = context;
|
||||
furi_check(instance->callback);
|
||||
|
||||
instance->state = NfcStateReady;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
NfcEvent event = {};
|
||||
|
||||
while(true) {
|
||||
event.type = NfcEventTypePollerReady;
|
||||
command = instance->callback(event, instance->context);
|
||||
if(command == NfcCommandStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
instance->state = NfcStateIdle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, uint16_t rx_bits) {
|
||||
furi_check(instance->col_res_status != Iso14443_3aColResStatusDone);
|
||||
BitBuffer* tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
|
||||
|
||||
bool processed = false;
|
||||
|
||||
if((rx_bits == 7) && (rx_data[0] == 0x52)) {
|
||||
instance->col_res_status = Iso14443_3aColResStatusInProgress;
|
||||
bit_buffer_copy_bytes(
|
||||
tx_buffer,
|
||||
instance->col_res_data.sens_resp.sens_resp,
|
||||
sizeof(instance->col_res_data.sens_resp.sens_resp));
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
processed = true;
|
||||
} else if(rx_bits == 2 * 8) {
|
||||
if((rx_data[0] == 0x93) && (rx_data[1] == 0x20)) {
|
||||
bit_buffer_copy_bytes(
|
||||
tx_buffer,
|
||||
(const uint8_t*)&instance->col_res_data.sdd_resp[0],
|
||||
sizeof(Iso14443_3aSddResp));
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
processed = true;
|
||||
} else if((rx_data[0] == 0x95) && (rx_data[1] == 0x20)) {
|
||||
bit_buffer_copy_bytes(
|
||||
tx_buffer,
|
||||
(const uint8_t*)&instance->col_res_data.sdd_resp[1],
|
||||
sizeof(Iso14443_3aSddResp));
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
processed = true;
|
||||
}
|
||||
} else if(rx_bits == 9 * 8) {
|
||||
if((rx_data[0] == 0x93) && (rx_data[1] == 0x70)) {
|
||||
bit_buffer_set_size_bytes(tx_buffer, 1);
|
||||
bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[0].sak);
|
||||
iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
processed = true;
|
||||
} else if((rx_data[0] == 0x95) && (rx_data[1] == 0x70)) {
|
||||
bit_buffer_set_size_bytes(tx_buffer, 1);
|
||||
bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[1].sak);
|
||||
iso14443_crc_append(Iso14443CrcTypeA, tx_buffer);
|
||||
nfc_listener_tx(instance, tx_buffer);
|
||||
instance->col_res_status = Iso14443_3aColResStatusDone;
|
||||
NfcEvent event = {.type = NfcEventTypeListenerActivated};
|
||||
instance->callback(event, instance->context);
|
||||
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!processed) {
|
||||
NfcMessage message = {.type = NfcMessageTypeTimeout};
|
||||
furi_message_queue_put(poller_queue, &message, FuriWaitForever);
|
||||
}
|
||||
|
||||
bit_buffer_free(tx_buffer);
|
||||
}
|
||||
|
||||
static int32_t nfc_worker_listener(void* context) {
|
||||
Nfc* instance = context;
|
||||
furi_check(instance->callback);
|
||||
|
||||
NfcMessage message = {};
|
||||
|
||||
NfcEventData event_data = {};
|
||||
event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
|
||||
NfcEvent nfc_event = {.data = event_data};
|
||||
|
||||
while(true) {
|
||||
furi_message_queue_get(listener_queue, &message, FuriWaitForever);
|
||||
bit_buffer_copy_bits(event_data.buffer, message.data.data, message.data.data_bits);
|
||||
if((message.data.data[0] == 0x52) && (message.data.data_bits == 7)) {
|
||||
instance->col_res_status = Iso14443_3aColResStatusIdle;
|
||||
}
|
||||
|
||||
if(message.type == NfcMessageTypeAbort) {
|
||||
break;
|
||||
} else if(message.type == NfcMessageTypeTx) {
|
||||
nfc_test_print(
|
||||
NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits);
|
||||
if(instance->software_col_res_required &&
|
||||
(instance->col_res_status != Iso14443_3aColResStatusDone)) {
|
||||
nfc_worker_listener_pass_col_res(
|
||||
instance, message.data.data, message.data.data_bits);
|
||||
} else {
|
||||
instance->state = NfcStateReady;
|
||||
nfc_event.type = NfcEventTypeRxEnd;
|
||||
instance->callback(nfc_event, instance->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance->state = NfcStateIdle;
|
||||
instance->col_res_status = Iso14443_3aColResStatusIdle;
|
||||
memset(&instance->col_res_data, 0, sizeof(instance->col_res_data));
|
||||
bit_buffer_free(nfc_event.data.buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->worker_thread == NULL);
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
furi_check(listener_queue == NULL);
|
||||
// Check that poller didn't start
|
||||
furi_check(poller_queue == NULL);
|
||||
} else {
|
||||
furi_check(poller_queue == NULL);
|
||||
// Check that poller is started after listener
|
||||
furi_check(listener_queue);
|
||||
}
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
listener_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));
|
||||
} else {
|
||||
poller_queue = furi_message_queue_alloc(4, sizeof(NfcMessage));
|
||||
}
|
||||
|
||||
instance->worker_thread = furi_thread_alloc();
|
||||
furi_thread_set_context(instance->worker_thread, instance);
|
||||
furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHigh);
|
||||
furi_thread_set_stack_size(instance->worker_thread, 8 * 1024);
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
furi_thread_set_name(instance->worker_thread, "NfcWorkerListener");
|
||||
furi_thread_set_callback(instance->worker_thread, nfc_worker_listener);
|
||||
} else {
|
||||
furi_thread_set_name(instance->worker_thread, "NfcWorkerPoller");
|
||||
furi_thread_set_callback(instance->worker_thread, nfc_worker_poller);
|
||||
}
|
||||
|
||||
furi_thread_start(instance->worker_thread);
|
||||
}
|
||||
|
||||
void nfc_stop(Nfc* instance) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->worker_thread);
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
NfcMessage message = {.type = NfcMessageTypeAbort};
|
||||
furi_message_queue_put(listener_queue, &message, FuriWaitForever);
|
||||
furi_thread_join(instance->worker_thread);
|
||||
|
||||
furi_message_queue_free(listener_queue);
|
||||
listener_queue = NULL;
|
||||
|
||||
furi_thread_free(instance->worker_thread);
|
||||
instance->worker_thread = NULL;
|
||||
} else {
|
||||
furi_thread_join(instance->worker_thread);
|
||||
|
||||
furi_message_queue_free(poller_queue);
|
||||
poller_queue = NULL;
|
||||
|
||||
furi_thread_free(instance->worker_thread);
|
||||
instance->worker_thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Called from worker thread
|
||||
|
||||
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {
|
||||
furi_check(instance);
|
||||
furi_check(poller_queue);
|
||||
furi_check(listener_queue);
|
||||
furi_check(tx_buffer);
|
||||
|
||||
NfcMessage message = {};
|
||||
message.type = NfcMessageTypeTx;
|
||||
message.data.data_bits = bit_buffer_get_size(tx_buffer);
|
||||
bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
|
||||
|
||||
furi_message_queue_put(poller_queue, &message, FuriWaitForever);
|
||||
|
||||
return NfcErrorNone;
|
||||
}
|
||||
|
||||
NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) {
|
||||
return nfc_listener_tx(instance, tx_buffer);
|
||||
}
|
||||
|
||||
NfcError
|
||||
nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
furi_check(poller_queue);
|
||||
furi_check(listener_queue);
|
||||
UNUSED(fwt);
|
||||
|
||||
NfcError error = NfcErrorNone;
|
||||
|
||||
NfcMessage message = {};
|
||||
message.type = NfcMessageTypeTx;
|
||||
message.data.data_bits = bit_buffer_get_size(tx_buffer);
|
||||
bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
|
||||
// Tx
|
||||
furi_check(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
// Rx
|
||||
FuriStatus status = furi_message_queue_get(poller_queue, &message, 50);
|
||||
|
||||
if(status == FuriStatusErrorTimeout) {
|
||||
error = NfcErrorTimeout;
|
||||
} else if(message.type == NfcMessageTypeTx) {
|
||||
bit_buffer_copy_bits(rx_buffer, message.data.data, message.data.data_bits);
|
||||
nfc_test_print(
|
||||
NfcTransportLogLevelWarning, "TAG", message.data.data, message.data.data_bits);
|
||||
} else if(message.type == NfcMessageTypeTimeout) {
|
||||
error = NfcErrorTimeout;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
NfcError nfc_iso14443a_poller_trx_custom_parity(
|
||||
Nfc* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
|
||||
}
|
||||
|
||||
// Technology specific API
|
||||
|
||||
NfcError nfc_iso14443a_poller_trx_short_frame(
|
||||
Nfc* instance,
|
||||
NfcIso14443aShortFrame frame,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
UNUSED(frame);
|
||||
|
||||
BitBuffer* tx_buffer = bit_buffer_alloc(32);
|
||||
bit_buffer_set_size(tx_buffer, 7);
|
||||
bit_buffer_set_byte(tx_buffer, 0, 0x52);
|
||||
|
||||
NfcError error = nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
|
||||
|
||||
bit_buffer_free(tx_buffer);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
NfcError nfc_iso14443a_poller_trx_sdd_frame(
|
||||
Nfc* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt);
|
||||
}
|
||||
|
||||
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
|
||||
UNUSED(instance);
|
||||
|
||||
return NfcErrorNone;
|
||||
}
|
||||
|
||||
NfcError nfc_felica_listener_set_sensf_res_data(
|
||||
Nfc* instance,
|
||||
const uint8_t* idm,
|
||||
const uint8_t idm_len,
|
||||
const uint8_t* pmm,
|
||||
const uint8_t pmm_len) {
|
||||
furi_assert(instance);
|
||||
furi_assert(idm);
|
||||
furi_assert(pmm);
|
||||
furi_assert(idm_len == 8);
|
||||
furi_assert(pmm_len == 8);
|
||||
|
||||
return NfcErrorNone;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#define TAG "Iso15693_3Listener"
|
||||
|
||||
#define ISO15693_3_LISTENER_BUFFER_SIZE (64U)
|
||||
#define ISO15693_3_LISTENER_BUFFER_SIZE (256U)
|
||||
|
||||
Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) {
|
||||
furi_assert(nfc);
|
||||
@@ -67,6 +67,7 @@ NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) {
|
||||
if(nfc_event->type == NfcEventTypeRxEnd) {
|
||||
BitBuffer* rx_buffer = nfc_event->data.buffer;
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {
|
||||
iso13239_crc_trim(rx_buffer);
|
||||
|
||||
|
||||
@@ -64,7 +64,9 @@ static Iso15693_3Error iso15693_3_listener_inventory_handler(
|
||||
if(afi_flag) {
|
||||
const uint8_t afi = *data++;
|
||||
// When AFI flag is set, ignore non-matching requests
|
||||
if(afi != instance->data->system_info.afi) break;
|
||||
if(afi != 0) {
|
||||
if(afi != instance->data->system_info.afi) break;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t mask_len = *data++;
|
||||
@@ -260,16 +262,9 @@ static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler(
|
||||
}
|
||||
|
||||
const uint32_t block_index_start = request->first_block_num;
|
||||
const uint32_t block_index_end = block_index_start + request->block_count;
|
||||
|
||||
const uint32_t block_count = request->block_count + 1;
|
||||
const uint32_t block_count_max = instance->data->system_info.block_count;
|
||||
const uint32_t block_count_available = block_count_max - block_index_start;
|
||||
|
||||
if(block_count > block_count_available) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
const uint32_t block_index_end =
|
||||
MIN((block_index_start + request->block_count + 1),
|
||||
((uint32_t)instance->data->system_info.block_count - 1));
|
||||
|
||||
error = iso15693_3_listener_extension_handler(
|
||||
instance,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#define SLIX_TYPE_INDICATOR_SLIX (0x02U)
|
||||
#define SLIX_TYPE_INDICATOR_SLIX2 (0x01U)
|
||||
|
||||
#define SLIX_CAPABILITIES_KEY "Capabilities"
|
||||
#define SLIX_PASSWORD_READ_KEY "Password Read"
|
||||
#define SLIX_PASSWORD_WRITE_KEY "Password Write"
|
||||
#define SLIX_PASSWORD_PRIVACY_KEY "Password Privacy"
|
||||
@@ -69,6 +70,11 @@ static const SlixTypeFeatures slix_type_features[] = {
|
||||
[SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2,
|
||||
};
|
||||
|
||||
static const char* slix_capabilities_names[SlixCapabilitiesCount] = {
|
||||
[SlixCapabilitiesDefault] = "Default",
|
||||
[SlixCapabilitiesAcceptAllPasswords] = "AcceptAllPasswords",
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* key;
|
||||
SlixTypeFeatures feature_flag;
|
||||
@@ -110,6 +116,7 @@ void slix_reset(SlixData* data) {
|
||||
furi_check(data);
|
||||
|
||||
iso15693_3_reset(data->iso15693_3_data);
|
||||
data->capabilities = SlixCapabilitiesDefault;
|
||||
slix_password_set_defaults(data->passwords);
|
||||
|
||||
memset(&data->system_info, 0, sizeof(SlixSystemInfo));
|
||||
@@ -123,6 +130,7 @@ void slix_copy(SlixData* data, const SlixData* other) {
|
||||
furi_check(other);
|
||||
|
||||
iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data);
|
||||
data->capabilities = other->capabilities;
|
||||
|
||||
memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount);
|
||||
memcpy(data->signature, other->signature, sizeof(SlixSignature));
|
||||
@@ -138,6 +146,30 @@ bool slix_verify(SlixData* data, const FuriString* device_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool slix_load_capabilities(SlixData* data, FlipperFormat* ff) {
|
||||
bool capabilities_loaded = false;
|
||||
FuriString* capabilities_str = furi_string_alloc();
|
||||
|
||||
if(!flipper_format_read_string(ff, SLIX_CAPABILITIES_KEY, capabilities_str)) {
|
||||
if(flipper_format_rewind(ff)) {
|
||||
data->capabilities = SlixCapabilitiesDefault;
|
||||
capabilities_loaded = true;
|
||||
}
|
||||
} else {
|
||||
for(size_t i = 0; i < COUNT_OF(slix_capabilities_names); i++) {
|
||||
if(furi_string_cmp_str(capabilities_str, slix_capabilities_names[i]) == 0) {
|
||||
data->capabilities = i;
|
||||
capabilities_loaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(capabilities_str);
|
||||
|
||||
return capabilities_loaded;
|
||||
}
|
||||
|
||||
static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {
|
||||
bool ret = true;
|
||||
|
||||
@@ -164,13 +196,14 @@ bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_check(ff);
|
||||
|
||||
bool loaded = false;
|
||||
|
||||
do {
|
||||
if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break;
|
||||
|
||||
const SlixType slix_type = slix_get_type(data);
|
||||
if(slix_type >= SlixTypeCount) break;
|
||||
|
||||
if(!slix_load_capabilities(data, ff)) break;
|
||||
|
||||
if(!slix_load_passwords(data->passwords, slix_type, ff)) break;
|
||||
|
||||
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {
|
||||
@@ -220,6 +253,33 @@ bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
static bool slix_save_capabilities(const SlixData* data, FlipperFormat* ff) {
|
||||
bool save_success = false;
|
||||
|
||||
FuriString* tmp_str = furi_string_alloc();
|
||||
do {
|
||||
furi_string_set_str(
|
||||
tmp_str, "SLIX capabilities field affects emulation modes. Possible options: ");
|
||||
for(size_t i = 0; i < SlixCapabilitiesCount; i++) {
|
||||
furi_string_cat_str(tmp_str, slix_capabilities_names[i]);
|
||||
if(i < SlixCapabilitiesCount - 1) {
|
||||
furi_string_cat(tmp_str, ", ");
|
||||
}
|
||||
}
|
||||
if(!flipper_format_write_comment_cstr(ff, furi_string_get_cstr(tmp_str))) break;
|
||||
|
||||
if(!flipper_format_write_string_cstr(
|
||||
ff, SLIX_CAPABILITIES_KEY, slix_capabilities_names[data->capabilities]))
|
||||
break;
|
||||
|
||||
save_success = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(tmp_str);
|
||||
|
||||
return save_success;
|
||||
}
|
||||
|
||||
static bool
|
||||
slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {
|
||||
bool ret = true;
|
||||
@@ -251,6 +311,8 @@ bool slix_save(const SlixData* data, FlipperFormat* ff) {
|
||||
if(!iso15693_3_save(data->iso15693_3_data, ff)) break;
|
||||
if(!flipper_format_write_comment_cstr(ff, SLIX_PROTOCOL_NAME " specific data")) break;
|
||||
|
||||
if(!slix_save_capabilities(data, ff)) break;
|
||||
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
ff,
|
||||
"Passwords are optional. If a password is omitted, a default value will be used"))
|
||||
|
||||
@@ -91,12 +91,20 @@ typedef struct {
|
||||
SlixLockBits lock_bits;
|
||||
} SlixSystemInfo;
|
||||
|
||||
typedef enum {
|
||||
SlixCapabilitiesDefault,
|
||||
SlixCapabilitiesAcceptAllPasswords,
|
||||
|
||||
SlixCapabilitiesCount,
|
||||
} SlixCapabilities;
|
||||
|
||||
typedef struct {
|
||||
Iso15693_3Data* iso15693_3_data;
|
||||
SlixSystemInfo system_info;
|
||||
SlixSignature signature;
|
||||
SlixPassword passwords[SlixPasswordTypeCount];
|
||||
SlixPrivacy privacy;
|
||||
SlixCapabilities capabilities;
|
||||
} SlixData;
|
||||
|
||||
SlixData* slix_alloc(void);
|
||||
|
||||
@@ -54,6 +54,13 @@ static SlixError slix_listener_set_password(
|
||||
}
|
||||
|
||||
SlixListenerSessionState* session_state = &instance->session_state;
|
||||
|
||||
// With AcceptAllPassword capability set skip password validation
|
||||
if(instance->data->capabilities == SlixCapabilitiesAcceptAllPasswords) {
|
||||
session_state->password_match[password_type] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
session_state->password_match[password_type] =
|
||||
(password == slix_get_password(slix_data, password_type));
|
||||
|
||||
|
||||
@@ -114,7 +114,8 @@ static NfcCommand slix_poller_handler_check_privacy_password(SlixPoller* instanc
|
||||
break;
|
||||
}
|
||||
|
||||
instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd);
|
||||
instance->error = slix_poller_set_password(
|
||||
instance, SlixPasswordTypePrivacy, pwd, instance->random_number);
|
||||
if(instance->error != SlixErrorNone) {
|
||||
command = NfcCommandReset;
|
||||
break;
|
||||
@@ -145,7 +146,8 @@ static NfcCommand slix_poller_handler_privacy_unlock(SlixPoller* instance) {
|
||||
instance->error = slix_poller_get_random_number(instance, &instance->random_number);
|
||||
if(instance->error != SlixErrorNone) break;
|
||||
|
||||
instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd);
|
||||
instance->error = slix_poller_set_password(
|
||||
instance, SlixPasswordTypePrivacy, pwd, instance->random_number);
|
||||
if(instance->error != SlixErrorNone) {
|
||||
command = NfcCommandReset;
|
||||
break;
|
||||
|
||||
@@ -107,12 +107,16 @@ SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[out] type SlixPasswordType instance.
|
||||
* @param[out] password SlixPassword instance.
|
||||
* @param[in] type SlixPasswordType instance.
|
||||
* @param[in] password SlixPassword instance.
|
||||
* @param[in] random_number SlixRandomNumber instance.
|
||||
* @return SlixErrorNone on success, an error code on failure.
|
||||
*/
|
||||
SlixError
|
||||
slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password);
|
||||
SlixError slix_poller_set_password(
|
||||
SlixPoller* instance,
|
||||
SlixPasswordType type,
|
||||
SlixPassword password,
|
||||
SlixRandomNumber random_number);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -92,8 +92,11 @@ SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber*
|
||||
return error;
|
||||
}
|
||||
|
||||
SlixError
|
||||
slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password) {
|
||||
SlixError slix_poller_set_password(
|
||||
SlixPoller* instance,
|
||||
SlixPasswordType type,
|
||||
SlixPassword password,
|
||||
SlixRandomNumber random_number) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool skip_uid = (type == SlixPasswordTypePrivacy);
|
||||
@@ -102,8 +105,8 @@ SlixError
|
||||
uint8_t password_type = (0x01 << type);
|
||||
bit_buffer_append_byte(instance->tx_buffer, password_type);
|
||||
|
||||
uint8_t rn_l = instance->random_number >> 8;
|
||||
uint8_t rn_h = instance->random_number;
|
||||
uint8_t rn_l = random_number >> 8;
|
||||
uint8_t rn_h = random_number;
|
||||
uint32_t double_rand_num = (rn_h << 24) | (rn_l << 16) | (rn_h << 8) | rn_l;
|
||||
uint32_t xored_password = double_rand_num ^ password;
|
||||
uint8_t xored_password_arr[4] = {};
|
||||
|
||||
@@ -25,6 +25,7 @@ env.Append(
|
||||
File("subghz_protocol_registry.h"),
|
||||
File("devices/cc1101_configs.h"),
|
||||
File("devices/cc1101_int/cc1101_int_interconnect.h"),
|
||||
File("subghz_file_encoder_worker.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#include <furi_hal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*SubGhzFileEncoderWorkerCallbackEnd)(void* context);
|
||||
|
||||
typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker;
|
||||
@@ -59,3 +63,7 @@ void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance);
|
||||
* @return bool - true if running
|
||||
*/
|
||||
bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,9 @@ env.Append(
|
||||
File("simple_array.h"),
|
||||
File("bit_buffer.h"),
|
||||
File("keys_dict.h"),
|
||||
File("pulse_protocols/pulse_glue.h"),
|
||||
File("md5_calc.h"),
|
||||
File("varint.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+97
-74
@@ -3,6 +3,9 @@
|
||||
#include <furi.h>
|
||||
#include <lib/heatshrink/heatshrink_encoder.h>
|
||||
#include <lib/heatshrink/heatshrink_decoder.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TAG "Compress"
|
||||
|
||||
/** Defines encoder and decoder window size */
|
||||
#define COMPRESS_EXP_BUFF_SIZE_LOG (8u)
|
||||
@@ -10,9 +13,16 @@
|
||||
/** Defines encoder and decoder lookahead buffer size */
|
||||
#define COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4u)
|
||||
|
||||
/** Buffer sizes for input and output data */
|
||||
#define COMPRESS_ICON_ENCODED_BUFF_SIZE (1024u)
|
||||
#define COMPRESS_ICON_DECODED_BUFF_SIZE (1024u)
|
||||
/** Buffer size for input data */
|
||||
#define COMPRESS_ICON_ENCODED_BUFF_SIZE (256u)
|
||||
|
||||
static bool compress_decode_internal(
|
||||
heatshrink_decoder* decoder,
|
||||
const uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size);
|
||||
|
||||
typedef struct {
|
||||
uint8_t is_compressed;
|
||||
@@ -24,55 +34,51 @@ _Static_assert(sizeof(CompressHeader) == 4, "Incorrect CompressHeader size");
|
||||
|
||||
struct CompressIcon {
|
||||
heatshrink_decoder* decoder;
|
||||
uint8_t decoded_buff[COMPRESS_ICON_DECODED_BUFF_SIZE];
|
||||
uint8_t* buffer;
|
||||
size_t buffer_size;
|
||||
};
|
||||
|
||||
CompressIcon* compress_icon_alloc(void) {
|
||||
CompressIcon* compress_icon_alloc(size_t decode_buf_size) {
|
||||
CompressIcon* instance = malloc(sizeof(CompressIcon));
|
||||
instance->decoder = heatshrink_decoder_alloc(
|
||||
COMPRESS_ICON_ENCODED_BUFF_SIZE,
|
||||
COMPRESS_EXP_BUFF_SIZE_LOG,
|
||||
COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG);
|
||||
heatshrink_decoder_reset(instance->decoder);
|
||||
memset(instance->decoded_buff, 0, sizeof(instance->decoded_buff));
|
||||
|
||||
instance->buffer_size = decode_buf_size + 4; /* To account for heatshrink's poller quirks */
|
||||
instance->buffer = malloc(instance->buffer_size);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void compress_icon_free(CompressIcon* instance) {
|
||||
furi_check(instance);
|
||||
free(instance->buffer);
|
||||
heatshrink_decoder_free(instance->decoder);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff) {
|
||||
void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** output) {
|
||||
furi_check(instance);
|
||||
furi_check(icon_data);
|
||||
furi_check(decoded_buff);
|
||||
furi_check(output);
|
||||
|
||||
CompressHeader* header = (CompressHeader*)icon_data;
|
||||
if(header->is_compressed) {
|
||||
size_t data_processed = 0;
|
||||
heatshrink_decoder_sink(
|
||||
size_t decoded_size = 0;
|
||||
/* If decompression fails - check that decode_buf_size is large enough */
|
||||
furi_check(compress_decode_internal(
|
||||
instance->decoder,
|
||||
(uint8_t*)&icon_data[sizeof(CompressHeader)],
|
||||
header->compressed_buff_size,
|
||||
&data_processed);
|
||||
while(1) {
|
||||
HSD_poll_res res = heatshrink_decoder_poll(
|
||||
instance->decoder,
|
||||
instance->decoded_buff,
|
||||
sizeof(instance->decoded_buff),
|
||||
&data_processed);
|
||||
furi_check((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE));
|
||||
if(res != HSDR_POLL_MORE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
heatshrink_decoder_reset(instance->decoder);
|
||||
*decoded_buff = instance->decoded_buff;
|
||||
icon_data,
|
||||
/* Decoder will check/process headers again - need to pass them */
|
||||
sizeof(CompressHeader) + header->compressed_buff_size,
|
||||
instance->buffer,
|
||||
instance->buffer_size,
|
||||
&decoded_size));
|
||||
*output = instance->buffer;
|
||||
} else {
|
||||
*decoded_buff = (uint8_t*)&icon_data[1];
|
||||
*output = (uint8_t*)&icon_data[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,12 +87,6 @@ struct Compress {
|
||||
heatshrink_decoder* decoder;
|
||||
};
|
||||
|
||||
static void compress_reset(Compress* compress) {
|
||||
furi_assert(compress);
|
||||
heatshrink_encoder_reset(compress->encoder);
|
||||
heatshrink_decoder_reset(compress->decoder);
|
||||
}
|
||||
|
||||
Compress* compress_alloc(uint16_t compress_buff_size) {
|
||||
Compress* compress = malloc(sizeof(Compress));
|
||||
compress->encoder =
|
||||
@@ -105,16 +105,16 @@ void compress_free(Compress* compress) {
|
||||
free(compress);
|
||||
}
|
||||
|
||||
bool compress_encode(
|
||||
Compress* compress,
|
||||
static bool compress_encode_internal(
|
||||
heatshrink_encoder* encoder,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size) {
|
||||
furi_assert(compress);
|
||||
furi_assert(data_in);
|
||||
furi_assert(data_in_size);
|
||||
furi_check(encoder);
|
||||
furi_check(data_in);
|
||||
furi_check(data_in_size);
|
||||
|
||||
size_t sink_size = 0;
|
||||
size_t poll_size = 0;
|
||||
@@ -125,10 +125,10 @@ bool compress_encode(
|
||||
size_t sunk = 0;
|
||||
size_t res_buff_size = sizeof(CompressHeader);
|
||||
|
||||
// Sink data to encoding buffer
|
||||
/* Sink data to encoding buffer */
|
||||
while((sunk < data_in_size) && !encode_failed) {
|
||||
sink_res = heatshrink_encoder_sink(
|
||||
compress->encoder, &data_in[sunk], data_in_size - sunk, &sink_size);
|
||||
sink_res =
|
||||
heatshrink_encoder_sink(encoder, &data_in[sunk], data_in_size - sunk, &sink_size);
|
||||
if(sink_res != HSER_SINK_OK) {
|
||||
encode_failed = true;
|
||||
break;
|
||||
@@ -136,10 +136,7 @@ bool compress_encode(
|
||||
sunk += sink_size;
|
||||
do {
|
||||
poll_res = heatshrink_encoder_poll(
|
||||
compress->encoder,
|
||||
&data_out[res_buff_size],
|
||||
data_out_size - res_buff_size,
|
||||
&poll_size);
|
||||
encoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size);
|
||||
if(poll_res < 0) {
|
||||
encode_failed = true;
|
||||
break;
|
||||
@@ -148,31 +145,30 @@ bool compress_encode(
|
||||
} while(poll_res == HSER_POLL_MORE);
|
||||
}
|
||||
|
||||
// Notify sinking complete and poll encoded data
|
||||
finish_res = heatshrink_encoder_finish(compress->encoder);
|
||||
/* Notify sinking complete and poll encoded data */
|
||||
finish_res = heatshrink_encoder_finish(encoder);
|
||||
if(finish_res < 0) {
|
||||
encode_failed = true;
|
||||
} else {
|
||||
do {
|
||||
poll_res = heatshrink_encoder_poll(
|
||||
compress->encoder,
|
||||
&data_out[res_buff_size],
|
||||
data_out_size - 4 - res_buff_size,
|
||||
&poll_size);
|
||||
encoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size);
|
||||
if(poll_res < 0) {
|
||||
encode_failed = true;
|
||||
break;
|
||||
}
|
||||
res_buff_size += poll_size;
|
||||
finish_res = heatshrink_encoder_finish(compress->encoder);
|
||||
finish_res = heatshrink_encoder_finish(encoder);
|
||||
} while(finish_res != HSER_FINISH_DONE);
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
// Write encoded data to output buffer if compression is efficient. Else - write header and original data
|
||||
/* Write encoded data to output buffer if compression is efficient. Otherwise, write header and original data */
|
||||
if(!encode_failed && (res_buff_size < data_in_size + 1)) {
|
||||
CompressHeader header = {
|
||||
.is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size};
|
||||
.is_compressed = 0x01,
|
||||
.reserved = 0x00,
|
||||
.compressed_buff_size = res_buff_size - sizeof(CompressHeader)};
|
||||
memcpy(data_out, &header, sizeof(header));
|
||||
*data_res_size = res_buff_size;
|
||||
} else if(data_out_size > data_in_size) {
|
||||
@@ -183,22 +179,21 @@ bool compress_encode(
|
||||
*data_res_size = 0;
|
||||
result = false;
|
||||
}
|
||||
compress_reset(compress);
|
||||
|
||||
heatshrink_encoder_reset(encoder);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool compress_decode(
|
||||
Compress* compress,
|
||||
uint8_t* data_in,
|
||||
static bool compress_decode_internal(
|
||||
heatshrink_decoder* decoder,
|
||||
const uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size) {
|
||||
furi_assert(compress);
|
||||
furi_assert(data_in);
|
||||
furi_assert(data_out);
|
||||
furi_assert(data_res_size);
|
||||
furi_check(decoder);
|
||||
furi_check(data_in);
|
||||
furi_check(data_out);
|
||||
furi_check(data_res_size);
|
||||
|
||||
bool result = false;
|
||||
bool decode_failed = false;
|
||||
@@ -211,12 +206,15 @@ bool compress_decode(
|
||||
|
||||
CompressHeader* header = (CompressHeader*)data_in;
|
||||
if(header->is_compressed) {
|
||||
// Sink data to decoding buffer
|
||||
/* Sink data to decoding buffer */
|
||||
size_t compressed_size = header->compressed_buff_size;
|
||||
size_t sunk = sizeof(CompressHeader);
|
||||
size_t sunk = 0;
|
||||
while(sunk < compressed_size && !decode_failed) {
|
||||
sink_res = heatshrink_decoder_sink(
|
||||
compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size);
|
||||
decoder,
|
||||
(uint8_t*)&data_in[sizeof(CompressHeader) + sunk],
|
||||
compressed_size - sunk,
|
||||
&sink_size);
|
||||
if(sink_res < 0) {
|
||||
decode_failed = true;
|
||||
break;
|
||||
@@ -224,25 +222,28 @@ bool compress_decode(
|
||||
sunk += sink_size;
|
||||
do {
|
||||
poll_res = heatshrink_decoder_poll(
|
||||
compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size);
|
||||
if(poll_res < 0) {
|
||||
decoder, &data_out[res_buff_size], data_out_size - res_buff_size, &poll_size);
|
||||
if((poll_res < 0) || ((data_out_size - res_buff_size) == 0)) {
|
||||
decode_failed = true;
|
||||
break;
|
||||
}
|
||||
res_buff_size += poll_size;
|
||||
} while(poll_res == HSDR_POLL_MORE);
|
||||
}
|
||||
// Notify sinking complete and poll decoded data
|
||||
/* Notify sinking complete and poll decoded data */
|
||||
if(!decode_failed) {
|
||||
finish_res = heatshrink_decoder_finish(compress->decoder);
|
||||
finish_res = heatshrink_decoder_finish(decoder);
|
||||
if(finish_res < 0) {
|
||||
decode_failed = true;
|
||||
} else {
|
||||
do {
|
||||
poll_res = heatshrink_decoder_poll(
|
||||
compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size);
|
||||
decoder,
|
||||
&data_out[res_buff_size],
|
||||
data_out_size - res_buff_size,
|
||||
&poll_size);
|
||||
res_buff_size += poll_size;
|
||||
finish_res = heatshrink_decoder_finish(compress->decoder);
|
||||
finish_res = heatshrink_decoder_finish(decoder);
|
||||
} while(finish_res != HSDR_FINISH_DONE);
|
||||
}
|
||||
}
|
||||
@@ -253,9 +254,31 @@ bool compress_decode(
|
||||
*data_res_size = data_in_size - 1;
|
||||
result = true;
|
||||
} else {
|
||||
/* Not enough space in output buffer */
|
||||
result = false;
|
||||
}
|
||||
compress_reset(compress);
|
||||
|
||||
heatshrink_decoder_reset(decoder);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool compress_encode(
|
||||
Compress* compress,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size) {
|
||||
return compress_encode_internal(
|
||||
compress->encoder, data_in, data_in_size, data_out, data_out_size, data_res_size);
|
||||
}
|
||||
|
||||
bool compress_decode(
|
||||
Compress* compress,
|
||||
uint8_t* data_in,
|
||||
size_t data_in_size,
|
||||
uint8_t* data_out,
|
||||
size_t data_out_size,
|
||||
size_t* data_res_size) {
|
||||
return compress_decode_internal(
|
||||
compress->decoder, data_in, data_in_size, data_out, data_out_size, data_res_size);
|
||||
}
|
||||
|
||||
+12
-6
@@ -16,10 +16,14 @@ extern "C" {
|
||||
typedef struct CompressIcon CompressIcon;
|
||||
|
||||
/** Initialize icon compressor
|
||||
*
|
||||
* @param[in] decode_buf_size The icon buffer size for decoding. Ensure that
|
||||
* it's big enough for any icons that you are
|
||||
* planning to decode with it.
|
||||
*
|
||||
* @return Compress Icon instance
|
||||
*/
|
||||
CompressIcon* compress_icon_alloc(void);
|
||||
CompressIcon* compress_icon_alloc(size_t decode_buf_size);
|
||||
|
||||
/** Free icon compressor
|
||||
*
|
||||
@@ -29,14 +33,16 @@ void compress_icon_free(CompressIcon* instance);
|
||||
|
||||
/** Decompress icon
|
||||
*
|
||||
* @warning decoded_buff pointer set by this function is valid till next
|
||||
* @warning output pointer set by this function is valid till next
|
||||
* `compress_icon_decode` or `compress_icon_free` call
|
||||
*
|
||||
* @param instance The Compress Icon instance
|
||||
* @param icon_data pointer to icon data
|
||||
* @param[in] decoded_buff pointer to decoded buffer pointer
|
||||
* @param instance The Compress Icon instance
|
||||
* @param icon_data pointer to icon data.
|
||||
* @param[in] output pointer to decoded buffer pointer. Data in buffer is
|
||||
* valid till next call. If icon data was not compressed,
|
||||
* pointer within icon_data is returned
|
||||
*/
|
||||
void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff);
|
||||
void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** output);
|
||||
|
||||
/** Compress control structure */
|
||||
typedef struct Compress Compress;
|
||||
|
||||
Reference in New Issue
Block a user