metroflip_scene_navigo.c 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. #include "../metroflip_i.h"
  2. #include <datetime.h>
  3. #include <dolphin/dolphin.h>
  4. #include <locale/locale.h>
  5. #include "navigo.h"
  6. #include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
  7. #define TAG "Metroflip:Scene:Navigo"
  8. int eventSizes[] = {8, 24, 8, 8, 8, 8, 24, 16, 16, 8, 16, 16, 8, 16,
  9. 16, 8, 5, 240, 16, 8, 16, 16, 16, 16, 16, 5, 16, 5};
  10. int* get_bit_positions(const char* binary_string, int* count) {
  11. int length = strlen(binary_string);
  12. int* positions = malloc(length * sizeof(int));
  13. int pos_index = 0;
  14. for(int i = 0; i < length; i++) {
  15. if(binary_string[length - 1 - i] == '1') {
  16. positions[pos_index++] = i - 1;
  17. }
  18. }
  19. *count = pos_index;
  20. return positions;
  21. }
  22. int is_event_present(int* array, int size, int number) {
  23. for(int i = 0; i < size; i++) {
  24. if(array[i] == number) {
  25. return 1;
  26. }
  27. }
  28. return 0;
  29. }
  30. int check_events(int* array, int size, int number) {
  31. int total = 0;
  32. for(int i = 0; i < size; i++) {
  33. if(array[i] < number) {
  34. total += eventSizes[array[i]];
  35. }
  36. }
  37. return total + 53;
  38. }
  39. int select_new_app(
  40. int new_app,
  41. BitBuffer* tx_buffer,
  42. BitBuffer* rx_buffer,
  43. Iso14443_4bPoller* iso14443_4b_poller,
  44. Metroflip* app,
  45. MetroflipPollerEventType* stage) {
  46. select_app[6] = new_app;
  47. bit_buffer_reset(tx_buffer);
  48. bit_buffer_append_bytes(tx_buffer, select_app, sizeof(select_app));
  49. int error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  50. if(error != Iso14443_4bErrorNone) {
  51. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  52. *stage = MetroflipPollerEventTypeFail;
  53. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  54. return error;
  55. }
  56. return 0;
  57. }
  58. int read_new_file(
  59. int new_file,
  60. BitBuffer* tx_buffer,
  61. BitBuffer* rx_buffer,
  62. Iso14443_4bPoller* iso14443_4b_poller,
  63. Metroflip* app,
  64. MetroflipPollerEventType* stage) {
  65. read_file[2] = new_file;
  66. bit_buffer_reset(tx_buffer);
  67. bit_buffer_append_bytes(tx_buffer, read_file, sizeof(read_file));
  68. Iso14443_4bError error =
  69. iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  70. if(error != Iso14443_4bErrorNone) {
  71. FURI_LOG_I(TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  72. *stage = MetroflipPollerEventTypeFail;
  73. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  74. return error;
  75. }
  76. return 0;
  77. }
  78. int check_response(
  79. BitBuffer* rx_buffer,
  80. Metroflip* app,
  81. MetroflipPollerEventType* stage,
  82. size_t* response_length) {
  83. *response_length = bit_buffer_get_size_bytes(rx_buffer);
  84. if(bit_buffer_get_byte(rx_buffer, *response_length - 2) != apdu_success[0] ||
  85. bit_buffer_get_byte(rx_buffer, *response_length - 1) != apdu_success[1]) {
  86. FURI_LOG_I(
  87. TAG,
  88. "Select profile app/file failed: %02x%02x",
  89. bit_buffer_get_byte(rx_buffer, *response_length - 2),
  90. bit_buffer_get_byte(rx_buffer, *response_length - 1));
  91. *stage = MetroflipPollerEventTypeFail;
  92. view_dispatcher_send_custom_event(
  93. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  94. return 1;
  95. }
  96. return 0;
  97. }
  98. const char* get_country(int country_num) {
  99. switch(country_num) {
  100. case 250:
  101. return "France";
  102. default:
  103. return "Unknown";
  104. }
  105. }
  106. const char* get_network(int network_num) {
  107. switch(network_num) {
  108. case 901:
  109. return "Ile-de-France Mobilites";
  110. default:
  111. return "Unknown";
  112. }
  113. }
  114. const char* get_transport_type(int type) {
  115. switch(type) {
  116. case BUS_URBAIN:
  117. return "Bus Urbain";
  118. case BUS_INTERURBAIN:
  119. return "Bus Interurbain";
  120. case METRO:
  121. return "Metro";
  122. case TRAM:
  123. return "Tram";
  124. case TRAIN:
  125. return "Train";
  126. case PARKING:
  127. return "Parking";
  128. default:
  129. return "Unknown";
  130. }
  131. }
  132. const char* get_service_provider(int provider) {
  133. switch(provider) {
  134. case 2:
  135. return "SNCF";
  136. case 3:
  137. return "RATP";
  138. case 115:
  139. return "CSO (VEOLIA)";
  140. case 116:
  141. return "R'Bus (VEOLIA)";
  142. case 156:
  143. return "Phebus";
  144. case 175:
  145. return "RATP (Veolia Transport Nanterre)";
  146. default:
  147. return "Unknown";
  148. }
  149. }
  150. const char* get_transition_type(int transition) {
  151. switch(transition) {
  152. case 1:
  153. return "Validation - entree";
  154. case 2:
  155. return "Validation - sortie";
  156. case 4:
  157. return "Controle volant (a bord)";
  158. case 5:
  159. return "Validation de test";
  160. case 6:
  161. return "Validation en correspondance - entree";
  162. case 7:
  163. return "Validation en correspondance - sortie";
  164. case 9:
  165. return "Annulation de validation";
  166. case 10:
  167. return "Validation - entree";
  168. case 13:
  169. return "Distribution";
  170. case 15:
  171. return "Invalidation";
  172. default: {
  173. char* transition_str = malloc(13);
  174. snprintf(transition_str, 13, "Unknown (%d)", transition);
  175. return transition_str;
  176. }
  177. }
  178. }
  179. const char* get_metro_station(int station_group_id, int station_id) {
  180. // Use NAVIGO_H constants
  181. if(station_group_id < 32 && station_id < 16) {
  182. return METRO_STATION_LIST[station_group_id][station_id];
  183. }
  184. // cast station_group_id-station_id to a string
  185. char* station = malloc(12 * sizeof(char));
  186. if(!station) {
  187. return "Unknown";
  188. }
  189. snprintf(station, 10, "%d-%d", station_group_id, station_id);
  190. return station;
  191. }
  192. const char* get_train_line(int station_group_id) {
  193. if(station_group_id < 77) {
  194. return TRAIN_LINES_LIST[station_group_id];
  195. }
  196. return "Unknown";
  197. }
  198. const char* get_train_station(int station_group_id, int station_id) {
  199. if(station_group_id < 77 && station_id < 19) {
  200. return TRAIN_STATION_LIST[station_group_id][station_id];
  201. }
  202. // cast station_group_id-station_id to a string
  203. char* station = malloc(12 * sizeof(char));
  204. if(!station) {
  205. return "Unknown";
  206. }
  207. snprintf(station, 10, "%d-%d", station_group_id, station_id);
  208. return station;
  209. }
  210. void show_event_info(NavigoCardEvent* event, FuriString* parsed_data) {
  211. if(event->transport_type == BUS_URBAIN || event->transport_type == BUS_INTERURBAIN ||
  212. event->transport_type == METRO || event->transport_type == TRAM) {
  213. if(event->route_number_available) {
  214. if(event->transport_type == METRO && event->route_number == 103) {
  215. furi_string_cat_printf(
  216. parsed_data,
  217. "%s 3 bis\n%s\n",
  218. get_transport_type(event->transport_type),
  219. get_transition_type(event->transition));
  220. } else {
  221. furi_string_cat_printf(
  222. parsed_data,
  223. "%s %d\n%s\n",
  224. get_transport_type(event->transport_type),
  225. event->route_number,
  226. get_transition_type(event->transition));
  227. }
  228. } else {
  229. furi_string_cat_printf(
  230. parsed_data,
  231. "%s\n%s\n",
  232. get_transport_type(event->transport_type),
  233. get_transition_type(event->transition));
  234. }
  235. furi_string_cat_printf(
  236. parsed_data, "Transporteur : %s\n", get_service_provider(event->service_provider));
  237. if(event->transport_type == METRO) {
  238. furi_string_cat_printf(
  239. parsed_data,
  240. "Station : %s\nSecteur : %s\n",
  241. get_metro_station(event->station_group_id, event->station_id),
  242. get_metro_station(event->station_group_id, 0));
  243. } else {
  244. furi_string_cat_printf(
  245. parsed_data, "ID Station : %d-%d\n", event->station_group_id, event->station_id);
  246. }
  247. if(event->location_gate_available) {
  248. furi_string_cat_printf(parsed_data, "Passage : %d\n", event->location_gate);
  249. }
  250. if(event->device_available) {
  251. if(event->transport_type == BUS_URBAIN || event->transport_type == BUS_INTERURBAIN) {
  252. const char* side = event->side == 0 ? "droit" : "gauche";
  253. furi_string_cat_printf(parsed_data, "Porte : %d\nCote %s\n", event->door, side);
  254. } else {
  255. furi_string_cat_printf(parsed_data, "Equipement : %d\n", event->device);
  256. }
  257. }
  258. if(event->mission_available) {
  259. furi_string_cat_printf(parsed_data, "Mission : %d\n", event->mission);
  260. }
  261. if(event->vehicle_id_available) {
  262. furi_string_cat_printf(parsed_data, "Vehicule : %d\n", event->vehicle_id);
  263. }
  264. if(event->used_contract_available) {
  265. furi_string_cat_printf(parsed_data, "Contrat : %d\n", event->used_contract);
  266. }
  267. locale_format_datetime_cat(parsed_data, &event->date, true);
  268. furi_string_cat_printf(parsed_data, "\n");
  269. } else if(event->transport_type == TRAIN) {
  270. if(event->route_number_available) {
  271. furi_string_cat_printf(
  272. parsed_data,
  273. "RER %c\nStation : %s\n",
  274. (65 + event->route_number - 17),
  275. get_train_station(event->station_group_id, event->station_id));
  276. } else {
  277. furi_string_cat_printf(
  278. parsed_data,
  279. "%s %s\nStation : %s\n",
  280. get_transport_type(event->transport_type),
  281. get_train_line(event->station_group_id),
  282. get_train_station(event->station_group_id, event->station_id));
  283. }
  284. if(event->route_number_available) {
  285. furi_string_cat_printf(parsed_data, "Route : %d\n", event->route_number);
  286. }
  287. if(event->location_gate_available) {
  288. furi_string_cat_printf(parsed_data, "Passage : %d\n", event->location_gate);
  289. }
  290. if(event->device_available) {
  291. furi_string_cat_printf(parsed_data, "Equipement : %d\n", event->device);
  292. }
  293. if(event->mission_available) {
  294. furi_string_cat_printf(parsed_data, "Mission : %d\n", event->mission);
  295. }
  296. if(event->vehicle_id_available) {
  297. furi_string_cat_printf(parsed_data, "Vehicule : %d\n", event->vehicle_id);
  298. }
  299. if(event->used_contract_available) {
  300. furi_string_cat_printf(parsed_data, "Contrat : %d\n", event->used_contract);
  301. }
  302. locale_format_datetime_cat(parsed_data, &event->date, true);
  303. furi_string_cat_printf(parsed_data, "\n");
  304. } else {
  305. furi_string_cat_printf(
  306. parsed_data,
  307. "%s - %s\n",
  308. get_transport_type(event->transport_type),
  309. get_transition_type(event->transition));
  310. furi_string_cat_printf(
  311. parsed_data, "Transporteur : %s\n", get_service_provider(event->service_provider));
  312. furi_string_cat_printf(
  313. parsed_data, "ID Station : %d-%d\n", event->station_group_id, event->station_id);
  314. if(event->location_gate_available) {
  315. furi_string_cat_printf(parsed_data, "Passage : %d\n", event->location_gate);
  316. }
  317. if(event->device_available) {
  318. furi_string_cat_printf(parsed_data, "Equipement : %d\n", event->device);
  319. }
  320. if(event->mission_available) {
  321. furi_string_cat_printf(parsed_data, "Mission : %d\n", event->mission);
  322. }
  323. if(event->vehicle_id_available) {
  324. furi_string_cat_printf(parsed_data, "Vehicule : %d\n", event->vehicle_id);
  325. }
  326. if(event->used_contract_available) {
  327. furi_string_cat_printf(parsed_data, "Contrat : %d\n", event->used_contract);
  328. }
  329. locale_format_datetime_cat(parsed_data, &event->date, true);
  330. furi_string_cat_printf(parsed_data, "\n");
  331. }
  332. }
  333. void show_contract_info(NavigoCardContract* contract, FuriString* parsed_data) {
  334. furi_string_cat_printf(parsed_data, "Balance : %.2f EUR\n", (double)contract->balance);
  335. furi_string_cat_printf(parsed_data, "Debut de validite:\n");
  336. locale_format_datetime_cat(parsed_data, &contract->start_dt, false);
  337. furi_string_cat_printf(parsed_data, "\n");
  338. }
  339. void show_environment_info(NavigoCardEnv* environment, FuriString* parsed_data) {
  340. furi_string_cat_printf(
  341. parsed_data, "Version de l'application : %d\n", environment->app_version);
  342. if(environment->country_num == 250) {
  343. furi_string_cat_printf(parsed_data, "Pays : France\n");
  344. } else {
  345. furi_string_cat_printf(parsed_data, "Pays : %d\n", environment->country_num);
  346. }
  347. if(environment->network_num == 901) {
  348. furi_string_cat_printf(parsed_data, "Reseau : Ile-de-France Mobilites\n");
  349. } else {
  350. furi_string_cat_printf(parsed_data, "Reseau : %d\n", environment->network_num);
  351. }
  352. furi_string_cat_printf(parsed_data, "Fin de validite:\n");
  353. locale_format_datetime_cat(parsed_data, &environment->end_dt, false);
  354. furi_string_cat_printf(parsed_data, "\n");
  355. }
  356. void update_page_info(NavigoContext* ctx, FuriString* parsed_data) {
  357. if(ctx->page_id == 0) {
  358. furi_string_cat_printf(parsed_data, "\e#Navigo :\n");
  359. furi_string_cat_printf(parsed_data, "\e#Contrat 1:\n");
  360. show_contract_info(&ctx->card->contracts[0], parsed_data);
  361. } else if(ctx->page_id == 1) {
  362. furi_string_cat_printf(parsed_data, "\e#Environnement :\n");
  363. show_environment_info(&ctx->card->environment, parsed_data);
  364. } else if(ctx->page_id == 2) {
  365. furi_string_cat_printf(parsed_data, "\e#Event 1 :\n");
  366. show_event_info(&ctx->card->events[0], parsed_data);
  367. } else if(ctx->page_id == 3) {
  368. furi_string_cat_printf(parsed_data, "\e#Event 2 :\n");
  369. show_event_info(&ctx->card->events[1], parsed_data);
  370. } else if(ctx->page_id == 4) {
  371. furi_string_cat_printf(parsed_data, "\e#Event 3 :\n");
  372. show_event_info(&ctx->card->events[2], parsed_data);
  373. }
  374. }
  375. void update_widget_elements(Widget* widget, NavigoContext* ctx, void* context) {
  376. if(ctx->page_id < 4) {
  377. widget_add_button_element(
  378. widget, GuiButtonTypeRight, "Next", metroflip_next_button_widget_callback, context);
  379. } else {
  380. widget_add_button_element(
  381. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  382. }
  383. if(ctx->page_id > 0) {
  384. widget_add_button_element(
  385. widget, GuiButtonTypeLeft, "Back", metroflip_back_button_widget_callback, context);
  386. }
  387. }
  388. void metroflip_back_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  389. NavigoContext* ctx = context;
  390. Metroflip* app = ctx->app;
  391. UNUSED(result);
  392. Widget* widget = app->widget;
  393. if(type == InputTypePress) {
  394. widget_reset(widget);
  395. FuriString* parsed_data = furi_string_alloc();
  396. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id - 1);
  397. if(ctx->page_id > 0) {
  398. ctx->page_id -= 1;
  399. }
  400. // Ensure no nested mutexes
  401. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  402. update_page_info(ctx, parsed_data);
  403. furi_mutex_release(ctx->mutex);
  404. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  405. // Ensure no nested mutexes
  406. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  407. update_widget_elements(widget, ctx, context);
  408. furi_mutex_release(ctx->mutex);
  409. furi_string_free(parsed_data);
  410. }
  411. }
  412. void metroflip_next_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  413. NavigoContext* ctx = context;
  414. Metroflip* app = ctx->app;
  415. UNUSED(result);
  416. Widget* widget = app->widget;
  417. if(type == InputTypePress) {
  418. widget_reset(widget);
  419. FuriString* parsed_data = furi_string_alloc();
  420. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id + 1);
  421. if(ctx->page_id < 4) {
  422. ctx->page_id += 1;
  423. } else {
  424. ctx->page_id = 0;
  425. scene_manager_search_and_switch_to_previous_scene(
  426. app->scene_manager, MetroflipSceneStart);
  427. }
  428. // Ensure no nested mutexes
  429. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  430. update_page_info(ctx, parsed_data);
  431. furi_mutex_release(ctx->mutex);
  432. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  433. // Ensure no nested mutexes
  434. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  435. update_widget_elements(widget, ctx, context);
  436. furi_mutex_release(ctx->mutex);
  437. furi_string_free(parsed_data);
  438. }
  439. }
  440. static NfcCommand metroflip_scene_navigo_poller_callback(NfcGenericEvent event, void* context) {
  441. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  442. NfcCommand next_command = NfcCommandContinue;
  443. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  444. Metroflip* app = context;
  445. FuriString* parsed_data = furi_string_alloc();
  446. Widget* widget = app->widget;
  447. furi_string_reset(app->text_box_store);
  448. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  449. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  450. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  451. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  452. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  453. if(stage == MetroflipPollerEventTypeStart) {
  454. nfc_device_set_data(
  455. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  456. Iso14443_4bError error;
  457. size_t response_length = 0;
  458. do {
  459. NavigoCardData* card = malloc(sizeof(NavigoCardData));
  460. // Initialize the card
  461. card->contracts = malloc(sizeof(NavigoCardContract));
  462. card->events = malloc(3 * sizeof(NavigoCardEvent));
  463. // Select app for contract 1
  464. error =
  465. select_new_app(0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  466. if(error != 0) {
  467. break;
  468. }
  469. // Check the response after selecting app
  470. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  471. break;
  472. }
  473. // read file 1
  474. error = read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  475. if(error != 0) {
  476. break;
  477. }
  478. // Check the response after reading the file
  479. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  480. break;
  481. }
  482. char bit_representation[response_length * 8 + 1];
  483. bit_representation[0] = '\0';
  484. for(size_t i = 0; i < response_length; i++) {
  485. char bits[9];
  486. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  487. byte_to_binary(byte, bits);
  488. strlcat(bit_representation, bits, sizeof(bit_representation));
  489. }
  490. bit_representation[response_length * 8] = '\0';
  491. int start = 55, end = 70;
  492. float decimal_value = bit_slice_to_dec(bit_representation, start, end);
  493. card->contracts[0].balance = decimal_value / 100;
  494. start = 80, end = 93;
  495. decimal_value = bit_slice_to_dec(bit_representation, start, end);
  496. uint64_t start_date_timestamp = (decimal_value * 24 * 3600) + (float)epoch + 3600;
  497. datetime_timestamp_to_datetime(start_date_timestamp, &card->contracts[0].start_dt);
  498. // Select app for environment
  499. error = select_new_app(0x1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  500. if(error != 0) {
  501. break;
  502. }
  503. // Check the response after selecting app
  504. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  505. break;
  506. }
  507. // read file 1
  508. error = read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  509. if(error != 0) {
  510. break;
  511. }
  512. // Check the response after reading the file
  513. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  514. break;
  515. }
  516. char environment_bit_representation[response_length * 8 + 1];
  517. environment_bit_representation[0] = '\0';
  518. for(size_t i = 0; i < response_length; i++) {
  519. char bits[9];
  520. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  521. byte_to_binary(byte, bits);
  522. strlcat(
  523. environment_bit_representation,
  524. bits,
  525. sizeof(environment_bit_representation));
  526. }
  527. start = 0;
  528. end = 5;
  529. card->environment.app_version =
  530. bit_slice_to_dec(environment_bit_representation, start, end);
  531. start = 13;
  532. end = 36;
  533. decimal_value = bit_slice_to_dec(environment_bit_representation, start, end);
  534. FURI_LOG_I(TAG, "Network ID: %d", (int)decimal_value);
  535. start = 13;
  536. end = 16;
  537. card->environment.country_num =
  538. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  539. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  540. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  541. start = 25;
  542. end = 28;
  543. card->environment.network_num =
  544. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  545. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  546. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  547. start = 45;
  548. end = 58;
  549. decimal_value = bit_slice_to_dec(environment_bit_representation, start, end);
  550. uint64_t end_validity_timestamp =
  551. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  552. datetime_timestamp_to_datetime(end_validity_timestamp, &card->environment.end_dt);
  553. // Select app for events
  554. error =
  555. select_new_app(0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  556. if(error != 0) {
  557. break;
  558. }
  559. // Check the response after selecting app
  560. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  561. break;
  562. }
  563. // furi_string_cat_printf(parsed_data, "\e#Events :\n");
  564. // Now send the read command
  565. for(size_t i = 1; i < 4; i++) {
  566. error =
  567. read_new_file(i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  568. if(error != 0) {
  569. break;
  570. }
  571. // Check the response after reading the file
  572. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  573. break;
  574. }
  575. char event_bit_representation[response_length * 8 + 1];
  576. event_bit_representation[0] = '\0';
  577. for(size_t i = 0; i < response_length; i++) {
  578. char bits[9];
  579. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  580. byte_to_binary(byte, bits);
  581. strlcat(event_bit_representation, bits, sizeof(event_bit_representation));
  582. }
  583. FURI_LOG_I(
  584. TAG, "Event %d bit_representation: %s", i, event_bit_representation);
  585. // furi_string_cat_printf(parsed_data, "Event 0%d :\n", i);
  586. int count = 0;
  587. int start = 25, end = 53;
  588. char bit_slice[end - start + 2];
  589. strncpy(bit_slice, event_bit_representation + start, end - start + 1);
  590. bit_slice[end - start + 1] = '\0';
  591. int* positions = get_bit_positions(bit_slice, &count);
  592. /*FURI_LOG_I(TAG, "Positions: ");
  593. for(int i = 0; i < count; i++) {
  594. FURI_LOG_I(TAG, "%d ", positions[i]);
  595. }*/
  596. // 2. EventCode
  597. // 8 bits
  598. int event_number = 2;
  599. if(is_event_present(positions, count, event_number)) {
  600. int positionOffset = check_events(positions, count, event_number);
  601. int start = positionOffset, end = positionOffset + 7;
  602. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  603. card->events[i - 1].transport_type = decimal_value >> 4;
  604. card->events[i - 1].transition = decimal_value & 15;
  605. FURI_LOG_I(
  606. TAG,
  607. "%s - %s",
  608. TRANSPORT_LIST[card->events[i - 1].transport_type],
  609. TRANSITION_LIST[card->events[i - 1].transition]);
  610. }
  611. // 4. EventServiceProvider
  612. // 8 bits
  613. event_number = 4;
  614. if(is_event_present(positions, count, event_number)) {
  615. int positionOffset = check_events(positions, count, event_number);
  616. start = positionOffset, end = positionOffset + 7;
  617. card->events[i - 1].service_provider =
  618. bit_slice_to_dec(event_bit_representation, start, end);
  619. FURI_LOG_I(
  620. TAG,
  621. "Transporteur : %s",
  622. SERVICE_PROVIDERS[card->events[i - 1].service_provider]);
  623. }
  624. // 8. EventLocationId
  625. // 16 bits
  626. event_number = 8;
  627. if(is_event_present(positions, count, event_number)) {
  628. int positionOffset = check_events(positions, count, event_number);
  629. start = positionOffset, end = positionOffset + 15;
  630. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  631. card->events[i - 1].station_group_id = decimal_value >> 9;
  632. card->events[i - 1].station_id = (decimal_value >> 4) & 31;
  633. if(card->events[i - 1].transport_type == METRO) {
  634. FURI_LOG_I(
  635. TAG,
  636. "Secteur %s - Station %s",
  637. METRO_STATION_LIST[card->events[i - 1].station_group_id][0],
  638. METRO_STATION_LIST[card->events[i - 1].station_group_id]
  639. [card->events[i - 1].station_id]);
  640. } else if(card->events[i - 1].transport_type == TRAIN) {
  641. FURI_LOG_I(
  642. TAG,
  643. "Ligne %s - Station %s",
  644. TRAIN_LINES_LIST[card->events[i - 1].station_group_id],
  645. TRAIN_STATION_LIST[card->events[i - 1].station_group_id]
  646. [card->events[i - 1].station_id]);
  647. } else {
  648. FURI_LOG_I(
  649. TAG,
  650. "Groupe ID %d - Station ID %d",
  651. card->events[i - 1].station_group_id,
  652. card->events[i - 1].station_id);
  653. }
  654. }
  655. // 9. EventLocationGate
  656. // 8 bits
  657. event_number = 9;
  658. if(is_event_present(positions, count, event_number)) {
  659. int positionOffset = check_events(positions, count, event_number);
  660. start = positionOffset, end = positionOffset + 7;
  661. card->events[i - 1].location_gate =
  662. bit_slice_to_dec(event_bit_representation, start, end);
  663. card->events[i - 1].location_gate_available = true;
  664. FURI_LOG_I(TAG, "Passage : %d", card->events[i - 1].location_gate);
  665. }
  666. // 10. EventDevice
  667. // 16 bits
  668. event_number = 10;
  669. if(is_event_present(positions, count, event_number)) {
  670. int positionOffset = check_events(positions, count, event_number);
  671. start = positionOffset, end = positionOffset + 15;
  672. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  673. card->events[i - 1].device = decimal_value >> 8;
  674. card->events[i - 1].door = card->events[i - 1].device / 2 + 1;
  675. card->events[i - 1].side = card->events[i - 1].device % 2;
  676. card->events[i - 1].device_available = true;
  677. const char* side = card->events[i - 1].side == 0 ? "droit" : "gauche";
  678. FURI_LOG_I(TAG, "Equipement : %d", card->events[i - 1].device);
  679. FURI_LOG_I(TAG, "Porte : %d - Côté %s", card->events[i - 1].door, side);
  680. }
  681. // 11. EventRouteNumber
  682. // 16 bits
  683. event_number = 11;
  684. if(is_event_present(positions, count, event_number)) {
  685. int positionOffset = check_events(positions, count, event_number);
  686. start = positionOffset, end = positionOffset + 15;
  687. card->events[i - 1].route_number =
  688. bit_slice_to_dec(event_bit_representation, start, end);
  689. card->events[i - 1].route_number_available = true;
  690. FURI_LOG_I(TAG, "Route : %d", card->events[i - 1].route_number);
  691. }
  692. // 13. EventJourneyRun
  693. // 16 bits
  694. event_number = 13;
  695. if(is_event_present(positions, count, event_number)) {
  696. int positionOffset = check_events(positions, count, event_number);
  697. start = positionOffset, end = positionOffset + 15;
  698. card->events[i - 1].mission =
  699. bit_slice_to_dec(event_bit_representation, start, end);
  700. card->events[i - 1].mission_available = true;
  701. FURI_LOG_I(TAG, "Mission : %d", card->events[i - 1].mission);
  702. }
  703. // 14. EventVehicleId
  704. // 16 bits
  705. event_number = 14;
  706. if(is_event_present(positions, count, event_number)) {
  707. int positionOffset = check_events(positions, count, event_number);
  708. start = positionOffset, end = positionOffset + 15;
  709. card->events[i - 1].vehicle_id =
  710. bit_slice_to_dec(event_bit_representation, start, end);
  711. card->events[i - 1].vehicle_id_available = true;
  712. FURI_LOG_I(TAG, "Vehicule : %d", card->events[i - 1].vehicle_id);
  713. }
  714. // 25. EventContractPointer
  715. // 5 bits
  716. event_number = 25;
  717. if(is_event_present(positions, count, event_number)) {
  718. int positionOffset = check_events(positions, count, event_number);
  719. start = positionOffset, end = positionOffset + 4;
  720. card->events[i - 1].used_contract =
  721. bit_slice_to_dec(event_bit_representation, start, end);
  722. card->events[i - 1].used_contract_available = true;
  723. FURI_LOG_I(TAG, "Contrat : %d", card->events[i - 1].used_contract);
  724. }
  725. free(positions);
  726. // EventDate
  727. // 14 bits
  728. start = 0, end = 13;
  729. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  730. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  731. datetime_timestamp_to_datetime(date_timestamp, &card->events[i - 1].date);
  732. // EventTime
  733. // 11 bits
  734. start = 14, end = 24;
  735. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  736. card->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  737. card->events[i - 1].date.minute = ((decimal_value * 60) % 3600) / 60;
  738. card->events[i - 1].date.second = ((decimal_value * 60) % 3600) % 60;
  739. FURI_LOG_I(
  740. TAG,
  741. "Date : %02d/%02d/%04d %02dh%02d",
  742. card->events[i - 1].date.day,
  743. card->events[i - 1].date.month,
  744. card->events[i - 1].date.year,
  745. card->events[i - 1].date.hour,
  746. card->events[i - 1].date.minute);
  747. }
  748. UNUSED(TRANSITION_LIST);
  749. UNUSED(TRANSPORT_LIST);
  750. UNUSED(SERVICE_PROVIDERS);
  751. widget_add_text_scroll_element(
  752. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  753. NavigoContext* context = malloc(sizeof(NavigoContext));
  754. context->app = app;
  755. context->card = card;
  756. context->page_id = 0;
  757. context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  758. // Ensure no nested mutexes
  759. furi_mutex_acquire(context->mutex, FuriWaitForever);
  760. update_page_info(context, parsed_data);
  761. furi_mutex_release(context->mutex);
  762. widget_add_text_scroll_element(
  763. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  764. // Ensure no nested mutexes
  765. furi_mutex_acquire(context->mutex, FuriWaitForever);
  766. update_widget_elements(widget, context, context);
  767. furi_mutex_release(context->mutex);
  768. furi_string_free(parsed_data);
  769. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  770. metroflip_app_blink_stop(app);
  771. stage = MetroflipPollerEventTypeSuccess;
  772. next_command = NfcCommandStop;
  773. } while(false);
  774. if(stage != MetroflipPollerEventTypeSuccess) {
  775. next_command = NfcCommandStop;
  776. }
  777. }
  778. }
  779. bit_buffer_free(tx_buffer);
  780. bit_buffer_free(rx_buffer);
  781. return next_command;
  782. }
  783. void metroflip_scene_navigo_on_enter(void* context) {
  784. Metroflip* app = context;
  785. dolphin_deed(DolphinDeedNfcRead);
  786. // Setup view
  787. Popup* popup = app->popup;
  788. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  789. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  790. // Start worker
  791. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  792. nfc_scanner_alloc(app->nfc);
  793. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  794. nfc_poller_start(app->poller, metroflip_scene_navigo_poller_callback, app);
  795. metroflip_app_blink_start(app);
  796. }
  797. bool metroflip_scene_navigo_on_event(void* context, SceneManagerEvent event) {
  798. Metroflip* app = context;
  799. bool consumed = false;
  800. if(event.type == SceneManagerEventTypeCustom) {
  801. if(event.event == MetroflipPollerEventTypeCardDetect) {
  802. Popup* popup = app->popup;
  803. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  804. consumed = true;
  805. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  806. Popup* popup = app->popup;
  807. popup_set_header(popup, "Read Error,\n wrong card", 68, 30, AlignLeft, AlignTop);
  808. consumed = true;
  809. } else if(event.event == MetroflipCustomEventPollerFail) {
  810. Popup* popup = app->popup;
  811. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  812. consumed = true;
  813. }
  814. } else if(event.type == SceneManagerEventTypeBack) {
  815. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  816. consumed = true;
  817. }
  818. return consumed;
  819. }
  820. void metroflip_scene_navigo_on_exit(void* context) {
  821. Metroflip* app = context;
  822. if(app->poller) {
  823. nfc_poller_stop(app->poller);
  824. nfc_poller_free(app->poller);
  825. }
  826. metroflip_app_blink_stop(app);
  827. widget_reset(app->widget);
  828. // Clear view
  829. popup_reset(app->popup);
  830. }