metroflip_scene_calypso.c 131 KB

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