metroflip_scene_calypso.c 85 KB

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