calypso.c 131 KB

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