flipper_file.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. #include <furi.h>
  2. #include "file_helper.h"
  3. #include "flipper_file_helper.h"
  4. #include "flipper_file.h"
  5. #include "flipper_file_i.h"
  6. #include <inttypes.h>
  7. #include <toolbox/hex.h>
  8. FlipperFile* flipper_file_alloc(Storage* storage) {
  9. // furi_assert(storage);
  10. FlipperFile* flipper_file = malloc(sizeof(FlipperFile));
  11. flipper_file->storage = storage;
  12. flipper_file->file = storage_file_alloc(flipper_file->storage);
  13. flipper_file->strict_mode = false;
  14. return flipper_file;
  15. }
  16. void flipper_file_free(FlipperFile* flipper_file) {
  17. furi_assert(flipper_file);
  18. if(storage_file_is_open(flipper_file->file)) {
  19. storage_file_close(flipper_file->file);
  20. }
  21. storage_file_free(flipper_file->file);
  22. free(flipper_file);
  23. }
  24. void flipper_file_set_strict_mode(FlipperFile* flipper_file, bool strict_mode) {
  25. furi_assert(flipper_file);
  26. flipper_file->strict_mode = strict_mode;
  27. }
  28. bool flipper_file_open_existing(FlipperFile* flipper_file, const char* filename) {
  29. furi_assert(flipper_file);
  30. bool result = storage_file_open(
  31. flipper_file->file, filename, FSAM_READ | FSAM_WRITE, FSOM_OPEN_EXISTING);
  32. return result;
  33. }
  34. bool flipper_file_open_append(FlipperFile* flipper_file, const char* filename) {
  35. furi_assert(flipper_file);
  36. bool result =
  37. storage_file_open(flipper_file->file, filename, FSAM_READ | FSAM_WRITE, FSOM_OPEN_APPEND);
  38. // Add EOL if it is not there
  39. if(storage_file_size(flipper_file->file) >= 1) {
  40. do {
  41. char last_char;
  42. result = false;
  43. if(!file_helper_seek(flipper_file->file, -1)) break;
  44. uint16_t bytes_were_read = storage_file_read(flipper_file->file, &last_char, 1);
  45. if(bytes_were_read != 1) break;
  46. if(last_char != flipper_file_eoln) {
  47. if(!file_helper_write_eol(flipper_file->file)) break;
  48. }
  49. result = true;
  50. } while(false);
  51. }
  52. return result;
  53. }
  54. bool flipper_file_open_always(FlipperFile* flipper_file, const char* filename) {
  55. furi_assert(flipper_file);
  56. bool result = storage_file_open(
  57. flipper_file->file, filename, FSAM_READ | FSAM_WRITE, FSOM_CREATE_ALWAYS);
  58. return result;
  59. }
  60. bool flipper_file_open_new(FlipperFile* flipper_file, const char* filename) {
  61. furi_assert(flipper_file);
  62. bool result =
  63. storage_file_open(flipper_file->file, filename, FSAM_READ | FSAM_WRITE, FSOM_CREATE_NEW);
  64. return result;
  65. }
  66. bool flipper_file_close(FlipperFile* flipper_file) {
  67. furi_assert(flipper_file);
  68. if(storage_file_is_open(flipper_file->file)) {
  69. return storage_file_close(flipper_file->file);
  70. }
  71. return true;
  72. }
  73. bool flipper_file_rewind(FlipperFile* flipper_file) {
  74. furi_assert(flipper_file);
  75. return storage_file_seek(flipper_file->file, 0, true);
  76. }
  77. bool flipper_file_read_header(FlipperFile* flipper_file, string_t filetype, uint32_t* version) {
  78. bool result = false;
  79. do {
  80. result = flipper_file_read_string(flipper_file, flipper_file_filetype_key, filetype);
  81. if(!result) break;
  82. result = flipper_file_read_uint32(flipper_file, flipper_file_version_key, version, 1);
  83. if(!result) break;
  84. } while(false);
  85. return result;
  86. }
  87. bool flipper_file_write_header(
  88. FlipperFile* flipper_file,
  89. string_t filetype,
  90. const uint32_t version) {
  91. bool result = false;
  92. do {
  93. result = flipper_file_write_string(flipper_file, flipper_file_filetype_key, filetype);
  94. if(!result) break;
  95. result = flipper_file_write_uint32(flipper_file, flipper_file_version_key, &version, 1);
  96. if(!result) break;
  97. } while(false);
  98. return result;
  99. }
  100. bool flipper_file_write_header_cstr(
  101. FlipperFile* flipper_file,
  102. const char* filetype,
  103. const uint32_t version) {
  104. bool result = false;
  105. string_t value;
  106. string_init_set(value, filetype);
  107. result = flipper_file_write_header(flipper_file, value, version);
  108. string_clear(value);
  109. return result;
  110. }
  111. bool flipper_file_get_value_count(FlipperFile* flipper_file, const char* key, uint32_t* count) {
  112. furi_assert(flipper_file);
  113. bool result = false;
  114. bool last = false;
  115. string_t value;
  116. string_init(value);
  117. uint32_t position = storage_file_tell(flipper_file->file);
  118. do {
  119. if(!flipper_file_seek_to_key(flipper_file->file, key, flipper_file->strict_mode)) break;
  120. // Balance between speed and memory consumption
  121. // I prefer lower speed but less memory consumption
  122. *count = 0;
  123. result = true;
  124. while(true) {
  125. if(!file_helper_read_value(flipper_file->file, value, &last)) {
  126. result = false;
  127. break;
  128. }
  129. *count = *count + 1;
  130. if(last) break;
  131. }
  132. } while(false);
  133. if(!storage_file_seek(flipper_file->file, position, true)) {
  134. result = false;
  135. }
  136. string_clear(value);
  137. return result;
  138. }
  139. bool flipper_file_write_comment(FlipperFile* flipper_file, string_t data) {
  140. furi_assert(flipper_file);
  141. bool result = false;
  142. do {
  143. const char comment_buffer[2] = {flipper_file_comment, ' '};
  144. result = file_helper_write(flipper_file->file, comment_buffer, sizeof(comment_buffer));
  145. if(!result) break;
  146. result = file_helper_write(flipper_file->file, string_get_cstr(data), string_size(data));
  147. if(!result) break;
  148. result = file_helper_write_eol(flipper_file->file);
  149. } while(false);
  150. return result;
  151. }
  152. bool flipper_file_write_comment_cstr(FlipperFile* flipper_file, const char* data) {
  153. bool result = false;
  154. string_t value;
  155. string_init_set(value, data);
  156. result = flipper_file_write_comment(flipper_file, value);
  157. string_clear(value);
  158. return result;
  159. }
  160. bool flipper_file_delete_key_and_call(
  161. FlipperFile* flipper_file,
  162. const char* key,
  163. flipper_file_cb call,
  164. const char* cb_key,
  165. const void* cb_data,
  166. const uint16_t cb_data_size) {
  167. bool result = false;
  168. File* scratch_file = storage_file_alloc(flipper_file->storage);
  169. do {
  170. // get size
  171. uint64_t file_size = storage_file_size(flipper_file->file);
  172. if(file_size == 0) break;
  173. if(!storage_file_seek(flipper_file->file, 0, true)) break;
  174. // find key
  175. if(!flipper_file_seek_to_key(flipper_file->file, key, flipper_file->strict_mode)) break;
  176. // get key start position
  177. uint64_t start_position = storage_file_tell(flipper_file->file) - strlen(key);
  178. if(start_position >= 2) {
  179. start_position -= 2;
  180. } else {
  181. // something wrong
  182. break;
  183. }
  184. // get value end position
  185. if(!file_helper_seek_to_next_line(flipper_file->file)) break;
  186. uint64_t end_position = storage_file_tell(flipper_file->file);
  187. // newline symbol
  188. if(end_position < file_size) {
  189. end_position += 1;
  190. }
  191. // open scratchpad
  192. const char* scratch_name = "";
  193. if(!flipper_file_get_scratchpad_name(&scratch_name)) break;
  194. if(!storage_file_open(
  195. scratch_file, scratch_name, FSAM_READ | FSAM_WRITE, FSOM_CREATE_ALWAYS))
  196. break;
  197. // copy key file before key to scratchpad
  198. if(!file_helper_copy(flipper_file->file, scratch_file, 0, start_position)) break;
  199. // do something in between if needed
  200. if(call != NULL) {
  201. if(!call(scratch_file, cb_key, cb_data, cb_data_size)) break;
  202. };
  203. // copy key file after key value to scratchpad
  204. if(!file_helper_copy(flipper_file->file, scratch_file, end_position, file_size)) break;
  205. file_size = storage_file_tell(scratch_file);
  206. if(file_size == 0) break;
  207. if(!storage_file_seek(flipper_file->file, 0, true)) break;
  208. // copy whole scratchpad file to the original file
  209. if(!file_helper_copy(scratch_file, flipper_file->file, 0, file_size)) break;
  210. // and truncate original file
  211. if(!storage_file_truncate(flipper_file->file)) break;
  212. // close and remove scratchpad file
  213. if(!storage_file_close(scratch_file)) break;
  214. if(storage_common_remove(flipper_file->storage, scratch_name) != FSE_OK) break;
  215. result = true;
  216. } while(false);
  217. storage_file_free(scratch_file);
  218. return result;
  219. }
  220. bool flipper_file_delete_key(FlipperFile* flipper_file, const char* key) {
  221. furi_assert(flipper_file);
  222. return flipper_file_delete_key_and_call(flipper_file, key, NULL, NULL, NULL, 0);
  223. }
  224. bool flipper_file_write_internal(
  225. File* file,
  226. const char* key,
  227. const void* _data,
  228. const uint16_t data_size,
  229. FlipperFileValueType type) {
  230. bool result = false;
  231. string_t value;
  232. string_init(value);
  233. do {
  234. result = flipper_file_write_key(file, key);
  235. if(!result) break;
  236. for(uint16_t i = 0; i < data_size; i++) {
  237. switch(type) {
  238. case FlipperFileValueHex: {
  239. const uint8_t* data = _data;
  240. string_printf(value, "%02X", data[i]);
  241. }; break;
  242. case FlipperFileValueFloat: {
  243. const float* data = _data;
  244. string_printf(value, "%f", data[i]);
  245. }; break;
  246. case FlipperFileValueInt32: {
  247. const int32_t* data = _data;
  248. string_printf(value, "%" PRIi32, data[i]);
  249. }; break;
  250. case FlipperFileValueUint32: {
  251. const uint32_t* data = _data;
  252. string_printf(value, "%" PRId32, data[i]);
  253. }; break;
  254. }
  255. if((i + 1) < data_size) {
  256. string_cat(value, " ");
  257. }
  258. result = file_helper_write(file, string_get_cstr(value), string_size(value));
  259. if(!result) break;
  260. }
  261. result = file_helper_write_eol(file);
  262. } while(false);
  263. string_clear(value);
  264. return result;
  265. }
  266. bool flipper_file_read_internal(
  267. File* file,
  268. const char* key,
  269. void* _data,
  270. const uint16_t data_size,
  271. bool strict_mode,
  272. FlipperFileValueType type) {
  273. bool result = false;
  274. string_t value;
  275. string_init(value);
  276. if(flipper_file_seek_to_key(file, key, strict_mode)) {
  277. result = true;
  278. for(uint16_t i = 0; i < data_size; i++) {
  279. bool last = false;
  280. result = file_helper_read_value(file, value, &last);
  281. if(result) {
  282. int scan_values = 0;
  283. switch(type) {
  284. case FlipperFileValueHex: {
  285. uint8_t* data = _data;
  286. // sscanf "%02X" does not work here
  287. if(hex_chars_to_uint8(
  288. string_get_char(value, 0), string_get_char(value, 1), &data[i])) {
  289. scan_values = 1;
  290. }
  291. }; break;
  292. case FlipperFileValueFloat: {
  293. float* data = _data;
  294. // newlib-nano does not have sscanf for floats
  295. // scan_values = sscanf(string_get_cstr(value), "%f", &data[i]);
  296. char* end_char;
  297. data[i] = strtof(string_get_cstr(value), &end_char);
  298. if(*end_char == 0) {
  299. // very probably ok
  300. scan_values = 1;
  301. }
  302. }; break;
  303. case FlipperFileValueInt32: {
  304. int32_t* data = _data;
  305. scan_values = sscanf(string_get_cstr(value), "%" PRIi32, &data[i]);
  306. }; break;
  307. case FlipperFileValueUint32: {
  308. uint32_t* data = _data;
  309. scan_values = sscanf(string_get_cstr(value), "%" PRId32, &data[i]);
  310. }; break;
  311. }
  312. if(scan_values != 1) {
  313. result = false;
  314. break;
  315. }
  316. } else {
  317. break;
  318. }
  319. if(last && ((i + 1) != data_size)) {
  320. result = false;
  321. break;
  322. }
  323. }
  324. }
  325. string_clear(value);
  326. return result;
  327. }
  328. File* flipper_file_get_file(FlipperFile* flipper_file) {
  329. furi_assert(flipper_file);
  330. furi_assert(flipper_file->file);
  331. return flipper_file->file;
  332. }