mosgortrans_util.c 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326
  1. #include "../metroflip/metroflip_api.h"
  2. #include <bit_lib.h>
  3. #include <datetime.h>
  4. #include <furi/core/string.h>
  5. #include <nfc/protocols/mf_classic/mf_classic.h>
  6. #include <furi_hal_rtc.h>
  7. #define TAG "Metroflip:Scene:Mosgortrans"
  8. void render_section_header(
  9. FuriString* str,
  10. const char* name,
  11. uint8_t prefix_separator_cnt,
  12. uint8_t suffix_separator_cnt) {
  13. for(uint8_t i = 0; i < prefix_separator_cnt; i++) {
  14. furi_string_cat_printf(str, ":");
  15. }
  16. furi_string_cat_printf(str, "[ %s ]", name);
  17. for(uint8_t i = 0; i < suffix_separator_cnt; i++) {
  18. furi_string_cat_printf(str, ":");
  19. }
  20. }
  21. void from_days_to_datetime(uint32_t days, DateTime* datetime, uint16_t start_year) {
  22. uint32_t timestamp = days * 24 * 60 * 60;
  23. DateTime start_datetime = {0};
  24. start_datetime.year = start_year - 1;
  25. start_datetime.month = 12;
  26. start_datetime.day = 31;
  27. timestamp += datetime_datetime_to_timestamp(&start_datetime);
  28. datetime_timestamp_to_datetime(timestamp, datetime);
  29. }
  30. void from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t start_year) {
  31. uint32_t timestamp = minutes * 60;
  32. DateTime start_datetime = {0};
  33. start_datetime.year = start_year - 1;
  34. start_datetime.month = 12;
  35. start_datetime.day = 31;
  36. timestamp += datetime_datetime_to_timestamp(&start_datetime);
  37. datetime_timestamp_to_datetime(timestamp, datetime);
  38. }
  39. void from_seconds_to_datetime(uint32_t seconds, DateTime* datetime, uint16_t start_year) {
  40. uint32_t timestamp = seconds;
  41. DateTime start_datetime = {0};
  42. start_datetime.year = start_year - 1;
  43. start_datetime.month = 12;
  44. start_datetime.day = 31;
  45. timestamp += datetime_datetime_to_timestamp(&start_datetime);
  46. datetime_timestamp_to_datetime(timestamp, datetime);
  47. }
  48. typedef struct {
  49. uint16_t view; //101
  50. uint16_t type; //102
  51. uint8_t layout; //111
  52. uint8_t layout2; //112
  53. uint16_t blank_type; //121
  54. uint16_t type_of_extended; //122
  55. uint8_t extended; //123
  56. uint8_t benefit_code; //124
  57. uint32_t number; //201
  58. uint16_t use_before_date; //202
  59. uint16_t use_before_date2; //202.2
  60. uint16_t use_with_date; //205
  61. uint8_t requires_activation; //301
  62. uint16_t activate_during; //302
  63. uint16_t extension_counter; //304
  64. uint8_t blocked; //303
  65. uint32_t valid_from_date; //311
  66. uint16_t valid_to_date; //312
  67. uint8_t valid_for_days; //313
  68. uint32_t valid_for_minutes; //314
  69. uint16_t valid_for_time; //316
  70. uint16_t valid_for_time2; //316.2
  71. uint32_t valid_to_time; //317
  72. uint16_t remaining_trips; //321
  73. uint8_t remaining_trips1; //321.1
  74. uint32_t remaining_funds; //322
  75. uint16_t total_trips; //331
  76. uint16_t start_trip_date; //402
  77. uint16_t start_trip_time; //403
  78. uint32_t start_trip_neg_minutes; //404
  79. uint32_t start_trip_minutes; //405
  80. uint8_t start_trip_seconds; //406
  81. uint8_t minutes_pass; //412
  82. uint8_t passage_5_minutes; //413
  83. uint8_t metro_ride_with; //414
  84. uint8_t transport_type; //421
  85. uint8_t transport_type_flag; //421.0
  86. uint8_t transport_type1; //421.1
  87. uint8_t transport_type2; //421.2
  88. uint8_t transport_type3; //421.3
  89. uint8_t transport_type4; //421.4
  90. uint16_t validator; //422
  91. uint8_t validator1; //422.1
  92. uint16_t validator2; //422.2
  93. uint16_t route; //424
  94. uint8_t passage_in_metro; //431
  95. uint8_t transfer_in_metro; //432
  96. uint8_t passages_ground_transport; //433
  97. uint8_t fare_trip; //441
  98. uint16_t crc16; //501.1
  99. uint16_t crc16_2; //501.2
  100. uint32_t hash; //502
  101. uint16_t hash1; //502.1
  102. uint32_t hash2; //502.2
  103. uint8_t geozone_a; //GeoZoneA
  104. uint8_t geozone_b; //GeoZoneB
  105. uint8_t company; //Company
  106. uint8_t units; //Units
  107. uint64_t rfu1; //rfu1
  108. uint16_t rfu2; //rfu2
  109. uint32_t rfu3; //rfu3
  110. uint8_t rfu4; //rfu4
  111. uint8_t rfu5; //rfu5
  112. uint8_t write_enabled; //write_enabled
  113. uint32_t tech_code; //TechCode
  114. uint8_t interval; //Interval
  115. uint16_t app_code1; //AppCode1
  116. uint16_t app_code2; //AppCode2
  117. uint16_t app_code3; //AppCode3
  118. uint16_t app_code4; //AppCode4
  119. uint16_t type1; //Type1
  120. uint16_t type2; //Type2
  121. uint16_t type3; //Type3
  122. uint16_t type4; //Type4
  123. uint8_t zoo; //zoo
  124. } BlockData;
  125. void parse_layout_2(BlockData* data_block, const MfClassicBlock* block) {
  126. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  127. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  128. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  129. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  130. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202
  131. data_block->benefit_code = bit_lib_get_bits(block->data, 0x48, 8); //124
  132. data_block->rfu1 = bit_lib_get_bits_32(block->data, 0x50, 32); //rfu1
  133. data_block->crc16 = bit_lib_get_bits_16(block->data, 0x70, 16); //501.1
  134. data_block->blocked = bit_lib_get_bits(block->data, 0x80, 1); //303
  135. data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0x81, 12); //403
  136. data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x8D, 16); //402
  137. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x9D, 16); //311
  138. data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0xAD, 16); //312
  139. data_block->start_trip_seconds = bit_lib_get_bits(block->data, 0xDB, 6); //406
  140. data_block->transport_type1 = bit_lib_get_bits(block->data, 0xC3, 2); //421.1
  141. data_block->transport_type2 = bit_lib_get_bits(block->data, 0xC5, 2); //421.2
  142. data_block->transport_type3 = bit_lib_get_bits(block->data, 0xC7, 2); //421.3
  143. data_block->transport_type4 = bit_lib_get_bits(block->data, 0xC9, 2); //421.4
  144. data_block->use_with_date = bit_lib_get_bits_16(block->data, 0xBD, 16); //205
  145. data_block->route = bit_lib_get_bits(block->data, 0xCD, 1); //424
  146. data_block->validator1 = bit_lib_get_bits_16(block->data, 0xCE, 15); //422.1
  147. data_block->validator = bit_lib_get_bits_16(block->data, 0xCD, 16);
  148. data_block->total_trips = bit_lib_get_bits_16(block->data, 0xDD, 16); //331
  149. data_block->write_enabled = bit_lib_get_bits(block->data, 0xED, 1); //write_enabled
  150. data_block->rfu2 = bit_lib_get_bits(block->data, 0xEE, 2); //rfu2
  151. data_block->crc16_2 = bit_lib_get_bits_16(block->data, 0xF0, 16); //501.2
  152. }
  153. void parse_layout_6(BlockData* data_block, const MfClassicBlock* block) {
  154. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  155. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  156. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  157. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  158. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202
  159. data_block->geozone_a = bit_lib_get_bits(block->data, 0x48, 4); //GeoZoneA
  160. data_block->geozone_b = bit_lib_get_bits(block->data, 0x4C, 4); //GeoZoneB
  161. data_block->blank_type = bit_lib_get_bits_16(block->data, 0x50, 10); //121
  162. data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x5A, 10); //122
  163. data_block->rfu1 = bit_lib_get_bits_16(block->data, 0x64, 12); //rfu1
  164. data_block->crc16 = bit_lib_get_bits_16(block->data, 0x70, 16); //501.1
  165. data_block->blocked = bit_lib_get_bits(block->data, 0x80, 1); //303
  166. data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0x81, 12); //403
  167. data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x8D, 16); //402
  168. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x9D, 16); //311
  169. data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0xAD, 16); //312
  170. data_block->company = bit_lib_get_bits(block->data, 0xBD, 4); //Company
  171. data_block->validator1 = bit_lib_get_bits(block->data, 0xC1, 4); //422.1
  172. data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xC5, 10); //321
  173. data_block->units = bit_lib_get_bits(block->data, 0xCF, 6); //Units
  174. data_block->validator2 = bit_lib_get_bits_16(block->data, 0xD5, 10); //422.2
  175. data_block->total_trips = bit_lib_get_bits_16(block->data, 0xDF, 16); //331
  176. data_block->extended = bit_lib_get_bits(block->data, 0xEF, 1); //123
  177. data_block->crc16_2 = bit_lib_get_bits_16(block->data, 0xF0, 16); //501.2
  178. }
  179. void parse_layout_8(BlockData* data_block, const MfClassicBlock* block) {
  180. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  181. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  182. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  183. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  184. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202
  185. data_block->rfu1 = bit_lib_get_bits_64(block->data, 0x48, 56); //rfu1
  186. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311
  187. data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313
  188. data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301
  189. data_block->rfu2 = bit_lib_get_bits(block->data, 0x99, 7); //rfu2
  190. data_block->remaining_trips1 = bit_lib_get_bits(block->data, 0xA0, 8); //321.1
  191. data_block->remaining_trips = bit_lib_get_bits(block->data, 0xA8, 8); //321
  192. data_block->validator1 = bit_lib_get_bits(block->data, 0xB0, 2); //422.1
  193. data_block->validator = bit_lib_get_bits_16(block->data, 0xB1, 15); //422
  194. data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502
  195. data_block->rfu3 = bit_lib_get_bits_32(block->data, 0xE0, 32); //rfu3
  196. }
  197. void parse_layout_A(BlockData* data_block, const MfClassicBlock* block) {
  198. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  199. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  200. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  201. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  202. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x40, 12); //311
  203. data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x4C, 19); //314
  204. data_block->requires_activation = bit_lib_get_bits(block->data, 0x5F, 1); //301
  205. data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x60, 19); //405
  206. data_block->minutes_pass = bit_lib_get_bits(block->data, 0x77, 7); //412
  207. data_block->transport_type_flag = bit_lib_get_bits(block->data, 0x7E, 2); //421.0
  208. data_block->remaining_trips = bit_lib_get_bits(block->data, 0x80, 8); //321
  209. data_block->validator = bit_lib_get_bits_16(block->data, 0x88, 16); //422
  210. data_block->transport_type1 = bit_lib_get_bits(block->data, 0x98, 2); //421.1
  211. data_block->transport_type2 = bit_lib_get_bits(block->data, 0x9A, 2); //421.2
  212. data_block->transport_type3 = bit_lib_get_bits(block->data, 0x9C, 2); //421.3
  213. data_block->transport_type4 = bit_lib_get_bits(block->data, 0x9E, 2); //421.4
  214. data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502
  215. }
  216. void parse_layout_C(BlockData* data_block, const MfClassicBlock* block) {
  217. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  218. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  219. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  220. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  221. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202
  222. data_block->rfu1 = bit_lib_get_bits_64(block->data, 0x48, 56); //rfu1
  223. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311
  224. data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313
  225. data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301
  226. data_block->rfu2 = bit_lib_get_bits_16(block->data, 0x99, 13); //rfu2
  227. data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA6, 10); //321
  228. data_block->validator = bit_lib_get_bits_16(block->data, 0xB0, 16); //422
  229. data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502
  230. data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0xE0, 16); //402
  231. data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xF0, 11); //403
  232. data_block->transport_type = bit_lib_get_bits(block->data, 0xFB, 2); //421
  233. data_block->rfu3 = bit_lib_get_bits(block->data, 0xFD, 2); //rfu3
  234. data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xFF, 1); //432
  235. }
  236. void parse_layout_D(BlockData* data_block, const MfClassicBlock* block) {
  237. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  238. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  239. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  240. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  241. data_block->rfu1 = bit_lib_get_bits(block->data, 0x38, 8); //rfu1
  242. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x40, 16); //202
  243. data_block->valid_for_time = bit_lib_get_bits_16(block->data, 0x50, 11); //316
  244. data_block->rfu2 = bit_lib_get_bits(block->data, 0x5B, 5); //rfu2
  245. data_block->use_before_date2 = bit_lib_get_bits_16(block->data, 0x60, 16); //202.2
  246. data_block->valid_for_time2 = bit_lib_get_bits_16(block->data, 0x70, 11); //316.2
  247. data_block->rfu3 = bit_lib_get_bits(block->data, 0x7B, 5); //rfu3
  248. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311
  249. data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313
  250. data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301
  251. data_block->rfu4 = bit_lib_get_bits(block->data, 0x99, 2); //rfu4
  252. data_block->passage_5_minutes = bit_lib_get_bits(block->data, 0x9B, 5); //413
  253. data_block->transport_type1 = bit_lib_get_bits(block->data, 0xA0, 2); //421.1
  254. data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xA2, 1); //431
  255. data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xA3, 3); //433
  256. data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA6, 10); //321
  257. data_block->validator = bit_lib_get_bits_16(block->data, 0xB0, 16); //422
  258. data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502
  259. data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0xE0, 16); //402
  260. data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xF0, 11); //403
  261. data_block->transport_type2 = bit_lib_get_bits(block->data, 0xFB, 2); //421.2
  262. data_block->rfu5 = bit_lib_get_bits(block->data, 0xFD, 2); //rfu5
  263. data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xFF, 1); //432
  264. }
  265. void parse_layout_E1(BlockData* data_block, const MfClassicBlock* block) {
  266. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  267. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  268. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  269. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  270. data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
  271. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x3D, 16); //202
  272. data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4D, 10); //121
  273. data_block->validator = bit_lib_get_bits_16(block->data, 0x80, 16); //422
  274. data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x90, 16); //402
  275. data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xA0, 11); //403
  276. data_block->transport_type1 = bit_lib_get_bits(block->data, 0xAB, 2); //421.1
  277. data_block->transport_type2 = bit_lib_get_bits(block->data, 0xAD, 2); //421.2
  278. data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xB1, 1); //432
  279. data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xB2, 1); //431
  280. data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xB3, 3); //433
  281. data_block->minutes_pass = bit_lib_get_bits(block->data, 0xB9, 8); //412
  282. data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xC4, 19); //322
  283. data_block->fare_trip = bit_lib_get_bits(block->data, 0xD7, 2); //441
  284. data_block->blocked = bit_lib_get_bits(block->data, 0x9D, 1); //303
  285. data_block->zoo = bit_lib_get_bits(block->data, 0xDA, 1); //zoo
  286. data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
  287. }
  288. void parse_layout_E2(BlockData* data_block, const MfClassicBlock* block) {
  289. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  290. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  291. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  292. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  293. data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
  294. data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122
  295. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 16); //202
  296. data_block->blank_type = bit_lib_get_bits_16(block->data, 0x57, 10); //121
  297. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x61, 16); //311
  298. data_block->activate_during = bit_lib_get_bits_16(block->data, 0x71, 9); //302
  299. data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x83, 20); //314
  300. data_block->minutes_pass = bit_lib_get_bits(block->data, 0x9A, 8); //412
  301. data_block->transport_type = bit_lib_get_bits(block->data, 0xA3, 2); //421
  302. data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xA5, 1); //431
  303. data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xA6, 1); //432
  304. data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA7, 10); //321
  305. data_block->validator = bit_lib_get_bits_16(block->data, 0xB1, 16); //422
  306. data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0xC4, 20); //404
  307. data_block->requires_activation = bit_lib_get_bits(block->data, 0xD8, 1); //301
  308. data_block->blocked = bit_lib_get_bits(block->data, 0xD9, 1); //303
  309. data_block->extended = bit_lib_get_bits(block->data, 0xDA, 1); //123
  310. data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
  311. }
  312. void parse_layout_E3(BlockData* data_block, const MfClassicBlock* block) {
  313. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  314. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  315. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  316. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  317. data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
  318. data_block->use_before_date = bit_lib_get_bits_16(block->data, 61, 16); //202
  319. data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4D, 10); //121
  320. data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xBC, 22); //322
  321. data_block->hash = bit_lib_get_bits_32(block->data, 224, 32); //502
  322. data_block->validator = bit_lib_get_bits_16(block->data, 0x80, 16); //422
  323. data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x90, 23); //405
  324. data_block->fare_trip = bit_lib_get_bits(block->data, 0xD2, 2); //441
  325. data_block->minutes_pass = bit_lib_get_bits(block->data, 0xAB, 7); //412
  326. data_block->transport_type_flag = bit_lib_get_bits(block->data, 0xB2, 2); //421.0
  327. data_block->transport_type1 = bit_lib_get_bits(block->data, 0xB4, 2); //421.1
  328. data_block->transport_type2 = bit_lib_get_bits(block->data, 0xB6, 2); //421.2
  329. data_block->transport_type3 = bit_lib_get_bits(block->data, 0xB8, 2); //421.3
  330. data_block->transport_type4 = bit_lib_get_bits(block->data, 0xBA, 2); //421.4
  331. data_block->blocked = bit_lib_get_bits(block->data, 0xD4, 1); //303
  332. }
  333. void parse_layout_E4(BlockData* data_block, const MfClassicBlock* block) {
  334. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  335. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  336. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  337. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  338. data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
  339. data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122
  340. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 13); //202
  341. data_block->blank_type = bit_lib_get_bits_16(block->data, 0x54, 10); //121
  342. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x5E, 13); //311
  343. data_block->activate_during = bit_lib_get_bits_16(block->data, 0x6B, 9); //302
  344. data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x74, 10); //304
  345. data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x80, 20); //314
  346. data_block->minutes_pass = bit_lib_get_bits(block->data, 0x98, 7); //412
  347. data_block->transport_type_flag = bit_lib_get_bits(block->data, 0x9F, 2); //421.0
  348. data_block->transport_type1 = bit_lib_get_bits(block->data, 0xA1, 2); //421.1
  349. data_block->transport_type2 = bit_lib_get_bits(block->data, 0xA3, 2); //421.2
  350. data_block->transport_type3 = bit_lib_get_bits(block->data, 0xA5, 2); //421.3
  351. data_block->transport_type4 = bit_lib_get_bits(block->data, 0xA7, 2); //421.4
  352. data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA9, 10); //321
  353. data_block->validator = bit_lib_get_bits_16(block->data, 0xB3, 16); //422
  354. data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0xC3, 20); //404
  355. data_block->requires_activation = bit_lib_get_bits(block->data, 0xD7, 1); //301
  356. data_block->blocked = bit_lib_get_bits(block->data, 0xD8, 1); //303
  357. data_block->extended = bit_lib_get_bits(block->data, 0xD9, 1); //123
  358. data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
  359. }
  360. void parse_layout_E5(BlockData* data_block, const MfClassicBlock* block) {
  361. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  362. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  363. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  364. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  365. data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
  366. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x3D, 13); //202
  367. data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4A, 10); //121
  368. data_block->valid_to_time = bit_lib_get_bits_32(block->data, 0x54, 23); //317
  369. data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x6B, 10); //304
  370. data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x80, 23); //405
  371. data_block->metro_ride_with = bit_lib_get_bits(block->data, 0x97, 7); //414
  372. data_block->minutes_pass = bit_lib_get_bits(block->data, 0x9E, 7); //412
  373. data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xA7, 19); //322
  374. data_block->validator = bit_lib_get_bits_16(block->data, 0xBA, 16); //422
  375. data_block->blocked = bit_lib_get_bits(block->data, 0xCA, 1); //303
  376. data_block->route = bit_lib_get_bits_16(block->data, 0xCC, 12); //424
  377. data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xD8, 7); //433
  378. data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
  379. }
  380. void parse_layout_E6(BlockData* data_block, const MfClassicBlock* block) {
  381. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  382. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  383. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  384. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  385. data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
  386. data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122
  387. data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 13); //202
  388. data_block->blank_type = bit_lib_get_bits_16(block->data, 0x54, 10); //121
  389. data_block->valid_from_date = bit_lib_get_bits_32(block->data, 0x5E, 23); //311
  390. data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x75, 10); //304
  391. data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x80, 20); //314
  392. data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0x94, 20); //404
  393. data_block->metro_ride_with = bit_lib_get_bits(block->data, 0xA8, 7); //414
  394. data_block->minutes_pass = bit_lib_get_bits(block->data, 0xAF, 7); //412
  395. data_block->remaining_trips = bit_lib_get_bits(block->data, 0xB6, 7); //321
  396. data_block->validator = bit_lib_get_bits_16(block->data, 0xBD, 16); //422
  397. data_block->blocked = bit_lib_get_bits(block->data, 0xCD, 1); //303
  398. data_block->extended = bit_lib_get_bits(block->data, 0xCE, 1); //123
  399. data_block->route = bit_lib_get_bits_16(block->data, 0xD4, 12); //424
  400. data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
  401. }
  402. void parse_layout_FCB(BlockData* data_block, const MfClassicBlock* block) {
  403. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  404. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  405. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  406. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  407. data_block->tech_code = bit_lib_get_bits_32(block->data, 0x38, 10); //tech_code
  408. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x42, 16); //311
  409. data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0x52, 16); //312
  410. data_block->interval = bit_lib_get_bits(block->data, 0x62, 4); //interval
  411. data_block->app_code1 = bit_lib_get_bits_16(block->data, 0x66, 10); //app_code1
  412. data_block->hash1 = bit_lib_get_bits_16(block->data, 0x70, 16); //502.1
  413. data_block->type1 = bit_lib_get_bits_16(block->data, 0x80, 10); //type1
  414. data_block->app_code2 = bit_lib_get_bits_16(block->data, 0x8A, 10); //app_code2
  415. data_block->type2 = bit_lib_get_bits_16(block->data, 0x94, 10); //type2
  416. data_block->app_code3 = bit_lib_get_bits_16(block->data, 0x9E, 10); //app_code3
  417. data_block->type3 = bit_lib_get_bits_16(block->data, 0xA8, 10); //type3
  418. data_block->app_code4 = bit_lib_get_bits_16(block->data, 0xB2, 10); //app_code4
  419. data_block->type4 = bit_lib_get_bits_16(block->data, 0xBC, 10); //type4
  420. data_block->hash2 = bit_lib_get_bits_32(block->data, 0xE0, 32); //502.2
  421. }
  422. void parse_layout_F0B(BlockData* data_block, const MfClassicBlock* block) {
  423. data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
  424. data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
  425. data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
  426. data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
  427. data_block->tech_code = bit_lib_get_bits_32(block->data, 0x38, 10); //tech_code
  428. data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x42, 16); //311
  429. data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0x52, 16); //312
  430. data_block->hash1 = bit_lib_get_bits_32(block->data, 0x70, 16); //502.1
  431. }
  432. void parse_transport_type(BlockData* data_block, FuriString* transport) {
  433. switch(data_block->transport_type_flag) {
  434. case 1:
  435. uint8_t transport_type =
  436. (data_block->transport_type1 || data_block->transport_type2 ||
  437. data_block->transport_type3 || data_block->transport_type4);
  438. switch(transport_type) {
  439. case 1:
  440. furi_string_cat(transport, "Metro");
  441. break;
  442. case 2:
  443. furi_string_cat(transport, "Monorail");
  444. break;
  445. case 3:
  446. furi_string_cat(transport, "MCC");
  447. break;
  448. default:
  449. furi_string_cat(transport, "Unknown");
  450. break;
  451. }
  452. break;
  453. case 2:
  454. furi_string_cat(transport, "Ground");
  455. break;
  456. default:
  457. furi_string_cat(transport, "");
  458. break;
  459. }
  460. }
  461. bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result) {
  462. BlockData data_block = {};
  463. const uint16_t valid_departments[] = {0x106, 0x108, 0x10A, 0x10E, 0x110, 0x117};
  464. uint16_t transport_department = bit_lib_get_bits_16(block->data, 0, 10);
  465. if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
  466. furi_string_cat_printf(result, "Transport department: %x\n", transport_department);
  467. }
  468. bool department_valid = false;
  469. for(uint8_t i = 0; i < 6; i++) {
  470. if(transport_department == valid_departments[i]) {
  471. department_valid = true;
  472. break;
  473. }
  474. }
  475. if(!department_valid) {
  476. return false;
  477. }
  478. FURI_LOG_D(TAG, "Transport department: %x", transport_department);
  479. uint16_t layout_type = bit_lib_get_bits_16(block->data, 52, 4);
  480. if(layout_type == 0xE) {
  481. layout_type = bit_lib_get_bits_16(block->data, 52, 9);
  482. } else if(layout_type == 0xF) {
  483. layout_type = bit_lib_get_bits_16(block->data, 52, 14);
  484. }
  485. if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
  486. furi_string_cat_printf(result, "Layout: %x\n", layout_type);
  487. }
  488. FURI_LOG_D(TAG, "Layout type %x", layout_type);
  489. switch(layout_type) {
  490. case 0x02: {
  491. parse_layout_2(&data_block, block);
  492. //number
  493. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  494. //use_before_date
  495. DateTime card_use_before_date_s = {0};
  496. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
  497. furi_string_cat_printf(
  498. result,
  499. "Use before: %02d.%02d.%04d\n",
  500. card_use_before_date_s.day,
  501. card_use_before_date_s.month,
  502. card_use_before_date_s.year);
  503. if(data_block.valid_from_date == 0 || data_block.valid_to_date == 0) {
  504. furi_string_cat(result, "\e#No ticket");
  505. return false;
  506. }
  507. //remaining_trips
  508. furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips);
  509. //valid_from_date
  510. DateTime card_valid_from_date_s = {0};
  511. from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
  512. furi_string_cat_printf(
  513. result,
  514. "Valid from: %02d.%02d.%04d\n",
  515. card_valid_from_date_s.day,
  516. card_valid_from_date_s.month,
  517. card_valid_from_date_s.year);
  518. //valid_to_date
  519. DateTime card_valid_to_date_s = {0};
  520. from_days_to_datetime(data_block.valid_to_date, &card_valid_to_date_s, 1992);
  521. furi_string_cat_printf(
  522. result,
  523. "Valid to: %02d.%02d.%04d\n",
  524. card_valid_to_date_s.day,
  525. card_valid_to_date_s.month,
  526. card_valid_to_date_s.year);
  527. //trip_number
  528. furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips);
  529. //trip_from
  530. DateTime card_start_trip_minutes_s = {0};
  531. from_seconds_to_datetime(
  532. data_block.start_trip_date * 24 * 60 * 60 + data_block.start_trip_time * 60 +
  533. data_block.start_trip_seconds,
  534. &card_start_trip_minutes_s,
  535. 1992);
  536. furi_string_cat_printf(
  537. result,
  538. "Trip from: %02d.%02d.%04d %02d:%02d",
  539. card_start_trip_minutes_s.day,
  540. card_start_trip_minutes_s.month,
  541. card_start_trip_minutes_s.year,
  542. card_start_trip_minutes_s.hour,
  543. card_start_trip_minutes_s.minute);
  544. break;
  545. }
  546. case 0x06: {
  547. parse_layout_6(&data_block, block);
  548. //number
  549. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  550. //use_before_date
  551. DateTime card_use_before_date_s = {0};
  552. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
  553. furi_string_cat_printf(
  554. result,
  555. "Use before: %02d.%02d.%04d\n",
  556. card_use_before_date_s.day,
  557. card_use_before_date_s.month,
  558. card_use_before_date_s.year);
  559. //remaining_trips
  560. furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
  561. //valid_from_date
  562. DateTime card_valid_from_date_s = {0};
  563. from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
  564. furi_string_cat_printf(
  565. result,
  566. "Valid from: %02d.%02d.%04d\n",
  567. card_valid_from_date_s.day,
  568. card_valid_from_date_s.month,
  569. card_valid_from_date_s.year);
  570. //valid_to_date
  571. DateTime card_valid_to_date_s = {0};
  572. from_days_to_datetime(data_block.valid_to_date, &card_valid_to_date_s, 1992);
  573. furi_string_cat_printf(
  574. result,
  575. "Valid to: %02d.%02d.%04d\n",
  576. card_valid_to_date_s.day,
  577. card_valid_to_date_s.month,
  578. card_valid_to_date_s.year);
  579. //trip_number
  580. furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips);
  581. //trip_from
  582. DateTime card_start_trip_minutes_s = {0};
  583. from_minutes_to_datetime(
  584. (data_block.start_trip_date) * 24 * 60 + data_block.start_trip_time,
  585. &card_start_trip_minutes_s,
  586. 1992);
  587. furi_string_cat_printf(
  588. result,
  589. "Trip from: %02d.%02d.%04d %02d:%02d\n",
  590. card_start_trip_minutes_s.day,
  591. card_start_trip_minutes_s.month,
  592. card_start_trip_minutes_s.year,
  593. card_start_trip_minutes_s.hour,
  594. card_start_trip_minutes_s.minute);
  595. //validator
  596. furi_string_cat_printf(
  597. result, "Validator: %05d", data_block.validator1 * 1024 + data_block.validator2);
  598. break;
  599. }
  600. case 0x08: {
  601. parse_layout_8(&data_block, block);
  602. //number
  603. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  604. //use_before_date
  605. DateTime card_use_before_date_s = {0};
  606. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
  607. //remaining_trips
  608. furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
  609. //valid_from_date
  610. DateTime card_valid_from_date_s = {0};
  611. from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
  612. furi_string_cat_printf(
  613. result,
  614. "Valid from: %02d.%02d.%04d\n",
  615. card_valid_from_date_s.day,
  616. card_valid_from_date_s.month,
  617. card_valid_from_date_s.year);
  618. //valid_to_date
  619. DateTime card_valid_to_date_s = {0};
  620. from_days_to_datetime(
  621. data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);
  622. furi_string_cat_printf(
  623. result,
  624. "Valid to: %02d.%02d.%04d",
  625. card_valid_to_date_s.day,
  626. card_valid_to_date_s.month,
  627. card_valid_to_date_s.year);
  628. break;
  629. }
  630. case 0x0A: {
  631. parse_layout_A(&data_block, block);
  632. //number
  633. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  634. //use_before_date
  635. DateTime card_use_before_date_s = {0};
  636. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2016);
  637. furi_string_cat_printf(
  638. result,
  639. "Use before: %02d.%02d.%04d\n",
  640. card_use_before_date_s.day,
  641. card_use_before_date_s.month,
  642. card_use_before_date_s.year);
  643. //remaining_trips
  644. furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
  645. //valid_from_date
  646. DateTime card_valid_from_date_s = {0};
  647. from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 2016);
  648. furi_string_cat_printf(
  649. result,
  650. "Valid from: %02d.%02d.%04d\n",
  651. card_valid_from_date_s.day,
  652. card_valid_from_date_s.month,
  653. card_valid_from_date_s.year);
  654. //valid_to_date
  655. DateTime card_valid_to_date_s = {0};
  656. from_minutes_to_datetime(
  657. data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,
  658. &card_valid_to_date_s,
  659. 2016);
  660. furi_string_cat_printf(
  661. result,
  662. "Valid to: %02d.%02d.%04d",
  663. card_valid_to_date_s.day,
  664. card_valid_to_date_s.month,
  665. card_valid_to_date_s.year);
  666. //trip_from
  667. if(data_block.start_trip_minutes) {
  668. DateTime card_start_trip_minutes_s = {0};
  669. from_minutes_to_datetime(
  670. data_block.valid_from_date * 24 * 60 + data_block.start_trip_minutes,
  671. &card_start_trip_minutes_s,
  672. 2016);
  673. furi_string_cat_printf(
  674. result,
  675. "\nTrip from: %02d.%02d.%04d %02d:%02d",
  676. card_start_trip_minutes_s.day,
  677. card_start_trip_minutes_s.month,
  678. card_start_trip_minutes_s.year,
  679. card_start_trip_minutes_s.hour,
  680. card_start_trip_minutes_s.minute);
  681. }
  682. //trip_switch
  683. if(data_block.minutes_pass) {
  684. DateTime card_start_switch_trip_minutes_s = {0};
  685. from_minutes_to_datetime(
  686. data_block.valid_from_date * 24 * 60 + data_block.start_trip_minutes +
  687. data_block.minutes_pass,
  688. &card_start_switch_trip_minutes_s,
  689. 2016);
  690. furi_string_cat_printf(
  691. result,
  692. "\nTrip switch: %02d.%02d.%04d %02d:%02d",
  693. card_start_switch_trip_minutes_s.day,
  694. card_start_switch_trip_minutes_s.month,
  695. card_start_switch_trip_minutes_s.year,
  696. card_start_switch_trip_minutes_s.hour,
  697. card_start_switch_trip_minutes_s.minute);
  698. }
  699. //transport
  700. FuriString* transport = furi_string_alloc();
  701. parse_transport_type(&data_block, transport);
  702. furi_string_cat_printf(result, "\nTransport: %s", furi_string_get_cstr(transport));
  703. //validator
  704. if(data_block.validator) {
  705. furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
  706. }
  707. furi_string_free(transport);
  708. break;
  709. }
  710. case 0x0C: {
  711. parse_layout_C(&data_block, block);
  712. //number
  713. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  714. //use_before_date
  715. DateTime card_use_before_date_s = {0};
  716. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
  717. //remaining_trips
  718. furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
  719. //valid_from_date
  720. DateTime card_valid_from_date_s = {0};
  721. from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
  722. furi_string_cat_printf(
  723. result,
  724. "Valid from: %02d.%02d.%04d\n",
  725. card_valid_from_date_s.day,
  726. card_valid_from_date_s.month,
  727. card_valid_from_date_s.year);
  728. //valid_to_date
  729. DateTime card_valid_to_date_s = {0};
  730. from_days_to_datetime(
  731. data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);
  732. furi_string_cat_printf(
  733. result,
  734. "Valid to: %02d.%02d.%04d\n",
  735. card_valid_to_date_s.day,
  736. card_valid_to_date_s.month,
  737. card_valid_to_date_s.year);
  738. //remaining_trips
  739. furi_string_cat_printf(result, "Trips left: %d", data_block.remaining_trips);
  740. //trip_from
  741. if(data_block.start_trip_date) { // TODO: (-nofl) unused
  742. DateTime card_start_trip_minutes_s = {0};
  743. from_minutes_to_datetime(
  744. data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,
  745. &card_start_trip_minutes_s,
  746. 1992);
  747. }
  748. //validator
  749. if(data_block.validator) {
  750. furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
  751. }
  752. break;
  753. }
  754. case 0x0D: {
  755. parse_layout_D(&data_block, block);
  756. //number
  757. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  758. //use_before_date
  759. DateTime card_use_before_date_s = {0};
  760. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
  761. furi_string_cat_printf(
  762. result,
  763. "Use before: %02d.%02d.%04d\n",
  764. card_use_before_date_s.day,
  765. card_use_before_date_s.month,
  766. card_use_before_date_s.year);
  767. //remaining_trips
  768. furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
  769. //valid_from_date
  770. DateTime card_valid_from_date_s = {0};
  771. from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
  772. furi_string_cat_printf(
  773. result,
  774. "Valid from: %02d.%02d.%04d\n",
  775. card_valid_from_date_s.day,
  776. card_valid_from_date_s.month,
  777. card_valid_from_date_s.year);
  778. //valid_to_date
  779. DateTime card_valid_to_date_s = {0};
  780. from_days_to_datetime(
  781. data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);
  782. furi_string_cat_printf(
  783. result,
  784. "Valid to: %02d.%02d.%04d",
  785. card_valid_to_date_s.day,
  786. card_valid_to_date_s.month,
  787. card_valid_to_date_s.year);
  788. //trip_from
  789. if(data_block.start_trip_date) { // TODO: (-nofl) unused
  790. DateTime card_start_trip_minutes_s = {0};
  791. from_minutes_to_datetime(
  792. data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,
  793. &card_start_trip_minutes_s,
  794. 1992);
  795. }
  796. //trip_switch
  797. if(data_block.passage_5_minutes) { // TODO: (-nofl) unused
  798. DateTime card_start_switch_trip_minutes_s = {0};
  799. from_minutes_to_datetime(
  800. data_block.start_trip_date * 24 * 60 + data_block.start_trip_time +
  801. data_block.passage_5_minutes,
  802. &card_start_switch_trip_minutes_s,
  803. 1992);
  804. }
  805. //validator
  806. if(data_block.validator) {
  807. furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
  808. }
  809. break;
  810. }
  811. case 0xE1:
  812. case 0x1C1: {
  813. parse_layout_E1(&data_block, block);
  814. //number
  815. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  816. //use_before_date
  817. DateTime card_use_before_date_s = {0};
  818. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
  819. furi_string_cat_printf(
  820. result,
  821. "Use before: %02d.%02d.%04d\n",
  822. card_use_before_date_s.day,
  823. card_use_before_date_s.month,
  824. card_use_before_date_s.year);
  825. //remaining_funds
  826. furi_string_cat_printf(result, "Balance: %ld rub\n", data_block.remaining_funds / 100);
  827. //trip_from
  828. if(data_block.start_trip_date) {
  829. DateTime card_start_trip_minutes_s = {0};
  830. from_minutes_to_datetime(
  831. data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,
  832. &card_start_trip_minutes_s,
  833. 1992);
  834. furi_string_cat_printf(
  835. result,
  836. "Trip from: %02d.%02d.%04d %02d:%02d\n",
  837. card_start_trip_minutes_s.day,
  838. card_start_trip_minutes_s.month,
  839. card_start_trip_minutes_s.year,
  840. card_start_trip_minutes_s.hour,
  841. card_start_trip_minutes_s.minute);
  842. }
  843. //transport
  844. FuriString* transport = furi_string_alloc();
  845. switch(data_block.transport_type1) {
  846. case 1:
  847. switch(data_block.transport_type2) {
  848. case 1:
  849. furi_string_cat(transport, "Metro");
  850. break;
  851. case 2:
  852. furi_string_cat(transport, "Monorail");
  853. break;
  854. default:
  855. furi_string_cat(transport, "Unknown");
  856. break;
  857. }
  858. break;
  859. case 2:
  860. furi_string_cat(transport, "Ground");
  861. break;
  862. case 3:
  863. furi_string_cat(transport, "MCC");
  864. break;
  865. default:
  866. furi_string_cat(transport, "");
  867. break;
  868. }
  869. furi_string_cat_printf(result, "Transport: %s", furi_string_get_cstr(transport));
  870. //validator
  871. if(data_block.validator) {
  872. furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
  873. }
  874. furi_string_free(transport);
  875. break;
  876. }
  877. case 0xE2:
  878. case 0x1C2: {
  879. parse_layout_E2(&data_block, block);
  880. //number
  881. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  882. //use_before_date
  883. DateTime card_use_before_date_s = {0};
  884. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
  885. furi_string_cat_printf(
  886. result,
  887. "Use before: %02d.%02d.%04d\n",
  888. card_use_before_date_s.day,
  889. card_use_before_date_s.month,
  890. card_use_before_date_s.year);
  891. //remaining_trips
  892. furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
  893. //valid_from_date
  894. DateTime card_valid_from_date_s = {0};
  895. from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
  896. furi_string_cat_printf(
  897. result,
  898. "Valid from: %02d.%02d.%04d",
  899. card_valid_from_date_s.day,
  900. card_valid_from_date_s.month,
  901. card_valid_from_date_s.year);
  902. //valid_to_date
  903. if(data_block.activate_during) {
  904. DateTime card_valid_to_date_s = {0};
  905. from_days_to_datetime(
  906. data_block.valid_from_date + data_block.activate_during,
  907. &card_valid_to_date_s,
  908. 1992);
  909. furi_string_cat_printf(
  910. result,
  911. "\nValid to: %02d.%02d.%04d",
  912. card_valid_to_date_s.day,
  913. card_valid_to_date_s.month,
  914. card_valid_to_date_s.year);
  915. } else {
  916. DateTime card_valid_to_date_s = {0};
  917. from_minutes_to_datetime(
  918. data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,
  919. &card_valid_to_date_s,
  920. 1992);
  921. furi_string_cat_printf(
  922. result,
  923. "\nValid to: %02d.%02d.%04d",
  924. card_valid_to_date_s.day,
  925. card_valid_to_date_s.month,
  926. card_valid_to_date_s.year);
  927. }
  928. //trip_from
  929. if(data_block.start_trip_neg_minutes) {
  930. DateTime card_start_trip_minutes_s = {0};
  931. from_minutes_to_datetime(
  932. data_block.valid_to_date * 24 * 60 + data_block.valid_for_minutes -
  933. data_block.start_trip_neg_minutes,
  934. &card_start_trip_minutes_s,
  935. 1992); //-time
  936. furi_string_cat_printf(
  937. result,
  938. "\nTrip from: %02d.%02d.%04d %02d:%02d",
  939. card_start_trip_minutes_s.day,
  940. card_start_trip_minutes_s.month,
  941. card_start_trip_minutes_s.year,
  942. card_start_trip_minutes_s.hour,
  943. card_start_trip_minutes_s.minute);
  944. }
  945. //trip_switch
  946. if(data_block.minutes_pass) {
  947. DateTime card_start_switch_trip_minutes_s = {0};
  948. from_minutes_to_datetime(
  949. data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes -
  950. data_block.start_trip_neg_minutes + data_block.minutes_pass,
  951. &card_start_switch_trip_minutes_s,
  952. 1992);
  953. furi_string_cat_printf(
  954. result,
  955. "\nTrip switch: %02d.%02d.%04d %02d:%02d",
  956. card_start_switch_trip_minutes_s.day,
  957. card_start_switch_trip_minutes_s.month,
  958. card_start_switch_trip_minutes_s.year,
  959. card_start_switch_trip_minutes_s.hour,
  960. card_start_switch_trip_minutes_s.minute);
  961. }
  962. //transport
  963. FuriString* transport = furi_string_alloc();
  964. switch(data_block.transport_type) { // TODO: (-nofl) unused
  965. case 1:
  966. furi_string_cat(transport, "Metro");
  967. break;
  968. case 2:
  969. furi_string_cat(transport, "Monorail");
  970. break;
  971. case 3:
  972. furi_string_cat(transport, "Ground");
  973. break;
  974. default:
  975. furi_string_cat(transport, "Unknown");
  976. break;
  977. }
  978. //validator
  979. if(data_block.validator) {
  980. furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
  981. }
  982. furi_string_free(transport);
  983. break;
  984. }
  985. case 0xE3:
  986. case 0x1C3: {
  987. parse_layout_E3(&data_block, block);
  988. // number
  989. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  990. // use_before_date
  991. DateTime card_use_before_date_s = {0};
  992. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
  993. furi_string_cat_printf(
  994. result,
  995. "Use before: %02d.%02d.%04d\n",
  996. card_use_before_date_s.day,
  997. card_use_before_date_s.month,
  998. card_use_before_date_s.year);
  999. // remaining_funds
  1000. furi_string_cat_printf(result, "Balance: %lu rub\n", data_block.remaining_funds);
  1001. // start_trip_minutes
  1002. DateTime card_start_trip_minutes_s = {0};
  1003. from_minutes_to_datetime(data_block.start_trip_minutes, &card_start_trip_minutes_s, 2016);
  1004. furi_string_cat_printf(
  1005. result,
  1006. "Trip from: %02d.%02d.%04d %02d:%02d\n",
  1007. card_start_trip_minutes_s.day,
  1008. card_start_trip_minutes_s.month,
  1009. card_start_trip_minutes_s.year,
  1010. card_start_trip_minutes_s.hour,
  1011. card_start_trip_minutes_s.minute);
  1012. // transport
  1013. FuriString* transport = furi_string_alloc();
  1014. parse_transport_type(&data_block, transport);
  1015. furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport));
  1016. // validator
  1017. furi_string_cat_printf(result, "Validator: %05d\n", data_block.validator);
  1018. // fare
  1019. FuriString* fare = furi_string_alloc();
  1020. switch(data_block.fare_trip) {
  1021. case 0:
  1022. furi_string_cat(fare, "");
  1023. break;
  1024. case 1:
  1025. furi_string_cat(fare, "Single");
  1026. break;
  1027. case 2:
  1028. furi_string_cat(fare, "90 minutes");
  1029. break;
  1030. default:
  1031. furi_string_cat(fare, "Unknown");
  1032. break;
  1033. }
  1034. furi_string_cat_printf(result, "Fare: %s", furi_string_get_cstr(fare));
  1035. furi_string_free(fare);
  1036. furi_string_free(transport);
  1037. break;
  1038. }
  1039. case 0xE4:
  1040. case 0x1C4: {
  1041. parse_layout_E4(&data_block, block);
  1042. // number
  1043. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  1044. // use_before_date
  1045. DateTime card_use_before_date_s = {0};
  1046. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2016);
  1047. furi_string_cat_printf(
  1048. result,
  1049. "Use before: %02d.%02d.%04d\n",
  1050. card_use_before_date_s.day,
  1051. card_use_before_date_s.month,
  1052. card_use_before_date_s.year);
  1053. // remaining_funds
  1054. furi_string_cat_printf(result, "Balance: %lu rub\n", data_block.remaining_funds);
  1055. // valid_from_date
  1056. DateTime card_use_from_date_s = {0};
  1057. from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 2016);
  1058. furi_string_cat_printf(
  1059. result,
  1060. "Valid from: %02d.%02d.%04d\n",
  1061. card_use_from_date_s.day,
  1062. card_use_from_date_s.month,
  1063. card_use_from_date_s.year);
  1064. // valid_to_date
  1065. DateTime card_use_to_date_s = {0};
  1066. if(data_block.requires_activation) {
  1067. from_days_to_datetime(
  1068. data_block.valid_from_date + data_block.activate_during,
  1069. &card_use_to_date_s,
  1070. 2016);
  1071. } else {
  1072. from_minutes_to_datetime(
  1073. data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,
  1074. &card_use_to_date_s,
  1075. 2016);
  1076. }
  1077. furi_string_cat_printf(
  1078. result,
  1079. "Valid to: %02d.%02d.%04d\n",
  1080. card_use_to_date_s.day,
  1081. card_use_to_date_s.month,
  1082. card_use_to_date_s.year);
  1083. // trip_number
  1084. // furi_string_cat_printf(result, "Trips left: %d", data_block.remaining_trips);
  1085. // trip_from
  1086. DateTime card_start_trip_minutes_s = {0};
  1087. from_minutes_to_datetime(
  1088. data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes -
  1089. data_block.start_trip_neg_minutes,
  1090. &card_start_trip_minutes_s,
  1091. 2016); // TODO: (-nofl) unused
  1092. //transport
  1093. FuriString* transport = furi_string_alloc();
  1094. parse_transport_type(&data_block, transport);
  1095. furi_string_cat_printf(result, "Transport: %s", furi_string_get_cstr(transport));
  1096. // validator
  1097. if(data_block.validator) {
  1098. furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
  1099. }
  1100. furi_string_free(transport);
  1101. break;
  1102. }
  1103. case 0xE5:
  1104. case 0x1C5: {
  1105. parse_layout_E5(&data_block, block);
  1106. //number
  1107. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  1108. //use_before_date
  1109. DateTime card_use_before_date_s = {0};
  1110. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2019);
  1111. furi_string_cat_printf(
  1112. result,
  1113. "Use before: %02d.%02d.%04d\n",
  1114. card_use_before_date_s.day,
  1115. card_use_before_date_s.month,
  1116. card_use_before_date_s.year);
  1117. //remaining_funds
  1118. furi_string_cat_printf(result, "Balance: %ld rub", data_block.remaining_funds / 100);
  1119. //start_trip_minutes
  1120. if(data_block.start_trip_minutes) {
  1121. DateTime card_start_trip_minutes_s = {0};
  1122. from_minutes_to_datetime(
  1123. data_block.start_trip_minutes, &card_start_trip_minutes_s, 2019);
  1124. furi_string_cat_printf(
  1125. result,
  1126. "\nTrip from: %02d.%02d.%04d %02d:%02d",
  1127. card_start_trip_minutes_s.day,
  1128. card_start_trip_minutes_s.month,
  1129. card_start_trip_minutes_s.year,
  1130. card_start_trip_minutes_s.hour,
  1131. card_start_trip_minutes_s.minute);
  1132. }
  1133. //start_m_trip_minutes
  1134. if(data_block.metro_ride_with) {
  1135. DateTime card_start_m_trip_minutes_s = {0};
  1136. from_minutes_to_datetime(
  1137. data_block.start_trip_minutes + data_block.metro_ride_with,
  1138. &card_start_m_trip_minutes_s,
  1139. 2019);
  1140. furi_string_cat_printf(
  1141. result,
  1142. "\n(M) from: %02d.%02d.%04d %02d:%02d",
  1143. card_start_m_trip_minutes_s.day,
  1144. card_start_m_trip_minutes_s.month,
  1145. card_start_m_trip_minutes_s.year,
  1146. card_start_m_trip_minutes_s.hour,
  1147. card_start_m_trip_minutes_s.minute);
  1148. }
  1149. if(data_block.minutes_pass) {
  1150. DateTime card_start_change_trip_minutes_s = {0};
  1151. from_minutes_to_datetime(
  1152. data_block.start_trip_minutes + data_block.minutes_pass,
  1153. &card_start_change_trip_minutes_s,
  1154. 2019);
  1155. furi_string_cat_printf(
  1156. result,
  1157. "\nTrip edit: %02d.%02d.%04d %02d:%02d",
  1158. card_start_change_trip_minutes_s.day,
  1159. card_start_change_trip_minutes_s.month,
  1160. card_start_change_trip_minutes_s.year,
  1161. card_start_change_trip_minutes_s.hour,
  1162. card_start_change_trip_minutes_s.minute);
  1163. }
  1164. //transport
  1165. //validator
  1166. if(data_block.validator) {
  1167. furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
  1168. }
  1169. break;
  1170. }
  1171. case 0xE6:
  1172. case 0x1C6: {
  1173. parse_layout_E6(&data_block, block);
  1174. //number
  1175. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  1176. //use_before_date
  1177. DateTime card_use_before_date_s = {0};
  1178. from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2019);
  1179. furi_string_cat_printf(
  1180. result,
  1181. "Use before: %02d.%02d.%04d\n",
  1182. card_use_before_date_s.day,
  1183. card_use_before_date_s.month,
  1184. card_use_before_date_s.year);
  1185. //remaining_trips
  1186. furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
  1187. //valid_from_date
  1188. DateTime card_use_from_date_s = {0};
  1189. from_minutes_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 2019);
  1190. furi_string_cat_printf(
  1191. result,
  1192. "Valid from: %02d.%02d.%04d\n",
  1193. card_use_from_date_s.day,
  1194. card_use_from_date_s.month,
  1195. card_use_from_date_s.year);
  1196. //valid_to_date
  1197. DateTime card_use_to_date_s = {0};
  1198. from_minutes_to_datetime(
  1199. data_block.valid_from_date + data_block.valid_for_minutes - 1,
  1200. &card_use_to_date_s,
  1201. 2019);
  1202. furi_string_cat_printf(
  1203. result,
  1204. "Valid to: %02d.%02d.%04d",
  1205. card_use_to_date_s.day,
  1206. card_use_to_date_s.month,
  1207. card_use_to_date_s.year);
  1208. //start_trip_minutes
  1209. if(data_block.start_trip_neg_minutes) {
  1210. DateTime card_start_trip_minutes_s = {0};
  1211. from_minutes_to_datetime(
  1212. data_block.valid_from_date + data_block.valid_for_minutes -
  1213. data_block.start_trip_neg_minutes,
  1214. &card_start_trip_minutes_s,
  1215. 2019); //-time
  1216. furi_string_cat_printf(
  1217. result,
  1218. "\nTrip from: %02d.%02d.%04d %02d:%02d",
  1219. card_start_trip_minutes_s.day,
  1220. card_start_trip_minutes_s.month,
  1221. card_start_trip_minutes_s.year,
  1222. card_start_trip_minutes_s.hour,
  1223. card_start_trip_minutes_s.minute);
  1224. }
  1225. //start_trip_m_minutes
  1226. if(data_block.metro_ride_with) {
  1227. DateTime card_start_trip_m_minutes_s = {0};
  1228. from_minutes_to_datetime(
  1229. data_block.valid_from_date + data_block.valid_for_minutes -
  1230. data_block.start_trip_neg_minutes + data_block.metro_ride_with,
  1231. &card_start_trip_m_minutes_s,
  1232. 2019);
  1233. furi_string_cat_printf(
  1234. result,
  1235. "\n(M) from: %02d.%02d.%04d %02d:%02d",
  1236. card_start_trip_m_minutes_s.day,
  1237. card_start_trip_m_minutes_s.month,
  1238. card_start_trip_m_minutes_s.year,
  1239. card_start_trip_m_minutes_s.hour,
  1240. card_start_trip_m_minutes_s.minute);
  1241. }
  1242. //transport
  1243. //validator
  1244. if(data_block.validator) {
  1245. furi_string_cat_printf(result, "\nValidator: %05d", data_block.validator);
  1246. }
  1247. break;
  1248. }
  1249. case 0x3CCB: {
  1250. parse_layout_FCB(&data_block, block);
  1251. //number
  1252. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  1253. //valid_from_date
  1254. DateTime card_use_from_date_s = {0};
  1255. from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 1992);
  1256. furi_string_cat_printf(
  1257. result,
  1258. "Valid from: %02d.%02d.%04d\n",
  1259. card_use_from_date_s.day,
  1260. card_use_from_date_s.month,
  1261. card_use_from_date_s.year);
  1262. //valid_to_date
  1263. DateTime card_use_to_date_s = {0};
  1264. from_days_to_datetime(data_block.valid_to_date, &card_use_to_date_s, 1992);
  1265. furi_string_cat_printf(
  1266. result,
  1267. "Valid to: %02d.%02d.%04d",
  1268. card_use_to_date_s.day,
  1269. card_use_to_date_s.month,
  1270. card_use_to_date_s.year);
  1271. break;
  1272. }
  1273. case 0x3C0B: {
  1274. //number
  1275. furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
  1276. //valid_from_date
  1277. DateTime card_use_from_date_s = {0};
  1278. from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 1992);
  1279. furi_string_cat_printf(
  1280. result,
  1281. "Valid from: %02d.%02d.%04d\n",
  1282. card_use_from_date_s.day,
  1283. card_use_from_date_s.month,
  1284. card_use_from_date_s.year);
  1285. //valid_to_date
  1286. DateTime card_use_to_date_s = {0};
  1287. from_days_to_datetime(data_block.valid_to_date, &card_use_to_date_s, 1992);
  1288. furi_string_cat_printf(
  1289. result,
  1290. "Valid to: %02d.%02d.%04d",
  1291. card_use_to_date_s.day,
  1292. card_use_to_date_s.month,
  1293. card_use_to_date_s.year);
  1294. break;
  1295. }
  1296. default:
  1297. result = NULL;
  1298. return false;
  1299. }
  1300. return true;
  1301. }