xremote_cross_remote.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #include "xremote_cross_remote.h"
  2. ARRAY_DEF(CrossRemoteItemArray, CrossRemoteItem*, M_PTR_OPLIST);
  3. struct CrossRemote {
  4. FuriString* name;
  5. FuriString* path;
  6. CrossRemoteItemArray_t items;
  7. int transmitting;
  8. };
  9. static void xremote_cross_remote_clear_items(CrossRemote* remote) {
  10. CrossRemoteItemArray_it_t it;
  11. for(CrossRemoteItemArray_it(it, remote->items); !CrossRemoteItemArray_end_p(it);
  12. CrossRemoteItemArray_next(it)) {
  13. xremote_cross_remote_item_free(*CrossRemoteItemArray_cref(it));
  14. }
  15. CrossRemoteItemArray_reset(remote->items);
  16. }
  17. static void xremote_cross_remote_find_vacant_remote_name(FuriString* name, const char* path) {
  18. Storage* storage = furi_record_open(RECORD_STORAGE);
  19. FuriString* base_path;
  20. base_path = furi_string_alloc_set(path);
  21. if(furi_string_end_with(base_path, XREMOTE_APP_EXTENSION)) {
  22. size_t filename_start = furi_string_search_rchar(base_path, '/');
  23. furi_string_left(base_path, filename_start);
  24. }
  25. furi_string_printf(
  26. base_path, "%s/%s%s", path, furi_string_get_cstr(name), XREMOTE_APP_EXTENSION);
  27. FS_Error status = storage_common_stat(storage, furi_string_get_cstr(base_path), NULL);
  28. if(status == FSE_OK) {
  29. // If name is taken, try another name2, name3 etc
  30. size_t dot = furi_string_search_rchar(base_path, '.');
  31. furi_string_left(base_path, dot);
  32. FuriString* path_temp;
  33. path_temp = furi_string_alloc();
  34. uint32_t i = 1;
  35. do {
  36. furi_string_printf(
  37. path_temp, "%s%lu%s", furi_string_get_cstr(base_path), ++i, XREMOTE_APP_EXTENSION);
  38. status = storage_common_stat(storage, furi_string_get_cstr(path_temp), NULL);
  39. } while(status == FSE_OK);
  40. furi_string_free(path_temp);
  41. if(status == FSE_NOT_EXIST) {
  42. furi_string_cat_printf(name, "%lu", i);
  43. }
  44. }
  45. furi_string_free(base_path);
  46. furi_record_close(RECORD_STORAGE);
  47. }
  48. CrossRemote* xremote_cross_remote_alloc() {
  49. CrossRemote* remote = malloc(sizeof(CrossRemote));
  50. CrossRemoteItemArray_init(remote->items);
  51. remote->name = furi_string_alloc();
  52. remote->path = furi_string_alloc();
  53. remote->transmitting = 0;
  54. return remote;
  55. }
  56. void xremote_cross_remote_set_transmitting(CrossRemote* remote, int status) {
  57. remote->transmitting = status;
  58. }
  59. int xremote_cross_remote_get_transmitting(CrossRemote* remote) {
  60. return remote->transmitting;
  61. }
  62. void xremote_cross_remote_free(CrossRemote* remote) {
  63. furi_string_free(remote->name);
  64. furi_string_free(remote->path);
  65. xremote_cross_remote_clear_items(remote);
  66. CrossRemoteItemArray_clear(remote->items);
  67. free(remote);
  68. }
  69. const char* xremote_cross_remote_get_name(CrossRemote* remote) {
  70. return furi_string_get_cstr(remote->name);
  71. }
  72. bool xremote_cross_remote_add_ir_item(
  73. CrossRemote* remote,
  74. const char* name,
  75. InfraredSignal* signal,
  76. uint32_t timing) {
  77. CrossRemoteItem* item = xremote_cross_remote_item_alloc();
  78. xremote_cross_remote_item_set_type(item, XRemoteRemoteItemTypeInfrared);
  79. xremote_cross_remote_item_set_name(item, name);
  80. xremote_cross_remote_item_set_time(item, timing);
  81. xremote_cross_remote_item_set_ir_signal(item, signal);
  82. CrossRemoteItemArray_push_back(remote->items, item);
  83. return true;
  84. }
  85. bool xremote_cross_remote_add_pause(CrossRemote* remote, int time) {
  86. CrossRemoteItem* item = xremote_cross_remote_item_alloc();
  87. xremote_cross_remote_item_set_type(item, XRemoteRemoteItemTypePause);
  88. char name[9];
  89. snprintf(name, 9, CROSS_REMOTE_PAUSE_NAME, time);
  90. xremote_cross_remote_item_set_name(item, name);
  91. xremote_cross_remote_item_set_time(item, time);
  92. CrossRemoteItemArray_push_back(remote->items, item);
  93. return true;
  94. }
  95. bool xremote_cross_remote_add_subghz(CrossRemote* remote, SubGhzRemote* subghz) {
  96. CrossRemoteItem* item = xremote_cross_remote_item_alloc();
  97. xremote_cross_remote_item_set_type(item, XRemoteRemoteItemTypeSubGhz);
  98. xremote_cross_remote_item_set_name(item, xremote_sg_remote_get_name(subghz));
  99. xremote_cross_remote_item_set_filename(item, xremote_sg_remote_get_filename(subghz));
  100. FURI_LOG_D(TAG, "add subghz: %s", xremote_sg_remote_get_filename(subghz));
  101. xremote_cross_remote_item_set_sg_signal(item, subghz);
  102. CrossRemoteItemArray_push_back(remote->items, item);
  103. return true;
  104. }
  105. size_t xremote_cross_remote_get_item_count(CrossRemote* remote) {
  106. return CrossRemoteItemArray_size(remote->items);
  107. }
  108. CrossRemoteItem* xremote_cross_remote_get_item(CrossRemote* remote, size_t index) {
  109. furi_assert(index < CrossRemoteItemArray_size(remote->items));
  110. return *CrossRemoteItemArray_get(remote->items, index);
  111. }
  112. void xremote_cross_remote_remove_item(CrossRemote* remote, size_t index) {
  113. CrossRemoteItemArray_erase(remote->items, index);
  114. }
  115. void xremote_cross_remote_rename_item(CrossRemote* remote, size_t index, const char* name) {
  116. CrossRemoteItem* item = xremote_cross_remote_get_item(remote, index);
  117. xremote_cross_remote_item_set_name(item, name);
  118. }
  119. int16_t xremote_cross_remote_get_item_type(CrossRemote* remote, size_t index) {
  120. CrossRemoteItem* item = xremote_cross_remote_get_item(remote, index);
  121. return xremote_cross_remote_item_get_type(item);
  122. }
  123. static void xremote_cross_remote_set_name(CrossRemote* remote, const char* name) {
  124. furi_string_set(remote->name, name);
  125. }
  126. static void xremote_cross_remote_set_path(CrossRemote* remote, const char* path) {
  127. furi_string_set(remote->path, path);
  128. }
  129. bool xremote_cross_remote_load(CrossRemote* remote, FuriString* path) {
  130. Storage* storage = furi_record_open(RECORD_STORAGE);
  131. FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
  132. FuriString* buf;
  133. buf = furi_string_alloc();
  134. FURI_LOG_I(TAG, "loading file: \'%s\'", furi_string_get_cstr(path));
  135. bool success = false;
  136. do {
  137. // File not found
  138. if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(path))) break;
  139. uint32_t version;
  140. // Read Version & Type
  141. if(!flipper_format_read_header(ff, buf, &version)) break;
  142. if(!furi_string_equal(buf, XREMOTE_FILE_TYPE) || (version != XREMOTE_FILE_VERSION)) break;
  143. // Init Remote
  144. path_extract_filename(path, buf, true);
  145. xremote_cross_remote_clear_items(remote);
  146. xremote_cross_remote_set_name(remote, furi_string_get_cstr(buf));
  147. xremote_cross_remote_set_path(remote, furi_string_get_cstr(path));
  148. // Load Items
  149. for(bool can_read = true; can_read;) {
  150. CrossRemoteItem* item = xremote_cross_remote_item_alloc();
  151. can_read = xremote_cross_remote_item_read(item, ff);
  152. if(can_read) {
  153. CrossRemoteItemArray_push_back(remote->items, item);
  154. } else {
  155. xremote_cross_remote_item_free(item);
  156. }
  157. }
  158. success = true;
  159. } while(false);
  160. furi_string_free(buf);
  161. flipper_format_buffered_file_close(ff);
  162. flipper_format_free(ff);
  163. furi_record_close(RECORD_STORAGE);
  164. return success;
  165. }
  166. static bool xremote_cross_remote_store(CrossRemote* remote) {
  167. Storage* storage = furi_record_open(RECORD_STORAGE);
  168. FlipperFormat* ff = flipper_format_file_alloc(storage);
  169. const char* path = furi_string_get_cstr(remote->path);
  170. FURI_LOG_I(TAG, "Storing file: \'%s\'", path);
  171. bool success = flipper_format_file_open_always(ff, path) &&
  172. flipper_format_write_header_cstr(ff, XREMOTE_FILE_TYPE, XREMOTE_FILE_VERSION);
  173. // save Items
  174. if(success) {
  175. CrossRemoteItemArray_it_t it;
  176. for(CrossRemoteItemArray_it(it, remote->items); !CrossRemoteItemArray_end_p(it);
  177. CrossRemoteItemArray_next(it)) {
  178. CrossRemoteItem* item = *CrossRemoteItemArray_cref(it);
  179. success = false;
  180. if(item->type == XRemoteRemoteItemTypeInfrared) {
  181. success = xremote_cross_remote_item_ir_signal_save(
  182. xremote_cross_remote_item_get_ir_signal(item),
  183. ff,
  184. xremote_cross_remote_item_get_name(item),
  185. xremote_cross_remote_item_get_time(item));
  186. } else if(item->type == XRemoteRemoteItemTypePause) {
  187. success = xremote_cross_remote_item_pause_save(
  188. ff, item->time, xremote_cross_remote_item_get_name(item));
  189. } else if(item->type == XRemoteRemoteItemTypeSubGhz) {
  190. success = xremote_cross_remote_item_sg_signal_save(
  191. xremote_cross_remote_item_get_sg_signal(item),
  192. ff,
  193. xremote_cross_remote_item_get_name(item),
  194. xremote_cross_remote_item_get_filename(item));
  195. }
  196. if(!success) {
  197. break;
  198. }
  199. }
  200. }
  201. flipper_format_free(ff);
  202. furi_record_close(RECORD_STORAGE);
  203. return success;
  204. }
  205. bool xremote_cross_remote_save_new(CrossRemote* remote, const char* name) {
  206. FuriString *new_name, *new_path;
  207. new_name = furi_string_alloc_set(name);
  208. new_path = furi_string_alloc_set(XREMOTE_APP_FOLDER);
  209. xremote_cross_remote_find_vacant_remote_name(new_name, furi_string_get_cstr(new_path));
  210. furi_string_cat_printf(
  211. new_path, "/%s%s", furi_string_get_cstr(new_name), XREMOTE_APP_EXTENSION);
  212. xremote_cross_remote_set_name(remote, furi_string_get_cstr(new_name));
  213. xremote_cross_remote_set_path(remote, furi_string_get_cstr(new_path));
  214. furi_string_free(new_name);
  215. furi_string_free(new_path);
  216. return xremote_cross_remote_store(remote);
  217. }
  218. static void xremote_cross_remote_reset(CrossRemote* remote) {
  219. furi_string_reset(remote->name);
  220. furi_string_reset(remote->path);
  221. xremote_cross_remote_clear_items(remote);
  222. remote->transmitting = 0;
  223. }
  224. bool xremote_cross_remote_delete(CrossRemote* remote) {
  225. Storage* storage = furi_record_open(RECORD_STORAGE);
  226. FS_Error status = storage_common_remove(storage, furi_string_get_cstr(remote->path));
  227. furi_record_close(RECORD_STORAGE);
  228. xremote_cross_remote_reset(remote);
  229. return (status == FSE_OK || status == FSE_NOT_EXIST);
  230. }