mosgortrans_util.c 61 KB

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