metroflip_scene_calypso.c 119 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323
  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. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  97. furi_string_cat_printf(
  98. parsed_data,
  99. "\e#%s %u:\n",
  100. get_network_string(ctx->card->card_type),
  101. ctx->card->card_number);
  102. return;
  103. }
  104. if(ctx->page_id <= 3) {
  105. switch(ctx->card->card_type) {
  106. case CALYPSO_CARD_NAVIGO: {
  107. furi_string_cat_printf(
  108. parsed_data,
  109. "\e#%s %u:\n",
  110. get_navigo_type(ctx->card->navigo->holder.card_status),
  111. ctx->card->card_number);
  112. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  113. show_navigo_contract_info(&ctx->card->navigo->contracts[ctx->page_id], parsed_data);
  114. break;
  115. }
  116. case CALYPSO_CARD_OPUS: {
  117. furi_string_cat_printf(parsed_data, "\e#Opus %u:\n", ctx->card->card_number);
  118. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  119. show_opus_contract_info(&ctx->card->opus->contracts[ctx->page_id], parsed_data);
  120. break;
  121. }
  122. case CALYPSO_CARD_RAVKAV: {
  123. if(ctx->card->card_number == 0) {
  124. furi_string_cat_printf(parsed_data, "\e#Anonymous Rav-Kav:\n");
  125. } else {
  126. furi_string_cat_printf(parsed_data, "\e#RavKav %u:\n", ctx->card->card_number);
  127. }
  128. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  129. show_ravkav_contract_info(&ctx->card->ravkav->contracts[ctx->page_id], parsed_data);
  130. break;
  131. }
  132. default: {
  133. furi_string_cat_printf(parsed_data, "\e#Unknown %u:\n", ctx->card->card_number);
  134. break;
  135. }
  136. }
  137. } else if(ctx->page_id == 4) {
  138. furi_string_cat_printf(parsed_data, "\e#Environment:\n");
  139. switch(ctx->card->card_type) {
  140. case CALYPSO_CARD_NAVIGO: {
  141. show_navigo_environment_info(&ctx->card->navigo->environment, parsed_data);
  142. break;
  143. }
  144. case CALYPSO_CARD_OPUS: {
  145. show_opus_environment_info(&ctx->card->opus->environment, parsed_data);
  146. break;
  147. }
  148. case CALYPSO_CARD_RAVKAV: {
  149. show_ravkav_environment_info(&ctx->card->ravkav->environment, parsed_data);
  150. break;
  151. }
  152. default: {
  153. break;
  154. }
  155. }
  156. } else if(ctx->page_id >= 5) {
  157. furi_string_cat_printf(parsed_data, "\e#Event %d:\n", ctx->page_id - 4);
  158. switch(ctx->card->card_type) {
  159. case CALYPSO_CARD_NAVIGO: {
  160. show_navigo_event_info(
  161. &ctx->card->navigo->events[ctx->page_id - 5],
  162. ctx->card->navigo->contracts,
  163. parsed_data);
  164. break;
  165. }
  166. case CALYPSO_CARD_OPUS: {
  167. show_opus_event_info(
  168. &ctx->card->opus->events[ctx->page_id - 5],
  169. ctx->card->opus->contracts,
  170. parsed_data);
  171. break;
  172. }
  173. case CALYPSO_CARD_RAVKAV: {
  174. show_ravkav_event_info(&ctx->card->ravkav->events[ctx->page_id - 5], parsed_data);
  175. break;
  176. }
  177. default: {
  178. break;
  179. }
  180. }
  181. } else if(ctx->page_id == 8 || ctx->page_id == 9 || ctx->page_id == 10) {
  182. furi_string_cat_printf(parsed_data, "\e#Special Event %d:\n", ctx->page_id - 7);
  183. switch(ctx->card->card_type) {
  184. case CALYPSO_CARD_NAVIGO: {
  185. show_navigo_special_event_info(
  186. &ctx->card->navigo->special_events[ctx->page_id - 8], parsed_data);
  187. break;
  188. }
  189. case CALYPSO_CARD_OPUS: {
  190. break;
  191. }
  192. default: {
  193. break;
  194. }
  195. }
  196. }
  197. }
  198. void update_widget_elements(void* context) {
  199. Metroflip* app = context;
  200. CalypsoContext* ctx = app->calypso_context;
  201. Widget* widget = app->widget;
  202. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO && ctx->card->card_type != CALYPSO_CARD_OPUS &&
  203. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  204. widget_add_button_element(
  205. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  206. return;
  207. }
  208. if(ctx->page_id < 10) {
  209. widget_add_button_element(
  210. widget, GuiButtonTypeRight, "Next", metroflip_next_button_widget_callback, context);
  211. } else {
  212. widget_add_button_element(
  213. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  214. }
  215. if(ctx->page_id > 0) {
  216. widget_add_button_element(
  217. widget, GuiButtonTypeLeft, "Back", metroflip_back_button_widget_callback, context);
  218. }
  219. }
  220. void metroflip_back_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  221. Metroflip* app = context;
  222. CalypsoContext* ctx = app->calypso_context;
  223. UNUSED(result);
  224. Widget* widget = app->widget;
  225. if(type == InputTypePress) {
  226. widget_reset(widget);
  227. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id - 1);
  228. if(ctx->page_id > 0) {
  229. if(ctx->page_id == 10 && ctx->card->special_events_count < 2) {
  230. ctx->page_id -= 1;
  231. }
  232. if(ctx->page_id == 9 && ctx->card->special_events_count < 1) {
  233. ctx->page_id -= 1;
  234. }
  235. if(ctx->page_id == 8 && ctx->card->events_count < 3) {
  236. ctx->page_id -= 1;
  237. }
  238. if(ctx->page_id == 7 && ctx->card->events_count < 2) {
  239. ctx->page_id -= 1;
  240. }
  241. if(ctx->page_id == 6 && ctx->card->events_count < 1) {
  242. ctx->page_id -= 1;
  243. }
  244. if(ctx->page_id == 4 && ctx->card->contracts_count < 4) {
  245. ctx->page_id -= 1;
  246. }
  247. if(ctx->page_id == 3 && ctx->card->contracts_count < 3) {
  248. ctx->page_id -= 1;
  249. }
  250. if(ctx->page_id == 2 && ctx->card->contracts_count < 2) {
  251. ctx->page_id -= 1;
  252. }
  253. ctx->page_id -= 1;
  254. }
  255. FuriString* parsed_data = furi_string_alloc();
  256. // Ensure no nested mutexes
  257. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  258. update_page_info(app, parsed_data);
  259. furi_mutex_release(ctx->mutex);
  260. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  261. // widget_add_icon_element(widget, 0, 0, &I_RFIDDolphinReceive_97x61);
  262. // Ensure no nested mutexes
  263. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  264. update_widget_elements(app);
  265. furi_mutex_release(ctx->mutex);
  266. furi_string_free(parsed_data);
  267. }
  268. }
  269. void metroflip_next_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  270. Metroflip* app = context;
  271. CalypsoContext* ctx = app->calypso_context;
  272. UNUSED(result);
  273. Widget* widget = app->widget;
  274. if(type == InputTypePress) {
  275. widget_reset(widget);
  276. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id + 1);
  277. if(ctx->card->card_type != CALYPSO_CARD_NAVIGO &&
  278. ctx->card->card_type != CALYPSO_CARD_OPUS &&
  279. ctx->card->card_type != CALYPSO_CARD_RAVKAV) {
  280. ctx->page_id = 0;
  281. scene_manager_search_and_switch_to_previous_scene(
  282. app->scene_manager, MetroflipSceneStart);
  283. return;
  284. }
  285. if(ctx->page_id < 10) {
  286. if(ctx->page_id == 0 && ctx->card->contracts_count < 2) {
  287. ctx->page_id += 1;
  288. }
  289. if(ctx->page_id == 1 && ctx->card->contracts_count < 3) {
  290. ctx->page_id += 1;
  291. }
  292. if(ctx->page_id == 2 && ctx->card->contracts_count < 4) {
  293. ctx->page_id += 1;
  294. }
  295. if(ctx->page_id == 4 && ctx->card->events_count < 1) {
  296. ctx->page_id += 1;
  297. }
  298. if(ctx->page_id == 5 && ctx->card->events_count < 2) {
  299. ctx->page_id += 1;
  300. }
  301. if(ctx->page_id == 6 && ctx->card->events_count < 3) {
  302. ctx->page_id += 1;
  303. }
  304. if(ctx->page_id == 7 && ctx->card->special_events_count < 1) {
  305. ctx->page_id += 1;
  306. }
  307. if(ctx->page_id == 8 && ctx->card->special_events_count < 2) {
  308. ctx->page_id += 1;
  309. }
  310. if(ctx->page_id == 9 && ctx->card->special_events_count < 3) {
  311. ctx->page_id = 0;
  312. scene_manager_search_and_switch_to_previous_scene(
  313. app->scene_manager, MetroflipSceneStart);
  314. return;
  315. }
  316. ctx->page_id += 1;
  317. } else {
  318. ctx->page_id = 0;
  319. scene_manager_search_and_switch_to_previous_scene(
  320. app->scene_manager, MetroflipSceneStart);
  321. return;
  322. }
  323. FuriString* parsed_data = furi_string_alloc();
  324. // Ensure no nested mutexes
  325. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  326. update_page_info(app, parsed_data);
  327. furi_mutex_release(ctx->mutex);
  328. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  329. // Ensure no nested mutexes
  330. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  331. update_widget_elements(app);
  332. furi_mutex_release(ctx->mutex);
  333. furi_string_free(parsed_data);
  334. }
  335. }
  336. void delay(int milliseconds) {
  337. furi_thread_flags_wait(0, FuriFlagWaitAny, milliseconds);
  338. }
  339. static NfcCommand metroflip_scene_calypso_poller_callback(NfcGenericEvent event, void* context) {
  340. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  341. NfcCommand next_command = NfcCommandContinue;
  342. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  343. Metroflip* app = context;
  344. FuriString* parsed_data = furi_string_alloc();
  345. Widget* widget = app->widget;
  346. furi_string_reset(app->text_box_store);
  347. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  348. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  349. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  350. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  351. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  352. if(stage == MetroflipPollerEventTypeStart) {
  353. // Start Flipper vibration
  354. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  355. notification_message(notification, &sequence_set_vibro_on);
  356. delay(50);
  357. notification_message(notification, &sequence_reset_vibro);
  358. nfc_device_set_data(
  359. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  360. Iso14443_4bError error;
  361. size_t response_length = 0;
  362. do {
  363. // Initialize the card data
  364. CalypsoCardData* card = malloc(sizeof(CalypsoCardData));
  365. // Select app ICC
  366. error = select_new_app(
  367. 0x00, 0x02, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  368. if(error != 0) {
  369. break;
  370. }
  371. // Check the response after selecting app
  372. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  373. break;
  374. }
  375. // Now send the read command for ICC
  376. error = read_new_file(0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  377. if(error != 0) {
  378. break;
  379. }
  380. // Check the response after reading the file
  381. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  382. break;
  383. }
  384. char icc_bit_representation[response_length * 8 + 1];
  385. icc_bit_representation[0] = '\0';
  386. for(size_t i = 0; i < response_length; i++) {
  387. char bits[9];
  388. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  389. byte_to_binary(byte, bits);
  390. strlcat(icc_bit_representation, bits, sizeof(icc_bit_representation));
  391. }
  392. icc_bit_representation[response_length * 8] = '\0';
  393. int start = 128, end = 159;
  394. card->card_number = bit_slice_to_dec(icc_bit_representation, start, end);
  395. // Select app for ticketing
  396. error = select_new_app(
  397. 0x20, 0x00, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  398. if(error != 0) {
  399. FURI_LOG_E(TAG, "Failed to select app for ticketing");
  400. break;
  401. }
  402. // Check the response after selecting app
  403. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  404. FURI_LOG_E(TAG, "Failed to check response after selecting app for ticketing");
  405. break;
  406. }
  407. // Select app for environment
  408. error = select_new_app(
  409. 0x20, 0x1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  410. if(error != 0) {
  411. break;
  412. }
  413. // Check the response after selecting app
  414. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  415. break;
  416. }
  417. // read file 1
  418. error = read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  419. if(error != 0) {
  420. break;
  421. }
  422. // Check the response after reading the file
  423. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  424. break;
  425. }
  426. char environment_bit_representation[response_length * 8 + 1];
  427. environment_bit_representation[0] = '\0';
  428. for(size_t i = 0; i < response_length; i++) {
  429. char bits[9];
  430. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  431. byte_to_binary(byte, bits);
  432. strlcat(
  433. environment_bit_representation,
  434. bits,
  435. sizeof(environment_bit_representation));
  436. }
  437. // FURI_LOG_I(
  438. // TAG, "Environment bit_representation: %s", environment_bit_representation);
  439. start = 13;
  440. end = 16;
  441. int country_num =
  442. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  443. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  444. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  445. start = 25;
  446. end = 28;
  447. int network_num =
  448. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  449. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  450. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  451. card->card_type = guess_card_type(country_num, network_num);
  452. switch(card->card_type) {
  453. case CALYPSO_CARD_NAVIGO: {
  454. card->navigo = malloc(sizeof(NavigoCardData));
  455. card->navigo->environment.country_num = country_num;
  456. card->navigo->environment.network_num = network_num;
  457. CalypsoApp* IntercodeEnvHolderStructure = get_intercode_structure_env_holder();
  458. // EnvApplicationVersionNumber
  459. const char* env_key = "EnvApplicationVersionNumber";
  460. int positionOffset = get_calypso_node_offset(
  461. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  462. int start = positionOffset,
  463. end = positionOffset +
  464. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  465. card->navigo->environment.app_version =
  466. bit_slice_to_dec(environment_bit_representation, start, end);
  467. // EnvApplicationValidityEndDate
  468. env_key = "EnvApplicationValidityEndDate";
  469. positionOffset = get_calypso_node_offset(
  470. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  471. start = positionOffset,
  472. end = positionOffset +
  473. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  474. float decimal_value =
  475. bit_slice_to_dec(environment_bit_representation, start, end);
  476. uint64_t end_validity_timestamp =
  477. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  478. datetime_timestamp_to_datetime(
  479. end_validity_timestamp, &card->navigo->environment.end_dt);
  480. // HolderDataCardStatus
  481. env_key = "HolderDataCardStatus";
  482. positionOffset = get_calypso_node_offset(
  483. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  484. start = positionOffset,
  485. end = positionOffset +
  486. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  487. card->navigo->holder.card_status =
  488. bit_slice_to_dec(environment_bit_representation, start, end);
  489. // HolderDataCommercialID
  490. env_key = "HolderDataCommercialID";
  491. positionOffset = get_calypso_node_offset(
  492. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  493. start = positionOffset,
  494. end = positionOffset +
  495. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  496. card->navigo->holder.commercial_id =
  497. bit_slice_to_dec(environment_bit_representation, start, end);
  498. // Free the calypso structure
  499. free_calypso_structure(IntercodeEnvHolderStructure);
  500. // Select app for contracts
  501. error = select_new_app(
  502. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  503. if(error != 0) {
  504. FURI_LOG_E(TAG, "Failed to select app for contracts");
  505. break;
  506. }
  507. // Check the response after selecting app
  508. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  509. FURI_LOG_E(
  510. TAG, "Failed to check response after selecting app for contracts");
  511. break;
  512. }
  513. // Prepare calypso structure
  514. CalypsoApp* IntercodeContractStructure = get_intercode_structure_contract();
  515. if(!IntercodeContractStructure) {
  516. FURI_LOG_E(TAG, "Failed to load Intercode Contract structure");
  517. break;
  518. }
  519. // Now send the read command for contracts
  520. for(size_t i = 1; i < 5; i++) {
  521. error = read_new_file(
  522. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  523. if(error != 0) {
  524. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  525. break;
  526. }
  527. // Check the response after reading the file
  528. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  529. FURI_LOG_E(
  530. TAG, "Failed to check response after reading contract %d", i);
  531. break;
  532. }
  533. char bit_representation[response_length * 8 + 1];
  534. bit_representation[0] = '\0';
  535. for(size_t i = 0; i < response_length; i++) {
  536. char bits[9];
  537. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  538. byte_to_binary(byte, bits);
  539. strlcat(bit_representation, bits, sizeof(bit_representation));
  540. }
  541. bit_representation[response_length * 8] = '\0';
  542. if(bit_slice_to_dec(
  543. bit_representation,
  544. 0,
  545. IntercodeContractStructure->container->elements[0].bitmap->size -
  546. 1) == 0) {
  547. break;
  548. }
  549. card->navigo->contracts[i - 1].present = 1;
  550. card->contracts_count++;
  551. // 2. ContractTariff
  552. const char* contract_key = "ContractTariff";
  553. if(is_calypso_node_present(
  554. bit_representation, contract_key, IntercodeContractStructure)) {
  555. int positionOffset = get_calypso_node_offset(
  556. bit_representation, contract_key, IntercodeContractStructure);
  557. int start = positionOffset,
  558. end = positionOffset +
  559. get_calypso_node_size(
  560. contract_key, IntercodeContractStructure) -
  561. 1;
  562. card->navigo->contracts[i - 1].tariff =
  563. bit_slice_to_dec(bit_representation, start, end);
  564. }
  565. // 3. ContractSerialNumber
  566. contract_key = "ContractSerialNumber";
  567. if(is_calypso_node_present(
  568. bit_representation, contract_key, IntercodeContractStructure)) {
  569. int positionOffset = get_calypso_node_offset(
  570. bit_representation, contract_key, IntercodeContractStructure);
  571. int start = positionOffset,
  572. end = positionOffset +
  573. get_calypso_node_size(
  574. contract_key, IntercodeContractStructure) -
  575. 1;
  576. card->navigo->contracts[i - 1].serial_number =
  577. bit_slice_to_dec(bit_representation, start, end);
  578. card->navigo->contracts[i - 1].serial_number_available = true;
  579. }
  580. // 8. ContractPayMethod
  581. contract_key = "ContractPayMethod";
  582. if(is_calypso_node_present(
  583. bit_representation, contract_key, IntercodeContractStructure)) {
  584. int positionOffset = get_calypso_node_offset(
  585. bit_representation, contract_key, IntercodeContractStructure);
  586. int start = positionOffset,
  587. end = positionOffset +
  588. get_calypso_node_size(
  589. contract_key, IntercodeContractStructure) -
  590. 1;
  591. card->navigo->contracts[i - 1].pay_method =
  592. bit_slice_to_dec(bit_representation, start, end);
  593. card->navigo->contracts[i - 1].pay_method_available = true;
  594. }
  595. // 10. ContractPriceAmount
  596. contract_key = "ContractPriceAmount";
  597. if(is_calypso_node_present(
  598. bit_representation, contract_key, IntercodeContractStructure)) {
  599. int positionOffset = get_calypso_node_offset(
  600. bit_representation, contract_key, IntercodeContractStructure);
  601. int start = positionOffset,
  602. end = positionOffset +
  603. get_calypso_node_size(
  604. contract_key, IntercodeContractStructure) -
  605. 1;
  606. card->navigo->contracts[i - 1].price_amount =
  607. bit_slice_to_dec(bit_representation, start, end) / 100.0;
  608. card->navigo->contracts[i - 1].price_amount_available = true;
  609. }
  610. // 13.0. ContractValidityStartDate
  611. contract_key = "ContractValidityStartDate";
  612. if(is_calypso_node_present(
  613. bit_representation, contract_key, IntercodeContractStructure)) {
  614. int positionOffset = get_calypso_node_offset(
  615. bit_representation, contract_key, IntercodeContractStructure);
  616. int start = positionOffset,
  617. end = positionOffset +
  618. get_calypso_node_size(
  619. contract_key, IntercodeContractStructure) -
  620. 1;
  621. float decimal_value =
  622. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  623. uint64_t start_validity_timestamp =
  624. (decimal_value + (float)epoch) + 3600;
  625. datetime_timestamp_to_datetime(
  626. start_validity_timestamp,
  627. &card->navigo->contracts[i - 1].start_date);
  628. }
  629. // 13.2. ContractValidityEndDate
  630. contract_key = "ContractValidityEndDate";
  631. if(is_calypso_node_present(
  632. bit_representation, contract_key, IntercodeContractStructure)) {
  633. int positionOffset = get_calypso_node_offset(
  634. bit_representation, contract_key, IntercodeContractStructure);
  635. int start = positionOffset,
  636. end = positionOffset +
  637. get_calypso_node_size(
  638. contract_key, IntercodeContractStructure) -
  639. 1;
  640. float decimal_value =
  641. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  642. uint64_t end_validity_timestamp =
  643. (decimal_value + (float)epoch) + 3600;
  644. datetime_timestamp_to_datetime(
  645. end_validity_timestamp, &card->navigo->contracts[i - 1].end_date);
  646. card->navigo->contracts[i - 1].end_date_available = true;
  647. }
  648. // 13.6. ContractValidityZones
  649. contract_key = "ContractValidityZones";
  650. if(is_calypso_node_present(
  651. bit_representation, contract_key, IntercodeContractStructure)) {
  652. int start = get_calypso_node_offset(
  653. bit_representation, contract_key, IntercodeContractStructure);
  654. // binary form is 00011111 for zones 5, 4, 3, 2, 1
  655. for(int j = 0; j < 5; j++) {
  656. card->navigo->contracts[i - 1].zones[j] = bit_slice_to_dec(
  657. bit_representation, start + 3 + j, start + 3 + j);
  658. }
  659. card->navigo->contracts[i - 1].zones_available = true;
  660. }
  661. // 13.7. ContractValidityJourneys -- pas sûr de le mettre lui
  662. // 15.0. ContractValiditySaleDate
  663. contract_key = "ContractValiditySaleDate";
  664. if(is_calypso_node_present(
  665. bit_representation, contract_key, IntercodeContractStructure)) {
  666. int positionOffset = get_calypso_node_offset(
  667. bit_representation, contract_key, IntercodeContractStructure);
  668. int start = positionOffset,
  669. end = positionOffset +
  670. get_calypso_node_size(
  671. contract_key, IntercodeContractStructure) -
  672. 1;
  673. float decimal_value =
  674. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  675. uint64_t sale_timestamp = (decimal_value + (float)epoch) + 3600;
  676. datetime_timestamp_to_datetime(
  677. sale_timestamp, &card->navigo->contracts[i - 1].sale_date);
  678. }
  679. // 15.2. ContractValiditySaleAgent - FIX NEEDED
  680. contract_key = "ContractValiditySaleAgent";
  681. /* if(is_calypso_node_present(
  682. bit_representation, contract_key, NavigoContractStructure)) { */
  683. int positionOffset = get_calypso_node_offset(
  684. bit_representation, contract_key, IntercodeContractStructure);
  685. int start = positionOffset,
  686. end = positionOffset +
  687. get_calypso_node_size(contract_key, IntercodeContractStructure) -
  688. 1;
  689. card->navigo->contracts[i - 1].sale_agent =
  690. bit_slice_to_dec(bit_representation, start, end);
  691. // }
  692. // 15.3. ContractValiditySaleDevice
  693. contract_key = "ContractValiditySaleDevice";
  694. if(is_calypso_node_present(
  695. bit_representation, contract_key, IntercodeContractStructure)) {
  696. int positionOffset = get_calypso_node_offset(
  697. bit_representation, contract_key, IntercodeContractStructure);
  698. int start = positionOffset,
  699. end = positionOffset +
  700. get_calypso_node_size(
  701. contract_key, IntercodeContractStructure) -
  702. 1;
  703. card->navigo->contracts[i - 1].sale_device =
  704. bit_slice_to_dec(bit_representation, start, end);
  705. }
  706. // 16. ContractStatus -- 0x1 ou 0xff
  707. contract_key = "ContractStatus";
  708. if(is_calypso_node_present(
  709. bit_representation, contract_key, IntercodeContractStructure)) {
  710. int positionOffset = get_calypso_node_offset(
  711. bit_representation, contract_key, IntercodeContractStructure);
  712. int start = positionOffset,
  713. end = positionOffset +
  714. get_calypso_node_size(
  715. contract_key, IntercodeContractStructure) -
  716. 1;
  717. card->navigo->contracts[i - 1].status =
  718. bit_slice_to_dec(bit_representation, start, end);
  719. }
  720. // 18. ContractAuthenticator
  721. contract_key = "ContractAuthenticator";
  722. if(is_calypso_node_present(
  723. bit_representation, contract_key, IntercodeContractStructure)) {
  724. int positionOffset = get_calypso_node_offset(
  725. bit_representation, contract_key, IntercodeContractStructure);
  726. int start = positionOffset,
  727. end = positionOffset +
  728. get_calypso_node_size(
  729. contract_key, IntercodeContractStructure) -
  730. 1;
  731. card->navigo->contracts[i - 1].authenticator =
  732. bit_slice_to_dec(bit_representation, start, end);
  733. }
  734. }
  735. // Free the calypso structure
  736. free_calypso_structure(IntercodeContractStructure);
  737. // Select app for counters (remaining tickets on Navigo Easy)
  738. error = select_new_app(
  739. 0x20, 0x69, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  740. if(error != 0) {
  741. break;
  742. }
  743. // Check the response after selecting app
  744. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  745. break;
  746. }
  747. // read file 1
  748. error =
  749. read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  750. if(error != 0) {
  751. break;
  752. }
  753. // Check the response after reading the file
  754. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  755. break;
  756. }
  757. char counter_bit_representation[response_length * 8 + 1];
  758. counter_bit_representation[0] = '\0';
  759. for(size_t i = 0; i < response_length; i++) {
  760. char bits[9];
  761. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  762. byte_to_binary(byte, bits);
  763. strlcat(
  764. counter_bit_representation, bits, sizeof(counter_bit_representation));
  765. }
  766. // FURI_LOG_I(TAG, "Counter bit_representation: %s", counter_bit_representation);
  767. // Ticket counts (contracts 1-4)
  768. for(int i = 0; i < 4; i++) {
  769. start = 0;
  770. end = 5;
  771. card->navigo->contracts[i].counter.count = bit_slice_to_dec(
  772. counter_bit_representation, 24 * i + start, 24 * i + end);
  773. start = 6;
  774. end = 23;
  775. card->navigo->contracts[i].counter.relative_first_stamp_15mn =
  776. bit_slice_to_dec(
  777. counter_bit_representation, 24 * i + start, 24 * i + end);
  778. }
  779. // Select app for events
  780. error = select_new_app(
  781. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  782. if(error != 0) {
  783. break;
  784. }
  785. // Check the response after selecting app
  786. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  787. break;
  788. }
  789. // Load the calypso structure for events
  790. CalypsoApp* IntercodeEventStructure = get_intercode_structure_event();
  791. if(!IntercodeEventStructure) {
  792. FURI_LOG_E(TAG, "Failed to load Intercode Event structure");
  793. break;
  794. }
  795. // furi_string_cat_printf(parsed_data, "\e#Events :\n");
  796. // Now send the read command for events
  797. for(size_t i = 1; i < 4; i++) {
  798. error = read_new_file(
  799. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  800. if(error != 0) {
  801. break;
  802. }
  803. // Check the response after reading the file
  804. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  805. break;
  806. }
  807. char event_bit_representation[response_length * 8 + 1];
  808. event_bit_representation[0] = '\0';
  809. for(size_t i = 0; i < response_length; i++) {
  810. char bits[9];
  811. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  812. byte_to_binary(byte, bits);
  813. strlcat(
  814. event_bit_representation, bits, sizeof(event_bit_representation));
  815. }
  816. // 2. EventCode
  817. const char* event_key = "EventCode";
  818. if(is_calypso_node_present(
  819. event_bit_representation, event_key, IntercodeEventStructure)) {
  820. int positionOffset = get_calypso_node_offset(
  821. event_bit_representation, event_key, IntercodeEventStructure);
  822. int start = positionOffset,
  823. end = positionOffset +
  824. get_calypso_node_size(event_key, IntercodeEventStructure) -
  825. 1;
  826. int decimal_value =
  827. bit_slice_to_dec(event_bit_representation, start, end);
  828. card->navigo->events[i - 1].transport_type = decimal_value >> 4;
  829. card->navigo->events[i - 1].transition = decimal_value & 15;
  830. }
  831. // 4. EventServiceProvider
  832. event_key = "EventServiceProvider";
  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. start = positionOffset,
  838. end = positionOffset +
  839. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  840. card->navigo->events[i - 1].service_provider =
  841. bit_slice_to_dec(event_bit_representation, start, end);
  842. }
  843. // 8. EventLocationId
  844. event_key = "EventLocationId";
  845. if(is_calypso_node_present(
  846. event_bit_representation, event_key, IntercodeEventStructure)) {
  847. int positionOffset = get_calypso_node_offset(
  848. event_bit_representation, event_key, IntercodeEventStructure);
  849. start = positionOffset,
  850. end = positionOffset +
  851. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  852. int decimal_value =
  853. bit_slice_to_dec(event_bit_representation, start, end);
  854. card->navigo->events[i - 1].station_group_id = decimal_value >> 9;
  855. card->navigo->events[i - 1].station_id = (decimal_value >> 4) & 31;
  856. card->navigo->events[i - 1].station_sub_id = decimal_value & 15;
  857. }
  858. // 9. EventLocationGate
  859. event_key = "EventLocationGate";
  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. card->navigo->events[i - 1].location_gate =
  868. bit_slice_to_dec(event_bit_representation, start, end);
  869. card->navigo->events[i - 1].location_gate_available = true;
  870. }
  871. // 10. EventDevice
  872. event_key = "EventDevice";
  873. if(is_calypso_node_present(
  874. event_bit_representation, event_key, IntercodeEventStructure)) {
  875. int positionOffset = get_calypso_node_offset(
  876. event_bit_representation, event_key, IntercodeEventStructure);
  877. start = positionOffset,
  878. end = positionOffset +
  879. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  880. int decimal_value =
  881. bit_slice_to_dec(event_bit_representation, start, end);
  882. card->navigo->events[i - 1].device = decimal_value;
  883. int bus_device = decimal_value >> 8;
  884. card->navigo->events[i - 1].door = bus_device / 2 + 1;
  885. card->navigo->events[i - 1].side = bus_device % 2;
  886. card->navigo->events[i - 1].device_available = true;
  887. }
  888. // 11. EventRouteNumber
  889. event_key = "EventRouteNumber";
  890. if(is_calypso_node_present(
  891. event_bit_representation, event_key, IntercodeEventStructure)) {
  892. int positionOffset = get_calypso_node_offset(
  893. event_bit_representation, event_key, IntercodeEventStructure);
  894. start = positionOffset,
  895. end = positionOffset +
  896. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  897. card->navigo->events[i - 1].route_number =
  898. bit_slice_to_dec(event_bit_representation, start, end);
  899. card->navigo->events[i - 1].route_number_available = true;
  900. }
  901. // 13. EventJourneyRun
  902. event_key = "EventJourneyRun";
  903. if(is_calypso_node_present(
  904. event_bit_representation, event_key, IntercodeEventStructure)) {
  905. int positionOffset = get_calypso_node_offset(
  906. event_bit_representation, event_key, IntercodeEventStructure);
  907. start = positionOffset,
  908. end = positionOffset +
  909. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  910. card->navigo->events[i - 1].mission =
  911. bit_slice_to_dec(event_bit_representation, start, end);
  912. card->navigo->events[i - 1].mission_available = true;
  913. }
  914. // 14. EventVehicleId
  915. event_key = "EventVehicleId";
  916. if(is_calypso_node_present(
  917. event_bit_representation, event_key, IntercodeEventStructure)) {
  918. int positionOffset = get_calypso_node_offset(
  919. event_bit_representation, event_key, IntercodeEventStructure);
  920. start = positionOffset,
  921. end = positionOffset +
  922. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  923. card->navigo->events[i - 1].vehicle_id =
  924. bit_slice_to_dec(event_bit_representation, start, end);
  925. card->navigo->events[i - 1].vehicle_id_available = true;
  926. }
  927. // 25. EventContractPointer
  928. event_key = "EventContractPointer";
  929. if(is_calypso_node_present(
  930. event_bit_representation, event_key, IntercodeEventStructure)) {
  931. int positionOffset = get_calypso_node_offset(
  932. event_bit_representation, event_key, IntercodeEventStructure);
  933. start = positionOffset,
  934. end = positionOffset +
  935. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  936. card->navigo->events[i - 1].used_contract =
  937. bit_slice_to_dec(event_bit_representation, start, end);
  938. card->navigo->events[i - 1].used_contract_available = true;
  939. if(card->navigo->events[i - 1].used_contract > 0) {
  940. card->events_count++;
  941. }
  942. }
  943. // EventDateStamp
  944. event_key = "EventDateStamp";
  945. int positionOffset = get_calypso_node_offset(
  946. event_bit_representation, event_key, IntercodeEventStructure);
  947. start = positionOffset,
  948. end = positionOffset +
  949. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  950. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  951. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  952. datetime_timestamp_to_datetime(
  953. date_timestamp, &card->navigo->events[i - 1].date);
  954. // EventTimeStamp
  955. event_key = "EventTimeStamp";
  956. positionOffset = get_calypso_node_offset(
  957. event_bit_representation, event_key, IntercodeEventStructure);
  958. start = positionOffset,
  959. end = positionOffset +
  960. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  961. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  962. card->navigo->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  963. card->navigo->events[i - 1].date.minute =
  964. ((decimal_value * 60) % 3600) / 60;
  965. card->navigo->events[i - 1].date.second =
  966. ((decimal_value * 60) % 3600) % 60;
  967. }
  968. // Select app for special events
  969. error = select_new_app(
  970. 0x20, 0x40, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  971. if(error != 0) {
  972. break;
  973. }
  974. // Check the response after selecting app
  975. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  976. break;
  977. }
  978. // Now send the read command for special events
  979. for(size_t i = 1; i < 4; i++) {
  980. error = read_new_file(
  981. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  982. if(error != 0) {
  983. break;
  984. }
  985. // Check the response after reading the file
  986. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  987. break;
  988. }
  989. char event_bit_representation[response_length * 8 + 1];
  990. event_bit_representation[0] = '\0';
  991. for(size_t i = 0; i < response_length; i++) {
  992. char bits[9];
  993. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  994. byte_to_binary(byte, bits);
  995. strlcat(
  996. event_bit_representation, bits, sizeof(event_bit_representation));
  997. }
  998. if(bit_slice_to_dec(
  999. event_bit_representation,
  1000. 0,
  1001. IntercodeEventStructure->container->elements[0].bitmap->size - 1) ==
  1002. 0) {
  1003. break;
  1004. } else {
  1005. card->special_events_count++;
  1006. }
  1007. // 2. EventCode
  1008. const char* event_key = "EventCode";
  1009. if(is_calypso_node_present(
  1010. event_bit_representation, event_key, IntercodeEventStructure)) {
  1011. int positionOffset = get_calypso_node_offset(
  1012. event_bit_representation, event_key, IntercodeEventStructure);
  1013. int start = positionOffset,
  1014. end = positionOffset +
  1015. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1016. 1;
  1017. int decimal_value =
  1018. bit_slice_to_dec(event_bit_representation, start, end);
  1019. card->navigo->special_events[i - 1].transport_type = decimal_value >>
  1020. 4;
  1021. card->navigo->special_events[i - 1].transition = decimal_value & 15;
  1022. }
  1023. // 3. EventResult
  1024. event_key = "EventResult";
  1025. if(is_calypso_node_present(
  1026. event_bit_representation, event_key, IntercodeEventStructure)) {
  1027. int positionOffset = get_calypso_node_offset(
  1028. event_bit_representation, event_key, IntercodeEventStructure);
  1029. int start = positionOffset,
  1030. end = positionOffset +
  1031. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1032. 1;
  1033. card->navigo->special_events[i - 1].result =
  1034. bit_slice_to_dec(event_bit_representation, start, end);
  1035. }
  1036. // 4. EventServiceProvider
  1037. event_key = "EventServiceProvider";
  1038. if(is_calypso_node_present(
  1039. event_bit_representation, event_key, IntercodeEventStructure)) {
  1040. int positionOffset = get_calypso_node_offset(
  1041. event_bit_representation, event_key, IntercodeEventStructure);
  1042. int start = positionOffset,
  1043. end = positionOffset +
  1044. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1045. 1;
  1046. card->navigo->special_events[i - 1].service_provider =
  1047. bit_slice_to_dec(event_bit_representation, start, end);
  1048. }
  1049. // 8. EventLocationId
  1050. event_key = "EventLocationId";
  1051. if(is_calypso_node_present(
  1052. event_bit_representation, event_key, IntercodeEventStructure)) {
  1053. int positionOffset = get_calypso_node_offset(
  1054. event_bit_representation, event_key, IntercodeEventStructure);
  1055. int start = positionOffset,
  1056. end = positionOffset +
  1057. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1058. 1;
  1059. int decimal_value =
  1060. bit_slice_to_dec(event_bit_representation, start, end);
  1061. card->navigo->special_events[i - 1].station_group_id = decimal_value >>
  1062. 9;
  1063. card->navigo->special_events[i - 1].station_id = (decimal_value >> 4) &
  1064. 31;
  1065. card->navigo->special_events[i - 1].station_sub_id = decimal_value &
  1066. 15;
  1067. }
  1068. // 10. EventDevice
  1069. event_key = "EventDevice";
  1070. if(is_calypso_node_present(
  1071. event_bit_representation, event_key, IntercodeEventStructure)) {
  1072. int positionOffset = get_calypso_node_offset(
  1073. event_bit_representation, event_key, IntercodeEventStructure);
  1074. int start = positionOffset,
  1075. end = positionOffset +
  1076. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1077. 1;
  1078. int decimal_value =
  1079. bit_slice_to_dec(event_bit_representation, start, end);
  1080. card->navigo->special_events[i - 1].device = decimal_value;
  1081. }
  1082. // 11. EventRouteNumber
  1083. event_key = "EventRouteNumber";
  1084. if(is_calypso_node_present(
  1085. event_bit_representation, event_key, IntercodeEventStructure)) {
  1086. int positionOffset = get_calypso_node_offset(
  1087. event_bit_representation, event_key, IntercodeEventStructure);
  1088. int start = positionOffset,
  1089. end = positionOffset +
  1090. get_calypso_node_size(event_key, IntercodeEventStructure) -
  1091. 1;
  1092. card->navigo->special_events[i - 1].route_number =
  1093. bit_slice_to_dec(event_bit_representation, start, end);
  1094. card->navigo->special_events[i - 1].route_number_available = true;
  1095. }
  1096. // EventDateStamp
  1097. event_key = "EventDateStamp";
  1098. int positionOffset = get_calypso_node_offset(
  1099. event_bit_representation, event_key, IntercodeEventStructure);
  1100. int start = positionOffset,
  1101. end = positionOffset +
  1102. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  1103. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1104. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  1105. datetime_timestamp_to_datetime(
  1106. date_timestamp, &card->navigo->special_events[i - 1].date);
  1107. // EventTimeStamp
  1108. event_key = "EventTimeStamp";
  1109. positionOffset = get_calypso_node_offset(
  1110. event_bit_representation, event_key, IntercodeEventStructure);
  1111. start = positionOffset,
  1112. end = positionOffset +
  1113. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  1114. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1115. card->navigo->special_events[i - 1].date.hour =
  1116. (decimal_value * 60) / 3600;
  1117. card->navigo->special_events[i - 1].date.minute =
  1118. ((decimal_value * 60) % 3600) / 60;
  1119. card->navigo->special_events[i - 1].date.second =
  1120. ((decimal_value * 60) % 3600) % 60;
  1121. }
  1122. // Free the calypso structure
  1123. free_calypso_structure(IntercodeEventStructure);
  1124. break;
  1125. }
  1126. case CALYPSO_CARD_OPUS: {
  1127. card->opus = malloc(sizeof(OpusCardData));
  1128. card->opus->environment.country_num = country_num;
  1129. card->opus->environment.network_num = network_num;
  1130. CalypsoApp* OpusEnvHolderStructure = get_opus_env_holder_structure();
  1131. // EnvApplicationVersionNumber
  1132. const char* env_key = "EnvApplicationVersionNumber";
  1133. int positionOffset = get_calypso_node_offset(
  1134. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1135. int start = positionOffset,
  1136. end = positionOffset +
  1137. get_calypso_node_size(env_key, OpusEnvHolderStructure) - 1;
  1138. card->opus->environment.app_version =
  1139. bit_slice_to_dec(environment_bit_representation, start, end);
  1140. // EnvApplicationValidityEndDate
  1141. env_key = "EnvApplicationValidityEndDate";
  1142. positionOffset = get_calypso_node_offset(
  1143. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1144. start = positionOffset,
  1145. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1146. 1;
  1147. float decimal_value =
  1148. bit_slice_to_dec(environment_bit_representation, start, end);
  1149. uint64_t end_validity_timestamp =
  1150. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  1151. datetime_timestamp_to_datetime(
  1152. end_validity_timestamp, &card->opus->environment.end_dt);
  1153. // HolderDataCardStatus
  1154. env_key = "HolderDataCardStatus";
  1155. positionOffset = get_calypso_node_offset(
  1156. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1157. start = positionOffset,
  1158. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1159. 1;
  1160. card->opus->holder.card_status =
  1161. bit_slice_to_dec(environment_bit_representation, start, end);
  1162. // HolderDataCommercialID
  1163. env_key = "HolderDataCommercialID";
  1164. positionOffset = get_calypso_node_offset(
  1165. environment_bit_representation, env_key, OpusEnvHolderStructure);
  1166. start = positionOffset,
  1167. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  1168. 1;
  1169. card->opus->holder.commercial_id =
  1170. bit_slice_to_dec(environment_bit_representation, start, end);
  1171. // Free the calypso structure
  1172. free_calypso_structure(OpusEnvHolderStructure);
  1173. // Select app for contracts
  1174. error = select_new_app(
  1175. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1176. if(error != 0) {
  1177. FURI_LOG_E(TAG, "Failed to select app for contracts");
  1178. break;
  1179. }
  1180. // Check the response after selecting app
  1181. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1182. FURI_LOG_E(
  1183. TAG, "Failed to check response after selecting app for contracts");
  1184. break;
  1185. }
  1186. // Prepare calypso structure
  1187. CalypsoApp* OpusContractStructure = get_opus_contract_structure();
  1188. if(!OpusContractStructure) {
  1189. FURI_LOG_E(TAG, "Failed to load Opus Contract structure");
  1190. break;
  1191. }
  1192. // Now send the read command for contracts
  1193. for(size_t i = 1; i < 5; i++) {
  1194. error = read_new_file(
  1195. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1196. if(error != 0) {
  1197. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  1198. break;
  1199. }
  1200. // Check the response after reading the file
  1201. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1202. FURI_LOG_E(
  1203. TAG, "Failed to check response after reading contract %d", i);
  1204. break;
  1205. }
  1206. char bit_representation[response_length * 8 + 1];
  1207. bit_representation[0] = '\0';
  1208. for(size_t i = 0; i < response_length; i++) {
  1209. char bits[9];
  1210. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1211. byte_to_binary(byte, bits);
  1212. strlcat(bit_representation, bits, sizeof(bit_representation));
  1213. }
  1214. bit_representation[response_length * 8] = '\0';
  1215. if(bit_slice_to_dec(
  1216. bit_representation,
  1217. 0,
  1218. OpusContractStructure->container->elements[1].bitmap->size - 1) ==
  1219. 0) {
  1220. break;
  1221. }
  1222. card->opus->contracts[i - 1].present = 1;
  1223. card->contracts_count++;
  1224. // ContractProvider
  1225. const char* contract_key = "ContractProvider";
  1226. if(is_calypso_node_present(
  1227. bit_representation, contract_key, OpusContractStructure)) {
  1228. int positionOffset = get_calypso_node_offset(
  1229. bit_representation, contract_key, OpusContractStructure);
  1230. int start = positionOffset,
  1231. end = positionOffset +
  1232. get_calypso_node_size(contract_key, OpusContractStructure) -
  1233. 1;
  1234. card->opus->contracts[i - 1].provider =
  1235. bit_slice_to_dec(bit_representation, start, end);
  1236. }
  1237. // ContractTariff
  1238. contract_key = "ContractTariff";
  1239. if(is_calypso_node_present(
  1240. bit_representation, contract_key, OpusContractStructure)) {
  1241. int positionOffset = get_calypso_node_offset(
  1242. bit_representation, contract_key, OpusContractStructure);
  1243. int start = positionOffset,
  1244. end = positionOffset +
  1245. get_calypso_node_size(contract_key, OpusContractStructure) -
  1246. 1;
  1247. card->opus->contracts[i - 1].tariff =
  1248. bit_slice_to_dec(bit_representation, start, end);
  1249. }
  1250. // ContractStartDate
  1251. contract_key = "ContractStartDate";
  1252. if(is_calypso_node_present(
  1253. bit_representation, contract_key, OpusContractStructure)) {
  1254. int positionOffset = get_calypso_node_offset(
  1255. bit_representation, contract_key, OpusContractStructure);
  1256. int start = positionOffset,
  1257. end = positionOffset +
  1258. get_calypso_node_size(contract_key, OpusContractStructure) -
  1259. 1;
  1260. float decimal_value =
  1261. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1262. uint64_t start_validity_timestamp =
  1263. (decimal_value + (float)epoch) + 3600;
  1264. datetime_timestamp_to_datetime(
  1265. start_validity_timestamp,
  1266. &card->opus->contracts[i - 1].start_date);
  1267. }
  1268. // ContractEndDate
  1269. contract_key = "ContractEndDate";
  1270. if(is_calypso_node_present(
  1271. bit_representation, contract_key, OpusContractStructure)) {
  1272. int positionOffset = get_calypso_node_offset(
  1273. bit_representation, contract_key, OpusContractStructure);
  1274. int start = positionOffset,
  1275. end = positionOffset +
  1276. get_calypso_node_size(contract_key, OpusContractStructure) -
  1277. 1;
  1278. float decimal_value =
  1279. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1280. uint64_t end_validity_timestamp =
  1281. (decimal_value + (float)epoch) + 3600;
  1282. datetime_timestamp_to_datetime(
  1283. end_validity_timestamp, &card->opus->contracts[i - 1].end_date);
  1284. }
  1285. // ContractStatus
  1286. contract_key = "ContractStatus";
  1287. if(is_calypso_node_present(
  1288. bit_representation, contract_key, OpusContractStructure)) {
  1289. int positionOffset = get_calypso_node_offset(
  1290. bit_representation, contract_key, OpusContractStructure);
  1291. int start = positionOffset,
  1292. end = positionOffset +
  1293. get_calypso_node_size(contract_key, OpusContractStructure) -
  1294. 1;
  1295. card->opus->contracts[i - 1].status =
  1296. bit_slice_to_dec(bit_representation, start, end);
  1297. }
  1298. // ContractSaleDate + ContractSaleTime
  1299. contract_key = "ContractSaleDate";
  1300. int positionOffset = get_calypso_node_offset(
  1301. bit_representation, contract_key, OpusContractStructure);
  1302. int start = positionOffset,
  1303. end = positionOffset +
  1304. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1305. uint64_t sale_date_timestamp =
  1306. (bit_slice_to_dec(bit_representation, start, end) + (float)epoch) +
  1307. 3600;
  1308. datetime_timestamp_to_datetime(
  1309. sale_date_timestamp, &card->opus->contracts[i - 1].sale_date);
  1310. contract_key = "ContractSaleTime";
  1311. positionOffset = get_calypso_node_offset(
  1312. bit_representation, contract_key, OpusContractStructure);
  1313. start = positionOffset,
  1314. end = positionOffset +
  1315. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1316. int decimal_value = bit_slice_to_dec(bit_representation, start, end);
  1317. card->opus->contracts[i - 1].sale_date.hour = (decimal_value * 60) / 3600;
  1318. card->opus->contracts[i - 1].sale_date.minute =
  1319. ((decimal_value * 60) % 3600) / 60;
  1320. card->opus->contracts[i - 1].sale_date.second =
  1321. ((decimal_value * 60) % 3600) % 60;
  1322. }
  1323. // Free the calypso structure
  1324. free_calypso_structure(OpusContractStructure);
  1325. // Select app for events
  1326. error = select_new_app(
  1327. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1328. if(error != 0) {
  1329. break;
  1330. }
  1331. // Check the response after selecting app
  1332. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1333. break;
  1334. }
  1335. // Load the calypso structure for events
  1336. CalypsoApp* OpusEventStructure = get_opus_event_structure();
  1337. if(!OpusEventStructure) {
  1338. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  1339. break;
  1340. }
  1341. // Now send the read command for events
  1342. for(size_t i = 1; i < 4; i++) {
  1343. error = read_new_file(
  1344. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1345. if(error != 0) {
  1346. break;
  1347. }
  1348. // Check the response after reading the file
  1349. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1350. break;
  1351. }
  1352. char event_bit_representation[response_length * 8 + 1];
  1353. event_bit_representation[0] = '\0';
  1354. for(size_t i = 0; i < response_length; i++) {
  1355. char bits[9];
  1356. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1357. byte_to_binary(byte, bits);
  1358. strlcat(
  1359. event_bit_representation, bits, sizeof(event_bit_representation));
  1360. }
  1361. // EventServiceProvider
  1362. const char* event_key = "EventServiceProvider";
  1363. if(is_calypso_node_present(
  1364. event_bit_representation, event_key, OpusEventStructure)) {
  1365. int positionOffset = get_calypso_node_offset(
  1366. event_bit_representation, event_key, OpusEventStructure);
  1367. int start = positionOffset,
  1368. end = positionOffset +
  1369. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1370. card->opus->events[i - 1].service_provider =
  1371. bit_slice_to_dec(event_bit_representation, start, end);
  1372. }
  1373. // EventRouteNumber
  1374. event_key = "EventRouteNumber";
  1375. if(is_calypso_node_present(
  1376. event_bit_representation, event_key, OpusEventStructure)) {
  1377. int positionOffset = get_calypso_node_offset(
  1378. event_bit_representation, event_key, OpusEventStructure);
  1379. int start = positionOffset,
  1380. end = positionOffset +
  1381. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1382. card->opus->events[i - 1].route_number =
  1383. bit_slice_to_dec(event_bit_representation, start, end);
  1384. card->opus->events[i - 1].route_number_available = true;
  1385. }
  1386. // EventContractPointer
  1387. event_key = "EventContractPointer";
  1388. if(is_calypso_node_present(
  1389. event_bit_representation, event_key, OpusEventStructure)) {
  1390. int positionOffset = get_calypso_node_offset(
  1391. event_bit_representation, event_key, OpusEventStructure);
  1392. int start = positionOffset,
  1393. end = positionOffset +
  1394. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1395. card->opus->events[i - 1].used_contract =
  1396. bit_slice_to_dec(event_bit_representation, start, end);
  1397. card->opus->events[i - 1].used_contract_available = true;
  1398. if(card->opus->events[i - 1].used_contract > 0) {
  1399. card->events_count++;
  1400. }
  1401. }
  1402. // EventDateStamp
  1403. event_key = "EventDateStamp";
  1404. int positionOffset = get_calypso_node_offset(
  1405. event_bit_representation, event_key, OpusEventStructure);
  1406. int start = positionOffset,
  1407. end = positionOffset +
  1408. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1409. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1410. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  1411. datetime_timestamp_to_datetime(
  1412. date_timestamp, &card->opus->events[i - 1].date);
  1413. // EventTimeStamp
  1414. event_key = "EventTimeStamp";
  1415. positionOffset = get_calypso_node_offset(
  1416. event_bit_representation, event_key, OpusEventStructure);
  1417. start = positionOffset,
  1418. end = positionOffset +
  1419. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1420. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1421. card->opus->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  1422. card->opus->events[i - 1].date.minute = ((decimal_value * 60) % 3600) / 60;
  1423. card->opus->events[i - 1].date.second = ((decimal_value * 60) % 3600) % 60;
  1424. }
  1425. // Free the calypso structure
  1426. free_calypso_structure(OpusEventStructure);
  1427. break;
  1428. }
  1429. case CALYPSO_CARD_UNKNOWN: {
  1430. start = 3;
  1431. end = 6;
  1432. country_num =
  1433. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  1434. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  1435. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  1436. start = 15;
  1437. end = 18;
  1438. network_num =
  1439. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  1440. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  1441. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  1442. card->card_type = guess_card_type(country_num, network_num);
  1443. if(card->card_type == CALYPSO_CARD_RAVKAV) {
  1444. card->ravkav = malloc(sizeof(RavKavCardData));
  1445. error = select_new_app(
  1446. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1447. if(error != 0) {
  1448. FURI_LOG_E(TAG, "Failed to select app for contracts");
  1449. break;
  1450. }
  1451. // Check the response after selecting app
  1452. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1453. FURI_LOG_E(
  1454. TAG, "Failed to check response after selecting app for contracts");
  1455. break;
  1456. }
  1457. // Prepare calypso structure
  1458. CalypsoApp* RavKavContractStructure = get_ravkav_contract_structure();
  1459. if(!RavKavContractStructure) {
  1460. FURI_LOG_E(TAG, "Failed to load RavKav Contract structure");
  1461. break;
  1462. }
  1463. // Now send the read command for contracts
  1464. for(size_t i = 1; i < 2; i++) {
  1465. error = read_new_file(
  1466. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1467. if(error != 0) {
  1468. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  1469. break;
  1470. }
  1471. // Check the response after reading the file
  1472. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1473. FURI_LOG_E(
  1474. TAG, "Failed to check response after reading contract %d", i);
  1475. break;
  1476. }
  1477. char bit_representation[response_length * 8 + 1];
  1478. bit_representation[0] = '\0';
  1479. for(size_t i = 0; i < response_length; i++) {
  1480. char bits[9];
  1481. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1482. byte_to_binary(byte, bits);
  1483. strlcat(bit_representation, bits, sizeof(bit_representation));
  1484. }
  1485. bit_representation[response_length * 8] = '\0';
  1486. card->ravkav->contracts[i - 1].present = 1;
  1487. card->contracts_count++;
  1488. // ContractVersion
  1489. const char* contract_key = "ContractVersion";
  1490. if(is_calypso_node_present(
  1491. bit_representation, contract_key, RavKavContractStructure)) {
  1492. int positionOffset = get_calypso_node_offset(
  1493. bit_representation, contract_key, RavKavContractStructure);
  1494. int start = positionOffset,
  1495. end = positionOffset +
  1496. get_calypso_node_size(
  1497. contract_key, RavKavContractStructure) -
  1498. 1;
  1499. card->ravkav->contracts[i - 1].version =
  1500. bit_slice_to_dec(bit_representation, start, end);
  1501. }
  1502. // ContractStartDate
  1503. contract_key = "ContractStartDate";
  1504. if(is_calypso_node_present(
  1505. bit_representation, contract_key, RavKavContractStructure)) {
  1506. int positionOffset = get_calypso_node_offset(
  1507. bit_representation, contract_key, RavKavContractStructure);
  1508. int start = positionOffset,
  1509. end = positionOffset +
  1510. get_calypso_node_size(
  1511. contract_key, RavKavContractStructure) -
  1512. 1;
  1513. int decimal_value =
  1514. bit_slice_to_dec(bit_representation, start, end);
  1515. uint32_t invertedDays = decimal_value ^ 0x3FFF;
  1516. int start_validity_timestamp =
  1517. (invertedDays * 3600 * 24) + epoch + 3600;
  1518. datetime_timestamp_to_datetime(
  1519. start_validity_timestamp,
  1520. &card->ravkav->contracts[i - 1].start_date);
  1521. }
  1522. // ContractProvider
  1523. contract_key = "ContractProvider";
  1524. if(is_calypso_node_present(
  1525. bit_representation, contract_key, RavKavContractStructure)) {
  1526. int positionOffset = get_calypso_node_offset(
  1527. bit_representation, contract_key, RavKavContractStructure);
  1528. int start = positionOffset,
  1529. end = positionOffset +
  1530. get_calypso_node_size(
  1531. contract_key, RavKavContractStructure) -
  1532. 1;
  1533. card->ravkav->contracts[i - 1].provider =
  1534. bit_slice_to_dec(bit_representation, start, end);
  1535. FURI_LOG_I(
  1536. TAG,
  1537. "issuer number: %d",
  1538. card->ravkav->contracts[i - 1].provider);
  1539. }
  1540. // ContractTariff
  1541. contract_key = "ContractTariff";
  1542. if(is_calypso_node_present(
  1543. bit_representation, contract_key, RavKavContractStructure)) {
  1544. int positionOffset = get_calypso_node_offset(
  1545. bit_representation, contract_key, RavKavContractStructure);
  1546. int start = positionOffset,
  1547. end = positionOffset +
  1548. get_calypso_node_size(
  1549. contract_key, RavKavContractStructure) -
  1550. 1;
  1551. card->ravkav->contracts[i - 1].tariff =
  1552. bit_slice_to_dec(bit_representation, start, end);
  1553. }
  1554. // ContractSaleDate
  1555. contract_key = "ContractSaleDate";
  1556. int positionOffset = get_calypso_node_offset(
  1557. bit_representation, contract_key, RavKavContractStructure);
  1558. int start = positionOffset,
  1559. end =
  1560. positionOffset +
  1561. get_calypso_node_size(contract_key, RavKavContractStructure) -
  1562. 1;
  1563. uint64_t sale_date_timestamp =
  1564. (bit_slice_to_dec(bit_representation, start, end) * 3600 * 24) +
  1565. (float)epoch + 3600;
  1566. datetime_timestamp_to_datetime(
  1567. sale_date_timestamp, &card->ravkav->contracts[i - 1].sale_date);
  1568. // ContractSaleDevice
  1569. contract_key = "ContractSaleDevice";
  1570. if(is_calypso_node_present(
  1571. bit_representation, contract_key, RavKavContractStructure)) {
  1572. int positionOffset = get_calypso_node_offset(
  1573. bit_representation, contract_key, RavKavContractStructure);
  1574. int start = positionOffset,
  1575. end = positionOffset +
  1576. get_calypso_node_size(
  1577. contract_key, RavKavContractStructure) -
  1578. 1;
  1579. card->ravkav->contracts[i - 1].sale_device =
  1580. bit_slice_to_dec(bit_representation, start, end);
  1581. }
  1582. // ContractSaleNumber
  1583. contract_key = "ContractSaleNumber";
  1584. if(is_calypso_node_present(
  1585. bit_representation, contract_key, RavKavContractStructure)) {
  1586. int positionOffset = get_calypso_node_offset(
  1587. bit_representation, contract_key, RavKavContractStructure);
  1588. int start = positionOffset,
  1589. end = positionOffset +
  1590. get_calypso_node_size(
  1591. contract_key, RavKavContractStructure) -
  1592. 1;
  1593. card->ravkav->contracts[i - 1].sale_number =
  1594. bit_slice_to_dec(bit_representation, start, end);
  1595. }
  1596. // ContractInterchange
  1597. contract_key = "ContractInterchange";
  1598. if(is_calypso_node_present(
  1599. bit_representation, contract_key, RavKavContractStructure)) {
  1600. int positionOffset = get_calypso_node_offset(
  1601. bit_representation, contract_key, RavKavContractStructure);
  1602. int start = positionOffset,
  1603. end = positionOffset +
  1604. get_calypso_node_size(
  1605. contract_key, RavKavContractStructure) -
  1606. 1;
  1607. card->ravkav->contracts[i - 1].interchange =
  1608. bit_slice_to_dec(bit_representation, start, end);
  1609. }
  1610. // ContractInterchange
  1611. contract_key = "ContractRestrictCode";
  1612. if(is_calypso_node_present(
  1613. bit_representation, contract_key, RavKavContractStructure)) {
  1614. int positionOffset = get_calypso_node_offset(
  1615. bit_representation, contract_key, RavKavContractStructure);
  1616. int start = positionOffset,
  1617. end = positionOffset +
  1618. get_calypso_node_size(
  1619. contract_key, RavKavContractStructure) -
  1620. 1;
  1621. card->ravkav->contracts[i - 1].restrict_code_available = true;
  1622. card->ravkav->contracts[i - 1].restrict_code =
  1623. bit_slice_to_dec(bit_representation, start, end);
  1624. }
  1625. // ContractRestrictDuration
  1626. contract_key = "ContractRestrictDuration";
  1627. if(is_calypso_node_present(
  1628. bit_representation, contract_key, RavKavContractStructure)) {
  1629. int positionOffset = get_calypso_node_offset(
  1630. bit_representation, contract_key, RavKavContractStructure);
  1631. int start = positionOffset,
  1632. end = positionOffset +
  1633. get_calypso_node_size(
  1634. contract_key, RavKavContractStructure) -
  1635. 1;
  1636. card->ravkav->contracts[i - 1].restrict_duration_available = true;
  1637. if(card->ravkav->contracts[i - 1].restrict_code == 16) {
  1638. card->ravkav->contracts[i - 1].restrict_duration =
  1639. bit_slice_to_dec(bit_representation, start, end) * 5;
  1640. } else {
  1641. card->ravkav->contracts[i - 1].restrict_duration =
  1642. bit_slice_to_dec(bit_representation, start, end) * 30;
  1643. }
  1644. }
  1645. // ContractEndDate
  1646. contract_key = "ContractEndDate";
  1647. if(is_calypso_node_present(
  1648. bit_representation, contract_key, RavKavContractStructure)) {
  1649. int positionOffset = get_calypso_node_offset(
  1650. bit_representation, contract_key, RavKavContractStructure);
  1651. int start = positionOffset,
  1652. end = positionOffset +
  1653. get_calypso_node_size(
  1654. contract_key, RavKavContractStructure) -
  1655. 1;
  1656. card->ravkav->contracts[i - 1].end_date_available = true;
  1657. int end_date_timestamp =
  1658. (bit_slice_to_dec(bit_representation, start, end) * 3600 *
  1659. 24) +
  1660. epoch + 3600;
  1661. datetime_timestamp_to_datetime(
  1662. end_date_timestamp, &card->ravkav->contracts[i - 1].end_date);
  1663. }
  1664. }
  1665. // Free the calypso structure
  1666. free_calypso_structure(RavKavContractStructure);
  1667. error = select_new_app(
  1668. 0x20, 0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1669. if(error != 0) {
  1670. FURI_LOG_E(TAG, "Failed to select app for environment");
  1671. break;
  1672. }
  1673. // Check the response after selecting app
  1674. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1675. FURI_LOG_E(
  1676. TAG,
  1677. "Failed to check response after selecting app for environment");
  1678. break;
  1679. }
  1680. // Prepare calypso structure
  1681. CalypsoApp* RavKavEnvStructure = get_ravkav_env_holder_structure();
  1682. if(!RavKavEnvStructure) {
  1683. FURI_LOG_E(TAG, "Failed to load RavKav environment structure");
  1684. break;
  1685. }
  1686. // Now send the read command for environment
  1687. error = read_new_file(
  1688. 1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1689. if(error != 0) {
  1690. FURI_LOG_E(TAG, "Failed to read environment");
  1691. break;
  1692. }
  1693. // Check the response after reading the file
  1694. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1695. FURI_LOG_E(TAG, "Failed to check response after reading environment");
  1696. break;
  1697. }
  1698. char env_bit_representation[response_length * 8 + 1];
  1699. env_bit_representation[0] = '\0';
  1700. for(size_t i = 0; i < response_length; i++) {
  1701. char bits[9];
  1702. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1703. byte_to_binary(byte, bits);
  1704. strlcat(env_bit_representation, bits, sizeof(env_bit_representation));
  1705. }
  1706. env_bit_representation[response_length * 8] = '\0';
  1707. // EnvApplicationVersionNumber
  1708. char* env_key = "EnvApplicationVersionNumber";
  1709. if(is_calypso_node_present(
  1710. env_bit_representation, env_key, RavKavEnvStructure)) {
  1711. int positionOffset = get_calypso_node_offset(
  1712. env_bit_representation, env_key, RavKavEnvStructure);
  1713. int start = positionOffset,
  1714. end = positionOffset +
  1715. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1716. card->ravkav->environment.app_num =
  1717. bit_slice_to_dec(env_bit_representation, start, end);
  1718. }
  1719. // EnvApplicationNumber
  1720. env_key = "EnvApplicationNumber";
  1721. if(is_calypso_node_present(
  1722. env_bit_representation, env_key, RavKavEnvStructure)) {
  1723. int positionOffset = get_calypso_node_offset(
  1724. env_bit_representation, env_key, RavKavEnvStructure);
  1725. int start = positionOffset,
  1726. end = positionOffset +
  1727. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1728. card->ravkav->environment.app_num =
  1729. bit_slice_to_dec(env_bit_representation, start, end);
  1730. }
  1731. // EnvDateOfIssue
  1732. env_key = "EnvDateOfIssue";
  1733. if(is_calypso_node_present(
  1734. env_bit_representation, env_key, RavKavEnvStructure)) {
  1735. int positionOffset = get_calypso_node_offset(
  1736. env_bit_representation, env_key, RavKavEnvStructure);
  1737. int start = positionOffset,
  1738. end = positionOffset +
  1739. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1740. uint64_t issue_date_timestamp =
  1741. (bit_slice_to_dec(env_bit_representation, start, end) * 3600 *
  1742. 24) +
  1743. (float)epoch + 3600;
  1744. datetime_timestamp_to_datetime(
  1745. issue_date_timestamp, &card->ravkav->environment.issue_dt);
  1746. }
  1747. // EnvEndValidity
  1748. env_key = "EnvEndValidity";
  1749. if(is_calypso_node_present(
  1750. env_bit_representation, env_key, RavKavEnvStructure)) {
  1751. int positionOffset = get_calypso_node_offset(
  1752. env_bit_representation, env_key, RavKavEnvStructure);
  1753. int start = positionOffset,
  1754. end = positionOffset +
  1755. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1756. uint64_t end_date_timestamp =
  1757. (bit_slice_to_dec(env_bit_representation, start, end) * 3600 *
  1758. 24) +
  1759. (float)epoch + 3600;
  1760. datetime_timestamp_to_datetime(
  1761. end_date_timestamp, &card->ravkav->environment.end_dt);
  1762. }
  1763. // EnvPayMethod
  1764. env_key = "EnvPayMethod";
  1765. if(is_calypso_node_present(
  1766. env_bit_representation, env_key, RavKavEnvStructure)) {
  1767. int positionOffset = get_calypso_node_offset(
  1768. env_bit_representation, env_key, RavKavEnvStructure);
  1769. int start = positionOffset,
  1770. end = positionOffset +
  1771. get_calypso_node_size(env_key, RavKavEnvStructure) - 1;
  1772. card->ravkav->environment.pay_method =
  1773. bit_slice_to_dec(env_bit_representation, start, end);
  1774. }
  1775. free_calypso_structure(RavKavEnvStructure);
  1776. // Select app for events
  1777. error = select_new_app(
  1778. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1779. if(error != 0) {
  1780. break;
  1781. }
  1782. // Check the response after selecting app
  1783. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1784. break;
  1785. }
  1786. // Load the calypso structure for events
  1787. CalypsoApp* RavKavEventStructure = get_ravkav_event_structure();
  1788. if(!RavKavEventStructure) {
  1789. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  1790. break;
  1791. }
  1792. // Now send the read command for events
  1793. for(size_t i = 1; i < 4; i++) {
  1794. error = read_new_file(
  1795. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1796. if(error != 0) {
  1797. break;
  1798. }
  1799. // Check the response after reading the file
  1800. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1801. break;
  1802. }
  1803. char event_bit_representation[response_length * 8 + 1];
  1804. event_bit_representation[0] = '\0';
  1805. for(size_t i = 0; i < response_length; i++) {
  1806. char bits[9];
  1807. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1808. byte_to_binary(byte, bits);
  1809. strlcat(
  1810. event_bit_representation,
  1811. bits,
  1812. sizeof(event_bit_representation));
  1813. }
  1814. FURI_LOG_I(TAG, "event bit repr %s", event_bit_representation);
  1815. // EventVersion
  1816. const char* event_key = "EventVersion";
  1817. if(is_calypso_node_present(
  1818. event_bit_representation, event_key, RavKavEventStructure)) {
  1819. int positionOffset = get_calypso_node_offset(
  1820. event_bit_representation, event_key, RavKavEventStructure);
  1821. int start = positionOffset,
  1822. end = positionOffset +
  1823. get_calypso_node_size(event_key, RavKavEventStructure) -
  1824. 1;
  1825. card->ravkav->events[i - 1].event_version =
  1826. bit_slice_to_dec(event_bit_representation, start, end);
  1827. }
  1828. // EventServiceProvider
  1829. event_key = "EventServiceProvider";
  1830. if(is_calypso_node_present(
  1831. event_bit_representation, event_key, RavKavEventStructure)) {
  1832. int positionOffset = get_calypso_node_offset(
  1833. event_bit_representation, event_key, RavKavEventStructure);
  1834. int start = positionOffset,
  1835. end = positionOffset +
  1836. get_calypso_node_size(event_key, RavKavEventStructure) -
  1837. 1;
  1838. FURI_LOG_I(TAG, "service provider: start: %d, end %d", start, end);
  1839. card->ravkav->events[i - 1].service_provider =
  1840. bit_slice_to_dec(event_bit_representation, start, end);
  1841. }
  1842. // EventContractID
  1843. event_key = "EventContractID";
  1844. if(is_calypso_node_present(
  1845. event_bit_representation, event_key, RavKavEventStructure)) {
  1846. int positionOffset = get_calypso_node_offset(
  1847. event_bit_representation, event_key, RavKavEventStructure);
  1848. int start = positionOffset,
  1849. end = positionOffset +
  1850. get_calypso_node_size(event_key, RavKavEventStructure) -
  1851. 1;
  1852. card->ravkav->events[i - 1].contract_id =
  1853. bit_slice_to_dec(event_bit_representation, start, end);
  1854. FURI_LOG_I(TAG, "2: start: %d, end %d", start, end);
  1855. }
  1856. // EventAreaID
  1857. event_key = "EventAreaID";
  1858. if(is_calypso_node_present(
  1859. event_bit_representation, event_key, RavKavEventStructure)) {
  1860. int positionOffset = get_calypso_node_offset(
  1861. event_bit_representation, event_key, RavKavEventStructure);
  1862. int start = positionOffset,
  1863. end = positionOffset +
  1864. get_calypso_node_size(event_key, RavKavEventStructure) -
  1865. 1;
  1866. card->ravkav->events[i - 1].area_id =
  1867. bit_slice_to_dec(event_bit_representation, start, end);
  1868. FURI_LOG_I(TAG, "3: start: %d, end %d", start, end);
  1869. }
  1870. // EventType
  1871. event_key = "EventType";
  1872. if(is_calypso_node_present(
  1873. event_bit_representation, event_key, RavKavEventStructure)) {
  1874. int positionOffset = get_calypso_node_offset(
  1875. event_bit_representation, event_key, RavKavEventStructure);
  1876. int start = positionOffset,
  1877. end = positionOffset +
  1878. get_calypso_node_size(event_key, RavKavEventStructure) -
  1879. 1;
  1880. card->ravkav->events[i - 1].type =
  1881. bit_slice_to_dec(event_bit_representation, start, end);
  1882. FURI_LOG_I(TAG, "4: start: %d, end %d", start, end);
  1883. }
  1884. // EventRouteNumber
  1885. event_key = "EventExtension";
  1886. if(is_calypso_node_present(
  1887. event_bit_representation, event_key, RavKavEventStructure)) {
  1888. int positionOffset = get_calypso_node_offset(
  1889. event_bit_representation, event_key, RavKavEventStructure);
  1890. int start = positionOffset,
  1891. end = positionOffset +
  1892. get_calypso_node_size(event_key, RavKavEventStructure) -
  1893. 1;
  1894. FURI_LOG_I(TAG, "event extension : start: %d, end %d", start, end);
  1895. FURI_LOG_I(
  1896. TAG,
  1897. "event extension bitmap: %d",
  1898. bit_slice_to_dec(event_bit_representation, start, end));
  1899. }
  1900. // EventTime
  1901. event_key = "EventTime";
  1902. if(is_calypso_node_present(
  1903. event_bit_representation, event_key, RavKavEventStructure)) {
  1904. int positionOffset = get_calypso_node_offset(
  1905. event_bit_representation, event_key, RavKavEventStructure);
  1906. int start = positionOffset,
  1907. end = positionOffset +
  1908. get_calypso_node_size(event_key, RavKavEventStructure) -
  1909. 1;
  1910. uint64_t event_timestamp =
  1911. bit_slice_to_dec(event_bit_representation, start, end) +
  1912. (float)epoch + 3600;
  1913. datetime_timestamp_to_datetime(
  1914. event_timestamp, &card->ravkav->events[i - 1].time);
  1915. FURI_LOG_I(TAG, "5: start: %d, end %d", start, end);
  1916. }
  1917. // EventInterchangeFlag
  1918. event_key = "EventInterchangeFlag";
  1919. if(is_calypso_node_present(
  1920. event_bit_representation, event_key, RavKavEventStructure)) {
  1921. int positionOffset = get_calypso_node_offset(
  1922. event_bit_representation, event_key, RavKavEventStructure);
  1923. int start = positionOffset,
  1924. end = positionOffset +
  1925. get_calypso_node_size(event_key, RavKavEventStructure) -
  1926. 1;
  1927. card->ravkav->events[i - 1].interchange_flag =
  1928. bit_slice_to_dec(event_bit_representation, start, end);
  1929. FURI_LOG_I(TAG, "6: start: %d, end %d", start, end);
  1930. }
  1931. // EventRouteNumber
  1932. event_key = "EventRouteNumber";
  1933. if(is_calypso_node_present(
  1934. event_bit_representation, event_key, RavKavEventStructure)) {
  1935. int positionOffset = get_calypso_node_offset(
  1936. event_bit_representation, event_key, RavKavEventStructure);
  1937. int start = positionOffset,
  1938. end = positionOffset +
  1939. get_calypso_node_size(event_key, RavKavEventStructure) -
  1940. 1;
  1941. card->ravkav->events[i - 1].route_number =
  1942. bit_slice_to_dec(event_bit_representation, start, end);
  1943. card->ravkav->events[i - 1].route_number_available = true;
  1944. FURI_LOG_I(TAG, "7: start: %d, end %d", start, end);
  1945. }
  1946. // EventRouteNumber
  1947. event_key = "EventfareCode";
  1948. if(is_calypso_node_present(
  1949. event_bit_representation, event_key, RavKavEventStructure)) {
  1950. int positionOffset = get_calypso_node_offset(
  1951. event_bit_representation, event_key, RavKavEventStructure);
  1952. int start = positionOffset,
  1953. end = positionOffset +
  1954. get_calypso_node_size(event_key, RavKavEventStructure) -
  1955. 1;
  1956. card->ravkav->events[i - 1].fare_code =
  1957. bit_slice_to_dec(event_bit_representation, start, end);
  1958. card->ravkav->events[i - 1].fare_code = true;
  1959. FURI_LOG_I(TAG, "8: start: %d, end %d", start, end);
  1960. }
  1961. // EventRouteNumber
  1962. event_key = "EventDebitAmount";
  1963. if(is_calypso_node_present(
  1964. event_bit_representation, event_key, RavKavEventStructure)) {
  1965. int positionOffset = get_calypso_node_offset(
  1966. event_bit_representation, event_key, RavKavEventStructure);
  1967. int start = positionOffset,
  1968. end = positionOffset +
  1969. get_calypso_node_size(event_key, RavKavEventStructure) -
  1970. 1;
  1971. card->ravkav->events[i - 1].debit_amount =
  1972. bit_slice_to_dec(event_bit_representation, start, end) / 100.0;
  1973. card->ravkav->events[i - 1].debit_amount_available = true;
  1974. FURI_LOG_I(TAG, "9: start: %d, end %d", start, end);
  1975. }
  1976. // EventRouteNumber
  1977. event_key = "Location";
  1978. if(is_calypso_node_present(
  1979. event_bit_representation, event_key, RavKavEventStructure)) {
  1980. int positionOffset = get_calypso_node_offset(
  1981. event_bit_representation, event_key, RavKavEventStructure);
  1982. int start = positionOffset,
  1983. end = positionOffset +
  1984. get_calypso_node_size(event_key, RavKavEventStructure) -
  1985. 1;
  1986. FURI_LOG_I(TAG, "location : start: %d, end %d", start, end);
  1987. FURI_LOG_I(
  1988. TAG,
  1989. "locatrion bitmap: %d",
  1990. bit_slice_to_dec(event_bit_representation, start, end));
  1991. }
  1992. }
  1993. // Free the calypso structure
  1994. free_calypso_structure(RavKavEventStructure);
  1995. break;
  1996. }
  1997. }
  1998. default:
  1999. break;
  2000. }
  2001. widget_add_text_scroll_element(
  2002. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  2003. CalypsoContext* context = malloc(sizeof(CalypsoContext));
  2004. context->card = card;
  2005. context->page_id = 0;
  2006. context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  2007. app->calypso_context = context;
  2008. // Ensure no nested mutexes
  2009. furi_mutex_acquire(context->mutex, FuriWaitForever);
  2010. update_page_info(app, parsed_data);
  2011. furi_mutex_release(context->mutex);
  2012. widget_add_text_scroll_element(
  2013. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  2014. // Ensure no nested mutexes
  2015. furi_mutex_acquire(context->mutex, FuriWaitForever);
  2016. update_widget_elements(app);
  2017. furi_mutex_release(context->mutex);
  2018. furi_string_free(parsed_data);
  2019. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  2020. metroflip_app_blink_stop(app);
  2021. stage = MetroflipPollerEventTypeSuccess;
  2022. next_command = NfcCommandStop;
  2023. } while(false);
  2024. if(stage != MetroflipPollerEventTypeSuccess) {
  2025. next_command = NfcCommandStop;
  2026. }
  2027. }
  2028. }
  2029. bit_buffer_free(tx_buffer);
  2030. bit_buffer_free(rx_buffer);
  2031. return next_command;
  2032. }
  2033. void metroflip_scene_calypso_on_enter(void* context) {
  2034. Metroflip* app = context;
  2035. dolphin_deed(DolphinDeedNfcRead);
  2036. // Setup view
  2037. Popup* popup = app->popup;
  2038. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  2039. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  2040. // Start worker
  2041. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  2042. nfc_scanner_alloc(app->nfc);
  2043. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  2044. nfc_poller_start(app->poller, metroflip_scene_calypso_poller_callback, app);
  2045. metroflip_app_blink_start(app);
  2046. }
  2047. bool metroflip_scene_calypso_on_event(void* context, SceneManagerEvent event) {
  2048. Metroflip* app = context;
  2049. bool consumed = false;
  2050. if(event.type == SceneManagerEventTypeCustom) {
  2051. if(event.event == MetroflipPollerEventTypeCardDetect) {
  2052. Popup* popup = app->popup;
  2053. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  2054. consumed = true;
  2055. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  2056. Popup* popup = app->popup;
  2057. popup_set_header(popup, "Read Error,\n wrong card", 68, 30, AlignLeft, AlignTop);
  2058. consumed = true;
  2059. } else if(event.event == MetroflipCustomEventPollerFail) {
  2060. Popup* popup = app->popup;
  2061. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  2062. consumed = true;
  2063. }
  2064. } else if(event.type == SceneManagerEventTypeBack) {
  2065. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  2066. consumed = true;
  2067. }
  2068. return consumed;
  2069. }
  2070. void metroflip_scene_calypso_on_exit(void* context) {
  2071. Metroflip* app = context;
  2072. if(app->poller) {
  2073. nfc_poller_stop(app->poller);
  2074. nfc_poller_free(app->poller);
  2075. }
  2076. metroflip_app_blink_stop(app);
  2077. widget_reset(app->widget);
  2078. // Clear view
  2079. popup_reset(app->popup);
  2080. if(app->calypso_context) {
  2081. CalypsoContext* ctx = app->calypso_context;
  2082. free(ctx->card->navigo);
  2083. free(ctx->card->opus);
  2084. free(ctx->card);
  2085. furi_mutex_free(ctx->mutex);
  2086. free(ctx);
  2087. app->calypso_context = NULL;
  2088. }
  2089. }