file-worker.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. #include "file-worker.h"
  2. #include "m-string.h"
  3. #include <hex.h>
  4. #include <sd-card-api.h>
  5. #include <furi.h>
  6. struct FileWorker {
  7. FS_Api* fs_api;
  8. SdCard_Api* sd_ex_api;
  9. bool silent;
  10. File file;
  11. char file_buf[48];
  12. size_t file_buf_cnt;
  13. };
  14. bool file_worker_check_common_errors(FileWorker* file_worker);
  15. void file_worker_show_error_internal(FileWorker* file_worker, const char* error_text);
  16. bool file_worker_read_internal(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read);
  17. bool file_worker_write_internal(
  18. FileWorker* file_worker,
  19. const void* buffer,
  20. uint16_t bytes_to_write);
  21. bool file_worker_tell_internal(FileWorker* file_worker, uint64_t* position);
  22. bool file_worker_seek_internal(FileWorker* file_worker, uint64_t position, bool from_start);
  23. FileWorker* file_worker_alloc(bool _silent) {
  24. FileWorker* file_worker = malloc(sizeof(FileWorker));
  25. file_worker->silent = _silent;
  26. file_worker->fs_api = furi_record_open("sdcard");
  27. file_worker->sd_ex_api = furi_record_open("sdcard-ex");
  28. file_worker->file_buf_cnt = 0;
  29. return file_worker;
  30. }
  31. void file_worker_free(FileWorker* file_worker) {
  32. furi_record_close("sdcard");
  33. furi_record_close("sdcard-ex");
  34. free(file_worker);
  35. }
  36. bool file_worker_open(
  37. FileWorker* file_worker,
  38. const char* filename,
  39. FS_AccessMode access_mode,
  40. FS_OpenMode open_mode) {
  41. bool result =
  42. file_worker->fs_api->file.open(&file_worker->file, filename, access_mode, open_mode);
  43. if(!result) {
  44. file_worker_show_error_internal(file_worker, "Cannot open\nfile");
  45. return false;
  46. }
  47. return file_worker_check_common_errors(file_worker);
  48. }
  49. bool file_worker_close(FileWorker* file_worker) {
  50. file_worker->fs_api->file.close(&file_worker->file);
  51. return file_worker_check_common_errors(file_worker);
  52. }
  53. bool file_worker_mkdir(FileWorker* file_worker, const char* dirname) {
  54. FS_Error fs_result = file_worker->fs_api->common.mkdir(dirname);
  55. if(fs_result != FSE_OK && fs_result != FSE_EXIST) {
  56. file_worker_show_error_internal(file_worker, "Cannot create\nfolder");
  57. return false;
  58. };
  59. return file_worker_check_common_errors(file_worker);
  60. }
  61. bool file_worker_remove(FileWorker* file_worker, const char* filename) {
  62. FS_Error fs_result = file_worker->fs_api->common.remove(filename);
  63. if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) {
  64. file_worker_show_error_internal(file_worker, "Cannot remove\nold file");
  65. return false;
  66. };
  67. return file_worker_check_common_errors(file_worker);
  68. }
  69. bool file_worker_read(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read) {
  70. if(!file_worker_read_internal(file_worker, buffer, bytes_to_read)) {
  71. return false;
  72. }
  73. return file_worker_check_common_errors(file_worker);
  74. }
  75. bool file_worker_read_until(FileWorker* file_worker, string_t str_result, char separator) {
  76. string_clean(str_result);
  77. const uint8_t buffer_size = 32;
  78. uint8_t buffer[buffer_size];
  79. do {
  80. uint16_t read_count =
  81. file_worker->fs_api->file.read(&file_worker->file, buffer, buffer_size);
  82. if(file_worker->file.error_id != FSE_OK) {
  83. file_worker_show_error_internal(file_worker, "Cannot read\nfile");
  84. return false;
  85. }
  86. bool result = false;
  87. for(uint16_t i = 0; i < read_count; i++) {
  88. if(buffer[i] == separator) {
  89. uint64_t position;
  90. if(!file_worker_tell_internal(file_worker, &position)) {
  91. return false;
  92. }
  93. position = position - read_count + i + 1;
  94. if(!file_worker_seek_internal(file_worker, position, true)) {
  95. return false;
  96. }
  97. result = true;
  98. break;
  99. } else {
  100. string_push_back(str_result, buffer[i]);
  101. }
  102. }
  103. if(result || read_count == 0) {
  104. break;
  105. }
  106. } while(true);
  107. return file_worker_check_common_errors(file_worker);
  108. }
  109. bool file_worker_read_hex(FileWorker* file_worker, uint8_t* buffer, uint16_t bytes_to_read) {
  110. uint8_t hi_nibble_value, low_nibble_value;
  111. uint8_t text[2];
  112. for(uint8_t i = 0; i < bytes_to_read; i++) {
  113. if(i != 0) {
  114. // space
  115. if(!file_worker_read_internal(file_worker, text, 1)) {
  116. return false;
  117. }
  118. }
  119. // actual data
  120. if(!file_worker_read_internal(file_worker, text, 2)) {
  121. return false;
  122. }
  123. // convert hex value to byte
  124. if(hex_char_to_hex_nibble(text[0], &hi_nibble_value) &&
  125. hex_char_to_hex_nibble(text[1], &low_nibble_value)) {
  126. buffer[i] = (hi_nibble_value << 4) | low_nibble_value;
  127. } else {
  128. file_worker_show_error_internal(file_worker, "Cannot parse\nfile");
  129. return false;
  130. }
  131. }
  132. return file_worker_check_common_errors(file_worker);
  133. }
  134. bool file_worker_tell(FileWorker* file_worker, uint64_t* position) {
  135. if(!file_worker_tell_internal(file_worker, position)) {
  136. return false;
  137. }
  138. return file_worker_check_common_errors(file_worker);
  139. }
  140. bool file_worker_seek(FileWorker* file_worker, uint64_t position, bool from_start) {
  141. if(!file_worker_seek_internal(file_worker, position, from_start)) {
  142. return false;
  143. }
  144. return file_worker_check_common_errors(file_worker);
  145. }
  146. bool file_worker_write(FileWorker* file_worker, const void* buffer, uint16_t bytes_to_write) {
  147. if(!file_worker_write_internal(file_worker, buffer, bytes_to_write)) {
  148. return false;
  149. }
  150. return file_worker_check_common_errors(file_worker);
  151. }
  152. bool file_worker_write_hex(FileWorker* file_worker, const uint8_t* buffer, uint16_t bytes_to_write) {
  153. const uint8_t byte_text_size = 3;
  154. char byte_text[byte_text_size];
  155. for(uint8_t i = 0; i < bytes_to_write; i++) {
  156. sniprintf(byte_text, byte_text_size, "%02X", buffer[i]);
  157. if(i != 0) {
  158. // space
  159. const char* space = " ";
  160. if(!file_worker_write_internal(file_worker, space, 1)) {
  161. return false;
  162. }
  163. }
  164. if(!file_worker_write_internal(file_worker, byte_text, 2)) {
  165. return false;
  166. }
  167. }
  168. return file_worker_check_common_errors(file_worker);
  169. }
  170. void file_worker_show_error(FileWorker* file_worker, const char* error_text) {
  171. file_worker->sd_ex_api->show_error(file_worker->sd_ex_api->context, error_text);
  172. }
  173. bool file_worker_file_select(
  174. FileWorker* file_worker,
  175. const char* path,
  176. const char* extension,
  177. char* result,
  178. uint8_t result_size,
  179. const char* selected_filename) {
  180. return file_worker->sd_ex_api->file_select(
  181. file_worker->sd_ex_api->context, path, extension, result, result_size, selected_filename);
  182. }
  183. bool file_worker_check_common_errors(FileWorker* file_worker) {
  184. /* TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info(). */
  185. FS_Error fs_err = file_worker->fs_api->common.get_fs_info(NULL, NULL);
  186. if(fs_err != FSE_OK)
  187. file_worker->sd_ex_api->show_error(file_worker->sd_ex_api->context, "SD card not found");
  188. return fs_err == FSE_OK;
  189. }
  190. void file_worker_show_error_internal(FileWorker* file_worker, const char* error_text) {
  191. if(!file_worker->silent) {
  192. file_worker_show_error(file_worker, error_text);
  193. }
  194. }
  195. bool file_worker_read_internal(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read) {
  196. uint16_t read_count =
  197. file_worker->fs_api->file.read(&file_worker->file, buffer, bytes_to_read);
  198. if(file_worker->file.error_id != FSE_OK || read_count != bytes_to_read) {
  199. file_worker_show_error_internal(file_worker, "Cannot read\nfile");
  200. return false;
  201. }
  202. return true;
  203. }
  204. bool file_worker_write_internal(
  205. FileWorker* file_worker,
  206. const void* buffer,
  207. uint16_t bytes_to_write) {
  208. uint16_t write_count =
  209. file_worker->fs_api->file.write(&file_worker->file, buffer, bytes_to_write);
  210. if(file_worker->file.error_id != FSE_OK || write_count != bytes_to_write) {
  211. file_worker_show_error_internal(file_worker, "Cannot write\nto file");
  212. return false;
  213. }
  214. return true;
  215. }
  216. bool file_worker_tell_internal(FileWorker* file_worker, uint64_t* position) {
  217. *position = file_worker->fs_api->file.tell(&file_worker->file);
  218. if(file_worker->file.error_id != FSE_OK) {
  219. file_worker_show_error_internal(file_worker, "Cannot tell\nfile offset");
  220. return false;
  221. }
  222. return true;
  223. }
  224. bool file_worker_seek_internal(FileWorker* file_worker, uint64_t position, bool from_start) {
  225. file_worker->fs_api->file.seek(&file_worker->file, position, from_start);
  226. if(file_worker->file.error_id != FSE_OK) {
  227. file_worker_show_error_internal(file_worker, "Cannot seek\nfile");
  228. return false;
  229. }
  230. return true;
  231. }
  232. bool file_worker_read_until_buffered(FileWorker* file_worker, string_t str_result, char* file_buf, size_t* file_buf_cnt, size_t file_buf_size, char separator) {
  233. furi_assert(string_capacity(str_result) > 0);
  234. furi_assert(file_buf_size <= 512); /* fs_api->file.read now supports up to 512 bytes reading at a time */
  235. string_clean(str_result);
  236. size_t newline_index = 0;
  237. bool found_eol = false;
  238. bool max_length_exceeded = false;
  239. size_t max_length = string_capacity(str_result) - 1;
  240. while(1) {
  241. if(*file_buf_cnt > 0) {
  242. size_t end_index = 0;
  243. char* endline_ptr = (char*)memchr(file_buf, separator, *file_buf_cnt);
  244. newline_index = endline_ptr - file_buf;
  245. if(endline_ptr == 0) {
  246. end_index = *file_buf_cnt;
  247. } else if(newline_index < *file_buf_cnt) {
  248. end_index = newline_index + 1;
  249. found_eol = true;
  250. } else {
  251. furi_assert(0);
  252. }
  253. if (max_length && (string_size(str_result) + end_index > max_length))
  254. max_length_exceeded = true;
  255. if (!max_length_exceeded) {
  256. for (size_t i = 0; i < end_index; ++i) {
  257. string_push_back(str_result, file_buf[i]);
  258. }
  259. }
  260. memmove(file_buf, &file_buf[end_index], *file_buf_cnt - end_index);
  261. *file_buf_cnt = *file_buf_cnt - end_index;
  262. if(found_eol) break;
  263. }
  264. *file_buf_cnt +=
  265. file_worker->fs_api->file.read(&file_worker->file, &file_buf[*file_buf_cnt], file_buf_size - *file_buf_cnt);
  266. if(file_worker->file.error_id != FSE_OK) {
  267. file_worker_show_error_internal(file_worker, "Cannot read\nfile");
  268. string_clear(str_result);
  269. *file_buf_cnt = 0;
  270. break;
  271. }
  272. if(*file_buf_cnt == 0) {
  273. break; // end of reading
  274. }
  275. }
  276. if (max_length_exceeded)
  277. string_clear(str_result);
  278. return string_size(str_result) || *file_buf_cnt;
  279. }
  280. bool file_worker_rename(FileWorker* file_worker, const char* old_path, const char* new_path) {
  281. FS_Error fs_result = file_worker->fs_api->common.rename(old_path, new_path);
  282. if(fs_result != FSE_OK && fs_result != FSE_EXIST) {
  283. file_worker_show_error_internal(file_worker, "Cannot rename\n file/directory");
  284. return false;
  285. }
  286. return file_worker_check_common_errors(file_worker);
  287. }
  288. bool file_worker_check_errors(FileWorker* file_worker) {
  289. return file_worker_check_common_errors(file_worker);
  290. }
  291. bool file_worker_is_file_exist(
  292. FileWorker* file_worker,
  293. const char* filename,
  294. bool* exist) {
  295. File file;
  296. *exist = file_worker->fs_api->file.open(&file, filename, FSAM_READ, FSOM_OPEN_EXISTING);
  297. if (*exist)
  298. file_worker->fs_api->file.close(&file);
  299. return file_worker_check_common_errors(file_worker);
  300. }