diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c index 7dac0a9d3..217e4a490 100644 --- a/applications/main/archive/scenes/archive_scene_delete.c +++ b/applications/main/archive/scenes/archive_scene_delete.c @@ -25,6 +25,12 @@ void archive_scene_delete_on_enter(void* context) { filename = furi_string_alloc(); ArchiveFile_t* current = archive_get_current_file(app->browser); + + FuriString* filename_no_ext = furi_string_alloc(); + path_extract_filename(current->path, filename_no_ext, true); + strlcpy(app->text_store, furi_string_get_cstr(filename_no_ext), MAX_NAME_LEN); + furi_string_free(filename_no_ext); + path_extract_filename(current->path, filename, false); char delete_str[64]; diff --git a/applications/main/nfc/api/mosgortrans/mosgortrans_util.c b/applications/main/nfc/api/mosgortrans/mosgortrans_util.c new file mode 100644 index 000000000..f2484a2af --- /dev/null +++ b/applications/main/nfc/api/mosgortrans/mosgortrans_util.c @@ -0,0 +1,1308 @@ +#include "mosgortrans_util.h" + +#define TAG "Mosgortrans" + +void from_days_to_datetime(uint32_t days, DateTime* datetime, uint16_t start_year) { + uint32_t timestamp = days * 24 * 60 * 60; + DateTime start_datetime = {0}; + start_datetime.year = start_year - 1; + start_datetime.month = 12; + start_datetime.day = 31; + timestamp += datetime_datetime_to_timestamp(&start_datetime); + datetime_timestamp_to_datetime(timestamp, datetime); +} + +void from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t start_year) { + uint32_t timestamp = minutes * 60; + DateTime start_datetime = {0}; + start_datetime.year = start_year - 1; + start_datetime.month = 12; + start_datetime.day = 31; + timestamp += datetime_datetime_to_timestamp(&start_datetime); + datetime_timestamp_to_datetime(timestamp, datetime); +} + +void from_seconds_to_datetime(uint32_t seconds, DateTime* datetime, uint16_t start_year) { + uint32_t timestamp = seconds; + DateTime start_datetime = {0}; + start_datetime.year = start_year - 1; + start_datetime.month = 12; + start_datetime.day = 31; + timestamp += datetime_datetime_to_timestamp(&start_datetime); + datetime_timestamp_to_datetime(timestamp, datetime); +} + +typedef struct { + uint16_t view; //101 + uint16_t type; //102 + uint8_t layout; //111 + uint8_t layout2; //112 + uint16_t blank_type; //121 + uint16_t type_of_extended; //122 + uint8_t extended; //123 + uint8_t benefit_code; //124 + uint32_t number; //201 + uint16_t use_before_date; //202 + uint16_t use_before_date2; //202.2 + uint16_t use_with_date; //205 + uint8_t requires_activation; //301 + uint16_t activate_during; //302 + uint16_t extension_counter; //304 + uint8_t blocked; //303 + uint32_t valid_from_date; //311 + uint16_t valid_to_date; //312 + uint8_t valid_for_days; //313 + uint32_t valid_for_minutes; //314 + uint16_t valid_for_time; //316 + uint16_t valid_for_time2; //316.2 + uint32_t valid_to_time; //317 + uint16_t remaining_trips; //321 + uint8_t remaining_trips1; //321.1 + uint32_t remaining_funds; //322 + uint16_t total_trips; //331 + uint16_t start_trip_date; //402 + uint16_t start_trip_time; //403 + uint32_t start_trip_neg_minutes; //404 + uint32_t start_trip_minutes; //405 + uint8_t start_trip_seconds; //406 + uint8_t minutes_pass; //412 + uint8_t passage_5_minutes; //413 + uint8_t metro_ride_with; //414 + uint8_t transport_type; //421 + uint8_t transport_type_flag; //421.0 + uint8_t transport_type1; //421.1 + uint8_t transport_type2; //421.2 + uint8_t transport_type3; //421.3 + uint8_t transport_type4; //421.4 + uint16_t validator; //422 + uint8_t validator1; //422.1 + uint16_t validator2; //422.2 + uint16_t route; //424 + uint8_t passage_in_metro; //431 + uint8_t transfer_in_metro; //432 + uint8_t passages_ground_transport; //433 + uint8_t fare_trip; //441 + uint16_t crc16; //501.1 + uint16_t crc16_2; //501.2 + uint32_t hash; //502 + uint16_t hash1; //502.1 + uint32_t hash2; //502.2 + uint8_t geozone_a; //GeoZoneA + uint8_t geozone_b; //GeoZoneB + uint8_t company; //Company + uint8_t units; //Units + uint64_t rfu1; //rfu1 + uint16_t rfu2; //rfu2 + uint32_t rfu3; //rfu3 + uint8_t rfu4; //rfu4 + uint8_t rfu5; //rfu5 + uint8_t write_enabled; //write_enabled + uint32_t tech_code; //TechCode + uint8_t interval; //Interval + uint16_t app_code1; //AppCode1 + uint16_t app_code2; //AppCode2 + uint16_t app_code3; //AppCode3 + uint16_t app_code4; //AppCode4 + uint16_t type1; //Type1 + uint16_t type2; //Type2 + uint16_t type3; //Type3 + uint16_t type4; //Type4 + uint8_t zoo; //zoo +} BlockData; + +void parse_layout_2(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202 + data_block->benefit_code = bit_lib_get_bits(block->data, 0x48, 8); //124 + data_block->rfu1 = bit_lib_get_bits_32(block->data, 0x50, 32); //rfu1 + data_block->crc16 = bit_lib_get_bits_16(block->data, 0x70, 16); //501.1 + data_block->blocked = bit_lib_get_bits(block->data, 0x80, 1); //303 + data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0x81, 12); //403 + data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x8D, 16); //402 + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x9D, 16); //311 + data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0xAD, 16); //312 + data_block->start_trip_seconds = bit_lib_get_bits(block->data, 0xDB, 6); //406 + data_block->transport_type1 = bit_lib_get_bits(block->data, 0xC3, 2); //421.1 + data_block->transport_type2 = bit_lib_get_bits(block->data, 0xC5, 2); //421.2 + data_block->transport_type3 = bit_lib_get_bits(block->data, 0xC7, 2); //421.3 + data_block->transport_type4 = bit_lib_get_bits(block->data, 0xC9, 2); //421.4 + data_block->use_with_date = bit_lib_get_bits_16(block->data, 0xBD, 16); //205 + data_block->route = bit_lib_get_bits(block->data, 0xCD, 1); //424 + data_block->validator1 = bit_lib_get_bits_16(block->data, 0xCE, 15); //422.1 + data_block->validator = bit_lib_get_bits_16(block->data, 0xCD, 16); + data_block->total_trips = bit_lib_get_bits_16(block->data, 0xDD, 16); //331 + data_block->write_enabled = bit_lib_get_bits(block->data, 0xED, 1); //write_enabled + data_block->rfu2 = bit_lib_get_bits(block->data, 0xEE, 2); //rfu2 + data_block->crc16_2 = bit_lib_get_bits_16(block->data, 0xF0, 16); //501.2 +} + +void parse_layout_6(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202 + data_block->geozone_a = bit_lib_get_bits(block->data, 0x48, 4); //GeoZoneA + data_block->geozone_b = bit_lib_get_bits(block->data, 0x4C, 4); //GeoZoneB + data_block->blank_type = bit_lib_get_bits_16(block->data, 0x50, 10); //121 + data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x5A, 10); //122 + data_block->rfu1 = bit_lib_get_bits_16(block->data, 0x64, 12); //rfu1 + data_block->crc16 = bit_lib_get_bits_16(block->data, 0x70, 16); //501.1 + data_block->blocked = bit_lib_get_bits(block->data, 0x80, 1); //303 + data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0x81, 12); //403 + data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x8D, 16); //402 + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x9D, 16); //311 + data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0xAD, 16); //312 + data_block->company = bit_lib_get_bits(block->data, 0xBD, 4); //Company + data_block->validator1 = bit_lib_get_bits(block->data, 0xC1, 4); //422.1 + data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xC5, 10); //321 + data_block->units = bit_lib_get_bits(block->data, 0xCF, 6); //Units + data_block->validator2 = bit_lib_get_bits_16(block->data, 0xD5, 10); //422.2 + data_block->total_trips = bit_lib_get_bits_16(block->data, 0xDF, 16); //331 + data_block->extended = bit_lib_get_bits(block->data, 0xEF, 1); //123 + data_block->crc16_2 = bit_lib_get_bits_16(block->data, 0xF0, 16); //501.2 +} + +void parse_layout_8(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202 + data_block->rfu1 = bit_lib_get_bits_64(block->data, 0x48, 56); //rfu1 + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311 + data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313 + data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301 + data_block->rfu2 = bit_lib_get_bits(block->data, 0x99, 7); //rfu2 + data_block->remaining_trips1 = bit_lib_get_bits(block->data, 0xA0, 8); //321.1 + data_block->remaining_trips = bit_lib_get_bits(block->data, 0xA8, 8); //321 + data_block->validator1 = bit_lib_get_bits(block->data, 0xB0, 2); //422.1 + data_block->validator = bit_lib_get_bits_16(block->data, 0xB1, 15); //422 + data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502 + data_block->rfu3 = bit_lib_get_bits_32(block->data, 0xE0, 32); //rfu3 +} + +void parse_layout_A(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x40, 12); //311 + data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x4C, 19); //314 + data_block->requires_activation = bit_lib_get_bits(block->data, 0x5F, 1); //301 + data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x60, 19); //405 + data_block->minutes_pass = bit_lib_get_bits(block->data, 0x77, 7); //412 + data_block->transport_type_flag = bit_lib_get_bits(block->data, 0x7E, 2); //421.0 + data_block->remaining_trips = bit_lib_get_bits(block->data, 0x80, 8); //321 + data_block->validator = bit_lib_get_bits_16(block->data, 0x88, 16); //422 + data_block->transport_type1 = bit_lib_get_bits(block->data, 0x98, 2); //421.1 + data_block->transport_type2 = bit_lib_get_bits(block->data, 0x9A, 2); //421.2 + data_block->transport_type3 = bit_lib_get_bits(block->data, 0x9C, 2); //421.3 + data_block->transport_type4 = bit_lib_get_bits(block->data, 0x9E, 2); //421.4 + data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502 +} + +void parse_layout_C(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202 + data_block->rfu1 = bit_lib_get_bits_64(block->data, 0x48, 56); //rfu1 + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311 + data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313 + data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301 + data_block->rfu2 = bit_lib_get_bits_16(block->data, 0x99, 13); //rfu2 + data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA6, 10); //321 + data_block->validator = bit_lib_get_bits_16(block->data, 0xB0, 16); //422 + data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502 + data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0xE0, 16); //402 + data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xF0, 11); //403 + data_block->transport_type = bit_lib_get_bits(block->data, 0xFB, 2); //421 + data_block->rfu3 = bit_lib_get_bits(block->data, 0xFD, 2); //rfu3 + data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xFF, 1); //432 +} + +void parse_layout_D(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->rfu1 = bit_lib_get_bits(block->data, 0x38, 8); //rfu1 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x40, 16); //202 + data_block->valid_for_time = bit_lib_get_bits_16(block->data, 0x50, 11); //316 + data_block->rfu2 = bit_lib_get_bits(block->data, 0x5B, 5); //rfu2 + data_block->use_before_date2 = bit_lib_get_bits_16(block->data, 0x60, 16); //202.2 + data_block->valid_for_time2 = bit_lib_get_bits_16(block->data, 0x70, 11); //316.2 + data_block->rfu3 = bit_lib_get_bits(block->data, 0x7B, 5); //rfu3 + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311 + data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313 + data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301 + data_block->rfu4 = bit_lib_get_bits(block->data, 0x99, 2); //rfu4 + data_block->passage_5_minutes = bit_lib_get_bits(block->data, 0x9B, 5); //413 + data_block->transport_type1 = bit_lib_get_bits(block->data, 0xA0, 2); //421.1 + data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xA2, 1); //431 + data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xA3, 3); //433 + data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA6, 10); //321 + data_block->validator = bit_lib_get_bits_16(block->data, 0xB0, 16); //422 + data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502 + data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0xE0, 16); //402 + data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xF0, 11); //403 + data_block->transport_type2 = bit_lib_get_bits(block->data, 0xFB, 2); //421.2 + data_block->rfu5 = bit_lib_get_bits(block->data, 0xFD, 2); //rfu5 + data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xFF, 1); //432 +} + +void parse_layout_E1(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x3D, 16); //202 + data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4D, 10); //121 + data_block->validator = bit_lib_get_bits_16(block->data, 0x80, 16); //422 + data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x90, 16); //402 + data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xA0, 11); //403 + data_block->transport_type1 = bit_lib_get_bits(block->data, 0xAB, 2); //421.1 + data_block->transport_type2 = bit_lib_get_bits(block->data, 0xAD, 2); //421.2 + data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xB1, 1); //432 + data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xB2, 1); //431 + data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xB3, 3); //433 + data_block->minutes_pass = bit_lib_get_bits(block->data, 0xB9, 8); //412 + data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xC4, 19); //322 + data_block->fare_trip = bit_lib_get_bits(block->data, 0xD7, 2); //441 + data_block->blocked = bit_lib_get_bits(block->data, 0x9D, 1); //303 + data_block->zoo = bit_lib_get_bits(block->data, 0xDA, 1); //zoo + data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502 +} + +void parse_layout_E2(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112 + data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 16); //202 + data_block->blank_type = bit_lib_get_bits_16(block->data, 0x57, 10); //121 + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x61, 16); //311 + data_block->activate_during = bit_lib_get_bits_16(block->data, 0x71, 9); //302 + data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x83, 20); //314 + data_block->minutes_pass = bit_lib_get_bits(block->data, 0x9A, 8); //412 + data_block->transport_type = bit_lib_get_bits(block->data, 0xA3, 2); //421 + data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xA5, 1); //431 + data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xA6, 1); //432 + data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA7, 10); //321 + data_block->validator = bit_lib_get_bits_16(block->data, 0xB1, 16); //422 + data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0xC4, 20); //404 + data_block->requires_activation = bit_lib_get_bits(block->data, 0xD8, 1); //301 + data_block->blocked = bit_lib_get_bits(block->data, 0xD9, 1); //303 + data_block->extended = bit_lib_get_bits(block->data, 0xDA, 1); //123 + data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502 +} + +void parse_layout_E3(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 61, 16); //202 + data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4D, 10); //121 + data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xBC, 22); //322 + data_block->hash = bit_lib_get_bits_32(block->data, 224, 32); //502 + data_block->validator = bit_lib_get_bits_16(block->data, 0x80, 16); //422 + data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x90, 23); //405 + data_block->fare_trip = bit_lib_get_bits(block->data, 0xD2, 2); //441 + data_block->minutes_pass = bit_lib_get_bits(block->data, 0xAB, 7); //412 + data_block->transport_type_flag = bit_lib_get_bits(block->data, 0xB2, 2); //421.0 + data_block->transport_type1 = bit_lib_get_bits(block->data, 0xB4, 2); //421.1 + data_block->transport_type2 = bit_lib_get_bits(block->data, 0xB6, 2); //421.2 + data_block->transport_type3 = bit_lib_get_bits(block->data, 0xB8, 2); //421.3 + data_block->transport_type4 = bit_lib_get_bits(block->data, 0xBA, 2); //421.4 + data_block->blocked = bit_lib_get_bits(block->data, 0xD4, 1); //303 +} + +void parse_layout_E4(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112 + data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 13); //202 + data_block->blank_type = bit_lib_get_bits_16(block->data, 0x54, 10); //121 + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x5E, 13); //311 + data_block->activate_during = bit_lib_get_bits_16(block->data, 0x6B, 9); //302 + data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x74, 10); //304 + data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x80, 20); //314 + data_block->minutes_pass = bit_lib_get_bits(block->data, 0x98, 7); //412 + data_block->transport_type_flag = bit_lib_get_bits(block->data, 0x9F, 2); //421.0 + data_block->transport_type1 = bit_lib_get_bits(block->data, 0xA1, 2); //421.1 + data_block->transport_type2 = bit_lib_get_bits(block->data, 0xA3, 2); //421.2 + data_block->transport_type3 = bit_lib_get_bits(block->data, 0xA5, 2); //421.3 + data_block->transport_type4 = bit_lib_get_bits(block->data, 0xA7, 2); //421.4 + data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA9, 10); //321 + data_block->validator = bit_lib_get_bits_16(block->data, 0xB3, 16); //422 + data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0xC3, 20); //404 + data_block->requires_activation = bit_lib_get_bits(block->data, 0xD7, 1); //301 + data_block->blocked = bit_lib_get_bits(block->data, 0xD8, 1); //303 + data_block->extended = bit_lib_get_bits(block->data, 0xD9, 1); //123 + data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502 +} + +void parse_layout_E5(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x3D, 13); //202 + data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4A, 10); //121 + data_block->valid_to_time = bit_lib_get_bits_32(block->data, 0x54, 23); //317 + data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x6B, 10); //304 + data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x80, 23); //405 + data_block->metro_ride_with = bit_lib_get_bits(block->data, 0x97, 7); //414 + data_block->minutes_pass = bit_lib_get_bits(block->data, 0x9E, 7); //412 + data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xA7, 19); //322 + data_block->validator = bit_lib_get_bits_16(block->data, 0xBA, 16); //422 + data_block->blocked = bit_lib_get_bits(block->data, 0xCA, 1); //303 + data_block->route = bit_lib_get_bits_16(block->data, 0xCC, 12); //424 + data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xD8, 7); //433 + data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502 +} + +void parse_layout_E6(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112 + data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122 + data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 13); //202 + data_block->blank_type = bit_lib_get_bits_16(block->data, 0x54, 10); //121 + data_block->valid_from_date = bit_lib_get_bits_32(block->data, 0x5E, 23); //311 + data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x75, 10); //304 + data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x80, 20); //314 + data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0x94, 20); //404 + data_block->metro_ride_with = bit_lib_get_bits(block->data, 0xA8, 7); //414 + data_block->minutes_pass = bit_lib_get_bits(block->data, 0xAF, 7); //412 + data_block->remaining_trips = bit_lib_get_bits(block->data, 0xB6, 7); //321 + data_block->validator = bit_lib_get_bits_16(block->data, 0xBD, 16); //422 + data_block->blocked = bit_lib_get_bits(block->data, 0xCD, 1); //303 + data_block->extended = bit_lib_get_bits(block->data, 0xCE, 1); //123 + data_block->route = bit_lib_get_bits_16(block->data, 0xD4, 12); //424 + data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502 +} + +void parse_layout_FCB(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->tech_code = bit_lib_get_bits_32(block->data, 0x38, 10); //tech_code + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x42, 16); //311 + data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0x52, 16); //312 + data_block->interval = bit_lib_get_bits(block->data, 0x62, 4); //interval + data_block->app_code1 = bit_lib_get_bits_16(block->data, 0x66, 10); //app_code1 + data_block->hash1 = bit_lib_get_bits_16(block->data, 0x70, 16); //502.1 + data_block->type1 = bit_lib_get_bits_16(block->data, 0x80, 10); //type1 + data_block->app_code2 = bit_lib_get_bits_16(block->data, 0x8A, 10); //app_code2 + data_block->type2 = bit_lib_get_bits_16(block->data, 0x94, 10); //type2 + data_block->app_code3 = bit_lib_get_bits_16(block->data, 0x9E, 10); //app_code3 + data_block->type3 = bit_lib_get_bits_16(block->data, 0xA8, 10); //type3 + data_block->app_code4 = bit_lib_get_bits_16(block->data, 0xB2, 10); //app_code4 + data_block->type4 = bit_lib_get_bits_16(block->data, 0xBC, 10); //type4 + data_block->hash2 = bit_lib_get_bits_32(block->data, 0xE0, 32); //502.2 +} + +void parse_layout_F0B(BlockData* data_block, const MfClassicBlock* block) { + data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101 + data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102 + data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201 + data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111 + data_block->tech_code = bit_lib_get_bits_32(block->data, 0x38, 10); //tech_code + data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x42, 16); //311 + data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0x52, 16); //312 + data_block->hash1 = bit_lib_get_bits_32(block->data, 0x70, 16); //502.1 +} + +void parse_transport_type(BlockData* data_block, FuriString* transport) { + switch(data_block->transport_type_flag) { + case 1: + uint8_t transport_type = + (data_block->transport_type1 || data_block->transport_type2 || + data_block->transport_type3 || data_block->transport_type4); + switch(transport_type) { + case 1: + furi_string_cat(transport, "Metro"); + break; + case 2: + furi_string_cat(transport, "Monorail"); + break; + case 3: + furi_string_cat(transport, "MCC"); + break; + default: + furi_string_cat(transport, "Unknown"); + break; + } + break; + case 2: + furi_string_cat(transport, "Ground"); + break; + default: + furi_string_cat(transport, ""); + break; + } +} + +bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result) { + BlockData data_block = {}; + const uint16_t valid_departments[] = {0x106, 0x108, 0x10A, 0x10E, 0x110, 0x117}; + uint16_t transport_departament = bit_lib_get_bits_16(block->data, 0, 10); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + furi_string_cat_printf(result, "Transport departament: %x\n", transport_departament); + } + bool departament_valid = false; + for(uint8_t i = 0; i < 6; i++) { + if(transport_departament == valid_departments[i]) { + departament_valid = true; + break; + } + } + if(!departament_valid) { + return false; + } + FURI_LOG_D(TAG, "Transport departament: %x", transport_departament); + uint16_t layout_type = bit_lib_get_bits_16(block->data, 52, 4); + if(layout_type == 0xE) { + layout_type = bit_lib_get_bits_16(block->data, 52, 9); + } else if(layout_type == 0xF) { + layout_type = bit_lib_get_bits_16(block->data, 52, 14); + } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + furi_string_cat_printf(result, "Layout: %x\n", layout_type); + } + FURI_LOG_D(TAG, "Layout type %x", layout_type); + switch(layout_type) { + case 0x02: { + parse_layout_2(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + + if(data_block.valid_from_date == 0 || data_block.valid_to_date == 0) { + furi_string_cat(result, "\e#No ticket\n"); + return true; + } + //remaining_trips + furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips); + //valid_from_date + DateTime card_valid_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_valid_from_date_s.day, + card_valid_from_date_s.month, + card_valid_from_date_s.year); + //valid_to_date + DateTime card_valid_to_date_s = {0}; + from_days_to_datetime(data_block.valid_to_date, &card_valid_to_date_s, 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_valid_to_date_s.day, + card_valid_to_date_s.month, + card_valid_to_date_s.year); + //trip_number + furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips); + //trip_from + DateTime card_start_trip_minutes_s = {0}; + from_seconds_to_datetime( + data_block.start_trip_date * 24 * 60 * 60 + data_block.start_trip_time * 60 + + data_block.start_trip_seconds, + &card_start_trip_minutes_s, + 1992); + furi_string_cat_printf( + result, + "Trip from: %02d.%02d.%04d %02d:%02d", + card_start_trip_minutes_s.day, + card_start_trip_minutes_s.month, + card_start_trip_minutes_s.year, + card_start_trip_minutes_s.hour, + card_start_trip_minutes_s.minute); + break; + } + case 0x06: { + parse_layout_6(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + //remaining_trips + furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips); + //valid_from_date + DateTime card_valid_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_valid_from_date_s.day, + card_valid_from_date_s.month, + card_valid_from_date_s.year); + //valid_to_date + DateTime card_valid_to_date_s = {0}; + from_days_to_datetime(data_block.valid_to_date, &card_valid_to_date_s, 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_valid_to_date_s.day, + card_valid_to_date_s.month, + card_valid_to_date_s.year); + //trip_number + furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips); + //trip_from + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + (data_block.start_trip_date) * 24 * 60 + data_block.start_trip_time, + &card_start_trip_minutes_s, + 1992); + furi_string_cat_printf( + result, + "Trip from: %02d.%02d.%04d %02d:%02d\n", + card_start_trip_minutes_s.day, + card_start_trip_minutes_s.month, + card_start_trip_minutes_s.year, + card_start_trip_minutes_s.hour, + card_start_trip_minutes_s.minute); + //validator + furi_string_cat_printf( + result, "Validator: %05d", data_block.validator1 * 1024 + data_block.validator2); + break; + } + case 0x08: { + parse_layout_8(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992); + //remaining_trips + furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips); + //valid_from_date + DateTime card_valid_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_valid_from_date_s.day, + card_valid_from_date_s.month, + card_valid_from_date_s.year); + //valid_to_date + DateTime card_valid_to_date_s = {0}; + from_days_to_datetime( + data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_valid_to_date_s.day, + card_valid_to_date_s.month, + card_valid_to_date_s.year); + break; + } + case 0x0A: { + parse_layout_A(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2016); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + //remaining_trips + furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips); + //valid_from_date + DateTime card_valid_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 2016); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_valid_from_date_s.day, + card_valid_from_date_s.month, + card_valid_from_date_s.year); + //valid_to_date + DateTime card_valid_to_date_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1, + &card_valid_to_date_s, + 2016); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_valid_to_date_s.day, + card_valid_to_date_s.month, + card_valid_to_date_s.year); + //trip_from + if(data_block.start_trip_minutes) { + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date * 24 * 60 + data_block.start_trip_minutes, + &card_start_trip_minutes_s, + 2016); + furi_string_cat_printf( + result, + "Trip from: %02d.%02d.%04d %02d:%02d\n", + card_start_trip_minutes_s.day, + card_start_trip_minutes_s.month, + card_start_trip_minutes_s.year, + card_start_trip_minutes_s.hour, + card_start_trip_minutes_s.minute); + } + //trip_switch + if(data_block.minutes_pass) { + DateTime card_start_switch_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date * 24 * 60 + data_block.start_trip_minutes + + data_block.minutes_pass, + &card_start_switch_trip_minutes_s, + 2016); + furi_string_cat_printf( + result, + "Trip switch: %02d.%02d.%04d %02d:%02d\n", + card_start_switch_trip_minutes_s.day, + card_start_switch_trip_minutes_s.month, + card_start_switch_trip_minutes_s.year, + card_start_switch_trip_minutes_s.hour, + card_start_switch_trip_minutes_s.minute); + } + //transport + FuriString* transport = furi_string_alloc(); + parse_transport_type(&data_block, transport); + furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport)); + //validator + if(data_block.validator) { + furi_string_cat_printf(result, "Validator: %05d\n", data_block.validator); + } + furi_string_free(transport); + break; + } + case 0x0C: { + parse_layout_C(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992); + //remaining_trips + furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips); + //valid_from_date + DateTime card_valid_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_valid_from_date_s.day, + card_valid_from_date_s.month, + card_valid_from_date_s.year); + //valid_to_date + DateTime card_valid_to_date_s = {0}; + from_days_to_datetime( + data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_valid_to_date_s.day, + card_valid_to_date_s.month, + card_valid_to_date_s.year); + //remaining_trips + furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips); + //trip_from + if(data_block.start_trip_date) { + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.start_trip_date * 24 * 60 + data_block.start_trip_time, + &card_start_trip_minutes_s, + 1992); + } + //validator + if(data_block.validator) { + furi_string_cat_printf(result, "Validator: %05d", data_block.validator); + } + break; + } + case 0x0D: { + parse_layout_D(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + //remaining_trips + furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips); + //valid_from_date + DateTime card_valid_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_valid_from_date_s.day, + card_valid_from_date_s.month, + card_valid_from_date_s.year); + //valid_to_date + DateTime card_valid_to_date_s = {0}; + from_days_to_datetime( + data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_valid_to_date_s.day, + card_valid_to_date_s.month, + card_valid_to_date_s.year); + //trip_from + if(data_block.start_trip_date) { + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.start_trip_date * 24 * 60 + data_block.start_trip_time, + &card_start_trip_minutes_s, + 1992); + } + //trip_switch + if(data_block.passage_5_minutes) { + DateTime card_start_switch_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.start_trip_date * 24 * 60 + data_block.start_trip_time + + data_block.passage_5_minutes, + &card_start_switch_trip_minutes_s, + 1992); + } + //validator + if(data_block.validator) { + furi_string_cat_printf(result, "Validator: %05d", data_block.validator); + } + break; + } + case 0xE1: + case 0x1C1: { + parse_layout_E1(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + //remaining_funds + furi_string_cat_printf(result, "Balance: %ld rub\n", data_block.remaining_funds / 100); + //trip_from + if(data_block.start_trip_date) { + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.start_trip_date * 24 * 60 + data_block.start_trip_time, + &card_start_trip_minutes_s, + 1992); + furi_string_cat_printf( + result, + "Trip from: %02d.%02d.%04d %02d:%02d\n", + card_start_trip_minutes_s.day, + card_start_trip_minutes_s.month, + card_start_trip_minutes_s.year, + card_start_trip_minutes_s.hour, + card_start_trip_minutes_s.minute); + } + //transport + FuriString* transport = furi_string_alloc(); + switch(data_block.transport_type1) { + case 1: + switch(data_block.transport_type2) { + case 1: + furi_string_cat(transport, "Metro"); + break; + case 2: + furi_string_cat(transport, "Monorail"); + break; + default: + furi_string_cat(transport, "Unknown"); + break; + } + break; + case 2: + furi_string_cat(transport, "Ground"); + break; + case 3: + furi_string_cat(transport, "MCC"); + break; + default: + furi_string_cat(transport, ""); + break; + } + furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport)); + //validator + if(data_block.validator) { + furi_string_cat_printf(result, "Validator: %05d", data_block.validator); + } + furi_string_free(transport); + break; + } + case 0xE2: + case 0x1C2: { + parse_layout_E2(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + //remaining_trips + furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips); + //valid_from_date + DateTime card_valid_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_valid_from_date_s.day, + card_valid_from_date_s.month, + card_valid_from_date_s.year); + //valid_to_date + if(data_block.activate_during) { + DateTime card_valid_to_date_s = {0}; + from_days_to_datetime( + data_block.valid_from_date + data_block.activate_during, + &card_valid_to_date_s, + 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_valid_to_date_s.day, + card_valid_to_date_s.month, + card_valid_to_date_s.year); + } else { + DateTime card_valid_to_date_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1, + &card_valid_to_date_s, + 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_valid_to_date_s.day, + card_valid_to_date_s.month, + card_valid_to_date_s.year); + } + //trip_from + if(data_block.start_trip_neg_minutes) { + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.valid_to_date * 24 * 60 + data_block.valid_for_minutes - + data_block.start_trip_neg_minutes, + &card_start_trip_minutes_s, + 1992); //-time + furi_string_cat_printf( + result, + "Trip from: %02d.%02d.%04d %02d:%02d\n", + card_start_trip_minutes_s.day, + card_start_trip_minutes_s.month, + card_start_trip_minutes_s.year, + card_start_trip_minutes_s.hour, + card_start_trip_minutes_s.minute); + } + //trip_switch + if(data_block.minutes_pass) { + DateTime card_start_switch_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - + data_block.start_trip_neg_minutes + data_block.minutes_pass, + &card_start_switch_trip_minutes_s, + 1992); + furi_string_cat_printf( + result, + "Trip switch: %02d.%02d.%04d %02d:%02d\n", + card_start_switch_trip_minutes_s.day, + card_start_switch_trip_minutes_s.month, + card_start_switch_trip_minutes_s.year, + card_start_switch_trip_minutes_s.hour, + card_start_switch_trip_minutes_s.minute); + } + //transport + FuriString* transport = furi_string_alloc(); + switch(data_block.transport_type) { + case 1: + furi_string_cat(transport, "Metro"); + break; + case 2: + furi_string_cat(transport, "Monorail"); + break; + case 3: + furi_string_cat(transport, "Ground"); + break; + default: + furi_string_cat(transport, "Unknown"); + break; + } + //validator + if(data_block.validator) { + furi_string_cat_printf(result, "Validator: %05d", data_block.validator); + } + furi_string_free(transport); + break; + } + case 0xE3: + case 0x1C3: { + parse_layout_E3(&data_block, block); + // number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + // use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + // remaining_funds + furi_string_cat_printf(result, "Balance: %lu rub\n", data_block.remaining_funds); + // start_trip_minutes + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime(data_block.start_trip_minutes, &card_start_trip_minutes_s, 2016); + furi_string_cat_printf( + result, + "Trip from: %02d.%02d.%04d %02d:%02d\n", + card_start_trip_minutes_s.day, + card_start_trip_minutes_s.month, + card_start_trip_minutes_s.year, + card_start_trip_minutes_s.hour, + card_start_trip_minutes_s.minute); + // transport + FuriString* transport = furi_string_alloc(); + parse_transport_type(&data_block, transport); + furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport)); + // validator + furi_string_cat_printf(result, "Validator: %05d\n", data_block.validator); + // fare + FuriString* fare = furi_string_alloc(); + switch(data_block.fare_trip) { + case 0: + furi_string_cat(fare, ""); + break; + case 1: + furi_string_cat(fare, "Single"); + break; + case 2: + furi_string_cat(fare, "90 minutes"); + break; + default: + furi_string_cat(fare, "Unknown"); + break; + } + furi_string_cat_printf(result, "Fare: %s", furi_string_get_cstr(fare)); + furi_string_free(fare); + furi_string_free(transport); + break; + } + case 0xE4: + case 0x1C4: { + parse_layout_E4(&data_block, block); + + // number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + // use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2016); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + // remaining_funds + furi_string_cat_printf(result, "Balance: %lu rub\n", data_block.remaining_funds); + // valid_from_date + DateTime card_use_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 2016); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_use_from_date_s.day, + card_use_from_date_s.month, + card_use_from_date_s.year); + // valid_to_date + DateTime card_use_to_date_s = {0}; + if(data_block.requires_activation) { + from_days_to_datetime( + data_block.valid_from_date + data_block.activate_during, + &card_use_to_date_s, + 2016); + } else { + from_minutes_to_datetime( + data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1, + &card_use_to_date_s, + 2016); + } + + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_use_to_date_s.day, + card_use_to_date_s.month, + card_use_to_date_s.year); + // trip_number + // furi_string_cat_printf(result, "Trips left: %d", data_block.remaining_trips); + // trip_from + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - + data_block.start_trip_neg_minutes, + &card_start_trip_minutes_s, + 2016); + //transport + FuriString* transport = furi_string_alloc(); + parse_transport_type(&data_block, transport); + furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport)); + // validator + if(data_block.validator) { + furi_string_cat_printf(result, "Validator: %05d", data_block.validator); + } + furi_string_free(transport); + break; + } + case 0xE5: + case 0x1C5: { + parse_layout_E5(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2019); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + //remaining_funds + furi_string_cat_printf(result, "Balance: %ld rub\n", data_block.remaining_funds / 100); + //start_trip_minutes + if(data_block.start_trip_minutes) { + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.start_trip_minutes, &card_start_trip_minutes_s, 2019); + furi_string_cat_printf( + result, + "Trip from: %02d.%02d.%04d %02d:%02d\n", + card_start_trip_minutes_s.day, + card_start_trip_minutes_s.month, + card_start_trip_minutes_s.year, + card_start_trip_minutes_s.hour, + card_start_trip_minutes_s.minute); + } + //start_m_trip_minutes + if(data_block.metro_ride_with) { + DateTime card_start_m_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.start_trip_minutes + data_block.metro_ride_with, + &card_start_m_trip_minutes_s, + 2019); + furi_string_cat_printf( + result, + "(M) from: %02d.%02d.%04d %02d:%02d\n", + card_start_m_trip_minutes_s.day, + card_start_m_trip_minutes_s.month, + card_start_m_trip_minutes_s.year, + card_start_m_trip_minutes_s.hour, + card_start_m_trip_minutes_s.minute); + } + if(data_block.minutes_pass) { + DateTime card_start_change_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.start_trip_minutes + data_block.minutes_pass, + &card_start_change_trip_minutes_s, + 2019); + furi_string_cat_printf( + result, + "Trip edit: %02d.%02d.%04d %02d:%02d\n", + card_start_change_trip_minutes_s.day, + card_start_change_trip_minutes_s.month, + card_start_change_trip_minutes_s.year, + card_start_change_trip_minutes_s.hour, + card_start_change_trip_minutes_s.minute); + } + //transport + //validator + if(data_block.validator) { + furi_string_cat_printf(result, "Validator: %05d", data_block.validator); + } + break; + } + case 0xE6: + case 0x1C6: { + parse_layout_E6(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //use_before_date + DateTime card_use_before_date_s = {0}; + from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2019); + furi_string_cat_printf( + result, + "Use before: %02d.%02d.%04d\n", + card_use_before_date_s.day, + card_use_before_date_s.month, + card_use_before_date_s.year); + //remaining_trips + furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips); + //valid_from_date + DateTime card_use_from_date_s = {0}; + from_minutes_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 2019); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_use_from_date_s.day, + card_use_from_date_s.month, + card_use_from_date_s.year); + //valid_to_date + DateTime card_use_to_date_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date + data_block.valid_for_minutes - 1, + &card_use_to_date_s, + 2019); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d\n", + card_use_to_date_s.day, + card_use_to_date_s.month, + card_use_to_date_s.year); + //start_trip_minutes + if(data_block.start_trip_neg_minutes) { + DateTime card_start_trip_minutes_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date + data_block.valid_for_minutes - + data_block.start_trip_neg_minutes, + &card_start_trip_minutes_s, + 2019); //-time + furi_string_cat_printf( + result, + "Trip from: %02d.%02d.%04d %02d:%02d\n", + card_start_trip_minutes_s.day, + card_start_trip_minutes_s.month, + card_start_trip_minutes_s.year, + card_start_trip_minutes_s.hour, + card_start_trip_minutes_s.minute); + } + //start_trip_m_minutes + if(data_block.metro_ride_with) { + DateTime card_start_trip_m_minutes_s = {0}; + from_minutes_to_datetime( + data_block.valid_from_date + data_block.valid_for_minutes - + data_block.start_trip_neg_minutes + data_block.metro_ride_with, + &card_start_trip_m_minutes_s, + 2019); + furi_string_cat_printf( + result, + "(M) from: %02d.%02d.%04d %02d:%02d\n", + card_start_trip_m_minutes_s.day, + card_start_trip_m_minutes_s.month, + card_start_trip_m_minutes_s.year, + card_start_trip_m_minutes_s.hour, + card_start_trip_m_minutes_s.minute); + } + //transport + //validator + if(data_block.validator) { + furi_string_cat_printf(result, "Validator: %05d", data_block.validator); + } + break; + } + case 0x3CCB: { + parse_layout_FCB(&data_block, block); + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //valid_from_date + DateTime card_use_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 1992); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_use_from_date_s.day, + card_use_from_date_s.month, + card_use_from_date_s.year); + //valid_to_date + DateTime card_use_to_date_s = {0}; + from_days_to_datetime(data_block.valid_to_date, &card_use_to_date_s, 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d", + card_use_to_date_s.day, + card_use_to_date_s.month, + card_use_to_date_s.year); + break; + } + case 0x3C0B: { + //number + furi_string_cat_printf(result, "Number: %010lu\n", data_block.number); + //valid_from_date + DateTime card_use_from_date_s = {0}; + from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 1992); + furi_string_cat_printf( + result, + "Valid from: %02d.%02d.%04d\n", + card_use_from_date_s.day, + card_use_from_date_s.month, + card_use_from_date_s.year); + //valid_to_date + DateTime card_use_to_date_s = {0}; + from_days_to_datetime(data_block.valid_to_date, &card_use_to_date_s, 1992); + furi_string_cat_printf( + result, + "Valid to: %02d.%02d.%04d", + card_use_to_date_s.day, + card_use_to_date_s.month, + card_use_to_date_s.year); + break; + } + default: + result = NULL; + return false; + } + + return true; +} diff --git a/applications/main/nfc/api/mosgortrans/mosgortrans_util.h b/applications/main/nfc/api/mosgortrans/mosgortrans_util.h new file mode 100644 index 000000000..e5da8ddeb --- /dev/null +++ b/applications/main/nfc/api/mosgortrans/mosgortrans_util.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/nfc/api/nfc_app_api_table_i.h b/applications/main/nfc/api/nfc_app_api_table_i.h index 08bff072e..bf0e926ee 100644 --- a/applications/main/nfc/api/nfc_app_api_table_i.h +++ b/applications/main/nfc/api/nfc_app_api_table_i.h @@ -1,4 +1,5 @@ #include "gallagher/gallagher_util.h" +#include "mosgortrans/mosgortrans_util.h" /* * A list of app's private functions and objects to expose for plugins. @@ -10,4 +11,8 @@ static constexpr auto nfc_app_api_table = sort(create_array_t( gallagher_deobfuscate_and_parse_credential, void, (GallagherCredential * credential, const uint8_t* cardholder_data_obfuscated)), - API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*))); + API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*), + API_METHOD( + mosgortrans_parse_transport_block, + bool, + (const MfClassicBlock* block, FuriString* result)))); diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 44d45ff20..0468e4bfb 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -146,6 +146,15 @@ App( sources=["plugins/supported_cards/aime.c"], ) +App( + appid="bip_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="bip_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/bip.c"], +) + App( appid="saflok_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/bip.c b/applications/main/nfc/plugins/supported_cards/bip.c new file mode 100644 index 000000000..f6fed6774 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/bip.c @@ -0,0 +1,368 @@ +#include "nfc_supported_card_plugin.h" + +#include +#include +#include +#include + +#define TAG "Bip" + +#define SECTOR_BLOCK_OFFSET(sector, block) (((sector) * 4) + (block)) + +static const uint64_t bip_keys_a[] = { + 0x3a42f33af429, + 0x6338a371c0ed, + 0xf124c2578ad0, + 0x32ac3b90ac13, + 0x4ad1e273eaf1, + 0xe2c42591368a, + 0x2a3c347a1200, + 0x16f3d5ab1139, + 0x937a4fff3011, + 0x35c3d2caee88, + 0x693143f10368, + 0xa3f97428dd01, + 0x63f17a449af0, + 0xc4652c54261c, + 0xd49e2826664f, + 0x3df14c8000a1, +}; + +static const uint64_t bip_keys_b[] = { + 0x1fc235ac1309, + 0x243f160918d1, + 0x9afc42372af1, + 0x682d401abb09, + 0x067db45454a9, + 0x15fc4c7613fe, + 0x68d30288910a, + 0xf59a36a2546d, + 0x64e3c10394c2, + 0xb736412614af, + 0x324f5df65310, + 0x643fb6de2217, + 0x82f435dedf01, + 0x0263de1278f3, + 0x51284c3686a6, + 0x6a470d54127c, +}; + +bool bip_verify(Nfc* nfc) { + bool verified = true; + + const uint8_t verify_sector = 0; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key_a_0 = {}; + bit_lib_num_to_bytes_be(bip_keys_a[0], COUNT_OF(key_a_0.data), key_a_0.data); + + MfClassicAuthContext auth_ctx = {}; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx); + + if(error == MfClassicErrorNotPresent) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + verified = false; + } + + return verified; +} + +static bool bip_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicType1k; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error == MfClassicErrorNotPresent) { + FURI_LOG_W(TAG, "Card not MIFARE Classic 1k"); + break; + } + + data->type = type; + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + bit_lib_num_to_bytes_be(bip_keys_a[i], sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + bit_lib_num_to_bytes_be(bip_keys_b[i], sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error == MfClassicErrorNotPresent) { + FURI_LOG_W(TAG, "Failed to read data. Bad keys?"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +typedef struct { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +} BipTimestamp; + +static void parse_bip_timestamp(const MfClassicBlock* block, BipTimestamp* timestamp) { + furi_assert(block); + furi_assert(timestamp); + + timestamp->day = (((block->data[1] << 8) + block->data[0]) >> 6) & 0x1f; + timestamp->month = (((block->data[1] << 8) + block->data[0]) >> 11) & 0xf; + timestamp->year = 2000 + ((((block->data[2] << 8) + block->data[1]) >> 7) & 0x1f); + timestamp->hour = (((block->data[3] << 8) + block->data[2]) >> 4) & 0x1f; + timestamp->minute = (((block->data[3] << 8) + block->data[2]) >> 9) & 0x3f; + timestamp->second = (((block->data[4] << 8) + block->data[3]) >> 7) & 0x3f; +} + +static int compare_bip_timestamp(const BipTimestamp* t1, const BipTimestamp* t2) { + furi_assert(t1); + furi_assert(t2); + if(t1->year != t2->year) { + return t1->year - t2->year; + } + if(t1->month != t2->month) { + return t1->month - t2->month; + } + if(t1->day != t2->day) { + return t1->day - t2->day; + } + if(t1->hour != t2->hour) { + return t1->hour - t2->hour; + } + if(t1->minute != t2->minute) { + return t1->minute - t2->minute; + } + if(t1->second != t2->second) { + return t1->second - t2->second; + } + return 0; +} + +static void print_bip_timestamp(const BipTimestamp* timestamp, FuriString* str) { + furi_assert(timestamp); + furi_assert(str); + furi_string_cat_printf( + str, + "%04u-%02u-%02u %02u:%02u:%02u", + timestamp->year, + timestamp->month, + timestamp->day, + timestamp->hour, + timestamp->minute, + timestamp->second); +} + +static bool is_bip_block_empty(const MfClassicBlock* block) { + furi_assert(block); + // check if all but last byte are zero (last is checksum) + for(size_t i = 0; i < sizeof(block->data) - 1; i++) { + if(block->data[i] != 0) { + return false; + } + } + return true; +} + +static void parse_uint16_le(const uint8_t* data, uint16_t* value) { + furi_assert(data); + furi_assert(value); + + *value = (data[0]) | (data[1] << 8); +} + +static void parse_uint32_le(const uint8_t* data, uint32_t* value) { + furi_assert(data); + furi_assert(value); + + *value = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +} + +static void parse_uint16_txn_amount(const uint8_t* data, uint16_t* value) { + furi_assert(data); + furi_assert(value); + + parse_uint16_le(data, value); + *value = *value >> 2; +} + +typedef struct { + BipTimestamp timestamp; + uint16_t amount; +} BipTransaction; + +static bool bip_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + bool parsed = true; + + struct { + uint32_t card_id; + uint16_t balance; + uint16_t flags; + BipTimestamp trip_time_window; + BipTransaction top_ups[3]; + BipTransaction charges[3]; + } bip_data = { + .card_id = 0, + .balance = 0, + .flags = 0, + .trip_time_window = {0, 0, 0, 0, 0, 0}, + .top_ups = + { + {{0, 0, 0, 0, 0, 0}, 0}, + {{0, 0, 0, 0, 0, 0}, 0}, + {{0, 0, 0, 0, 0, 0}, 0}, + }, + .charges = + { + {{0, 0, 0, 0, 0, 0}, 0}, + {{0, 0, 0, 0, 0, 0}, 0}, + {{0, 0, 0, 0, 0, 0}, 0}, + }, + }; + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + do { + // verify first sector keys + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0); + uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6); + if(key != bip_keys_a[0]) { + parsed = false; + break; + } + key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6); + if(key != bip_keys_b[0]) { + parsed = false; + break; + } + + // Get Card ID, little-endian 4 bytes at sector 0 block 1, bytes 4-7 + parse_uint32_le(&data->block[SECTOR_BLOCK_OFFSET(0, 1)].data[4], &bip_data.card_id); + + // Get balance, little-endian 2 bytes at sector 8 block 1, bytes 0-1 + parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[0], &bip_data.balance); + + // Get balance flags (negative balance, etc.), little-endian 2 bytes at sector 8 block 1, bytes 2-3 + parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[2], &bip_data.flags); + + // Get trip time window, proprietary format, at sector 5 block 1, bytes 0-7 + parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(5, 1)], &bip_data.trip_time_window); + + // Last 3 top-ups: sector 10, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 9-10 + for(size_t i = 0; i < 3; i++) { + if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(10, i)])) { + continue; + } + BipTransaction* top_up = &bip_data.top_ups[i]; + parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(10, i)], &top_up->timestamp); + parse_uint16_txn_amount( + &data->block[SECTOR_BLOCK_OFFSET(10, i)].data[9], &top_up->amount); + } + + // Last 3 charges (i.e. trips), sector 11, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 10-11 + for(size_t i = 0; i < 3; i++) { + if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(11, i)])) { + continue; + } + BipTransaction* charge = &bip_data.charges[i]; + parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(11, i)], &charge->timestamp); + parse_uint16_txn_amount( + &data->block[SECTOR_BLOCK_OFFSET(11, i)].data[10], &charge->amount); + } + + // All data is now parsed and stored in bip_data, now print it + + // Print basic info + furi_string_printf( + parsed_data, + "\e#Tarjeta Bip!\n" + "Card Number: %lu\n" + "Balance: $%hu (flags %hu)\n" + "Current Trip Window Ends:\n @", + bip_data.card_id, + bip_data.balance, + bip_data.flags); + + print_bip_timestamp(&bip_data.trip_time_window, parsed_data); + + // Find newest top-up + size_t newest_top_up = 0; + for(size_t i = 1; i < 3; i++) { + const BipTimestamp* newest = &bip_data.top_ups[newest_top_up].timestamp; + const BipTimestamp* current = &bip_data.top_ups[i].timestamp; + if(compare_bip_timestamp(current, newest) > 0) { + newest_top_up = i; + } + } + + // Print top-ups, newest first + furi_string_cat_printf(parsed_data, "\n\e#Last Top-ups"); + for(size_t i = 0; i < 3; i++) { + const BipTransaction* top_up = &bip_data.top_ups[(3u + newest_top_up - i) % 3]; + furi_string_cat_printf(parsed_data, "\n+$%d\n @", top_up->amount); + print_bip_timestamp(&top_up->timestamp, parsed_data); + } + + // Find newest charge + size_t newest_charge = 0; + for(size_t i = 1; i < 3; i++) { + const BipTimestamp* newest = &bip_data.charges[newest_charge].timestamp; + const BipTimestamp* current = &bip_data.charges[i].timestamp; + if(compare_bip_timestamp(current, newest) > 0) { + newest_charge = i; + } + } + + // Print charges + furi_string_cat_printf(parsed_data, "\n\e#Last Charges (Trips)"); + for(size_t i = 0; i < 3; i++) { + const BipTransaction* charge = &bip_data.charges[(3u + newest_charge - i) % 3]; + furi_string_cat_printf(parsed_data, "\n-$%d\n @", charge->amount); + print_bip_timestamp(&charge->timestamp, parsed_data); + } + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin bip_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = bip_verify, + .read = bip_read, + .parse = bip_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor bip_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &bip_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* bip_plugin_ep() { + return &bip_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index 06da5424c..50806e137 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -1,10 +1,13 @@ #include "nfc_supported_card_plugin.h" -#include +#include -#include +#include -#include -#include +#include +#include +#include +#include "../../api/mosgortrans/mosgortrans_util.h" +#include "furi_hal_rtc.h" #define TAG "Troika" @@ -38,1003 +41,48 @@ static const MfClassicKeyPair troika_1k_keys[] = { }; static const MfClassicKeyPair troika_4k_keys[] = { - {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, - {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, - {.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698}, - {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dbb}, - {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763}, - {.a = 0x9becdf3d9273, .b = 0xf8493407799d}, {.a = 0x08b386463229, .b = 0x5efbaecef46b}, - {.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, - {.a = 0x0e8f64340ba4, .b = 0x4acec1205d75}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, - {.a = 0x6b02733bb6ec, .b = 0x7038cd25c408}, {.a = 0x403d706ba880, .b = 0xb39d19a280df}, - {.a = 0xc11f4597efb5, .b = 0x70d901648cb9}, {.a = 0x0db520c78c1c, .b = 0x73e5b9d9d3a4}, - {.a = 0x3ebce0925b2f, .b = 0x372cc880f216}, {.a = 0x16a27af45407, .b = 0x9868925175ba}, - {.a = 0xaba208516740, .b = 0xce26ecb95252}, {.a = 0xcd64e567abcd, .b = 0x8f79c4fd8a01}, - {.a = 0x764cd061f1e6, .b = 0xa74332f74994}, {.a = 0x1cc219e9fec1, .b = 0xb90de525ceb6}, - {.a = 0x2fe3cb83ea43, .b = 0xfba88f109b32}, {.a = 0x07894ffec1d6, .b = 0xefcb0e689db3}, - {.a = 0x04c297b91308, .b = 0xc8454c154cb5}, {.a = 0x7a38e3511a38, .b = 0xab16584c972a}, - {.a = 0x7545df809202, .b = 0xecf751084a80}, {.a = 0x5125974cd391, .b = 0xd3eafb5df46d}, - {.a = 0x7a86aa203788, .b = 0xe41242278ca2}, {.a = 0xafcef64c9913, .b = 0x9db96dca4324}, - {.a = 0x04eaa462f70b, .b = 0xac17b93e2fae}, {.a = 0xe734c210f27e, .b = 0x29ba8c3e9fda}, - {.a = 0xd5524f591eed, .b = 0x5daf42861b4d}, {.a = 0xe4821a377b75, .b = 0xe8709e486465}, - {.a = 0x518dc6eea089, .b = 0x97c64ac98ca4}, {.a = 0xbb52f8cce07f, .b = 0x6b6119752c70}, + {.a = 0xEC29806D9738, .b = 0xFBF225DC5D58}, //1 + {.a = 0xA0A1A2A3A4A5, .b = 0x7DE02A7F6025}, //2 + {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //3 + {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //4 + {.a = 0x73068F118C13, .b = 0x2B7F3253FAC5}, //5 + {.a = 0xFBC2793D540B, .b = 0xD3A297DC2698}, //6 + {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //7 + {.a = 0xAE3D65A3DAD4, .b = 0x0F1C63013DBA}, //8 + {.a = 0xA73F5DC1D333, .b = 0xE35173494A81}, //9 + {.a = 0x69A32F1C2F19, .b = 0x6B8BD9860763}, //10 + {.a = 0x9BECDF3D9273, .b = 0xF8493407799D}, //11 + {.a = 0x08B386463229, .b = 0x5EFBAECEF46B}, //12 + {.a = 0xCD4C61C26E3D, .b = 0x31C7610DE3B0}, //13 + {.a = 0xA82607B01C0D, .b = 0x2910989B6880}, //14 + {.a = 0x0E8F64340BA4, .b = 0x4ACEC1205D75}, //15 + {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //16 + {.a = 0x6B02733BB6EC, .b = 0x7038CD25C408}, //17 + {.a = 0x403D706BA880, .b = 0xB39D19A280DF}, //18 + {.a = 0xC11F4597EFB5, .b = 0x70D901648CB9}, //19 + {.a = 0x0DB520C78C1C, .b = 0x73E5B9D9D3A4}, //20 + {.a = 0x3EBCE0925B2F, .b = 0x372CC880F216}, //21 + {.a = 0x16A27AF45407, .b = 0x9868925175BA}, //22 + {.a = 0xABA208516740, .b = 0xCE26ECB95252}, //23 + {.a = 0xCD64E567ABCD, .b = 0x8F79C4FD8A01}, //24 + {.a = 0x764CD061F1E6, .b = 0xA74332F74994}, //25 + {.a = 0x1CC219E9FEC1, .b = 0xB90DE525CEB6}, //26 + {.a = 0x2FE3CB83EA43, .b = 0xFBA88F109B32}, //27 + {.a = 0x07894FFEC1D6, .b = 0xEFCB0E689DB3}, //28 + {.a = 0x04C297B91308, .b = 0xC8454C154CB5}, //29 + {.a = 0x7A38E3511A38, .b = 0xAB16584C972A}, //30 + {.a = 0x7545DF809202, .b = 0xECF751084A80}, //31 + {.a = 0x5125974CD391, .b = 0xD3EAFB5DF46D}, //32 + {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //33 + {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //34 + {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //35 + {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //36 + {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //37 + {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //38 + {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //39 + {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //40 }; -void from_days_to_datetime(uint16_t days, DateTime* datetime, uint16_t start_year) { - uint32_t timestamp = days * 24 * 60 * 60; - DateTime start_datetime = {0}; - start_datetime.year = start_year - 1; - start_datetime.month = 12; - start_datetime.day = 31; - timestamp += datetime_datetime_to_timestamp(&start_datetime); - datetime_timestamp_to_datetime(timestamp, datetime); -} - -void from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t start_year) { - uint32_t timestamp = minutes * 60; - DateTime start_datetime = {0}; - start_datetime.year = start_year - 1; - start_datetime.month = 12; - start_datetime.day = 31; - timestamp += datetime_datetime_to_timestamp(&start_datetime); - datetime_timestamp_to_datetime(timestamp, datetime); -} - -bool parse_transport_block(const MfClassicBlock* block, FuriString* result) { - uint16_t transport_departament = bit_lib_get_bits_16(block->data, 0, 10); - - FURI_LOG_I(TAG, "Transport departament: %x", transport_departament); - - uint16_t layout_type = bit_lib_get_bits_16(block->data, 52, 4); - if(layout_type == 0xE) { - layout_type = bit_lib_get_bits_16(block->data, 52, 9); - } else if(layout_type == 0xF) { - layout_type = bit_lib_get_bits_16(block->data, 52, 14); - } - - FURI_LOG_I(TAG, "Layout type %x", layout_type); - - uint16_t card_view = 0; - uint16_t card_type = 0; - uint32_t card_number = 0; - uint8_t card_layout = 0; - uint8_t card_layout2 = 0; - uint16_t card_use_before_date = 0; - uint16_t card_blank_type = 0; - uint32_t card_start_trip_minutes = 0; - uint8_t card_minutes_pass = 0; - uint32_t card_remaining_funds = 0; - uint16_t card_validator = 0; - uint8_t card_blocked = 0; - uint32_t card_hash = 0; - - switch(layout_type) { - case 0x02: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_use_before_date = bit_lib_get_bits_16(block->data, 56, 16); //202 - uint8_t card_benefit_code = bit_lib_get_bits(block->data, 72, 8); //124 - uint32_t card_rfu1 = bit_lib_get_bits_32(block->data, 80, 32); //rfu1 - uint16_t card_crc16 = bit_lib_get_bits_16(block->data, 112, 16); //501.1 - card_blocked = bit_lib_get_bits(block->data, 128, 1); //303 - uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 177, 12); //403 - uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 189, 16); //402 - uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 157, 16); //311 - uint16_t card_valid_by_date = bit_lib_get_bits_16(block->data, 173, 16); //312 - uint8_t card_start_trip_seconds = bit_lib_get_bits(block->data, 189, 6); //406 - uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 180, 2); //421.1 - uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 182, 2); //421.2 - uint8_t card_transport_type3 = bit_lib_get_bits(block->data, 184, 2); //421.3 - uint8_t card_transport_type4 = bit_lib_get_bits(block->data, 186, 2); //421.4 - uint16_t card_use_with_date = bit_lib_get_bits_16(block->data, 189, 16); //205 - uint8_t card_route = bit_lib_get_bits(block->data, 205, 1); //424 - uint16_t card_validator1 = bit_lib_get_bits_16(block->data, 206, 15); //422.1 - card_validator = bit_lib_get_bits_16(block->data, 205, 16); //422 - uint16_t card_total_trips = bit_lib_get_bits_16(block->data, 221, 16); //331 - uint8_t card_write_enabled = bit_lib_get_bits(block->data, 237, 1); //write_enabled - uint8_t card_rfu2 = bit_lib_get_bits(block->data, 238, 2); //rfu2 - uint16_t card_crc16_2 = bit_lib_get_bits_16(block->data, 240, 16); //501.2 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x", - card_view, - card_type, - card_number, - card_use_before_date, - card_benefit_code, - card_rfu1, - card_crc16, - card_blocked, - card_start_trip_time, - card_start_trip_date, - card_valid_from_date, - card_valid_by_date, - card_start_trip_seconds, - card_transport_type1, - card_transport_type2, - card_transport_type3, - card_transport_type4, - card_use_with_date, - card_route, - card_validator1, - card_validator, - card_total_trips, - card_write_enabled, - card_rfu2, - card_crc16_2); - if(card_valid_by_date == 0) { - return false; - } - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_valid_by_date, &card_use_before_date_s, 1992); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime( - (card_start_trip_date) * 24 * 60 + card_start_trip_time, - &card_start_trip_minutes_s, - 1992); - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrips: %d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_total_trips, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_validator); - break; - } - case 0x06: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_use_before_date = bit_lib_get_bits_16(block->data, 56, 16); //202 - uint8_t card_geozone_a = bit_lib_get_bits(block->data, 72, 4); //GeoZoneA - uint8_t card_geozone_b = bit_lib_get_bits(block->data, 76, 4); //GeoZoneB - card_blank_type = bit_lib_get_bits_16(block->data, 80, 10); //121. - uint16_t card_type_of_extended = bit_lib_get_bits_16(block->data, 90, 10); //122 - uint32_t card_rfu1 = bit_lib_get_bits_16(block->data, 100, 12); //rfu1 - uint16_t card_crc16 = bit_lib_get_bits_16(block->data, 112, 16); //501.1 - card_blocked = bit_lib_get_bits(block->data, 128, 1); //303 - uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 129, 12); //403 - uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 141, 16); //402 - uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 157, 16); //311 - uint16_t card_valid_by_date = bit_lib_get_bits_16(block->data, 173, 16); //312 - uint16_t card_company = bit_lib_get_bits(block->data, 189, 4); //Company - uint8_t card_validator1 = bit_lib_get_bits(block->data, 193, 4); //422.1 - uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 197, 10); //321 - uint8_t card_units = bit_lib_get_bits(block->data, 207, 6); //Units - uint16_t card_validator2 = bit_lib_get_bits_16(block->data, 213, 10); //422.2 - uint16_t card_total_trips = bit_lib_get_bits_16(block->data, 223, 16); //331 - uint8_t card_extended = bit_lib_get_bits(block->data, 239, 1); //123 - uint16_t card_crc16_2 = bit_lib_get_bits_16(block->data, 240, 16); //501.2 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x", - card_view, - card_type, - card_number, - card_use_before_date, - card_geozone_a, - card_geozone_b, - card_blank_type, - card_type_of_extended, - card_rfu1, - card_crc16, - card_blocked, - card_start_trip_time, - card_start_trip_date, - card_valid_from_date, - card_valid_by_date, - card_company, - card_validator1, - card_remaining_trips, - card_units, - card_validator2, - card_total_trips, - card_extended, - card_crc16_2); - card_validator = card_validator1 * 1024 + card_validator2; - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_valid_by_date, &card_use_before_date_s, 1992); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime( - (card_start_trip_date) * 24 * 60 + card_start_trip_time, - &card_start_trip_minutes_s, - 1992); - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrips left: %d of %d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_remaining_trips, - card_total_trips, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_validator); - break; - } - case 0x08: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_use_before_date = bit_lib_get_bits_16(block->data, 56, 16); //202 - uint64_t card_rfu1 = bit_lib_get_bits_64(block->data, 72, 56); //rfu1 - uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 128, 16); //311 - uint8_t card_valid_for_days = bit_lib_get_bits(block->data, 144, 8); //313 - uint8_t card_requires_activation = bit_lib_get_bits(block->data, 152, 1); //301 - uint8_t card_rfu2 = bit_lib_get_bits(block->data, 153, 7); //rfu2 - uint8_t card_remaining_trips1 = bit_lib_get_bits(block->data, 160, 8); //321.1 - uint8_t card_remaining_trips = bit_lib_get_bits(block->data, 168, 8); //321 - uint8_t card_validator1 = bit_lib_get_bits(block->data, 193, 2); //422.1 - uint16_t card_validator = bit_lib_get_bits_16(block->data, 177, 15); //422 - card_hash = bit_lib_get_bits_32(block->data, 192, 32); //502 - uint32_t card_rfu3 = bit_lib_get_bits_32(block->data, 224, 32); //rfu3 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %llx %x %x %x %x %x %x %x %x %lx %x %lx", - card_view, - card_type, - card_number, - card_use_before_date, - card_rfu1, - card_valid_from_date, - card_valid_for_days, - card_requires_activation, - card_rfu2, - card_remaining_trips1, - card_remaining_trips, - card_validator1, - card_validator, - card_hash, - card_valid_from_date, - card_rfu3); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992); - - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrips left: %d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_remaining_trips, - card_validator); - break; - } - case 0x0A: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 64, 12); //311 - uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->data, 76, 19); //314 - uint8_t card_requires_activation = bit_lib_get_bits(block->data, 95, 1); //301 - card_start_trip_minutes = bit_lib_get_bits_32(block->data, 96, 19); //405 - card_minutes_pass = bit_lib_get_bits(block->data, 119, 7); //412 - uint8_t card_transport_type_flag = bit_lib_get_bits(block->data, 126, 2); //421.0 - uint8_t card_remaining_trips = bit_lib_get_bits(block->data, 128, 8); //321 - uint16_t card_validator = bit_lib_get_bits_16(block->data, 136, 16); //422 - uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 152, 2); //421.1 - uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 154, 2); //421.2 - uint8_t card_transport_type3 = bit_lib_get_bits(block->data, 156, 2); //421.3 - uint8_t card_transport_type4 = bit_lib_get_bits(block->data, 158, 2); //421.4 - card_hash = bit_lib_get_bits_32(block->data, 192, 32); //502 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %lx %x %lx %x %x %x %x %x %x %x %x %lx", - card_view, - card_type, - card_number, - card_use_before_date, - card_valid_from_date, - card_valid_for_minutes, - card_requires_activation, - card_start_trip_minutes, - card_minutes_pass, - card_transport_type_flag, - card_remaining_trips, - card_validator, - card_transport_type1, - card_transport_type2, - card_transport_type3, - card_transport_type4, - card_hash); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2016); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 2016); - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_remaining_trips, - card_validator); - break; - } - case 0x0C: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_use_before_date = bit_lib_get_bits_16(block->data, 56, 16); //202 - uint64_t card_rfu1 = bit_lib_get_bits_64(block->data, 72, 56); //rfu1 - uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 128, 16); //311 - uint8_t card_valid_for_days = bit_lib_get_bits(block->data, 144, 8); //313 - uint8_t card_requires_activation = bit_lib_get_bits(block->data, 152, 1); //301 - uint16_t card_rfu2 = bit_lib_get_bits_16(block->data, 153, 13); //rfu2 - uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 166, 10); //321 - uint16_t card_validator = bit_lib_get_bits_16(block->data, 176, 16); //422 - card_hash = bit_lib_get_bits_32(block->data, 192, 32); //502 - uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 224, 16); //402 - uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 240, 11); //403 - uint8_t card_transport_type = bit_lib_get_bits(block->data, 251, 2); //421 - uint8_t card_rfu3 = bit_lib_get_bits(block->data, 253, 2); //rfu3 - uint8_t card_transfer_in_metro = bit_lib_get_bits(block->data, 255, 1); //432 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %llx %x %x %x %x %x %x %x %x %x %x %x", - card_view, - card_type, - card_number, - card_use_before_date, - card_rfu1, - card_valid_from_date, - card_valid_for_days, - card_requires_activation, - card_rfu2, - card_remaining_trips, - card_validator, - card_start_trip_date, - card_start_trip_time, - card_transport_type, - card_rfu3, - card_transfer_in_metro); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992); - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime( - (card_start_trip_date) * 24 * 60 + card_start_trip_time, - &card_start_trip_minutes_s, - 1992); - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_remaining_trips, - card_validator); - break; - } - case 0x0D: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - uint8_t card_rfu1 = bit_lib_get_bits(block->data, 56, 8); //rfu1 - card_use_before_date = bit_lib_get_bits_16(block->data, 64, 16); //202 - uint16_t card_valid_for_time = bit_lib_get_bits_16(block->data, 80, 11); //316 - uint8_t card_rfu2 = bit_lib_get_bits(block->data, 91, 5); //rfu2 - uint16_t card_use_before_date2 = bit_lib_get_bits_16(block->data, 96, 16); //202.2 - uint16_t card_valid_for_time2 = bit_lib_get_bits_16(block->data, 123, 11); //316.2 - uint8_t card_rfu3 = bit_lib_get_bits(block->data, 123, 5); //rfu3 - uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 128, 16); //311 - uint8_t card_valid_for_days = bit_lib_get_bits(block->data, 144, 8); //313 - uint8_t card_requires_activation = bit_lib_get_bits(block->data, 152, 1); //301 - uint8_t card_rfu4 = bit_lib_get_bits(block->data, 153, 2); //rfu4 - uint8_t card_passage_5_minutes = bit_lib_get_bits(block->data, 155, 5); //413 - uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 160, 2); //421.1 - uint8_t card_passage_in_metro = bit_lib_get_bits(block->data, 162, 1); //431 - uint8_t card_passages_ground_transport = bit_lib_get_bits(block->data, 163, 3); //433 - uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 166, 10); //321 - uint16_t card_validator = bit_lib_get_bits_16(block->data, 176, 16); //422 - card_hash = bit_lib_get_bits_32(block->data, 192, 32); //502 - uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 224, 16); //402 - uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 240, 11); //403 - uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 251, 2); //421.2 - uint8_t card_rfu5 = bit_lib_get_bits(block->data, 253, 2); //rfu5 - uint8_t card_transfer_in_metro = bit_lib_get_bits(block->data, 255, 1); //432 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x", - card_view, - card_type, - card_number, - card_layout, - card_rfu1, - card_use_before_date, - card_valid_for_time, - card_rfu2, - card_use_before_date2, - card_valid_for_time2, - card_rfu3, - card_valid_from_date, - card_valid_for_days, - card_requires_activation, - card_rfu4, - card_passage_5_minutes, - card_transport_type1, - card_passage_in_metro, - card_passages_ground_transport, - card_remaining_trips, - card_validator, - card_start_trip_date, - card_start_trip_time, - card_transport_type2, - card_rfu5, - card_transfer_in_metro); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992); - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime( - (card_start_trip_date) * 24 * 60 + card_start_trip_time, - &card_start_trip_minutes_s, - 1992); - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_remaining_trips, - card_validator); - break; - } - case 0x1C1: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112 - card_use_before_date = bit_lib_get_bits_16(block->data, 61, 16); //202. - card_blank_type = bit_lib_get_bits_16(block->data, 77, 10); //121. - card_validator = bit_lib_get_bits_16(block->data, 128, 16); //422 - uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 144, 16); //402 - uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 160, 11); //403 - uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 171, 2); //421.1 - uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 173, 2); //421.2 - uint8_t card_transfer_in_metro = bit_lib_get_bits(block->data, 177, 1); //432 - uint8_t card_passage_in_metro = bit_lib_get_bits(block->data, 178, 1); //431 - uint8_t card_passages_ground_transport = bit_lib_get_bits(block->data, 179, 3); //433 - card_minutes_pass = bit_lib_get_bits(block->data, 185, 8); //412. - card_remaining_funds = bit_lib_get_bits_32(block->data, 196, 19) / 100; //322 - uint8_t card_fare_trip = bit_lib_get_bits(block->data, 215, 2); //441 - card_blocked = bit_lib_get_bits(block->data, 202, 1); //303 - uint8_t card_zoo = bit_lib_get_bits(block->data, 218, 1); //zoo - card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %lx %x %x %x %lx", - card_view, - card_type, - card_number, - card_layout, - card_layout2, - card_use_before_date, - card_blank_type, - card_validator, - card_start_trip_date, - card_start_trip_time, - card_transport_type1, - card_transport_type2, - card_transfer_in_metro, - card_passage_in_metro, - card_passages_ground_transport, - card_minutes_pass, - card_remaining_funds, - card_fare_trip, - card_blocked, - card_zoo, - card_hash); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 1992); - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_validator); - break; - } - case 0x1C2: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112 - uint16_t card_type_of_extended = bit_lib_get_bits_16(block->data, 61, 10); //122 - card_use_before_date = bit_lib_get_bits_16(block->data, 71, 16); //202. - card_blank_type = bit_lib_get_bits_16(block->data, 87, 10); //121. - uint16_t card_valid_to_date = bit_lib_get_bits_16(block->data, 97, 16); //311 - uint16_t card_activate_during = bit_lib_get_bits_16(block->data, 113, 9); //302 - uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->data, 131, 20); //314 - card_minutes_pass = bit_lib_get_bits(block->data, 154, 8); //412. - uint8_t card_transport_type = bit_lib_get_bits(block->data, 163, 2); //421 - uint8_t card_passage_in_metro = bit_lib_get_bits(block->data, 165, 1); //431 - uint8_t card_transfer_in_metro = bit_lib_get_bits(block->data, 166, 1); //432 - uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 167, 10); //321 - card_validator = bit_lib_get_bits_16(block->data, 177, 16); //422 - uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 196, 20); //404 - uint8_t card_requires_activation = bit_lib_get_bits(block->data, 216, 1); //301 - card_blocked = bit_lib_get_bits(block->data, 217, 1); //303 - uint8_t card_extended = bit_lib_get_bits(block->data, 218, 1); //123 - card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %x %x %x %lx %x %x %x %x %x %x %lx %x %x %x %lx", - card_view, - card_type, - card_number, - card_layout, - card_layout2, - card_type_of_extended, - card_use_before_date, - card_blank_type, - card_valid_to_date, - card_activate_during, - card_valid_for_minutes, - card_minutes_pass, - card_transport_type, - card_passage_in_metro, - card_transfer_in_metro, - card_remaining_trips, - card_validator, - card_start_trip_neg_minutes, - card_requires_activation, - card_blocked, - card_extended, - card_hash); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2016); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime( - (card_valid_to_date) * 24 * 60 + card_valid_for_minutes - card_start_trip_neg_minutes, - &card_start_trip_minutes_s, - 2016); //-time - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_validator); - break; - } - case 0x1C3: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112 - card_use_before_date = bit_lib_get_bits_16(block->data, 61, 16); //202 - card_blank_type = bit_lib_get_bits_16(block->data, 77, 10); //121 - card_remaining_funds = bit_lib_get_bits_32(block->data, 188, 22) / 100; //322 - card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502 - card_validator = bit_lib_get_bits_16(block->data, 128, 16); //422 - card_start_trip_minutes = bit_lib_get_bits_32(block->data, 144, 23); //405 - uint8_t card_fare_trip = bit_lib_get_bits(block->data, 210, 2); //441 - card_minutes_pass = bit_lib_get_bits(block->data, 171, 7); //412 - uint8_t card_transport_type_flag = bit_lib_get_bits(block->data, 178, 2); //421.0 - uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 180, 2); //421.1 - uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 182, 2); //421.2 - uint8_t card_transport_type3 = bit_lib_get_bits(block->data, 184, 2); //421.3 - uint8_t card_transport_type4 = bit_lib_get_bits(block->data, 186, 2); //421.4 - card_blocked = bit_lib_get_bits(block->data, 212, 1); //303 - FURI_LOG_D( - TAG, - "Card view: %x, type: %x, number: %lx, layout: %x, layout2: %x, use before date: %x, blank type: %x, remaining funds: %lx, hash: %lx, validator: %x, start trip minutes: %lx, fare trip: %x, minutes pass: %x, transport type flag: %x, transport type1: %x, transport type2: %x, transport type3: %x, transport type4: %x, blocked: %x", - card_view, - card_type, - card_number, - card_layout, - card_layout2, - card_use_before_date, - card_blank_type, - card_remaining_funds, - card_hash, - card_validator, - card_start_trip_minutes, - card_fare_trip, - card_minutes_pass, - card_transport_type_flag, - card_transport_type1, - card_transport_type2, - card_transport_type3, - card_transport_type4, - card_blocked); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 2016); - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nBalance: %ld rub\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_remaining_funds, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_validator); - break; - } - case 0x1C4: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112 - uint16_t card_type_of_extended = bit_lib_get_bits_16(block->data, 61, 10); //122 - card_use_before_date = bit_lib_get_bits_16(block->data, 71, 13); //202. - card_blank_type = bit_lib_get_bits_16(block->data, 84, 10); //121. - uint16_t card_valid_to_date = bit_lib_get_bits_16(block->data, 94, 13); //311 - uint16_t card_activate_during = bit_lib_get_bits_16(block->data, 107, 9); //302 - uint16_t card_extension_counter = bit_lib_get_bits_16(block->data, 116, 10); //304 - uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->data, 128, 20); //314 - card_minutes_pass = bit_lib_get_bits(block->data, 158, 7); //412. - uint8_t card_transport_type_flag = bit_lib_get_bits(block->data, 178, 2); //421.0 - uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 180, 2); //421.1 - uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 182, 2); //421.2 - uint8_t card_transport_type3 = bit_lib_get_bits(block->data, 184, 2); //421.3 - uint8_t card_transport_type4 = bit_lib_get_bits(block->data, 186, 2); //421.4 - uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 169, 10); //321 - card_validator = bit_lib_get_bits_16(block->data, 179, 16); //422 - uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 195, 20); //404 - uint8_t card_requires_activation = bit_lib_get_bits(block->data, 215, 1); //301 - card_blocked = bit_lib_get_bits(block->data, 216, 1); //303 - uint8_t card_extended = bit_lib_get_bits(block->data, 217, 1); //123 - card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %x %x %x %x %lx %x %x %x %x %x %x %x %x %lx %x %x %x %lx", - card_view, - card_type, - card_number, - card_layout, - card_layout2, - card_type_of_extended, - card_use_before_date, - card_blank_type, - card_valid_to_date, - card_activate_during, - card_extension_counter, - card_valid_for_minutes, - card_minutes_pass, - card_transport_type_flag, - card_transport_type1, - card_transport_type2, - card_transport_type3, - card_transport_type4, - card_remaining_trips, - card_validator, - card_start_trip_neg_minutes, - card_requires_activation, - card_blocked, - card_extended, - card_hash); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2016); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime( - (card_use_before_date + 1) * 24 * 60 + card_valid_for_minutes - - card_start_trip_neg_minutes, - &card_start_trip_minutes_s, - 2011); //-time - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_validator); - break; - } - case 0x1C5: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112 - card_use_before_date = bit_lib_get_bits_16(block->data, 61, 13); //202. - card_blank_type = bit_lib_get_bits_16(block->data, 74, 10); //121. - uint32_t card_valid_to_time = bit_lib_get_bits_32(block->data, 84, 23); //317 - uint16_t card_extension_counter = bit_lib_get_bits_16(block->data, 107, 10); //304 - card_start_trip_minutes = bit_lib_get_bits_32(block->data, 128, 23); //405 - uint8_t card_metro_ride_with = bit_lib_get_bits(block->data, 151, 7); //414 - card_minutes_pass = bit_lib_get_bits(block->data, 158, 7); //412. - card_remaining_funds = bit_lib_get_bits_32(block->data, 167, 19) / 100; //322 - card_validator = bit_lib_get_bits_16(block->data, 186, 16); //422 - card_blocked = bit_lib_get_bits(block->data, 202, 1); //303 - uint16_t card_route = bit_lib_get_bits_16(block->data, 204, 12); //424 - uint8_t card_passages_ground_transport = bit_lib_get_bits(block->data, 216, 7); //433 - card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %lx %x %lx %x %x %lx %x %x %x %x %lx", - card_view, - card_type, - card_number, - card_layout, - card_layout2, - card_use_before_date, - card_blank_type, - card_valid_to_time, - card_extension_counter, - card_start_trip_minutes, - card_metro_ride_with, - card_minutes_pass, - card_remaining_funds, - card_validator, - card_blocked, - card_route, - card_passages_ground_transport, - card_hash); - DateTime card_use_before_date_s = {0}; - - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2019); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 2019); - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nBalance: %ld rub\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_remaining_funds, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_validator); - break; - } - case 0x1C6: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112 - uint16_t card_type_of_extended = bit_lib_get_bits_16(block->data, 61, 10); //122 - card_use_before_date = bit_lib_get_bits_16(block->data, 71, 13); //202. - card_blank_type = bit_lib_get_bits_16(block->data, 84, 10); //121. - uint32_t card_valid_from_date = bit_lib_get_bits_32(block->data, 94, 23); //311 - uint16_t card_extension_counter = bit_lib_get_bits_16(block->data, 117, 10); //304 - uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->data, 128, 20); //314 - uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 148, 20); //404 - uint8_t card_metro_ride_with = bit_lib_get_bits(block->data, 168, 7); //414 - card_minutes_pass = bit_lib_get_bits(block->data, 175, 7); //412. - uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 182, 7); //321 - card_validator = bit_lib_get_bits_16(block->data, 189, 16); //422 - card_blocked = bit_lib_get_bits(block->data, 205, 1); //303 - uint8_t card_extended = bit_lib_get_bits(block->data, 206, 1); //123 - uint16_t card_route = bit_lib_get_bits_16(block->data, 212, 12); //424 - card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %x %lx %x %lx %lx %x %x %x %x %x %x %x %lx", - card_view, - card_type, - card_number, - card_layout, - card_layout2, - card_type_of_extended, - card_use_before_date, - card_blank_type, - card_valid_from_date, - card_extension_counter, - card_valid_for_minutes, - card_start_trip_neg_minutes, - card_metro_ride_with, - card_minutes_pass, - card_remaining_trips, - card_validator, - card_blocked, - card_extended, - card_route, - card_hash); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2019); - - DateTime card_start_trip_minutes_s = {0}; - from_minutes_to_datetime( - card_valid_from_date + card_valid_for_minutes - card_start_trip_neg_minutes, - &card_start_trip_minutes_s, - 2019); //-time - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_start_trip_minutes_s.day, - card_start_trip_minutes_s.month, - card_start_trip_minutes_s.year, - card_start_trip_minutes_s.hour, - card_start_trip_minutes_s.minute, - card_validator); - break; - } - case 0x3CCB: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - uint16_t card_tech_code = bit_lib_get_bits_32(block->data, 56, 10); //tech_code - uint16_t card_valid_to_minutes = bit_lib_get_bits_16(block->data, 66, 16); //311 - uint16_t card_valid_by_date = bit_lib_get_bits_16(block->data, 82, 16); //312 - uint8_t card_interval = bit_lib_get_bits(block->data, 98, 4); //interval - uint16_t card_app_code1 = bit_lib_get_bits_16(block->data, 102, 16); //app_code1 - uint16_t card_hash1 = bit_lib_get_bits_16(block->data, 112, 16); //502.1 - uint16_t card_type1 = bit_lib_get_bits_16(block->data, 128, 10); //type1 - uint16_t card_app_code2 = bit_lib_get_bits_16(block->data, 138, 10); //app_code2 - uint16_t card_type2 = bit_lib_get_bits_16(block->data, 148, 10); //type2 - uint16_t card_app_code3 = bit_lib_get_bits_16(block->data, 158, 10); //app_code3 - uint16_t card_type3 = bit_lib_get_bits_16(block->data, 148, 10); //type3 - uint16_t card_app_code4 = bit_lib_get_bits_16(block->data, 168, 10); //app_code4 - uint16_t card_type4 = bit_lib_get_bits_16(block->data, 178, 10); //type4 - card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502.2 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %lx", - card_view, - card_type, - card_number, - card_layout, - card_tech_code, - card_use_before_date, - card_blank_type, - card_valid_to_minutes, - card_valid_by_date, - card_interval, - card_app_code1, - card_hash1, - card_type1, - card_app_code2, - card_type2, - card_app_code3, - card_type3, - card_app_code4, - card_type4, - card_hash); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_valid_by_date, &card_use_before_date_s, 1992); - - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_validator); - break; - } - case 0x3C0B: { - card_view = bit_lib_get_bits_16(block->data, 0, 10); //101 - card_type = bit_lib_get_bits_16(block->data, 10, 10); //102 - card_number = bit_lib_get_bits_32(block->data, 20, 32); //201 - card_layout = bit_lib_get_bits(block->data, 52, 4); //111 - uint16_t card_tech_code = bit_lib_get_bits_32(block->data, 56, 10); //tech_code - uint16_t card_valid_to_minutes = bit_lib_get_bits_16(block->data, 66, 16); //311 - uint16_t card_valid_by_date = bit_lib_get_bits_16(block->data, 82, 16); //312 - uint16_t card_hash = bit_lib_get_bits_16(block->data, 112, 16); //502.1 - - FURI_LOG_D( - TAG, - "%x %x %lx %x %x %x %x %x %x %x", - card_view, - card_type, - card_number, - card_layout, - card_tech_code, - card_use_before_date, - card_blank_type, - card_valid_to_minutes, - card_valid_by_date, - card_hash); - DateTime card_use_before_date_s = {0}; - from_days_to_datetime(card_valid_by_date, &card_use_before_date_s, 1992); - - furi_string_printf( - result, - "Number: %010lu\nValid for: %02d.%02d.%04d\nValidator: %05d", - card_number, - card_use_before_date_s.day, - card_use_before_date_s.month, - card_use_before_date_s.year, - card_validator); - break; - } - default: - return false; - } - - return true; -} - static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) { bool success = true; @@ -1042,7 +90,7 @@ static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) config->data_sector = 11; config->keys = troika_1k_keys; } else if(type == MfClassicType4k) { - config->data_sector = 11; + config->data_sector = 8; // Further testing needed config->keys = troika_4k_keys; } else { success = false; @@ -1150,24 +198,30 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) { FuriString* metro_result = furi_string_alloc(); FuriString* ground_result = furi_string_alloc(); FuriString* tat_result = furi_string_alloc(); - bool result1 = parse_transport_block(&data->block[32], metro_result); - bool result2 = parse_transport_block(&data->block[28], ground_result); - bool result3 = parse_transport_block(&data->block[16], tat_result); + + bool result1 = mosgortrans_parse_transport_block(&data->block[32], metro_result); + bool result2 = mosgortrans_parse_transport_block(&data->block[28], ground_result); + bool result3 = mosgortrans_parse_transport_block(&data->block[16], tat_result); + furi_string_cat_printf(parsed_data, "\e#Troyka card\n"); if(result1) { furi_string_cat_printf( parsed_data, "\e#Metro\n%s\n", furi_string_get_cstr(metro_result)); } + if(result2) { furi_string_cat_printf( parsed_data, "\e#Ediniy\n%s\n", furi_string_get_cstr(ground_result)); } + if(result3) { furi_string_cat_printf(parsed_data, "\e#TAT\n%s\n", furi_string_get_cstr(tat_result)); } + furi_string_free(tat_result); furi_string_free(ground_result); furi_string_free(metro_result); + parsed = result1 || result2 || result3; } while(false); diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c index e4610fb94..7b72c5fc9 100644 --- a/applications/main/nfc/plugins/supported_cards/washcity.c +++ b/applications/main/nfc/plugins/supported_cards/washcity.c @@ -55,20 +55,20 @@ static bool washcity_verify(Nfc* nfc) { bool verified = false; do { - const uint8_t ticket_sector_number = 0; - const uint8_t ticket_block_number = - mf_classic_get_first_block_num_of_sector(ticket_sector_number) + 1; - FURI_LOG_D(TAG, "Verifying sector %u", ticket_sector_number); + const uint8_t verify_sector_number = 1; + const uint8_t verify_block_number = + mf_classic_get_first_block_num_of_sector(verify_sector_number); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector_number); MfClassicKey key = {0}; bit_lib_num_to_bytes_be( - washcity_1k_keys[ticket_sector_number].a, COUNT_OF(key.data), key.data); + washcity_1k_keys[verify_sector_number].a, COUNT_OF(key.data), key.data); MfClassicAuthContext auth_context; MfClassicError error = mf_classic_poller_sync_auth( - nfc, ticket_block_number, &key, MfClassicKeyTypeA, &auth_context); + nfc, verify_block_number, &key, MfClassicKeyTypeA, &auth_context); if(error != MfClassicErrorNone) { - FURI_LOG_D(TAG, "Failed to read block %u: %d", ticket_block_number, error); + FURI_LOG_D(TAG, "Failed to read block %u: %d", verify_block_number, error); break; }