From ed57ddb14be48be3134bbca57726cff247e1d6e1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 29 Oct 2024 02:28:33 +0300 Subject: [PATCH] NFC: Plaintain parser fix by mxcdoam https://github.com/flipperdevices/flipperzero-firmware/pull/3975/files --- .../nfc/plugins/supported_cards/plantain.c | 84 +++++++++++++++++-- 1 file changed, 77 insertions(+), 7 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index c38140de27..9f2491691b 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -201,8 +201,9 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) { static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { furi_assert(device); - + size_t uid_len = 0; const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); bool parsed = false; @@ -220,12 +221,30 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { if(key != cfg.keys[cfg.data_sector].a) break; furi_string_printf(parsed_data, "\e#Plantain card\n"); + + const uint8_t* temp_ptr = &uid[0]; + + // UID is read from last to first byte + uint8_t card_number_tmp[uid_len]; + + if(uid_len == 4) { + for(size_t i = 0; i < 4; i++) { + card_number_tmp[i] = temp_ptr[3 - i]; + } + } else if(uid_len == 7) { + for(size_t i = 0; i < 7; i++) { + card_number_tmp[i] = temp_ptr[6 - i]; + } + } else { + break; + } + //UID is converted to a card number uint64_t card_number = 0; - for(size_t i = 0; i < 7; i++) { - card_number = (card_number << 8) | data->block[0].data[6 - i]; + for(size_t i = 0; i < uid_len; i++) { + card_number = (card_number << 8) | card_number_tmp[i]; } - // Print card number with 4-digit groups + // Print card number with 4-digit groups. "3" in "3078" denotes a ticket type "3 - full ticket", will differ on discounted cards. furi_string_cat_printf(parsed_data, "Number: "); FuriString* card_number_s = furi_string_alloc(); furi_string_cat_printf(card_number_s, "%llu", card_number); @@ -237,6 +256,7 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_push_back(tmp_s, ' '); } furi_string_cat_printf(parsed_data, "%s\n", furi_string_get_cstr(tmp_s)); + // this works for 2K Plantain if(data->type == MfClassicType1k) { //balance uint32_t balance = 0; @@ -290,20 +310,70 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { last_payment_date.year, last_payment_date.hour, last_payment_date.minute); - //payment summ + //payment amount. This needs to be investigated more, currently it shows incorrect amount on some cards. uint16_t last_payment = (data->block[18].data[9] << 8) | data->block[18].data[8]; furi_string_cat_printf(parsed_data, "Amount: %d rub", last_payment / 100); furi_string_free(card_number_s); furi_string_free(tmp_s); + //This is for 4K Plantains. } else if(data->type == MfClassicType4k) { + //balance + uint32_t balance = 0; + for(uint8_t i = 0; i < 4; i++) { + balance = (balance << 8) | data->block[16].data[3 - i]; + } + furi_string_cat_printf(parsed_data, "Balance: %ld rub\n", balance / 100); + //trips - uint8_t trips_metro = data->block[36].data[0]; - uint8_t trips_ground = data->block[36].data[1]; + uint8_t trips_metro = data->block[21].data[0]; + uint8_t trips_ground = data->block[21].data[1]; furi_string_cat_printf(parsed_data, "Trips: %d\n", trips_metro + trips_ground); + //trip time + uint32_t last_trip_timestamp = 0; + for(uint8_t i = 0; i < 3; i++) { + last_trip_timestamp = (last_trip_timestamp << 8) | data->block[21].data[4 - i]; + } + DateTime last_trip = {0}; + from_minutes_to_datetime(last_trip_timestamp + 24 * 60, &last_trip, 2010); + furi_string_cat_printf( + parsed_data, + "Trip start: %02d.%02d.%04d %02d:%02d\n", + last_trip.day, + last_trip.month, + last_trip.year, + last_trip.hour, + last_trip.minute); + //validator + uint16_t validator = (data->block[20].data[5] << 8) | data->block[20].data[4]; + furi_string_cat_printf(parsed_data, "Validator: %d\n", validator); + //tariff + uint16_t fare = (data->block[20].data[7] << 8) | data->block[20].data[6]; + furi_string_cat_printf(parsed_data, "Tariff: %d rub\n", fare / 100); //trips in metro furi_string_cat_printf(parsed_data, "Trips (Metro): %d\n", trips_metro); //trips on ground furi_string_cat_printf(parsed_data, "Trips (Ground): %d\n", trips_ground); + //last payment + uint32_t last_payment_timestamp = 0; + for(uint8_t i = 0; i < 3; i++) { + last_payment_timestamp = (last_payment_timestamp << 8) | + data->block[18].data[4 - i]; + } + DateTime last_payment_date = {0}; + from_minutes_to_datetime(last_payment_timestamp + 24 * 60, &last_payment_date, 2010); + furi_string_cat_printf( + parsed_data, + "Last pay: %02d.%02d.%04d %02d:%02d\n", + last_payment_date.day, + last_payment_date.month, + last_payment_date.year, + last_payment_date.hour, + last_payment_date.minute); + //payment amount + uint16_t last_payment = (data->block[18].data[9] << 8) | data->block[18].data[8]; + furi_string_cat_printf(parsed_data, "Amount: %d rub", last_payment / 100); + furi_string_free(card_number_s); + furi_string_free(tmp_s); } parsed = true; } while(false);