shapshup_files.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #include "shapshup_files.h"
  2. #include <inttypes.h>
  3. #include <lib/subghz/types.h>
  4. #define TAG "ShapShupFiles"
  5. #define RAW_KEY_NAME "RAW_Data"
  6. const size_t buffer_size = 32;
  7. static bool stream_read_valid_key_shapshup(Stream* stream, FuriString* key) {
  8. furi_string_reset(key);
  9. uint8_t buffer[buffer_size];
  10. bool found = false;
  11. bool error = false;
  12. bool accumulate = true;
  13. bool new_line = true;
  14. while(true) {
  15. size_t was_read = stream_read(stream, buffer, buffer_size);
  16. if(was_read == 0) break;
  17. for(size_t i = 0; i < was_read; i++) {
  18. uint8_t data = buffer[i];
  19. if(data == flipper_format_eoln) {
  20. // EOL found, clean data, start accumulating data and set the new_line flag
  21. furi_string_reset(key);
  22. accumulate = true;
  23. new_line = true;
  24. } else if(data == flipper_format_eolr) {
  25. // ignore
  26. } else if(data == flipper_format_comment && new_line) {
  27. // if there is a comment character and we are at the beginning of a new line
  28. // do not accumulate comment data and reset the new_line flag
  29. accumulate = false;
  30. new_line = false;
  31. } else if(data == flipper_format_delimiter) {
  32. if(new_line) {
  33. // we are on a "new line" and found the delimiter
  34. // this can only be if we have previously found some kind of key, so
  35. // clear the data, set the flag that we no longer want to accumulate data
  36. // and reset the new_line flag
  37. furi_string_reset(key);
  38. accumulate = false;
  39. new_line = false;
  40. } else {
  41. // parse the delimiter only if we are accumulating data
  42. if(accumulate) {
  43. // we found the delimiter, move the rw pointer to the delimiter location
  44. // and signal that we have found something
  45. if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
  46. error = true;
  47. break;
  48. }
  49. found = true;
  50. break;
  51. }
  52. }
  53. } else {
  54. // just new symbol, reset the new_line flag
  55. new_line = false;
  56. if(accumulate) {
  57. // and accumulate data if we want
  58. furi_string_push_back(key, data);
  59. }
  60. }
  61. }
  62. if(found || error) break;
  63. }
  64. return found;
  65. }
  66. bool stream_seek_to_key_shapshup(Stream* stream, const char* key, bool strict_mode) {
  67. bool found = false;
  68. FuriString* read_key;
  69. read_key = furi_string_alloc();
  70. while(!stream_eof(stream)) {
  71. if(stream_read_valid_key_shapshup(stream, read_key)) {
  72. if(furi_string_cmp_str(read_key, key) == 0) {
  73. if(!stream_seek(stream, 2, StreamOffsetFromCurrent)) {
  74. break;
  75. }
  76. found = true;
  77. break;
  78. } else if(strict_mode) {
  79. found = false;
  80. break;
  81. }
  82. }
  83. }
  84. furi_string_free(read_key);
  85. return found;
  86. }
  87. static inline bool is_space_shapshup(char c) {
  88. return c == ' ' || c == '\t' || c == flipper_format_eolr;
  89. }
  90. static bool stream_read_value_shapshup(Stream* stream, FuriString* value, bool* last) {
  91. enum { LeadingSpace, ReadValue, TrailingSpace } state = LeadingSpace;
  92. const size_t buffer_size = 32;
  93. uint8_t buffer[buffer_size];
  94. bool result = false;
  95. bool error = false;
  96. furi_string_reset(value);
  97. while(true) {
  98. size_t was_read = stream_read(stream, buffer, buffer_size);
  99. if(was_read == 0) {
  100. if(state != LeadingSpace && stream_eof(stream)) {
  101. result = true;
  102. *last = true;
  103. } else {
  104. error = true;
  105. }
  106. }
  107. for(uint16_t i = 0; i < was_read; i++) {
  108. const uint8_t data = buffer[i];
  109. if(state == LeadingSpace) {
  110. if(is_space_shapshup(data)) {
  111. continue;
  112. } else if(data == flipper_format_eoln) {
  113. stream_seek(stream, i - was_read, StreamOffsetFromCurrent);
  114. error = true;
  115. break;
  116. } else {
  117. state = ReadValue;
  118. furi_string_push_back(value, data);
  119. }
  120. } else if(state == ReadValue) {
  121. if(is_space_shapshup(data)) {
  122. state = TrailingSpace;
  123. } else if(data == flipper_format_eoln) {
  124. if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
  125. error = true;
  126. } else {
  127. result = true;
  128. *last = true;
  129. }
  130. break;
  131. } else {
  132. furi_string_push_back(value, data);
  133. }
  134. } else if(state == TrailingSpace) {
  135. if(is_space_shapshup(data)) {
  136. continue;
  137. } else if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
  138. error = true;
  139. } else {
  140. *last = (data == flipper_format_eoln);
  141. result = true;
  142. }
  143. break;
  144. }
  145. }
  146. if(error || result) break;
  147. }
  148. return result;
  149. }
  150. bool read_int32_shapshup(Stream* stream, int32_t* _data, uint16_t data_size) {
  151. bool result = false;
  152. result = true;
  153. FuriString* value;
  154. value = furi_string_alloc();
  155. for(size_t i = 0; i < data_size; i++) {
  156. bool last = false;
  157. result = stream_read_value_shapshup(stream, value, &last);
  158. if(result) {
  159. int scan_values = 0;
  160. int32_t* data = _data;
  161. scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]);
  162. if(scan_values != 1) {
  163. result = false;
  164. break;
  165. }
  166. } else {
  167. break;
  168. }
  169. if(last && ((i + 1) != data_size)) {
  170. result = false;
  171. break;
  172. }
  173. }
  174. furi_string_free(value);
  175. return result;
  176. }
  177. ShapShupRawFile* load_file_shapshup(const char* file_path) {
  178. furi_assert(file_path);
  179. Storage* storage = furi_record_open(RECORD_STORAGE);
  180. ShapShupRawFile* instance = malloc(sizeof(ShapShupRawFile));
  181. instance->total_len = 0;
  182. instance->total_count = 0;
  183. instance->min_value = 0;
  184. instance->max_value = 0;
  185. instance->max_len = 0;
  186. instance->min_len = 0;
  187. array_raw_init(instance->values);
  188. FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
  189. Stream* fff_data_stream = NULL;
  190. FuriString* temp_str;
  191. temp_str = furi_string_alloc();
  192. uint32_t temp_data32;
  193. instance->result = ShapShupFileResultUnknown;
  194. do {
  195. if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
  196. instance->result = ShapShupFileResultOpenError;
  197. FURI_LOG_E(TAG, shapshup_files_result_description(instance->result));
  198. break;
  199. }
  200. fff_data_stream = flipper_format_get_raw_stream(fff_data_file);
  201. if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
  202. instance->result = ShapShupFileResultIncorrectHeader;
  203. FURI_LOG_E(TAG, shapshup_files_result_description(instance->result));
  204. break;
  205. }
  206. if(temp_data32 != SUBGHZ_KEY_FILE_VERSION) {
  207. instance->result = ShapShupFileResultTypeOfVersionMismatch;
  208. FURI_LOG_E(TAG, shapshup_files_result_description(instance->result));
  209. break;
  210. }
  211. if(furi_string_cmp_str(temp_str, SUBGHZ_RAW_FILE_TYPE) != 0) {
  212. instance->result = ShapShupFileResultNotRawFile;
  213. FURI_LOG_E(
  214. TAG,
  215. "%s, Value: %s",
  216. shapshup_files_result_description(instance->result),
  217. furi_string_get_cstr(temp_str));
  218. break;
  219. }
  220. if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
  221. instance->result = ShapShupFileResultMissingFrequency;
  222. FURI_LOG_E(TAG, shapshup_files_result_description(instance->result));
  223. break;
  224. } else {
  225. instance->frequency = temp_data32;
  226. }
  227. if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
  228. instance->result = ShapShupFileResultMissingPreset;
  229. FURI_LOG_E(TAG, shapshup_files_result_description(instance->result));
  230. break;
  231. }
  232. if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
  233. instance->result = ShapShupFileResultMissingProtocol;
  234. FURI_LOG_E(TAG, shapshup_files_result_description(instance->result));
  235. break;
  236. }
  237. if(!stream_seek_to_key_shapshup(fff_data_stream, RAW_KEY_NAME, false)) {
  238. instance->result = ShapShupFileResultKeyNotFound;
  239. FURI_LOG_E(TAG, shapshup_files_result_description(instance->result));
  240. break;
  241. }
  242. // file_offset_start = stream_tell(fff_data_stream);
  243. instance->result = ShapShupFileResultOk;
  244. } while(false);
  245. uint64_t min_len = 0 - 1;
  246. uint64_t max_len = 0;
  247. if(instance->result == ShapShupFileResultOk) {
  248. int32_t value = 0;
  249. do {
  250. if(!read_int32_shapshup(fff_data_stream, &value, 1)) {
  251. break;
  252. }
  253. uint64_t abs_value = value < 0 ? value * -1 : value;
  254. if(value < instance->min_value) {
  255. instance->min_value = value;
  256. } else if(value > instance->max_value) {
  257. instance->max_value = value;
  258. }
  259. if(abs_value > max_len) {
  260. max_len = abs_value;
  261. } else if(abs_value < min_len) {
  262. min_len = abs_value;
  263. }
  264. array_raw_push_back(instance->values, value);
  265. instance->total_count++;
  266. instance->total_len += abs_value;
  267. } while(true);
  268. instance->max_len = max_len;
  269. instance->min_len = min_len;
  270. } else {
  271. array_raw_clear(instance->values);
  272. }
  273. FURI_LOG_I(
  274. TAG, "total_count: %lld, total_len: %lld", instance->total_count, instance->total_len);
  275. furi_string_free(temp_str);
  276. flipper_format_file_close(fff_data_file);
  277. flipper_format_free(fff_data_file);
  278. furi_record_close(RECORD_STORAGE);
  279. return instance;
  280. }
  281. void clean_raw_values(ShapShupRawFile* raw_file) {
  282. if(raw_file != NULL) {
  283. array_raw_clear(raw_file->values);
  284. free(raw_file);
  285. raw_file = NULL;
  286. }
  287. }
  288. static const char* shapshup_file_result_descriptions[] = {
  289. [ShapShupFileResultOk] = "OK",
  290. [ShapShupFileResultOpenError] = "Error open file",
  291. [ShapShupFileResultIncorrectHeader] = "Missing or incorrect header",
  292. [ShapShupFileResultTypeOfVersionMismatch] = "Type or version mismatch",
  293. [ShapShupFileResultNotRawFile] = "Not RAW file",
  294. [ShapShupFileResultMissingFrequency] = "Missing Frequency",
  295. [ShapShupFileResultMissingPreset] = "Missing Preset",
  296. [ShapShupFileResultMissingProtocol] = "Missing Protocol",
  297. [ShapShupFileResultKeyNotFound] = "Key not found",
  298. [ShapShupFileResultUnknown] = "Unknown error",
  299. };
  300. const char* shapshup_files_result_description(ShapShupFileResults index) {
  301. return shapshup_file_result_descriptions[index];
  302. }