metroflip_scene_calypso.c 85 KB

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