metroflip_scene_calypso.c 85 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714
  1. #include "metroflip_scene_calypso.h"
  2. #include "../metroflip_i.h"
  3. #include <datetime.h>
  4. #include <dolphin/dolphin.h>
  5. #include <notification/notification_messages.h>
  6. #include <locale/locale.h>
  7. #include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
  8. #define TAG "Metroflip:Scene:Calypso"
  9. int select_new_app(
  10. int new_app_directory,
  11. int new_app,
  12. BitBuffer* tx_buffer,
  13. BitBuffer* rx_buffer,
  14. Iso14443_4bPoller* iso14443_4b_poller,
  15. Metroflip* app,
  16. MetroflipPollerEventType* stage) {
  17. select_app[5] = new_app_directory;
  18. select_app[6] = new_app;
  19. bit_buffer_reset(tx_buffer);
  20. bit_buffer_append_bytes(tx_buffer, select_app, sizeof(select_app));
  21. FURI_LOG_D(
  22. TAG,
  23. "SEND %02x %02x %02x %02x %02x %02x %02x %02x",
  24. select_app[0],
  25. select_app[1],
  26. select_app[2],
  27. select_app[3],
  28. select_app[4],
  29. select_app[5],
  30. select_app[6],
  31. select_app[7]);
  32. int error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  33. if(error != Iso14443_4bErrorNone) {
  34. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  35. *stage = MetroflipPollerEventTypeFail;
  36. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  37. return error;
  38. }
  39. return 0;
  40. }
  41. int read_new_file(
  42. int new_file,
  43. BitBuffer* tx_buffer,
  44. BitBuffer* rx_buffer,
  45. Iso14443_4bPoller* iso14443_4b_poller,
  46. Metroflip* app,
  47. MetroflipPollerEventType* stage) {
  48. read_file[2] = new_file;
  49. bit_buffer_reset(tx_buffer);
  50. bit_buffer_append_bytes(tx_buffer, read_file, sizeof(read_file));
  51. FURI_LOG_D(
  52. TAG,
  53. "SEND %02x %02x %02x %02x %02x",
  54. read_file[0],
  55. read_file[1],
  56. read_file[2],
  57. read_file[3],
  58. read_file[4]);
  59. Iso14443_4bError error =
  60. iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  61. if(error != Iso14443_4bErrorNone) {
  62. FURI_LOG_I(TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  63. *stage = MetroflipPollerEventTypeFail;
  64. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  65. return error;
  66. }
  67. return 0;
  68. }
  69. int check_response(
  70. BitBuffer* rx_buffer,
  71. Metroflip* app,
  72. MetroflipPollerEventType* stage,
  73. size_t* response_length) {
  74. *response_length = bit_buffer_get_size_bytes(rx_buffer);
  75. if(bit_buffer_get_byte(rx_buffer, *response_length - 2) != apdu_success[0] ||
  76. bit_buffer_get_byte(rx_buffer, *response_length - 1) != apdu_success[1]) {
  77. int error_code_1 = bit_buffer_get_byte(rx_buffer, *response_length - 2);
  78. int error_code_2 = bit_buffer_get_byte(rx_buffer, *response_length - 1);
  79. FURI_LOG_E(TAG, "Select profile app/file failed: %02x%02x", error_code_1, error_code_2);
  80. if(error_code_1 == 0x6a && error_code_2 == 0x82) {
  81. FURI_LOG_E(TAG, "Wrong parameter(s) P1-P2 - File not found");
  82. } else if(error_code_1 == 0x69 && error_code_2 == 0x82) {
  83. FURI_LOG_E(TAG, "Command not allowed - Security status not satisfied");
  84. }
  85. *stage = MetroflipPollerEventTypeFail;
  86. view_dispatcher_send_custom_event(
  87. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  88. return 1;
  89. }
  90. return 0;
  91. }
  92. void update_page_info(void* context, FuriString* parsed_data) {
  93. Metroflip* app = context;
  94. CalypsoContext* ctx = app->calypso_context;
  95. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO && ctx->card->card_type != CALYPSO_CARD_OPUS) {
  96. furi_string_cat_printf(
  97. parsed_data,
  98. "\e#%s %u:\n",
  99. get_network_string(ctx->card->card_type),
  100. ctx->card->card_number);
  101. return;
  102. }
  103. if(ctx->page_id == 0) {
  104. switch(ctx->card->card_type) {
  105. case CALYPSO_CARD_NAVIGO: {
  106. furi_string_cat_printf(parsed_data, "\e#Navigo %u:\n", ctx->card->card_number);
  107. furi_string_cat_printf(parsed_data, "\e#Environment:\n");
  108. show_navigo_environment_info(
  109. &ctx->card->navigo->environment, &ctx->card->navigo->holder, parsed_data);
  110. break;
  111. }
  112. case CALYPSO_CARD_OPUS: {
  113. furi_string_cat_printf(parsed_data, "\e#Opus %u:\n", ctx->card->card_number);
  114. furi_string_cat_printf(parsed_data, "\e#Environment:\n");
  115. show_opus_environment_info(&ctx->card->opus->environment, parsed_data);
  116. break;
  117. }
  118. default: {
  119. furi_string_cat_printf(parsed_data, "\e#Unknown %u:\n", ctx->card->card_number);
  120. break;
  121. }
  122. }
  123. } else if(ctx->page_id == 1 || ctx->page_id == 2 || ctx->page_id == 3 || ctx->page_id == 4) {
  124. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id);
  125. switch(ctx->card->card_type) {
  126. case CALYPSO_CARD_NAVIGO: {
  127. show_navigo_contract_info(
  128. &ctx->card->navigo->contracts[ctx->page_id - 1], parsed_data);
  129. break;
  130. }
  131. case CALYPSO_CARD_OPUS: {
  132. show_opus_contract_info(&ctx->card->opus->contracts[ctx->page_id - 1], parsed_data);
  133. break;
  134. }
  135. default: {
  136. break;
  137. }
  138. }
  139. } else if(ctx->page_id == 5 || ctx->page_id == 6 || ctx->page_id == 7) {
  140. furi_string_cat_printf(parsed_data, "\e#Event %d:\n", ctx->page_id - 4);
  141. switch(ctx->card->card_type) {
  142. case CALYPSO_CARD_NAVIGO: {
  143. show_navigo_event_info(
  144. &ctx->card->navigo->events[ctx->page_id - 5],
  145. ctx->card->navigo->contracts,
  146. parsed_data);
  147. break;
  148. }
  149. case CALYPSO_CARD_OPUS: {
  150. show_opus_event_info(
  151. &ctx->card->opus->events[ctx->page_id - 5],
  152. ctx->card->opus->contracts,
  153. parsed_data);
  154. break;
  155. }
  156. default: {
  157. break;
  158. }
  159. }
  160. } else if(ctx->page_id == 8 || ctx->page_id == 9 || ctx->page_id == 10) {
  161. furi_string_cat_printf(parsed_data, "\e#Special Event %d:\n", ctx->page_id - 7);
  162. switch(ctx->card->card_type) {
  163. case CALYPSO_CARD_NAVIGO: {
  164. show_navigo_special_event_info(
  165. &ctx->card->navigo->special_events[ctx->page_id - 8], parsed_data);
  166. break;
  167. }
  168. case CALYPSO_CARD_OPUS: {
  169. break;
  170. }
  171. default: {
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. void update_widget_elements(void* context) {
  178. Metroflip* app = context;
  179. CalypsoContext* ctx = app->calypso_context;
  180. Widget* widget = app->widget;
  181. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO && ctx->card->card_type != CALYPSO_CARD_OPUS) {
  182. widget_add_button_element(
  183. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  184. return;
  185. }
  186. if(ctx->page_id < 10) {
  187. widget_add_button_element(
  188. widget, GuiButtonTypeRight, "Next", metroflip_next_button_widget_callback, context);
  189. } else {
  190. widget_add_button_element(
  191. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  192. }
  193. if(ctx->page_id > 0) {
  194. widget_add_button_element(
  195. widget, GuiButtonTypeLeft, "Back", metroflip_back_button_widget_callback, context);
  196. }
  197. }
  198. void metroflip_back_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  199. Metroflip* app = context;
  200. CalypsoContext* ctx = app->calypso_context;
  201. UNUSED(result);
  202. Widget* widget = app->widget;
  203. if(type == InputTypePress) {
  204. widget_reset(widget);
  205. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id - 1);
  206. if(ctx->page_id > 0) {
  207. if(ctx->page_id == 10 && ctx->card->special_events_count < 2) {
  208. ctx->page_id -= 1;
  209. }
  210. if(ctx->page_id == 9 && ctx->card->special_events_count < 1) {
  211. ctx->page_id -= 1;
  212. }
  213. if(ctx->page_id == 8 && ctx->card->events_count < 3) {
  214. ctx->page_id -= 1;
  215. }
  216. if(ctx->page_id == 7 && ctx->card->events_count < 2) {
  217. ctx->page_id -= 1;
  218. }
  219. if(ctx->page_id == 6 && ctx->card->events_count < 1) {
  220. ctx->page_id -= 1;
  221. }
  222. if(ctx->page_id == 5 && ctx->card->contracts_count < 4) {
  223. ctx->page_id -= 1;
  224. }
  225. if(ctx->page_id == 4 && ctx->card->contracts_count < 3) {
  226. ctx->page_id -= 1;
  227. }
  228. if(ctx->page_id == 3 && ctx->card->contracts_count < 2) {
  229. ctx->page_id -= 1;
  230. }
  231. ctx->page_id -= 1;
  232. }
  233. FuriString* parsed_data = furi_string_alloc();
  234. // Ensure no nested mutexes
  235. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  236. update_page_info(app, parsed_data);
  237. furi_mutex_release(ctx->mutex);
  238. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  239. // widget_add_icon_element(widget, 0, 0, &I_RFIDDolphinReceive_97x61);
  240. // Ensure no nested mutexes
  241. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  242. update_widget_elements(app);
  243. furi_mutex_release(ctx->mutex);
  244. furi_string_free(parsed_data);
  245. }
  246. }
  247. void metroflip_next_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  248. Metroflip* app = context;
  249. CalypsoContext* ctx = app->calypso_context;
  250. UNUSED(result);
  251. Widget* widget = app->widget;
  252. if(type == InputTypePress) {
  253. widget_reset(widget);
  254. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id + 1);
  255. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO &&
  256. ctx->card->card_type != CALYPSO_CARD_OPUS) {
  257. ctx->page_id = 0;
  258. scene_manager_search_and_switch_to_previous_scene(
  259. app->scene_manager, MetroflipSceneStart);
  260. return;
  261. }
  262. if(ctx->page_id < 10) {
  263. if(ctx->page_id == 1 && ctx->card->contracts_count < 2) {
  264. ctx->page_id += 1;
  265. }
  266. if(ctx->page_id == 2 && ctx->card->contracts_count < 3) {
  267. ctx->page_id += 1;
  268. }
  269. if(ctx->page_id == 3 && ctx->card->contracts_count < 4) {
  270. ctx->page_id += 1;
  271. }
  272. if(ctx->page_id == 4 && ctx->card->events_count < 1) {
  273. ctx->page_id += 1;
  274. }
  275. if(ctx->page_id == 5 && ctx->card->events_count < 2) {
  276. ctx->page_id += 1;
  277. }
  278. if(ctx->page_id == 6 && ctx->card->events_count < 3) {
  279. ctx->page_id += 1;
  280. }
  281. if(ctx->page_id == 7 && ctx->card->special_events_count < 1) {
  282. ctx->page_id += 1;
  283. }
  284. if(ctx->page_id == 8 && ctx->card->special_events_count < 2) {
  285. ctx->page_id += 1;
  286. }
  287. if(ctx->page_id == 9 && ctx->card->special_events_count < 3) {
  288. ctx->page_id = 0;
  289. scene_manager_search_and_switch_to_previous_scene(
  290. app->scene_manager, MetroflipSceneStart);
  291. return;
  292. }
  293. ctx->page_id += 1;
  294. } else {
  295. ctx->page_id = 0;
  296. scene_manager_search_and_switch_to_previous_scene(
  297. app->scene_manager, MetroflipSceneStart);
  298. return;
  299. }
  300. FuriString* parsed_data = furi_string_alloc();
  301. // Ensure no nested mutexes
  302. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  303. update_page_info(app, parsed_data);
  304. furi_mutex_release(ctx->mutex);
  305. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  306. // Ensure no nested mutexes
  307. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  308. update_widget_elements(app);
  309. furi_mutex_release(ctx->mutex);
  310. furi_string_free(parsed_data);
  311. }
  312. }
  313. void delay(int milliseconds) {
  314. furi_thread_flags_wait(0, FuriFlagWaitAny, milliseconds);
  315. }
  316. static NfcCommand metroflip_scene_navigo_poller_callback(NfcGenericEvent event, void* context) {
  317. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  318. NfcCommand next_command = NfcCommandContinue;
  319. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  320. Metroflip* app = context;
  321. FuriString* parsed_data = furi_string_alloc();
  322. Widget* widget = app->widget;
  323. furi_string_reset(app->text_box_store);
  324. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  325. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  326. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  327. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  328. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  329. if(stage == MetroflipPollerEventTypeStart) {
  330. // Start Flipper vibration
  331. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  332. notification_message(notification, &sequence_set_vibro_on);
  333. delay(50);
  334. notification_message(notification, &sequence_reset_vibro);
  335. nfc_device_set_data(
  336. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  337. Iso14443_4bError error;
  338. size_t response_length = 0;
  339. do {
  340. // Initialize the card data
  341. CalypsoCardData* card = malloc(sizeof(CalypsoCardData));
  342. // Select app ICC
  343. error = select_new_app(
  344. 0x00, 0x02, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  345. if(error != 0) {
  346. break;
  347. }
  348. // Check the response after selecting app
  349. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  350. break;
  351. }
  352. // Now send the read command for ICC
  353. error = read_new_file(0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  354. if(error != 0) {
  355. break;
  356. }
  357. // Check the response after reading the file
  358. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  359. break;
  360. }
  361. char icc_bit_representation[response_length * 8 + 1];
  362. icc_bit_representation[0] = '\0';
  363. for(size_t i = 0; i < response_length; i++) {
  364. char bits[9];
  365. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  366. byte_to_binary(byte, bits);
  367. strlcat(icc_bit_representation, bits, sizeof(icc_bit_representation));
  368. }
  369. icc_bit_representation[response_length * 8] = '\0';
  370. int start = 128, end = 159;
  371. card->card_number = bit_slice_to_dec(icc_bit_representation, start, end);
  372. // Select app for ticketing
  373. error = select_new_app(
  374. 0x20, 0x00, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  375. if(error != 0) {
  376. FURI_LOG_E(TAG, "Failed to select app for ticketing");
  377. break;
  378. }
  379. // Check the response after selecting app
  380. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  381. FURI_LOG_E(TAG, "Failed to check response after selecting app for ticketing");
  382. break;
  383. }
  384. // Select app for environment
  385. error = select_new_app(
  386. 0x20, 0x1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  387. if(error != 0) {
  388. break;
  389. }
  390. // Check the response after selecting app
  391. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  392. break;
  393. }
  394. // read file 1
  395. error = read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  396. if(error != 0) {
  397. break;
  398. }
  399. // Check the response after reading the file
  400. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  401. break;
  402. }
  403. char environment_bit_representation[response_length * 8 + 1];
  404. environment_bit_representation[0] = '\0';
  405. for(size_t i = 0; i < response_length; i++) {
  406. char bits[9];
  407. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  408. byte_to_binary(byte, bits);
  409. strlcat(
  410. environment_bit_representation,
  411. bits,
  412. sizeof(environment_bit_representation));
  413. }
  414. // FURI_LOG_I(
  415. // TAG, "Environment bit_representation: %s", environment_bit_representation);
  416. start = 13;
  417. end = 16;
  418. int country_num =
  419. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  420. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  421. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  422. start = 25;
  423. end = 28;
  424. int network_num =
  425. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  426. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  427. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  428. card->card_type = guess_card_type(country_num, network_num);
  429. switch(card->card_type) {
  430. case CALYPSO_CARD_NAVIGO: {
  431. card->navigo = malloc(sizeof(NavigoCardData));
  432. card->navigo->environment.country_num = country_num;
  433. card->navigo->environment.network_num = network_num;
  434. CalypsoApp* IntercodeEnvHolderStructure = get_intercode_structure_env_holder();
  435. // EnvApplicationVersionNumber
  436. const char* env_key = "EnvApplicationVersionNumber";
  437. int positionOffset = get_calypso_node_offset(
  438. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  439. int start = positionOffset,
  440. end = positionOffset +
  441. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  442. card->navigo->environment.app_version =
  443. bit_slice_to_dec(environment_bit_representation, start, end);
  444. // EnvApplicationValidityEndDate
  445. env_key = "EnvApplicationValidityEndDate";
  446. positionOffset = get_calypso_node_offset(
  447. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  448. start = positionOffset,
  449. end = positionOffset +
  450. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  451. float decimal_value =
  452. bit_slice_to_dec(environment_bit_representation, start, end);
  453. uint64_t end_validity_timestamp =
  454. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  455. datetime_timestamp_to_datetime(
  456. end_validity_timestamp, &card->navigo->environment.end_dt);
  457. // HolderDataCardStatus
  458. env_key = "HolderDataCardStatus";
  459. positionOffset = get_calypso_node_offset(
  460. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  461. start = positionOffset,
  462. end = positionOffset +
  463. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  464. card->navigo->holder.card_status =
  465. bit_slice_to_dec(environment_bit_representation, start, end);
  466. // HolderDataCommercialID
  467. env_key = "HolderDataCommercialID";
  468. positionOffset = get_calypso_node_offset(
  469. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  470. start = positionOffset,
  471. end = positionOffset +
  472. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  473. card->navigo->holder.commercial_id =
  474. bit_slice_to_dec(environment_bit_representation, start, end);
  475. // Free the calypso structure
  476. free_calypso_structure(IntercodeEnvHolderStructure);
  477. // Select app for contracts
  478. error = select_new_app(
  479. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  480. if(error != 0) {
  481. FURI_LOG_E(TAG, "Failed to select app for contracts");
  482. break;
  483. }
  484. // Check the response after selecting app
  485. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  486. FURI_LOG_E(
  487. TAG, "Failed to check response after selecting app for contracts");
  488. break;
  489. }
  490. // Prepare calypso structure
  491. CalypsoApp* IntercodeContractStructure = get_intercode_structure_contract();
  492. if(!IntercodeContractStructure) {
  493. FURI_LOG_E(TAG, "Failed to load Intercode Contract structure");
  494. break;
  495. }
  496. // Now send the read command for contracts
  497. for(size_t i = 1; i < 5; i++) {
  498. error = read_new_file(
  499. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  500. if(error != 0) {
  501. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  502. break;
  503. }
  504. // Check the response after reading the file
  505. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  506. FURI_LOG_E(
  507. TAG, "Failed to check response after reading contract %d", i);
  508. break;
  509. }
  510. char bit_representation[response_length * 8 + 1];
  511. bit_representation[0] = '\0';
  512. for(size_t i = 0; i < response_length; i++) {
  513. char bits[9];
  514. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  515. byte_to_binary(byte, bits);
  516. strlcat(bit_representation, bits, sizeof(bit_representation));
  517. }
  518. bit_representation[response_length * 8] = '\0';
  519. if(bit_slice_to_dec(
  520. bit_representation,
  521. 0,
  522. IntercodeContractStructure->container->elements[0].bitmap->size -
  523. 1) == 0) {
  524. break;
  525. }
  526. card->navigo->contracts[i - 1].present = 1;
  527. card->contracts_count++;
  528. // 2. ContractTariff
  529. const char* contract_key = "ContractTariff";
  530. if(is_calypso_node_present(
  531. bit_representation, contract_key, IntercodeContractStructure)) {
  532. int positionOffset = get_calypso_node_offset(
  533. bit_representation, contract_key, IntercodeContractStructure);
  534. int start = positionOffset,
  535. end = positionOffset +
  536. get_calypso_node_size(
  537. contract_key, IntercodeContractStructure) -
  538. 1;
  539. card->navigo->contracts[i - 1].tariff =
  540. bit_slice_to_dec(bit_representation, start, end);
  541. }
  542. // 3. ContractSerialNumber
  543. contract_key = "ContractSerialNumber";
  544. if(is_calypso_node_present(
  545. bit_representation, contract_key, IntercodeContractStructure)) {
  546. int positionOffset = get_calypso_node_offset(
  547. bit_representation, contract_key, IntercodeContractStructure);
  548. int start = positionOffset,
  549. end = positionOffset +
  550. get_calypso_node_size(
  551. contract_key, IntercodeContractStructure) -
  552. 1;
  553. card->navigo->contracts[i - 1].serial_number =
  554. bit_slice_to_dec(bit_representation, start, end);
  555. card->navigo->contracts[i - 1].serial_number_available = true;
  556. }
  557. // 8. ContractPayMethod
  558. contract_key = "ContractPayMethod";
  559. if(is_calypso_node_present(
  560. bit_representation, contract_key, IntercodeContractStructure)) {
  561. int positionOffset = get_calypso_node_offset(
  562. bit_representation, contract_key, IntercodeContractStructure);
  563. int start = positionOffset,
  564. end = positionOffset +
  565. get_calypso_node_size(
  566. contract_key, IntercodeContractStructure) -
  567. 1;
  568. card->navigo->contracts[i - 1].pay_method =
  569. bit_slice_to_dec(bit_representation, start, end);
  570. card->navigo->contracts[i - 1].pay_method_available = true;
  571. }
  572. // 10. ContractPriceAmount
  573. contract_key = "ContractPriceAmount";
  574. if(is_calypso_node_present(
  575. bit_representation, contract_key, IntercodeContractStructure)) {
  576. int positionOffset = get_calypso_node_offset(
  577. bit_representation, contract_key, IntercodeContractStructure);
  578. int start = positionOffset,
  579. end = positionOffset +
  580. get_calypso_node_size(
  581. contract_key, IntercodeContractStructure) -
  582. 1;
  583. card->navigo->contracts[i - 1].price_amount =
  584. bit_slice_to_dec(bit_representation, start, end) / 100.0;
  585. card->navigo->contracts[i - 1].price_amount_available = true;
  586. }
  587. // 13.0. ContractValidityStartDate
  588. contract_key = "ContractValidityStartDate";
  589. if(is_calypso_node_present(
  590. bit_representation, contract_key, IntercodeContractStructure)) {
  591. int positionOffset = get_calypso_node_offset(
  592. bit_representation, contract_key, IntercodeContractStructure);
  593. int start = positionOffset,
  594. end = positionOffset +
  595. get_calypso_node_size(
  596. contract_key, IntercodeContractStructure) -
  597. 1;
  598. float decimal_value =
  599. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  600. uint64_t start_validity_timestamp =
  601. (decimal_value + (float)epoch) + 3600;
  602. datetime_timestamp_to_datetime(
  603. start_validity_timestamp,
  604. &card->navigo->contracts[i - 1].start_date);
  605. }
  606. // 13.2. ContractValidityEndDate
  607. contract_key = "ContractValidityEndDate";
  608. if(is_calypso_node_present(
  609. bit_representation, contract_key, IntercodeContractStructure)) {
  610. int positionOffset = get_calypso_node_offset(
  611. bit_representation, contract_key, IntercodeContractStructure);
  612. int start = positionOffset,
  613. end = positionOffset +
  614. get_calypso_node_size(
  615. contract_key, IntercodeContractStructure) -
  616. 1;
  617. float decimal_value =
  618. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  619. uint64_t end_validity_timestamp =
  620. (decimal_value + (float)epoch) + 3600;
  621. datetime_timestamp_to_datetime(
  622. end_validity_timestamp, &card->navigo->contracts[i - 1].end_date);
  623. card->navigo->contracts[i - 1].end_date_available = true;
  624. }
  625. // 13.6. ContractValidityZones
  626. contract_key = "ContractValidityZones";
  627. if(is_calypso_node_present(
  628. bit_representation, contract_key, IntercodeContractStructure)) {
  629. int start = get_calypso_node_offset(
  630. bit_representation, contract_key, IntercodeContractStructure);
  631. // binary form is 00011111 for zones 5, 4, 3, 2, 1
  632. for(int j = 0; j < 5; j++) {
  633. card->navigo->contracts[i - 1].zones[j] = bit_slice_to_dec(
  634. bit_representation, start + 3 + j, start + 3 + j);
  635. }
  636. card->navigo->contracts[i - 1].zones_available = true;
  637. }
  638. // 13.7. ContractValidityJourneys
  639. contract_key = "ContractValidityJourneys";
  640. if(is_calypso_node_present(
  641. bit_representation, contract_key, IntercodeContractStructure)) {
  642. int positionOffset = get_calypso_node_offset(
  643. bit_representation, contract_key, IntercodeContractStructure);
  644. int start = positionOffset,
  645. end = positionOffset +
  646. get_calypso_node_size(
  647. contract_key, IntercodeContractStructure) -
  648. 1;
  649. int decimal_value = bit_slice_to_dec(bit_representation, start, end);
  650. // first 5 bits -> CounterStructureNumber
  651. // last 8 bits -> CounterLastLoad
  652. // other bits -> RFU
  653. card->navigo->contracts[i - 1].counter.struct_number = decimal_value >>
  654. 11;
  655. card->navigo->contracts[i - 1].counter.last_load = decimal_value &
  656. 0xFF;
  657. card->navigo->contracts[i - 1].counter_present = true;
  658. }
  659. // 15.0. ContractValiditySaleDate
  660. contract_key = "ContractValiditySaleDate";
  661. if(is_calypso_node_present(
  662. bit_representation, contract_key, IntercodeContractStructure)) {
  663. int positionOffset = get_calypso_node_offset(
  664. bit_representation, contract_key, IntercodeContractStructure);
  665. int start = positionOffset,
  666. end = positionOffset +
  667. get_calypso_node_size(
  668. contract_key, IntercodeContractStructure) -
  669. 1;
  670. float decimal_value =
  671. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  672. uint64_t sale_timestamp = (decimal_value + (float)epoch) + 3600;
  673. datetime_timestamp_to_datetime(
  674. sale_timestamp, &card->navigo->contracts[i - 1].sale_date);
  675. }
  676. // 15.2. ContractValiditySaleAgent - FIX NEEDED
  677. contract_key = "ContractValiditySaleAgent";
  678. /* if(is_calypso_node_present(
  679. bit_representation, contract_key, NavigoContractStructure)) { */
  680. int positionOffset = get_calypso_node_offset(
  681. bit_representation, contract_key, IntercodeContractStructure);
  682. int start = positionOffset,
  683. end = positionOffset +
  684. get_calypso_node_size(contract_key, IntercodeContractStructure) -
  685. 1;
  686. card->navigo->contracts[i - 1].sale_agent =
  687. bit_slice_to_dec(bit_representation, start, end);
  688. // }
  689. // 15.3. ContractValiditySaleDevice
  690. contract_key = "ContractValiditySaleDevice";
  691. if(is_calypso_node_present(
  692. bit_representation, contract_key, IntercodeContractStructure)) {
  693. int positionOffset = get_calypso_node_offset(
  694. bit_representation, contract_key, IntercodeContractStructure);
  695. int start = positionOffset,
  696. end = positionOffset +
  697. get_calypso_node_size(
  698. contract_key, IntercodeContractStructure) -
  699. 1;
  700. card->navigo->contracts[i - 1].sale_device =
  701. bit_slice_to_dec(bit_representation, start, end);
  702. }
  703. // 16. ContractStatus -- 0x1 ou 0xff
  704. contract_key = "ContractStatus";
  705. if(is_calypso_node_present(
  706. bit_representation, contract_key, IntercodeContractStructure)) {
  707. int positionOffset = get_calypso_node_offset(
  708. bit_representation, contract_key, IntercodeContractStructure);
  709. int start = positionOffset,
  710. end = positionOffset +
  711. get_calypso_node_size(
  712. contract_key, IntercodeContractStructure) -
  713. 1;
  714. card->navigo->contracts[i - 1].status =
  715. bit_slice_to_dec(bit_representation, start, end);
  716. }
  717. // 18. ContractAuthenticator
  718. contract_key = "ContractAuthenticator";
  719. if(is_calypso_node_present(
  720. bit_representation, contract_key, IntercodeContractStructure)) {
  721. int positionOffset = get_calypso_node_offset(
  722. bit_representation, contract_key, IntercodeContractStructure);
  723. int start = positionOffset,
  724. end = positionOffset +
  725. get_calypso_node_size(
  726. contract_key, IntercodeContractStructure) -
  727. 1;
  728. card->navigo->contracts[i - 1].authenticator =
  729. bit_slice_to_dec(bit_representation, start, end);
  730. }
  731. }
  732. // Free the calypso structure
  733. free_calypso_structure(IntercodeContractStructure);
  734. // Select app for counters (remaining tickets on Navigo Easy)
  735. error = select_new_app(
  736. 0x20, 0x69, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  737. if(error != 0) {
  738. break;
  739. }
  740. // Check the response after selecting app
  741. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  742. break;
  743. }
  744. // read file 1
  745. error =
  746. read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  747. if(error != 0) {
  748. break;
  749. }
  750. // Check the response after reading the file
  751. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  752. break;
  753. }
  754. char counter_bit_representation[response_length * 8 + 1];
  755. counter_bit_representation[0] = '\0';
  756. for(size_t i = 0; i < response_length; i++) {
  757. char bits[9];
  758. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  759. byte_to_binary(byte, bits);
  760. strlcat(
  761. counter_bit_representation, bits, sizeof(counter_bit_representation));
  762. }
  763. // FURI_LOG_I(TAG, "Counter bit_representation: %s", counter_bit_representation);
  764. // Ticket counts (contracts 1-4)
  765. for(int i = 0; i < 4; i++) {
  766. if(card->navigo->contracts[i].present == 0) {
  767. continue;
  768. }
  769. if(card->navigo->contracts[i].counter_present == 0) {
  770. continue;
  771. }
  772. start = 0;
  773. end = 5;
  774. card->navigo->contracts[i].counter.count = bit_slice_to_dec(
  775. counter_bit_representation, 24 * i + start, 24 * i + end);
  776. start = 6;
  777. end = 23;
  778. card->navigo->contracts[i].counter.relative_first_stamp_15mn =
  779. bit_slice_to_dec(
  780. counter_bit_representation, 24 * i + start, 24 * i + end);
  781. }
  782. // Select app for events
  783. error = select_new_app(
  784. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  785. if(error != 0) {
  786. break;
  787. }
  788. // Check the response after selecting app
  789. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  790. break;
  791. }
  792. // Load the calypso structure for events
  793. CalypsoApp* IntercodeEventStructure = get_intercode_structure_event();
  794. if(!IntercodeEventStructure) {
  795. FURI_LOG_E(TAG, "Failed to load Intercode Event structure");
  796. break;
  797. }
  798. // furi_string_cat_printf(parsed_data, "\e#Events :\n");
  799. // Now send the read command for events
  800. for(size_t i = 1; i < 4; i++) {
  801. error = read_new_file(
  802. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  803. if(error != 0) {
  804. break;
  805. }
  806. // Check the response after reading the file
  807. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  808. break;
  809. }
  810. char event_bit_representation[response_length * 8 + 1];
  811. event_bit_representation[0] = '\0';
  812. for(size_t i = 0; i < response_length; i++) {
  813. char bits[9];
  814. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  815. byte_to_binary(byte, bits);
  816. strlcat(
  817. event_bit_representation, bits, sizeof(event_bit_representation));
  818. }
  819. // 2. EventCode
  820. const char* event_key = "EventCode";
  821. if(is_calypso_node_present(
  822. event_bit_representation, event_key, IntercodeEventStructure)) {
  823. int positionOffset = get_calypso_node_offset(
  824. event_bit_representation, event_key, IntercodeEventStructure);
  825. int start = positionOffset,
  826. end = positionOffset +
  827. get_calypso_node_size(event_key, IntercodeEventStructure) -
  828. 1;
  829. int decimal_value =
  830. bit_slice_to_dec(event_bit_representation, start, end);
  831. card->navigo->events[i - 1].transport_type = decimal_value >> 4;
  832. card->navigo->events[i - 1].transition = decimal_value & 15;
  833. }
  834. // 4. EventServiceProvider
  835. event_key = "EventServiceProvider";
  836. if(is_calypso_node_present(
  837. event_bit_representation, event_key, IntercodeEventStructure)) {
  838. int positionOffset = get_calypso_node_offset(
  839. event_bit_representation, event_key, IntercodeEventStructure);
  840. start = positionOffset,
  841. end = positionOffset +
  842. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  843. card->navigo->events[i - 1].service_provider =
  844. bit_slice_to_dec(event_bit_representation, start, end);
  845. }
  846. // 8. EventLocationId
  847. event_key = "EventLocationId";
  848. if(is_calypso_node_present(
  849. event_bit_representation, event_key, IntercodeEventStructure)) {
  850. int positionOffset = get_calypso_node_offset(
  851. event_bit_representation, event_key, IntercodeEventStructure);
  852. start = positionOffset,
  853. end = positionOffset +
  854. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  855. int decimal_value =
  856. bit_slice_to_dec(event_bit_representation, start, end);
  857. card->navigo->events[i - 1].station_group_id = decimal_value >> 9;
  858. card->navigo->events[i - 1].station_id = (decimal_value >> 4) & 31;
  859. card->navigo->events[i - 1].station_sub_id = decimal_value & 15;
  860. }
  861. // 9. EventLocationGate
  862. event_key = "EventLocationGate";
  863. if(is_calypso_node_present(
  864. event_bit_representation, event_key, IntercodeEventStructure)) {
  865. int positionOffset = get_calypso_node_offset(
  866. event_bit_representation, event_key, IntercodeEventStructure);
  867. start = positionOffset,
  868. end = positionOffset +
  869. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  870. card->navigo->events[i - 1].location_gate =
  871. bit_slice_to_dec(event_bit_representation, start, end);
  872. card->navigo->events[i - 1].location_gate_available = true;
  873. }
  874. // 10. EventDevice
  875. event_key = "EventDevice";
  876. if(is_calypso_node_present(
  877. event_bit_representation, event_key, IntercodeEventStructure)) {
  878. int positionOffset = get_calypso_node_offset(
  879. event_bit_representation, event_key, IntercodeEventStructure);
  880. start = positionOffset,
  881. end = positionOffset +
  882. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  883. int decimal_value =
  884. bit_slice_to_dec(event_bit_representation, start, end);
  885. card->navigo->events[i - 1].device = decimal_value;
  886. int bus_device = decimal_value >> 8;
  887. card->navigo->events[i - 1].door = bus_device / 2 + 1;
  888. card->navigo->events[i - 1].side = bus_device % 2;
  889. card->navigo->events[i - 1].device_available = true;
  890. }
  891. // 11. EventRouteNumber
  892. event_key = "EventRouteNumber";
  893. if(is_calypso_node_present(
  894. event_bit_representation, event_key, IntercodeEventStructure)) {
  895. int positionOffset = get_calypso_node_offset(
  896. event_bit_representation, event_key, IntercodeEventStructure);
  897. start = positionOffset,
  898. end = positionOffset +
  899. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  900. card->navigo->events[i - 1].route_number =
  901. bit_slice_to_dec(event_bit_representation, start, end);
  902. card->navigo->events[i - 1].route_number_available = true;
  903. }
  904. // 13. EventJourneyRun
  905. event_key = "EventJourneyRun";
  906. if(is_calypso_node_present(
  907. event_bit_representation, event_key, IntercodeEventStructure)) {
  908. int positionOffset = get_calypso_node_offset(
  909. event_bit_representation, event_key, IntercodeEventStructure);
  910. start = positionOffset,
  911. end = positionOffset +
  912. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  913. card->navigo->events[i - 1].mission =
  914. bit_slice_to_dec(event_bit_representation, start, end);
  915. card->navigo->events[i - 1].mission_available = true;
  916. }
  917. // 14. EventVehicleId
  918. event_key = "EventVehicleId";
  919. if(is_calypso_node_present(
  920. event_bit_representation, event_key, IntercodeEventStructure)) {
  921. int positionOffset = get_calypso_node_offset(
  922. event_bit_representation, event_key, IntercodeEventStructure);
  923. start = positionOffset,
  924. end = positionOffset +
  925. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  926. card->navigo->events[i - 1].vehicle_id =
  927. bit_slice_to_dec(event_bit_representation, start, end);
  928. card->navigo->events[i - 1].vehicle_id_available = true;
  929. }
  930. // 25. EventContractPointer
  931. event_key = "EventContractPointer";
  932. if(is_calypso_node_present(
  933. event_bit_representation, event_key, IntercodeEventStructure)) {
  934. int positionOffset = get_calypso_node_offset(
  935. event_bit_representation, event_key, IntercodeEventStructure);
  936. start = positionOffset,
  937. end = positionOffset +
  938. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  939. card->navigo->events[i - 1].used_contract =
  940. bit_slice_to_dec(event_bit_representation, start, end);
  941. card->navigo->events[i - 1].used_contract_available = true;
  942. if(card->navigo->events[i - 1].used_contract > 0) {
  943. card->events_count++;
  944. }
  945. }
  946. // EventDateStamp
  947. event_key = "EventDateStamp";
  948. int positionOffset = get_calypso_node_offset(
  949. event_bit_representation, event_key, IntercodeEventStructure);
  950. start = positionOffset,
  951. end = positionOffset +
  952. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  953. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  954. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  955. datetime_timestamp_to_datetime(
  956. date_timestamp, &card->navigo->events[i - 1].date);
  957. // EventTimeStamp
  958. event_key = "EventTimeStamp";
  959. positionOffset = get_calypso_node_offset(
  960. event_bit_representation, event_key, IntercodeEventStructure);
  961. start = positionOffset,
  962. end = positionOffset +
  963. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  964. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  965. card->navigo->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  966. card->navigo->events[i - 1].date.minute =
  967. ((decimal_value * 60) % 3600) / 60;
  968. card->navigo->events[i - 1].date.second =
  969. ((decimal_value * 60) % 3600) % 60;
  970. }
  971. // Select app for special events
  972. error = select_new_app(
  973. 0x20, 0x40, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  974. if(error != 0) {
  975. break;
  976. }
  977. // Check the response after selecting app
  978. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  979. break;
  980. }
  981. // Now send the read command for special events
  982. for(size_t i = 1; i < 4; i++) {
  983. error = read_new_file(
  984. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  985. if(error != 0) {
  986. break;
  987. }
  988. // Check the response after reading the file
  989. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  990. break;
  991. }
  992. char event_bit_representation[response_length * 8 + 1];
  993. event_bit_representation[0] = '\0';
  994. for(size_t i = 0; i < response_length; i++) {
  995. char bits[9];
  996. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  997. byte_to_binary(byte, bits);
  998. strlcat(
  999. event_bit_representation, bits, sizeof(event_bit_representation));
  1000. }
  1001. if(bit_slice_to_dec(
  1002. event_bit_representation,
  1003. 0,
  1004. IntercodeEventStructure->container->elements[0].bitmap->size - 1) ==
  1005. 0) {
  1006. break;
  1007. } else {
  1008. card->special_events_count++;
  1009. }
  1010. // 2. EventCode
  1011. const char* event_key = "EventCode";
  1012. if(is_calypso_node_present(
  1013. event_bit_representation, event_key, IntercodeEventStructure)) {
  1014. int positionOffset = get_calypso_node_offset(
  1015. event_bit_representation, event_key, IntercodeEventStructure);
  1016. int start = positionOffset,
  1017. end = positionOffset +
  1018. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1019. 1;
  1020. int decimal_value =
  1021. bit_slice_to_dec(event_bit_representation, start, end);
  1022. card->navigo->special_events[i - 1].transport_type = decimal_value >>
  1023. 4;
  1024. card->navigo->special_events[i - 1].transition = decimal_value & 15;
  1025. }
  1026. // 3. EventResult
  1027. event_key = "EventResult";
  1028. if(is_calypso_node_present(
  1029. event_bit_representation, event_key, IntercodeEventStructure)) {
  1030. int positionOffset = get_calypso_node_offset(
  1031. event_bit_representation, event_key, IntercodeEventStructure);
  1032. int start = positionOffset,
  1033. end = positionOffset +
  1034. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1035. 1;
  1036. card->navigo->special_events[i - 1].result =
  1037. bit_slice_to_dec(event_bit_representation, start, end);
  1038. }
  1039. // 4. EventServiceProvider
  1040. event_key = "EventServiceProvider";
  1041. if(is_calypso_node_present(
  1042. event_bit_representation, event_key, IntercodeEventStructure)) {
  1043. int positionOffset = get_calypso_node_offset(
  1044. event_bit_representation, event_key, IntercodeEventStructure);
  1045. int start = positionOffset,
  1046. end = positionOffset +
  1047. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1048. 1;
  1049. card->navigo->special_events[i - 1].service_provider =
  1050. bit_slice_to_dec(event_bit_representation, start, end);
  1051. }
  1052. // 8. EventLocationId
  1053. event_key = "EventLocationId";
  1054. if(is_calypso_node_present(
  1055. event_bit_representation, event_key, IntercodeEventStructure)) {
  1056. int positionOffset = get_calypso_node_offset(
  1057. event_bit_representation, event_key, IntercodeEventStructure);
  1058. int start = positionOffset,
  1059. end = positionOffset +
  1060. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1061. 1;
  1062. int decimal_value =
  1063. bit_slice_to_dec(event_bit_representation, start, end);
  1064. card->navigo->special_events[i - 1].station_group_id = decimal_value >>
  1065. 9;
  1066. card->navigo->special_events[i - 1].station_id = (decimal_value >> 4) &
  1067. 31;
  1068. card->navigo->special_events[i - 1].station_sub_id = decimal_value &
  1069. 15;
  1070. }
  1071. // 10. EventDevice
  1072. event_key = "EventDevice";
  1073. if(is_calypso_node_present(
  1074. event_bit_representation, event_key, IntercodeEventStructure)) {
  1075. int positionOffset = get_calypso_node_offset(
  1076. event_bit_representation, event_key, IntercodeEventStructure);
  1077. int start = positionOffset,
  1078. end = positionOffset +
  1079. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1080. 1;
  1081. int decimal_value =
  1082. bit_slice_to_dec(event_bit_representation, start, end);
  1083. card->navigo->special_events[i - 1].device = decimal_value;
  1084. }
  1085. // 11. EventRouteNumber
  1086. event_key = "EventRouteNumber";
  1087. if(is_calypso_node_present(
  1088. event_bit_representation, event_key, IntercodeEventStructure)) {
  1089. int positionOffset = get_calypso_node_offset(
  1090. event_bit_representation, event_key, IntercodeEventStructure);
  1091. int start = positionOffset,
  1092. end = positionOffset +
  1093. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1094. 1;
  1095. card->navigo->special_events[i - 1].route_number =
  1096. bit_slice_to_dec(event_bit_representation, start, end);
  1097. card->navigo->special_events[i - 1].route_number_available = true;
  1098. }
  1099. // EventDateStamp
  1100. event_key = "EventDateStamp";
  1101. int positionOffset = get_calypso_node_offset(
  1102. event_bit_representation, event_key, IntercodeEventStructure);
  1103. int start = positionOffset,
  1104. end = positionOffset +
  1105. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  1106. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1107. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  1108. datetime_timestamp_to_datetime(
  1109. date_timestamp, &card->navigo->special_events[i - 1].date);
  1110. // EventTimeStamp
  1111. event_key = "EventTimeStamp";
  1112. positionOffset = get_calypso_node_offset(
  1113. event_bit_representation, event_key, IntercodeEventStructure);
  1114. start = positionOffset,
  1115. end = positionOffset +
  1116. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  1117. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1118. card->navigo->special_events[i - 1].date.hour =
  1119. (decimal_value * 60) / 3600;
  1120. card->navigo->special_events[i - 1].date.minute =
  1121. ((decimal_value * 60) % 3600) / 60;
  1122. card->navigo->special_events[i - 1].date.second =
  1123. ((decimal_value * 60) % 3600) % 60;
  1124. }
  1125. // Free the calypso structure
  1126. free_calypso_structure(IntercodeEventStructure);
  1127. break;
  1128. }
  1129. case CALYPSO_CARD_OPUS: {
  1130. card->opus = malloc(sizeof(OpusCardData));
  1131. card->opus->environment.country_num = country_num;
  1132. card->opus->environment.network_num = network_num;
  1133. CalypsoApp* OpusEnvHolderStructure = get_opus_env_holder_structure();
  1134. // EnvApplicationVersionNumber
  1135. const char* env_key = "EnvApplicationVersionNumber";
  1136. int positionOffset = get_calypso_node_offset(
  1137. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1138. int start = positionOffset,
  1139. end = positionOffset +
  1140. get_calypso_node_size(env_key, OpusEnvHolderStructure) - 1;
  1141. card->opus->environment.app_version =
  1142. bit_slice_to_dec(environment_bit_representation, start, end);
  1143. // EnvApplicationValidityEndDate
  1144. env_key = "EnvApplicationValidityEndDate";
  1145. positionOffset = get_calypso_node_offset(
  1146. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1147. start = positionOffset,
  1148. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1149. 1;
  1150. float decimal_value =
  1151. bit_slice_to_dec(environment_bit_representation, start, end);
  1152. uint64_t end_validity_timestamp =
  1153. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  1154. datetime_timestamp_to_datetime(
  1155. end_validity_timestamp, &card->opus->environment.end_dt);
  1156. // HolderDataCardStatus
  1157. env_key = "HolderDataCardStatus";
  1158. positionOffset = get_calypso_node_offset(
  1159. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1160. start = positionOffset,
  1161. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1162. 1;
  1163. card->opus->holder.card_status =
  1164. bit_slice_to_dec(environment_bit_representation, start, end);
  1165. // HolderDataCommercialID
  1166. env_key = "HolderDataCommercialID";
  1167. positionOffset = get_calypso_node_offset(
  1168. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1169. start = positionOffset,
  1170. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1171. 1;
  1172. card->opus->holder.commercial_id =
  1173. bit_slice_to_dec(environment_bit_representation, start, end);
  1174. // Free the calypso structure
  1175. free_calypso_structure(OpusEnvHolderStructure);
  1176. // Select app for contracts
  1177. error = select_new_app(
  1178. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1179. if(error != 0) {
  1180. FURI_LOG_E(TAG, "Failed to select app for contracts");
  1181. break;
  1182. }
  1183. // Check the response after selecting app
  1184. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1185. FURI_LOG_E(
  1186. TAG, "Failed to check response after selecting app for contracts");
  1187. break;
  1188. }
  1189. // Prepare calypso structure
  1190. CalypsoApp* OpusContractStructure = get_opus_contract_structure();
  1191. if(!OpusContractStructure) {
  1192. FURI_LOG_E(TAG, "Failed to load Opus Contract structure");
  1193. break;
  1194. }
  1195. // Now send the read command for contracts
  1196. for(size_t i = 1; i < 5; i++) {
  1197. error = read_new_file(
  1198. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1199. if(error != 0) {
  1200. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  1201. break;
  1202. }
  1203. // Check the response after reading the file
  1204. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1205. FURI_LOG_E(
  1206. TAG, "Failed to check response after reading contract %d", i);
  1207. break;
  1208. }
  1209. char bit_representation[response_length * 8 + 1];
  1210. bit_representation[0] = '\0';
  1211. for(size_t i = 0; i < response_length; i++) {
  1212. char bits[9];
  1213. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1214. byte_to_binary(byte, bits);
  1215. strlcat(bit_representation, bits, sizeof(bit_representation));
  1216. }
  1217. bit_representation[response_length * 8] = '\0';
  1218. if(bit_slice_to_dec(
  1219. bit_representation,
  1220. 0,
  1221. OpusContractStructure->container->elements[1].bitmap->size - 1) ==
  1222. 0) {
  1223. break;
  1224. }
  1225. card->opus->contracts[i - 1].present = 1;
  1226. card->contracts_count++;
  1227. // ContractProvider
  1228. const char* contract_key = "ContractProvider";
  1229. if(is_calypso_node_present(
  1230. bit_representation, contract_key, OpusContractStructure)) {
  1231. int positionOffset = get_calypso_node_offset(
  1232. bit_representation, contract_key, OpusContractStructure);
  1233. int start = positionOffset,
  1234. end = positionOffset +
  1235. get_calypso_node_size(contract_key, OpusContractStructure) -
  1236. 1;
  1237. card->opus->contracts[i - 1].provider =
  1238. bit_slice_to_dec(bit_representation, start, end);
  1239. }
  1240. // ContractTariff
  1241. contract_key = "ContractTariff";
  1242. if(is_calypso_node_present(
  1243. bit_representation, contract_key, OpusContractStructure)) {
  1244. int positionOffset = get_calypso_node_offset(
  1245. bit_representation, contract_key, OpusContractStructure);
  1246. int start = positionOffset,
  1247. end = positionOffset +
  1248. get_calypso_node_size(contract_key, OpusContractStructure) -
  1249. 1;
  1250. card->opus->contracts[i - 1].tariff =
  1251. bit_slice_to_dec(bit_representation, start, end);
  1252. }
  1253. // ContractStartDate
  1254. contract_key = "ContractStartDate";
  1255. if(is_calypso_node_present(
  1256. bit_representation, contract_key, OpusContractStructure)) {
  1257. int positionOffset = get_calypso_node_offset(
  1258. bit_representation, contract_key, OpusContractStructure);
  1259. int start = positionOffset,
  1260. end = positionOffset +
  1261. get_calypso_node_size(contract_key, OpusContractStructure) -
  1262. 1;
  1263. float decimal_value =
  1264. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1265. uint64_t start_validity_timestamp =
  1266. (decimal_value + (float)epoch) + 3600;
  1267. datetime_timestamp_to_datetime(
  1268. start_validity_timestamp,
  1269. &card->opus->contracts[i - 1].start_date);
  1270. }
  1271. // ContractEndDate
  1272. contract_key = "ContractEndDate";
  1273. if(is_calypso_node_present(
  1274. bit_representation, contract_key, OpusContractStructure)) {
  1275. int positionOffset = get_calypso_node_offset(
  1276. bit_representation, contract_key, OpusContractStructure);
  1277. int start = positionOffset,
  1278. end = positionOffset +
  1279. get_calypso_node_size(contract_key, OpusContractStructure) -
  1280. 1;
  1281. float decimal_value =
  1282. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1283. uint64_t end_validity_timestamp =
  1284. (decimal_value + (float)epoch) + 3600;
  1285. datetime_timestamp_to_datetime(
  1286. end_validity_timestamp, &card->opus->contracts[i - 1].end_date);
  1287. }
  1288. // ContractStatus
  1289. contract_key = "ContractStatus";
  1290. if(is_calypso_node_present(
  1291. bit_representation, contract_key, OpusContractStructure)) {
  1292. int positionOffset = get_calypso_node_offset(
  1293. bit_representation, contract_key, OpusContractStructure);
  1294. int start = positionOffset,
  1295. end = positionOffset +
  1296. get_calypso_node_size(contract_key, OpusContractStructure) -
  1297. 1;
  1298. card->opus->contracts[i - 1].status =
  1299. bit_slice_to_dec(bit_representation, start, end);
  1300. }
  1301. // ContractSaleDate + ContractSaleTime
  1302. contract_key = "ContractSaleDate";
  1303. int positionOffset = get_calypso_node_offset(
  1304. bit_representation, contract_key, OpusContractStructure);
  1305. int start = positionOffset,
  1306. end = positionOffset +
  1307. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1308. uint64_t sale_date_timestamp =
  1309. (bit_slice_to_dec(bit_representation, start, end) + (float)epoch) +
  1310. 3600;
  1311. datetime_timestamp_to_datetime(
  1312. sale_date_timestamp, &card->opus->contracts[i - 1].sale_date);
  1313. contract_key = "ContractSaleTime";
  1314. positionOffset = get_calypso_node_offset(
  1315. bit_representation, contract_key, OpusContractStructure);
  1316. start = positionOffset,
  1317. end = positionOffset +
  1318. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1319. int decimal_value = bit_slice_to_dec(bit_representation, start, end);
  1320. card->opus->contracts[i - 1].sale_date.hour = (decimal_value * 60) / 3600;
  1321. card->opus->contracts[i - 1].sale_date.minute =
  1322. ((decimal_value * 60) % 3600) / 60;
  1323. card->opus->contracts[i - 1].sale_date.second =
  1324. ((decimal_value * 60) % 3600) % 60;
  1325. }
  1326. // Free the calypso structure
  1327. free_calypso_structure(OpusContractStructure);
  1328. // Select app for events
  1329. error = select_new_app(
  1330. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1331. if(error != 0) {
  1332. break;
  1333. }
  1334. // Check the response after selecting app
  1335. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1336. break;
  1337. }
  1338. // Load the calypso structure for events
  1339. CalypsoApp* OpusEventStructure = get_opus_event_structure();
  1340. if(!OpusEventStructure) {
  1341. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  1342. break;
  1343. }
  1344. // Now send the read command for events
  1345. for(size_t i = 1; i < 4; i++) {
  1346. error = read_new_file(
  1347. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1348. if(error != 0) {
  1349. break;
  1350. }
  1351. // Check the response after reading the file
  1352. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1353. break;
  1354. }
  1355. char event_bit_representation[response_length * 8 + 1];
  1356. event_bit_representation[0] = '\0';
  1357. for(size_t i = 0; i < response_length; i++) {
  1358. char bits[9];
  1359. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1360. byte_to_binary(byte, bits);
  1361. strlcat(
  1362. event_bit_representation, bits, sizeof(event_bit_representation));
  1363. }
  1364. // EventServiceProvider
  1365. const char* event_key = "EventServiceProvider";
  1366. if(is_calypso_node_present(
  1367. event_bit_representation, event_key, OpusEventStructure)) {
  1368. int positionOffset = get_calypso_node_offset(
  1369. event_bit_representation, event_key, OpusEventStructure);
  1370. int start = positionOffset,
  1371. end = positionOffset +
  1372. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1373. card->opus->events[i - 1].service_provider =
  1374. bit_slice_to_dec(event_bit_representation, start, end);
  1375. }
  1376. // EventRouteNumber
  1377. event_key = "EventRouteNumber";
  1378. if(is_calypso_node_present(
  1379. event_bit_representation, event_key, OpusEventStructure)) {
  1380. int positionOffset = get_calypso_node_offset(
  1381. event_bit_representation, event_key, OpusEventStructure);
  1382. int start = positionOffset,
  1383. end = positionOffset +
  1384. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1385. card->opus->events[i - 1].route_number =
  1386. bit_slice_to_dec(event_bit_representation, start, end);
  1387. card->opus->events[i - 1].route_number_available = true;
  1388. }
  1389. // EventContractPointer
  1390. event_key = "EventContractPointer";
  1391. if(is_calypso_node_present(
  1392. event_bit_representation, event_key, OpusEventStructure)) {
  1393. int positionOffset = get_calypso_node_offset(
  1394. event_bit_representation, event_key, OpusEventStructure);
  1395. int start = positionOffset,
  1396. end = positionOffset +
  1397. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1398. card->opus->events[i - 1].used_contract =
  1399. bit_slice_to_dec(event_bit_representation, start, end);
  1400. card->opus->events[i - 1].used_contract_available = true;
  1401. if(card->opus->events[i - 1].used_contract > 0) {
  1402. card->events_count++;
  1403. }
  1404. }
  1405. // EventDateStamp
  1406. event_key = "EventDateStamp";
  1407. int positionOffset = get_calypso_node_offset(
  1408. event_bit_representation, event_key, OpusEventStructure);
  1409. int start = positionOffset,
  1410. end = positionOffset +
  1411. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1412. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1413. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  1414. datetime_timestamp_to_datetime(
  1415. date_timestamp, &card->opus->events[i - 1].date);
  1416. // EventTimeStamp
  1417. event_key = "EventTimeStamp";
  1418. positionOffset = get_calypso_node_offset(
  1419. event_bit_representation, event_key, OpusEventStructure);
  1420. start = positionOffset,
  1421. end = positionOffset +
  1422. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1423. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1424. card->opus->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  1425. card->opus->events[i - 1].date.minute = ((decimal_value * 60) % 3600) / 60;
  1426. card->opus->events[i - 1].date.second = ((decimal_value * 60) % 3600) % 60;
  1427. }
  1428. // Free the calypso structure
  1429. free_calypso_structure(OpusEventStructure);
  1430. break;
  1431. }
  1432. case CALYPSO_CARD_UNKNOWN: {
  1433. start = 3;
  1434. end = 6;
  1435. country_num =
  1436. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  1437. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  1438. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  1439. start = 15;
  1440. end = 18;
  1441. network_num =
  1442. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  1443. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  1444. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  1445. card->card_type = guess_card_type(country_num, network_num);
  1446. if(card->card_type == CALYPSO_CARD_RAVKAV) {
  1447. }
  1448. break;
  1449. }
  1450. default:
  1451. break;
  1452. }
  1453. widget_add_text_scroll_element(
  1454. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  1455. CalypsoContext* context = malloc(sizeof(CalypsoContext));
  1456. context->card = card;
  1457. context->page_id = 0;
  1458. context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  1459. app->calypso_context = context;
  1460. // Ensure no nested mutexes
  1461. furi_mutex_acquire(context->mutex, FuriWaitForever);
  1462. update_page_info(app, parsed_data);
  1463. furi_mutex_release(context->mutex);
  1464. widget_add_text_scroll_element(
  1465. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  1466. // Ensure no nested mutexes
  1467. furi_mutex_acquire(context->mutex, FuriWaitForever);
  1468. update_widget_elements(app);
  1469. furi_mutex_release(context->mutex);
  1470. furi_string_free(parsed_data);
  1471. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  1472. metroflip_app_blink_stop(app);
  1473. stage = MetroflipPollerEventTypeSuccess;
  1474. next_command = NfcCommandStop;
  1475. } while(false);
  1476. if(stage != MetroflipPollerEventTypeSuccess) {
  1477. next_command = NfcCommandStop;
  1478. }
  1479. }
  1480. }
  1481. bit_buffer_free(tx_buffer);
  1482. bit_buffer_free(rx_buffer);
  1483. return next_command;
  1484. }
  1485. void metroflip_scene_navigo_on_enter(void* context) {
  1486. Metroflip* app = context;
  1487. dolphin_deed(DolphinDeedNfcRead);
  1488. // Setup view
  1489. Popup* popup = app->popup;
  1490. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  1491. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  1492. // Start worker
  1493. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  1494. nfc_scanner_alloc(app->nfc);
  1495. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  1496. nfc_poller_start(app->poller, metroflip_scene_navigo_poller_callback, app);
  1497. metroflip_app_blink_start(app);
  1498. }
  1499. bool metroflip_scene_navigo_on_event(void* context, SceneManagerEvent event) {
  1500. Metroflip* app = context;
  1501. bool consumed = false;
  1502. if(event.type == SceneManagerEventTypeCustom) {
  1503. if(event.event == MetroflipPollerEventTypeCardDetect) {
  1504. Popup* popup = app->popup;
  1505. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  1506. consumed = true;
  1507. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  1508. Popup* popup = app->popup;
  1509. popup_set_header(popup, "Read Error,\n wrong card", 68, 30, AlignLeft, AlignTop);
  1510. consumed = true;
  1511. } else if(event.event == MetroflipCustomEventPollerFail) {
  1512. Popup* popup = app->popup;
  1513. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  1514. consumed = true;
  1515. }
  1516. } else if(event.type == SceneManagerEventTypeBack) {
  1517. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  1518. consumed = true;
  1519. }
  1520. return consumed;
  1521. }
  1522. void metroflip_scene_navigo_on_exit(void* context) {
  1523. Metroflip* app = context;
  1524. if(app->poller) {
  1525. nfc_poller_stop(app->poller);
  1526. nfc_poller_free(app->poller);
  1527. }
  1528. metroflip_app_blink_stop(app);
  1529. widget_reset(app->widget);
  1530. // Clear view
  1531. popup_reset(app->popup);
  1532. if(app->calypso_context) {
  1533. CalypsoContext* ctx = app->calypso_context;
  1534. free(ctx->card->navigo);
  1535. free(ctx->card->opus);
  1536. free(ctx->card);
  1537. furi_mutex_free(ctx->mutex);
  1538. free(ctx);
  1539. app->calypso_context = NULL;
  1540. }
  1541. }