diff --git a/applications/main/nfc/plugins/supported_cards/clipper.c b/applications/main/nfc/plugins/supported_cards/clipper.c index 62d06e6ef..860c1c72e 100644 --- a/applications/main/nfc/plugins/supported_cards/clipper.c +++ b/applications/main/nfc/plugins/supported_cards/clipper.c @@ -24,8 +24,8 @@ #include #include -#include #include +#include #include // @@ -173,7 +173,6 @@ static void furi_string_cat_timestamp( const char* date_hdr, const char* time_hdr, uint32_t tmst_1900); -static void epoch_1900_datetime_to_furi(uint32_t seconds, DateTime* out); static bool get_file_contents( const MfDesfireApplication* app, const MfDesfireFileId* id, @@ -530,7 +529,7 @@ static void furi_string_cat_timestamp( uint32_t tmst_1900) { DateTime tm; - epoch_1900_datetime_to_furi(tmst_1900, &tm); + datetime_timestamp_to_datetime(tmst_1900, &tm); FuriString* date_str = furi_string_alloc(); locale_format_date(date_str, &tm, locale_get_date_format(), "-"); @@ -550,57 +549,6 @@ static void furi_string_cat_timestamp( furi_string_free(time_str); } -// Convert a "1900"-based timestamp to Furi time, assuming a UTC/GMT timezone. -static void epoch_1900_datetime_to_furi(uint32_t seconds, DateTime* out) { - uint16_t year, month, day, hour, minute, second; - - // Calculate absolute number of days elapsed since the 1900 epoch - // and save the residual for the time within the day. - uint32_t absolute_days = seconds / 86400; - uint32_t seconds_within_day = seconds % 86400; - - // Calculate day of the week. - // January 1, 1900 was a Monday ("day of week" = 1) - uint8_t dow = (absolute_days + 1) % 7; - - // - // Compute the date by simply marching through time in as large chunks - // as possible. - // - - for(year = 1900;; year++) { - uint16_t year_days = datetime_get_days_per_year(year); - if(absolute_days >= year_days) - absolute_days -= year_days; - else - break; - } - - bool is_leap = datetime_is_leap_year(year); - - for(month = 1;; month++) { - uint8_t days_in_month = datetime_get_days_per_month(is_leap, month); - if(absolute_days >= days_in_month) - absolute_days -= days_in_month; - else - break; - } - - day = absolute_days + 1; - hour = seconds_within_day / 3600; - uint16_t sub_hour = seconds_within_day % 3600; - minute = sub_hour / 60; - second = sub_hour % 60; - - out->year = year; - out->month = month; - out->day = day; - out->hour = hour; - out->minute = minute; - out->second = second; - out->weekday = dow; -} - /* Actual implementation of app<>plugin interface */ static const NfcSupportedCardsPlugin clipper_plugin = { .protocol = NfcProtocolMfDesfire, diff --git a/applications/main/nfc/plugins/supported_cards/emv.c b/applications/main/nfc/plugins/supported_cards/emv.c index 4d9372be6..c79e76a00 100644 --- a/applications/main/nfc/plugins/supported_cards/emv.c +++ b/applications/main/nfc/plugins/supported_cards/emv.c @@ -23,6 +23,10 @@ #include "protocols/emv/emv.h" #include "helpers/nfc_emv_parser.h" +#include +#include +#include + #define TAG "EMV" bool emv_get_currency_name(uint16_t cur_code, FuriString* currency_name) { @@ -96,34 +100,49 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { parsed = true; } - if(app.effective_month) { - char day[] = "--"; - if(app.effective_day) itoa(app.effective_day, day, 16); - if(day[1] == '\0') { - day[1] = day[0]; - day[0] = '0'; - } + bool nevermind = false; + DateTime effective_datetime = { + 0, + 0, + 0, + bit_lib_bytes_to_num_bcd(&app.effective_day, 1, &nevermind), + bit_lib_bytes_to_num_bcd(&app.effective_month, 1, &nevermind), + 2000 + bit_lib_bytes_to_num_bcd(&app.effective_year, 1, &nevermind), + 0}; + DateTime expiration_datetime = { + 0, + 0, + 0, + bit_lib_bytes_to_num_bcd(&app.exp_day, 1, &nevermind), + bit_lib_bytes_to_num_bcd(&app.exp_month, 1, &nevermind), + 2000 + bit_lib_bytes_to_num_bcd(&app.exp_year, 1, &nevermind), + 0}; + LocaleDateFormat date_format = locale_get_date_format(); + const char* separator = (date_format == LocaleDateFormatDMY) ? "." : "/"; + + FuriString* effective_date_str = furi_string_alloc(); + locale_format_date(effective_date_str, &effective_datetime, date_format, separator); + + FuriString* expiration_date_str = furi_string_alloc(); + locale_format_date(expiration_date_str, &expiration_datetime, date_format, separator); + + if(app.effective_month) { furi_string_cat_printf( parsed_data, - "Effective: %s.%02X.20%02X\n", - day, - app.effective_month, - app.effective_year); + "Effective: %s\n", + app.effective_day ? furi_string_get_cstr(effective_date_str) : + furi_string_get_cstr(effective_date_str) + 3); parsed = true; } if(app.exp_month) { - char day[] = "--"; - if(app.exp_day) itoa(app.exp_day, day, 16); - if(day[1] == '\0') { - day[1] = day[0]; - day[0] = '0'; - } - furi_string_cat_printf( - parsed_data, "Expires: %s.%02X.20%02X\n", day, app.exp_month, app.exp_year); + parsed_data, + "Expires: %s\n", + app.exp_day ? furi_string_get_cstr(expiration_date_str) : + furi_string_get_cstr(expiration_date_str) + 3); parsed = true; } @@ -154,6 +173,10 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) { if(!parsed) furi_string_cat_printf(parsed_data, "No data was parsed\n"); + furi_string_free(str); + furi_string_free(effective_date_str); + furi_string_free(expiration_date_str); + parsed = true; } while(false); diff --git a/applications/main/nfc/plugins/supported_cards/itso.c b/applications/main/nfc/plugins/supported_cards/itso.c index ea7314048..a5b8b43ad 100644 --- a/applications/main/nfc/plugins/supported_cards/itso.c +++ b/applications/main/nfc/plugins/supported_cards/itso.c @@ -89,6 +89,7 @@ static bool itso_parse(const NfcDevice* device, FuriString* parsed_data) { DateTime timestamp = {0}; datetime_timestamp_to_datetime(unixTimestamp, ×tamp); + FuriString* timestamp_str = furi_string_alloc(); locale_format_date(timestamp_str, ×tamp, locale_get_date_format(), "-"); diff --git a/applications/main/nfc/plugins/supported_cards/kazan.c b/applications/main/nfc/plugins/supported_cards/kazan.c index ffd3a4871..3c71a3c74 100644 --- a/applications/main/nfc/plugins/supported_cards/kazan.c +++ b/applications/main/nfc/plugins/supported_cards/kazan.c @@ -23,6 +23,7 @@ #include #include +#include #define TAG "Kazan" @@ -317,74 +318,75 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_cat_printf( parsed_data, "\e#Kazan transport card\nCard number: %lu\n", card_number); + LocaleDateFormat date_format = locale_get_date_format(); + const char* separator = (date_format == LocaleDateFormatDMY) ? "." : "/"; + + FuriString* valid_from_str = furi_string_alloc(); + locale_format_date(valid_from_str, &valid_from, date_format, separator); + + FuriString* valid_to_str = furi_string_alloc(); + locale_format_date(valid_to_str, &valid_to, date_format, separator); + + FuriString* last_trip_date_str = furi_string_alloc(); + locale_format_date(last_trip_date_str, &last_trip, date_format, separator); + + FuriString* last_trip_time_str = furi_string_alloc(); + locale_format_time(last_trip_time_str, &last_trip, locale_get_time_format(), false); + if(subscription_type == SUBSCRIPTION_TYPE_PURSE) { furi_string_cat_printf( parsed_data, - "Type: purse\nBalance: %lu RUR\nBalance valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u", + "Type: purse\nBalance: %lu RUR\nBalance valid:\nfrom: %s\nto: %s", trip_counter, - valid_from.day, - valid_from.month, - valid_from.year, - valid_to.day, - valid_to.month, - valid_to.year); + furi_string_get_cstr(valid_from_str), + furi_string_get_cstr(valid_to_str)); } if(subscription_type == SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS) { furi_string_cat_printf( parsed_data, - "Type: abonnement\nTariff: %s\nTrips left: %lu\nCard valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u", + "Type: abonnement\nTariff: %s\nTrips left: %lu\nCard valid:\nfrom: %s\nto: %s", furi_string_get_cstr(tariff_name), trip_counter, - valid_from.day, - valid_from.month, - valid_from.year, - valid_to.day, - valid_to.month, - valid_to.year); + furi_string_get_cstr(valid_from_str), + furi_string_get_cstr(valid_to_str)); } if(subscription_type == SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME) { furi_string_cat_printf( parsed_data, - "Type: abonnement\nTariff: %s\nTotal valid time: %lu days\nCard valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u", + "Type: abonnement\nTariff: %s\nTotal valid time: %lu days\nCard valid:\nfrom: %s\nto: %s", furi_string_get_cstr(tariff_name), trip_counter, - valid_from.day, - valid_from.month, - valid_from.year, - valid_to.day, - valid_to.month, - valid_to.year); + furi_string_get_cstr(valid_from_str), + furi_string_get_cstr(valid_to_str)); } if(subscription_type == SUBSCRIPTION_TYPE_UNKNOWN) { furi_string_cat_printf( parsed_data, - "Type: unknown\nTariff: %s\nCounter: %lu\nValid from: %02u.%02u.%u\nValid to: %02u.%02u.%u", + "Type: unknown\nTariff: %s\nCounter: %lu\nValid from: %s\nValid to: %s", furi_string_get_cstr(tariff_name), trip_counter, - valid_from.day, - valid_from.month, - valid_from.year, - valid_to.day, - valid_to.month, - valid_to.year); + furi_string_get_cstr(valid_from_str), + furi_string_get_cstr(valid_to_str)); } if(is_last_trip_valid) { furi_string_cat_printf( parsed_data, - "\nLast trip: %02u.%02u.%u at %02u:%02u", - last_trip.day, - last_trip.month, - last_trip.year, - last_trip.hour, - last_trip.minute); + "\nLast trip: %s at %s", + furi_string_get_cstr(last_trip_date_str), + furi_string_get_cstr(last_trip_time_str)); } furi_string_free(tariff_name); + furi_string_free(valid_from_str); + furi_string_free(valid_to_str); + furi_string_free(last_trip_date_str); + furi_string_free(last_trip_time_str); + parsed = true; } while(false); diff --git a/applications/main/nfc/plugins/supported_cards/opal.c b/applications/main/nfc/plugins/supported_cards/opal.c index f6a4d22a2..0fb5d95f1 100644 --- a/applications/main/nfc/plugins/supported_cards/opal.c +++ b/applications/main/nfc/plugins/supported_cards/opal.c @@ -82,7 +82,7 @@ static_assert(sizeof(OpalFile) == 16, "OpalFile"); // // Opal measures days since 1980-01-01 and minutes since midnight, and presumes // all days are 1440 minutes. -static void opal_date_time_to_furi(uint16_t days, uint16_t minutes, DateTime* out) { +static void opal_days_minutes_to_datetime(uint16_t days, uint16_t minutes, DateTime* out) { out->year = 1980; out->month = 1; // 1980-01-01 is a Tuesday @@ -155,7 +155,7 @@ static bool opal_parse(const NfcDevice* device, FuriString* parsed_data) { const int32_t balance_dollars = balance / 100; DateTime timestamp; - opal_date_time_to_furi(opal_file->days, opal_file->minutes, ×tamp); + opal_days_minutes_to_datetime(opal_file->days, opal_file->minutes, ×tamp); // Usages 4..6 associated with the Manly Ferry, which correspond to // usages 1..3 for other modes. diff --git a/applications/main/nfc/plugins/supported_cards/umarsh.c b/applications/main/nfc/plugins/supported_cards/umarsh.c index 9ee8e48ce..7a84a835c 100644 --- a/applications/main/nfc/plugins/supported_cards/umarsh.c +++ b/applications/main/nfc/plugins/supported_cards/umarsh.c @@ -30,6 +30,7 @@ #include #include +#include #define TAG "Umarsh" @@ -91,6 +92,19 @@ static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) { bool is_last_refill_datetime_valid = parse_datetime(last_refill_date, &last_refill_datetime); + LocaleDateFormat date_format = locale_get_date_format(); + const char* separator = (date_format == LocaleDateFormatDMY) ? "." : "/"; + + FuriString* expiry_datetime_str = furi_string_alloc(); + locale_format_date(expiry_datetime_str, &expiry_datetime, date_format, separator); + + FuriString* valid_to_datetime_str = furi_string_alloc(); + locale_format_date(valid_to_datetime_str, &valid_to_datetime, date_format, separator); + + FuriString* last_refill_datetime_str = furi_string_alloc(); + locale_format_date( + last_refill_datetime_str, &last_refill_datetime, date_format, separator); + furi_string_cat_printf( parsed_data, "\e#Umarsh\nCard number: %lu\nRegion: %02u\nTerminal number: %lu\nRefill counter: %u\nBalance: %u.%02u RUR", @@ -103,25 +117,17 @@ static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) { if(is_expiry_datetime_valid) furi_string_cat_printf( - parsed_data, - "\nExpires: %02u.%02u.%u", - expiry_datetime.day, - expiry_datetime.month, - expiry_datetime.year); + parsed_data, "\nExpires: %s", furi_string_get_cstr(expiry_datetime_str)); if(is_valid_to_datetime_valid) furi_string_cat_printf( - parsed_data, - "\nValid to: %02u.%02u.%u", - valid_to_datetime.day, - valid_to_datetime.month, - valid_to_datetime.year); + parsed_data, "\nValid to: %s", furi_string_get_cstr(valid_to_datetime_str)); if(is_last_refill_datetime_valid) furi_string_cat_printf( - parsed_data, - "\nLast refill: %02u.%02u.%u", - last_refill_datetime.day, - last_refill_datetime.month, - last_refill_datetime.year); + parsed_data, "\nLast refill: %s", furi_string_get_cstr(last_refill_datetime_str)); + + furi_string_free(expiry_datetime_str); + furi_string_free(valid_to_datetime_str); + furi_string_free(last_refill_datetime_str); parsed = true; } while(false); diff --git a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c index 5ca9b3517..3a07b95db 100644 --- a/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c +++ b/applications/main/nfc/plugins/supported_cards/zolotaya_korona.c @@ -24,6 +24,7 @@ #include "protocols/mf_classic/mf_classic.h" #include +#include #include #define TAG "Zolotaya Korona" @@ -124,6 +125,23 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da uint32_t balance_rub = balance / 100; uint8_t balance_kop = balance % 100; + LocaleDateFormat date_format = locale_get_date_format(); + const char* separator = (date_format == LocaleDateFormatDMY) ? "." : "/"; + + FuriString* last_refill_date_str = furi_string_alloc(); + locale_format_date(last_refill_date_str, &last_refill_datetime, date_format, separator); + + FuriString* last_refill_time_str = furi_string_alloc(); + locale_format_time( + last_refill_time_str, &last_refill_datetime, locale_get_time_format(), false); + + FuriString* last_trip_date_str = furi_string_alloc(); + locale_format_date(last_trip_date_str, &last_trip_datetime, date_format, separator); + + FuriString* last_trip_time_str = furi_string_alloc(); + locale_format_time( + last_trip_time_str, &last_trip_datetime, locale_get_time_format(), false); + furi_string_cat_printf( parsed_data, "\e#Zolotaya korona\nCard number: %u%015llu\nRegion: %u\nBalance: %lu.%02u RUR\nPrev. balance: %lu.%02u RUR", @@ -137,25 +155,19 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da furi_string_cat_printf( parsed_data, - "\nLast refill amount: %lu.%02u RUR\nRefill counter: %u\nLast refill: %u.%02u.%02u %02u:%02u\nRefill machine id: %u", + "\nLast refill amount: %lu.%02u RUR\nRefill counter: %u\nLast refill: %s at %s\nRefill machine id: %u", last_refill_amount_rub, last_refill_amount_kop, refill_counter, - last_refill_datetime.day, - last_refill_datetime.month, - last_refill_datetime.year, - last_refill_datetime.hour, - last_refill_datetime.minute, + furi_string_get_cstr(last_refill_date_str), + furi_string_get_cstr(last_refill_time_str), refill_machine_id); furi_string_cat_printf( parsed_data, - "\nLast trip: %u.%02u.%02u %02u:%02u\nTrack number: %u\nValidator: %c%06lu", - last_trip_datetime.day, - last_trip_datetime.month, - last_trip_datetime.year, - last_trip_datetime.hour, - last_trip_datetime.minute, + "\nLast trip: %s at %s\nTrack number: %u\nValidator: %c%06lu", + furi_string_get_cstr(last_trip_date_str), + furi_string_get_cstr(last_trip_time_str), track_number, validator_first_letter, validator_id); @@ -169,6 +181,12 @@ static bool zolotaya_korona_parse(const NfcDevice* device, FuriString* parsed_da discount_code); } + furi_string_free(last_refill_date_str); + furi_string_free(last_refill_time_str); + + furi_string_free(last_trip_date_str); + furi_string_free(last_trip_time_str); + parsed = true; } while(false);