From 52361b4adf8ca9614321fa21af2960ac60469c20 Mon Sep 17 00:00:00 2001 From: DerSkythe Date: Mon, 10 Oct 2022 20:20:56 +0400 Subject: [PATCH] Add split RAW files ability --- applications/main/subghz/subghz_history.c | 413 +++++++++++++++++- .../main/subghz/subghz_history_private.h | 93 +++- 2 files changed, 491 insertions(+), 15 deletions(-) diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 9a42fc9bb..b4395c92e 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -1,7 +1,10 @@ #include "subghz_history.h" #include "subghz_history_private.h" #include +#include #include +#include "flipper_format_stream_i.h" +#include #define SUBGHZ_HISTORY_MAX 60 @@ -11,10 +14,12 @@ */ #define SUBGHZ_HISTORY_TMP_DIR EXT_PATH("subghz/tmp_history") #define SUBGHZ_HISTORY_TMP_EXTENSION ".tmp" -#define SUBGHZ_HISTORY_TMP_SIGNAL_MAX_LEVEL_DURATION 700 -#define SUBGHZ_HISTORY_TMP_SIGNAL_MIN_LEVEL_DURATION 100 +#define SUBGHZ_HISTORY_TMP_SIGNAL_MAX 700 +#define SUBGHZ_HISTORY_TMP_SIGNAL_MIN 100 #define SUBGHZ_HISTORY_TMP_REMOVE_FILES true #define SUBGHZ_HISTORY_TMP_RAW_KEY "RAW_Data" +#define MAX_LINE 500 +const size_t buffer_size = 32; #define TAG "SubGhzHistory" @@ -436,7 +441,7 @@ bool subghz_history_add_to_history( #ifdef FURI_DEBUG FURI_LOG_I(TAG, "Save temp file: %s", furi_string_get_cstr(dir_path)); #endif - if(!subghz_history_tmp_write_file_split(instance, item, dir_path)) { + if(!subghz_history_tmp_write_file_split(instance, item, furi_string_get_cstr(dir_path))) { // Plan B! subghz_history_tmp_write_file_full(instance, item, dir_path); } @@ -455,19 +460,405 @@ bool subghz_history_add_to_history( return true; } +static inline bool is_space_playground(char c) { + return c == ' ' || c == '\t' || c == flipper_format_eolr; +} + +bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key) { + furi_string_reset(key); + uint8_t buffer[buffer_size]; + + bool found = false; + bool error = false; + bool accumulate = true; + bool new_line = true; + + while(true) { + size_t was_read = stream_read(stream, buffer, buffer_size); + if(was_read == 0) break; + + for(size_t i = 0; i < was_read; i++) { + uint8_t data = buffer[i]; + if(data == flipper_format_eoln) { + // EOL found, clean data, start accumulating data and set the new_line flag + furi_string_reset(key); + accumulate = true; + new_line = true; + } else if(data == flipper_format_eolr) { + // ignore + } else if(data == flipper_format_comment && new_line) { + // if there is a comment character and we are at the beginning of a new line + // do not accumulate comment data and reset the new_line flag + accumulate = false; + new_line = false; + } else if(data == flipper_format_delimiter) { + if(new_line) { + // we are on a "new line" and found the delimiter + // this can only be if we have previously found some kind of key, so + // clear the data, set the flag that we no longer want to accumulate data + // and reset the new_line flag + furi_string_reset(key); + accumulate = false; + new_line = false; + } else { + // parse the delimiter only if we are accumulating data + if(accumulate) { + // we found the delimiter, move the rw pointer to the delimiter location + // and signal that we have found something + if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { + error = true; + break; + } + + found = true; + break; + } + } + } else { + // just new symbol, reset the new_line flag + new_line = false; + if(accumulate) { + // and accumulate data if we want + furi_string_push_back(key, data); + } + } + } + + if(found || error) break; + } + + return found; +} + +bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) { + bool found = false; + FuriString* read_key; + + read_key = furi_string_alloc(); + + while(!stream_eof(stream)) { + if(subghz_history_stream_read_valid_key(stream, read_key)) { + if(furi_string_cmp_str(read_key, key) == 0) { + if(!stream_seek(stream, 2, StreamOffsetFromCurrent)) { + break; + } + found = true; + break; + } else if(strict_mode) { + found = false; + break; + } + } + } + furi_string_free(read_key); + + return found; +} + +bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last) { + enum { LeadingSpace, ReadValue, TrailingSpace } state = LeadingSpace; + const size_t buffer_size = 32; + uint8_t buffer[buffer_size]; + bool result = false; + bool error = false; + + furi_string_reset(value); + + while(true) { + size_t was_read = stream_read(stream, buffer, buffer_size); + + if(was_read == 0) { + if(state != LeadingSpace && stream_eof(stream)) { + result = true; + *last = true; + } else { + error = true; + } + } + + for(uint16_t i = 0; i < was_read; i++) { + const uint8_t data = buffer[i]; + + if(state == LeadingSpace) { + if(is_space_playground(data)) { + continue; + } else if(data == flipper_format_eoln) { + stream_seek(stream, i - was_read, StreamOffsetFromCurrent); + error = true; + break; + } else { + state = ReadValue; + furi_string_push_back(value, data); + } + } else if(state == ReadValue) { + if(is_space_playground(data)) { + state = TrailingSpace; + } else if(data == flipper_format_eoln) { + if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { + error = true; + } else { + result = true; + *last = true; + } + break; + } else { + furi_string_push_back(value, data); + } + } else if(state == TrailingSpace) { + if(is_space_playground(data)) { + continue; + } else if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { + error = true; + } else { + *last = (data == flipper_format_eoln); + result = true; + } + break; + } + } + + if(error || result) break; + } + + return result; +} + +bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size) { + bool result = false; + result = true; + FuriString* value; + value = furi_string_alloc(); + + for(size_t i = 0; i < data_size; i++) { + bool last = false; + result = subghz_history_stream_read_value(stream, value, &last); + if(result) { + int scan_values = 0; + + int32_t* data = _data; + scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]); + + if(scan_values != 1) { + result = false; + break; + } + } else { + break; + } + + if(last && ((i + 1) != data_size)) { + result = false; + break; + } + } + + furi_string_free(value); + return result; +} + +uint32_t subghz_history_rand_range(uint32_t min, uint32_t max) { + // size of range, inclusive + const uint32_t length_of_range = max - min + 1; + + // add n so that we don't return a number below our range + return (uint32_t)(rand() % length_of_range + min); +} + +bool subghz_history_write_file_noise( + Stream* file, + bool is_negative_start, + size_t current_position, + bool empty_line) { + size_t was_write = 0; + if(empty_line) { + was_write = stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY); + + if(was_write <= 0) { + FURI_LOG_E(TAG, "Can't write key!"); + return false; + } + } + + int8_t first; + int8_t second; + if(is_negative_start) { + first = -1; + second = 1; + } else { + first = 1; + second = -1; + } + while(current_position < MAX_LINE) { + was_write = stream_write_format( + file, + "%ld %ld ", + subghz_history_rand_range( + SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) * + first, + subghz_history_rand_range( + SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) * + second); + + if(was_write <= 0) { + FURI_LOG_E(TAG, "Can't write random values!"); + return false; + } + + current_position += was_write; + } + + // Step back to write \n instead of space + size_t offset = stream_tell(file); + if(stream_seek(file, offset - 1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Step back failed!"); + return false; + } + + return stream_write_char(file, flipper_format_eoln) > 0; +} + +bool subghz_history_write_file_data( + Stream* src, + Stream* file, + bool* is_negative_start, + size_t* current_position) { + size_t offset_file = 0; + bool result = false; + int32_t value = 0; + + do { + if(!subghz_history_read_int32(src, &value, 1)) { + result = true; + break; + } + offset_file = stream_tell(file); + stream_write_format(file, "%ld ", value); + *current_position += stream_tell(file) - offset_file; + + if(*current_position > MAX_LINE) { + if((is_negative_start && value > 0) || (!is_negative_start && value < 0)) { + // Align values + continue; + } + + if(stream_write_format(file, "\n%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) { + FURI_LOG_E(TAG, "Can't write new line!"); + result = false; + break; + } + *current_position = 0; + } + } while(true); + + *is_negative_start = value < 0; + + return result; +} + bool subghz_history_tmp_write_file_split( SubGhzHistory* instance, void* current_item, - FuriString* dir_path) { - UNUSED(instance); - UNUSED(current_item); - UNUSED(dir_path); - /*furi_assert(instance); + const char* dir_path) { + furi_assert(instance); furi_assert(current_item); - furi_assert(dir_path);*/ - //SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; + furi_assert(dir_path); +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "Save temp file splitted: %s", dir_path); +#endif + SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; - return false; + uint8_t buffer[buffer_size]; + Stream* src = flipper_format_get_raw_stream(item->flipper_string); + stream_rewind(src); + + FlipperFormat* flipper_format_file = flipper_format_file_alloc(instance->storage); + bool result = false; + FuriString* temp_str = furi_string_alloc(); + + do { + if(storage_file_exists(instance->storage, dir_path) && + storage_common_remove(instance->storage, dir_path) != FSE_OK) { + FURI_LOG_E(TAG, "Can't delete old file!"); + break; + } + path_extract_dirname(dir_path, temp_str); + FS_Error fs_result = + storage_common_mkdir(instance->storage, furi_string_get_cstr(temp_str)); + if(fs_result != FSE_OK && fs_result != FSE_EXIST) { + FURI_LOG_E(TAG, "Can't create dir!"); + break; + } + result = flipper_format_file_open_always(flipper_format_file, dir_path); + if(!result) { + FURI_LOG_E(TAG, "Can't open file for write!"); + break; + } + Stream* file = flipper_format_get_raw_stream(flipper_format_file); + + if(!subghz_history_stream_seek_to_key(src, SUBGHZ_HISTORY_TMP_RAW_KEY, false)) { + FURI_LOG_E(TAG, "Can't find key!"); + break; + } + bool is_negative_start = false; + bool found = false; + + size_t offset_start; + offset_start = stream_tell(src); + + // Check for negative value at the start and end to align file by correct values + size_t was_read = stream_read(src, buffer, 1); + if(was_read <= 0) { + FURI_LOG_E(TAG, "Can't obtain first mark!"); + break; + } + + is_negative_start = buffer[0] == '-'; + + // Ready to write stream to file + size_t current_position; + stream_rewind(src); + current_position = stream_copy(src, file, offset_start); + if(current_position != offset_start) { + FURI_LOG_E(TAG, "Invalid copy header data from one stream to another!"); + break; + } + + found = true; + + current_position = 0; + if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) { + FURI_LOG_E(TAG, "Add start noise failed!"); + break; + } + + if(stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) { + FURI_LOG_E(TAG, "Can't write new line!"); + result = false; + break; + } + + if(!subghz_history_write_file_data(src, file, &is_negative_start, ¤t_position)) { + FURI_LOG_E(TAG, "Split by lines failed!"); + break; + } + + if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) { + FURI_LOG_E(TAG, "Add end noise failed!"); + break; + } + + if(!subghz_history_write_file_noise(file, is_negative_start, 0, true)) { + FURI_LOG_E(TAG, "Add end noise failed!"); + break; + } + + result = found; + } while(false); + flipper_format_file_close(flipper_format_file); + flipper_format_free(flipper_format_file); + furi_string_free(temp_str); + + return result; } void subghz_history_tmp_write_file_full( diff --git a/applications/main/subghz/subghz_history_private.h b/applications/main/subghz/subghz_history_private.h index aac330c33..7c554621b 100644 --- a/applications/main/subghz/subghz_history_private.h +++ b/applications/main/subghz/subghz_history_private.h @@ -1,6 +1,7 @@ #pragma once #include "subghz_history.h" +#include /** * @brief Generate filename like 000.tmp @@ -52,7 +53,7 @@ void subghz_history_item_free(void* current_item); void subghz_history_clean_item_array(SubGhzHistory* instance); /** - * @brief Write temp file fully, without spliting + * @brief Write temp file fully, without splitting * * @param instance - SubGhzHistory* * @param current_item - SubGhzHistoryItem* @@ -64,15 +65,99 @@ void subghz_history_tmp_write_file_full( FuriString* dir_path); /** - * @brief Write temp splited to lines + * @brief Write temp spited to lines * * @param instance - SubGhzHistory* * @param current_item - SubGhzHistoryItem* * @param dir_path - full path to file * @return true - file saved - * @return false - error occured + * @return false - error occurred */ bool subghz_history_tmp_write_file_split( SubGhzHistory* instance, void* current_item, - FuriString* dir_path); \ No newline at end of file + const char* dir_path); + +/** + * @brief generate random value + * + * @param min - min value + * @param max - max value + * @return uint32_t + */ +uint32_t subghz_history_rand_range(uint32_t min, uint32_t max); + +/** + * @brief write random noise signals to file applying to max line value + * + * @param file - Stream* + * @param is_negative_start - first value is negative or positive? + * @param current_position - 0 if started from begining + * @param empty_line - add RAW_Data to this line + * @return true + * @return false + */ +bool subghz_history_write_file_noise( + Stream* file, + bool is_negative_start, + size_t current_position, + bool empty_line); + +/** + * @brief taken from flipper_format_stream_read_value_line but takes only one int32 value + * + * @param stream - Stream* + * @param _data - int32_t* output data + * @param data_size - size of data + * @return true + * @return false + */ +bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size); + +/** + * @brief write payload to file spliting by lines + * + * @param src - Stream* of source + * @param file - Stream* of file + * @param is_negative_start - first value is negative or positive? + * @param current_position - by default is 0 but in this value returned last position of payload + * @return true + * @return false + */ +bool subghz_history_write_file_data( + Stream* src, + Stream* file, + bool* is_negative_start, + size_t* current_position); + +/** + * @brief taken from flipper_format_stream_read_valid_key + * + * @param stream - Stream* + * @param key - FuriString* output value + * @return true + * @return false + */ +bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key); + +/** + * @brief taken from flipper_format_stream_seek_to_key + * + * @param stream - Stream* + * @param key - key + * @param strict_mode - false + * @return true + * @return false + */ +bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode); + +/** + * @brief taken from flipper_format_stream_read_value + * + * @param stream - Stream* + * @param value - FuriString* output value + * @param last - return position is last flag + * @return true + * @return false + */ +bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last); \ No newline at end of file