diff --git a/applications/tests/flipper_format/flipper_format_string_test.c b/applications/tests/flipper_format/flipper_format_string_test.c index 9986c523..ebc3008f 100644 --- a/applications/tests/flipper_format/flipper_format_string_test.c +++ b/applications/tests/flipper_format/flipper_format_string_test.c @@ -11,22 +11,30 @@ static const uint32_t test_version = 666; static const char* test_string_key = "String data"; static const char* test_string_data = "String"; static const char* test_string_updated_data = "New string"; +static const char* test_string_updated_2_data = "And some more"; static const char* test_int_key = "Int32 data"; static const int32_t test_int_data[] = {1234, -6345, 7813, 0}; static const int32_t test_int_updated_data[] = {-1337, 69}; +static const int32_t test_int_updated_2_data[] = {-3, -2, -1, 0, 1, 2, 3}; static const char* test_uint_key = "Uint32 data"; static const uint32_t test_uint_data[] = {1234, 0, 5678, 9098, 7654321}; static const uint32_t test_uint_updated_data[] = {8, 800, 555, 35, 35}; +static const uint32_t test_uint_updated_2_data[] = {20, 21}; static const char* test_float_key = "Float data"; static const float test_float_data[] = {1.5f, 1000.0f}; static const float test_float_updated_data[] = {1.2f}; +static const float test_float_updated_2_data[] = {0.01f, 0.0f, -51.6f}; static const char* test_hex_key = "Hex data"; static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE}; static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA}; +static const uint8_t test_hex_updated_2_data[] = {0xCA, 0xCA, 0x05}; + +static const char* test_hex_new_key = "New Hex data"; +static const uint8_t test_hex_new_data[] = {0xFF, 0x6A, 0x91}; static const char* test_data_nix = "Filetype: Flipper Format test\n" "Version: 666\n" @@ -59,7 +67,40 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { uint32_t count; + // key exist test + size_t position_before = stream_tell(flipper_format_get_raw_stream(flipper_format)); + mu_check(flipper_format_key_exist(flipper_format, test_hex_key)); + mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format))); + + mu_check(!flipper_format_key_exist(flipper_format, "invalid key")); + mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format))); + + // stream seek to end test + mu_check(flipper_format_seek_to_end(flipper_format)); + mu_assert_int_eq( + stream_size(flipper_format_get_raw_stream(flipper_format)), + stream_tell(flipper_format_get_raw_stream(flipper_format))); + + // key exist test + position_before = stream_tell(flipper_format_get_raw_stream(flipper_format)); + mu_check(flipper_format_key_exist(flipper_format, test_hex_key)); + mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format))); + + mu_check(!flipper_format_key_exist(flipper_format, "invalid key")); + mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format))); + + // rewind mu_check(flipper_format_rewind(flipper_format)); + + // key exist test + position_before = stream_tell(flipper_format_get_raw_stream(flipper_format)); + mu_check(flipper_format_key_exist(flipper_format, test_hex_key)); + mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format))); + + mu_check(!flipper_format_key_exist(flipper_format, "invalid key")); + mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format))); + + // read test string_init(tmpstr); mu_check(flipper_format_read_header(flipper_format, tmpstr, &version)); @@ -94,6 +135,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { string_clear(tmpstr); + // update data mu_check(flipper_format_rewind(flipper_format)); mu_check(flipper_format_update_string_cstr( flipper_format, test_string_key, test_string_updated_data)); @@ -106,6 +148,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { mu_check(flipper_format_update_hex( flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_data))); + // read updated data test uint32_t uint32_updated_data[COUNT_OF(test_uint_updated_data)]; int32_t int32_updated_data[COUNT_OF(test_int_updated_data)]; float float_updated_data[COUNT_OF(test_float_updated_data)]; @@ -149,9 +192,76 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { string_clear(tmpstr); + // update data + mu_check(flipper_format_rewind(flipper_format)); + mu_check(flipper_format_insert_or_update_string_cstr( + flipper_format, test_string_key, test_string_updated_2_data)); + mu_check(flipper_format_insert_or_update_int32( + flipper_format, test_int_key, ARRAY_W_COUNT(test_int_updated_2_data))); + mu_check(flipper_format_insert_or_update_uint32( + flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_updated_2_data))); + mu_check(flipper_format_insert_or_update_float( + flipper_format, test_float_key, ARRAY_W_COUNT(test_float_updated_2_data))); + mu_check(flipper_format_insert_or_update_hex( + flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_2_data))); + mu_check(flipper_format_insert_or_update_hex( + flipper_format, test_hex_new_key, ARRAY_W_COUNT(test_hex_new_data))); + + uint32_t uint32_updated_2_data[COUNT_OF(test_uint_updated_2_data)]; + int32_t int32_updated_2_data[COUNT_OF(test_int_updated_2_data)]; + float float_updated_2_data[COUNT_OF(test_float_updated_2_data)]; + uint8_t hex_updated_2_data[COUNT_OF(test_hex_updated_2_data)]; + uint8_t hex_new_data[COUNT_OF(test_hex_new_data)]; + + mu_check(flipper_format_rewind(flipper_format)); + string_init(tmpstr); + + mu_check(flipper_format_read_header(flipper_format, tmpstr, &version)); + mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr)); + mu_assert_int_eq(test_version, version); + + mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr)); + mu_assert_string_eq(test_string_updated_2_data, string_get_cstr(tmpstr)); + + mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count)); + mu_assert_int_eq(COUNT_OF(test_int_updated_2_data), count); + mu_check(flipper_format_read_int32( + flipper_format, test_int_key, ARRAY_W_COUNT(int32_updated_2_data))); + mu_check(memcmp(test_int_updated_2_data, ARRAY_W_BSIZE(int32_updated_2_data)) == 0); + + mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count)); + mu_assert_int_eq(COUNT_OF(test_uint_updated_2_data), count); + mu_check(flipper_format_read_uint32( + flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_2_data))); + mu_check(memcmp(test_uint_updated_2_data, ARRAY_W_BSIZE(uint32_updated_2_data)) == 0); + + mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count)); + mu_assert_int_eq(COUNT_OF(test_float_updated_2_data), count); + mu_check(flipper_format_read_float( + flipper_format, test_float_key, ARRAY_W_COUNT(float_updated_2_data))); + mu_check(memcmp(test_float_updated_2_data, ARRAY_W_BSIZE(float_updated_2_data)) == 0); + + mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count)); + mu_assert_int_eq(COUNT_OF(test_hex_updated_2_data), count); + mu_check( + flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_updated_2_data))); + mu_check(memcmp(test_hex_updated_2_data, ARRAY_W_BSIZE(hex_updated_2_data)) == 0); + + mu_check(flipper_format_get_value_count(flipper_format, test_hex_new_key, &count)); + mu_assert_int_eq(COUNT_OF(test_hex_new_data), count); + mu_check( + flipper_format_read_hex(flipper_format, test_hex_new_key, ARRAY_W_COUNT(hex_new_data))); + mu_check(memcmp(test_hex_new_data, ARRAY_W_BSIZE(hex_new_data)) == 0); + + mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr)); + + string_clear(tmpstr); + + // delete key test mu_check(flipper_format_rewind(flipper_format)); mu_check(flipper_format_delete_key(flipper_format, test_uint_key)); + // deleted key read test mu_check(flipper_format_rewind(flipper_format)); mu_check(!flipper_format_read_uint32( flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_data))); diff --git a/lib/flipper_format/flipper_format.c b/lib/flipper_format/flipper_format.c index 074d6da2..dd3676c5 100644 --- a/lib/flipper_format/flipper_format.c +++ b/lib/flipper_format/flipper_format.c @@ -102,6 +102,20 @@ bool flipper_format_rewind(FlipperFormat* flipper_format) { return stream_rewind(flipper_format->stream); } +bool flipper_format_seek_to_end(FlipperFormat* flipper_format) { + furi_assert(flipper_format); + return stream_seek(flipper_format->stream, 0, StreamOffsetFromEnd); +} + +bool flipper_format_key_exist(FlipperFormat* flipper_format, const char* key) { + size_t pos = stream_tell(flipper_format->stream); + stream_seek(flipper_format->stream, 0, StreamOffsetFromStart); + bool result = flipper_format_stream_seek_to_key(flipper_format->stream, key, false); + stream_seek(flipper_format->stream, pos, StreamOffsetFromStart); + + return result; +} + bool flipper_format_read_header( FlipperFormat* flipper_format, string_t filetype, @@ -320,7 +334,7 @@ bool flipper_format_update_string(FlipperFormat* flipper_format, const char* key FlipperStreamWriteData write_data = { .key = key, .type = FlipperStreamValueStr, - .data = data, + .data = string_get_cstr(data), .data_size = 1, }; bool result = flipper_format_stream_delete_key_and_write( @@ -408,3 +422,103 @@ bool flipper_format_update_hex( flipper_format->stream, &write_data, flipper_format->strict_mode); return result; } + +bool flipper_format_insert_or_update_string( + FlipperFormat* flipper_format, + const char* key, + string_t data) { + bool result = false; + + if(!flipper_format_key_exist(flipper_format, key)) { + flipper_format_seek_to_end(flipper_format); + result = flipper_format_write_string(flipper_format, key, data); + } else { + result = flipper_format_update_string(flipper_format, key, data); + } + + return result; +} + +bool flipper_format_insert_or_update_string_cstr( + FlipperFormat* flipper_format, + const char* key, + const char* data) { + bool result = false; + + if(!flipper_format_key_exist(flipper_format, key)) { + flipper_format_seek_to_end(flipper_format); + result = flipper_format_write_string_cstr(flipper_format, key, data); + } else { + result = flipper_format_update_string_cstr(flipper_format, key, data); + } + + return result; +} + +bool flipper_format_insert_or_update_uint32( + FlipperFormat* flipper_format, + const char* key, + const uint32_t* data, + const uint16_t data_size) { + bool result = false; + + if(!flipper_format_key_exist(flipper_format, key)) { + flipper_format_seek_to_end(flipper_format); + result = flipper_format_write_uint32(flipper_format, key, data, data_size); + } else { + result = flipper_format_update_uint32(flipper_format, key, data, data_size); + } + + return result; +} + +bool flipper_format_insert_or_update_int32( + FlipperFormat* flipper_format, + const char* key, + const int32_t* data, + const uint16_t data_size) { + bool result = false; + + if(!flipper_format_key_exist(flipper_format, key)) { + flipper_format_seek_to_end(flipper_format); + result = flipper_format_write_int32(flipper_format, key, data, data_size); + } else { + result = flipper_format_update_int32(flipper_format, key, data, data_size); + } + + return result; +} + +bool flipper_format_insert_or_update_float( + FlipperFormat* flipper_format, + const char* key, + const float* data, + const uint16_t data_size) { + bool result = false; + + if(!flipper_format_key_exist(flipper_format, key)) { + flipper_format_seek_to_end(flipper_format); + result = flipper_format_write_float(flipper_format, key, data, data_size); + } else { + result = flipper_format_update_float(flipper_format, key, data, data_size); + } + + return result; +} + +bool flipper_format_insert_or_update_hex( + FlipperFormat* flipper_format, + const char* key, + const uint8_t* data, + const uint16_t data_size) { + bool result = false; + + if(!flipper_format_key_exist(flipper_format, key)) { + flipper_format_seek_to_end(flipper_format); + result = flipper_format_write_hex(flipper_format, key, data, data_size); + } else { + result = flipper_format_update_hex(flipper_format, key, data, data_size); + } + + return result; +} \ No newline at end of file diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index c23e02fc..a67c7a47 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -179,6 +179,22 @@ void flipper_format_set_strict_mode(FlipperFormat* flipper_format, bool strict_m */ bool flipper_format_rewind(FlipperFormat* flipper_format); +/** + * Move the RW pointer at the end. Can be useful if you want to add some data after reading. + * @param flipper_format Pointer to a FlipperFormat instance + * @return True on success + */ +bool flipper_format_seek_to_end(FlipperFormat* flipper_format); + +/** + * Check if the key exists. + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @return true key exists + * @return false key is not exists + */ +bool flipper_format_key_exist(FlipperFormat* flipper_format, const char* key); + /** * Read the header (file type and version). * @param flipper_format Pointer to a FlipperFormat instance @@ -466,6 +482,89 @@ bool flipper_format_update_hex( const uint8_t* data, const uint16_t data_size); +/** + * Updates the value of the first matching key to a string value, or adds the key and value if the key did not exist. + * Sets the RW pointer to a position at the end of inserted data. + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @param data Value + * @return True on success + */ +bool flipper_format_insert_or_update_string( + FlipperFormat* flipper_format, + const char* key, + string_t data); + +/** + * Updates the value of the first matching key to a string value, or adds the key and value if the key did not exist. + * Plain C version. + * Sets the RW pointer to a position at the end of inserted data. + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @param data Value + * @return True on success + */ +bool flipper_format_insert_or_update_string_cstr( + FlipperFormat* flipper_format, + const char* key, + const char* data); + +/** + * Updates the value of the first matching key to a uint32 array value, or adds the key and value if the key did not exist. + * Sets the RW pointer to a position at the end of inserted data. + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @param data Value + * @return True on success + */ +bool flipper_format_insert_or_update_uint32( + FlipperFormat* flipper_format, + const char* key, + const uint32_t* data, + const uint16_t data_size); + +/** + * Updates the value of the first matching key to a int32 array value, or adds the key and value if the key did not exist. + * Sets the RW pointer to a position at the end of inserted data. + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @param data Value + * @return True on success + */ +bool flipper_format_insert_or_update_int32( + FlipperFormat* flipper_format, + const char* key, + const int32_t* data, + const uint16_t data_size); + +/** + * Updates the value of the first matching key to a float array value, or adds the key and value if the key did not exist. + * Sets the RW pointer to a position at the end of inserted data. + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @param data Value + * @return True on success + */ +bool flipper_format_insert_or_update_float( + FlipperFormat* flipper_format, + const char* key, + const float* data, + const uint16_t data_size); + +/** + * Updates the value of the first matching key to an array of hex-formatted bytes, or adds the key and value if the key did not exist. + *Sets the RW pointer to a position at the end of inserted data. + * @param flipper_format Pointer to a FlipperFormat instance + * @param key Key + * @param data Value + * @return True on success + */ +bool flipper_format_insert_or_update_hex( + FlipperFormat* flipper_format, + const char* key, + const uint8_t* data, + const uint16_t data_size); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/lib/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index 02425d63..44084b05 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -93,7 +93,7 @@ static bool flipper_format_stream_read_valid_key(Stream* stream, string_t key) { return found; } -static bool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) { +bool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) { bool found = false; string_t read_key; diff --git a/lib/flipper_format/flipper_format_stream_i.h b/lib/flipper_format/flipper_format_stream_i.h index ee825d0e..74cf8f1a 100644 --- a/lib/flipper_format/flipper_format_stream_i.h +++ b/lib/flipper_format/flipper_format_stream_i.h @@ -18,6 +18,17 @@ extern "C" { */ bool flipper_format_stream_write_eol(Stream* stream); +/** + * Seek to the key from the current position of the stream. + * Position will be at the beginning of the value corresponding to the key, if the key is found,, or at the end of the stream. + * @param stream + * @param key + * @param strict_mode + * @return true key is found + * @return false key is not found + */ +bool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode); + #ifdef __cplusplus } #endif \ No newline at end of file