Struct&var refactor for clarity

This commit is contained in:
Zachary Weiss
2024-04-09 20:19:27 -04:00
parent 33cfd6340a
commit 2150f18496

View File

@@ -94,7 +94,7 @@
// timestep is one minute // timestep is one minute
#define CHARLIE_TIME_DELTA_SECS 60 #define CHARLIE_TIME_DELTA_SECS 60
#define CHARLIE_END_VALID_DELTA_SECS 60 * 8 #define CHARLIE_END_VALID_DELTA_SECS 60 * 8
#define CHARLIE_N_TRIP_HISTORY 10 #define CHARLIE_N_TRANSACTION_HISTORY 10
#define CHARLIE_N_PASSES 4 #define CHARLIE_N_PASSES 4
typedef struct { typedef struct {
@@ -147,7 +147,7 @@ typedef struct {
uint8_t g_flag; uint8_t g_flag;
Money fare; Money fare;
uint16_t f_flag; uint16_t f_flag;
} Trip; } Transaction;
typedef struct { typedef struct {
bool valid; bool valid;
@@ -206,7 +206,7 @@ static const IdMapping charliecard_types[] = {
{.id = 166, .name = "30 Day Commuter Rail Zone 1A Pass"}, {.id = 166, .name = "30 Day Commuter Rail Zone 1A Pass"},
{.id = 167, .name = "30 Day Commuter Rail Zone 1 Pass"}, {.id = 167, .name = "30 Day Commuter Rail Zone 1 Pass"},
{.id = 168, .name = "30 Day Commuter Rail Zone 2 Pass"}, {.id = 168, .name = "30 Day Commuter Rail Zone 2 Pass"},
{.id = 169, .name = "30 Day Commuter Rail Zone 3 Pass"}, {.id = 169, .name = "30 Day ComZmuter Rail Zone 3 Pass"},
{.id = 170, .name = "30 Day Commuter Rail Zone 4 Pass"}, {.id = 170, .name = "30 Day Commuter Rail Zone 4 Pass"},
{.id = 171, .name = "30 Day Commuter Rail Zone 5 Pass"}, {.id = 171, .name = "30 Day Commuter Rail Zone 5 Pass"},
{.id = 172, .name = "30 Day Commuter Rail Zone 6 Pass"}, {.id = 172, .name = "30 Day Commuter Rail Zone 6 Pass"},
@@ -717,7 +717,11 @@ static Pass
// the uk1 and date fields, and is always set to 1 regardless // the uk1 and date fields, and is always set to 1 regardless
// same is true of type & end-validity split found in balance sector // same is true of type & end-validity split found in balance sector
// //
// // likely fields incl
// — type #,
// — a secondary date field (eg start/end, end validity or normal format)
// — ID of FVM from which the pass was loaded
// check for empty, if so, return struct filled w/ 0s // check for empty, if so, return struct filled w/ 0s
// (incl "valid" field: hence, "valid" is false-y) // (incl "valid" field: hence, "valid" is false-y)
if(pos_to_num(data, sector_num, block_num, byte_num, 6) == 0x002000000000) { if(pos_to_num(data, sector_num, block_num, byte_num, 6) == 0x002000000000) {
@@ -727,7 +731,7 @@ static Pass
// const DateTime start = date_parse(data, sector_num, block_num, byte_num + 1); // const DateTime start = date_parse(data, sector_num, block_num, byte_num + 1);
const uint16_t pre = pos_to_num(data, sector_num, block_num, byte_num, 2) >> 6; const uint16_t pre = pos_to_num(data, sector_num, block_num, byte_num, 2) >> 6;
const uint16_t post = pos_to_num(data, sector_num, block_num, byte_num + 4, 2); const uint16_t post = (pos_to_num(data, sector_num, block_num, byte_num + 4, 2) >> 2) & 0x3ff;
// these values make sense for a date, but implied position of type // these values make sense for a date, but implied position of type
// before end validity, as seen in balance sector, doesn't seem // before end validity, as seen in balance sector, doesn't seem
@@ -740,8 +744,9 @@ static Pass
return (Pass){true, pre, post, date}; return (Pass){true, pre, post, date};
} }
static Trip trip_parse(const MfClassicData* data, uint8_t sector, uint8_t block, uint8_t byte) { static Transaction
/* This function parses individual trips. Each trip packs 7 bytes, stored as follows: transaction_parse(const MfClassicData* data, uint8_t sector, uint8_t block, uint8_t byte) {
/* This function parses individual transactions. Each transaction packs 7 bytes, stored as follows:
0 1 2 3 4 5 6 0 1 2 3 4 5 6
+----.----.----+----.--+-+----.----+ +----.----.----+----.--+-+----.----+
@@ -753,23 +758,23 @@ static Trip trip_parse(const MfClassicData* data, uint8_t sector, uint8_t block,
Gate ID ("loc") is only the first 13 bits of 0x3:0x5, the final three bits appear to be flags ("f"). Gate ID ("loc") is only the first 13 bits of 0x3:0x5, the final three bits appear to be flags ("f").
Least significant flag bit seems to indicate: Least significant flag bit seems to indicate:
— When f & 1 == 0, fare (the amount by which balance is decremented) — When f & 1 == 1, fare (the amount by which balance is decremented)
— When f & 1 == 1, refill (the amount by which balance is incremented) — When f & 1 == 0, refill (the amount by which balance is incremented)
MSB (sign bit) of amt seems to serve the same role, just inverted, ie MSB (sign bit) of amt seems to serve the same role, just inverted, ie
— When amt & 0x8000 == 0x8000, fare — When amt & 0x8000 == 0, fare
— When amt & 0x8000 == 0, refill — When amt & 0x8000 == 0x8000, refill
Remaining unknown bits: Remaining unknown bits:
— f & 0b100 — f & 0b100
— f & 0b010 — f & 0b010
— amt & 1; does not seem to correspond with card type, last trip, first trip, refill v. fare, etc — amt & 1; does not seem to correspond with card type, last transaction, first transaction, refill v. fare, etc
*/ */
const DateTime date = date_parse(data, sector, block, byte); const DateTime date = date_parse(data, sector, block, byte);
const uint16_t gate = pos_to_num(data, sector, block, byte + 3, 2) >> 3; const uint16_t gate = pos_to_num(data, sector, block, byte + 3, 2) >> 3;
const uint8_t g_flag = pos_to_num(data, sector, block, byte + 3, 2) & 0b111; const uint8_t g_flag = pos_to_num(data, sector, block, byte + 3, 2) & 0b111;
const Money fare = money_parse(data, sector, block, byte + 5); const Money fare = money_parse(data, sector, block, byte + 5);
const uint16_t f_flag = pos_to_num(data, sector, block, byte + 5, 2) & 0x8001; const uint16_t f_flag = pos_to_num(data, sector, block, byte + 5, 2) & 0x8001;
return (Trip){date, gate, g_flag, fare, f_flag}; return (Transaction){date, gate, g_flag, fare, f_flag};
} }
// ********************************************************** // **********************************************************
@@ -810,10 +815,10 @@ static CounterSector counter_sector_parse(const MfClassicData* data) {
// 0x060 | uses2 | uk | ... 00 00 ... | // 0x060 | uses2 | uk | ... 00 00 ... |
// +----.----+----+----.----.----.----.----.----.----.----.----.----.----.----.----+ // +----.----+----+----.----.----.----.----.----.----.----.----.----.----.----.----+
// //
// uk := "unknown"; if nonzero, seems to only occupy the first 4 bits (ie, mask w/ 0xF0), // uk := "unknown"; if nonzero, seems to only occupy the first 4 bits (ie, uk & 0xF0 == uk),
// with the remaining 4 zero // with the remaining 4 zero
// Card has two transaction sectors (2 & 3) containing balance data, with two // Card has two sectors (2 & 3) containing balance data, with two
// corresponding trip counters in 0x50:0x51 & 0x60:0x61 (sector 1, byte 0:1 of blocks 1 & 2). // corresponding trip counters in 0x50:0x51 & 0x60:0x61 (sector 1, byte 0:1 of blocks 1 & 2).
// The *lower* of the two values *minus one* is the true use count, // The *lower* of the two values *minus one* is the true use count,
@@ -866,7 +871,7 @@ static Pass* passes_parse(const MfClassicData* data) {
// //
// 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0 1 2 3 4 5 6 7 8 9 A B C D E F
// +----.----.----.----.----.----+----+----.----.----.----.----.----+----+----.----+ // +----.----.----.----.----.----+----+----.----.----.----.----.----+----+----.----+
// 0x100 | pass0? | 00 | pass1? | 00 | crc | 0x140 // 0x100 | pass0/2? | 00 | pass1/3? | 00 | crc | 0x140
// +----.----.----.----.----.----+----+----.----.----.----.----.----+----+----.----+ // +----.----.----.----.----.----+----+----.----.----.----.----.----+----+----.----+
// 0x110 | ... 00 00 ... | crc | 0x150 // 0x110 | ... 00 00 ... | crc | 0x150
// +----.----.----.----.----.----.----.----.----.----.----.----.----.----+----.----+ // +----.----.----.----.----.----.----.----.----.----.----.----.----.----+----.----+
@@ -886,16 +891,16 @@ static Pass* passes_parse(const MfClassicData* data) {
return passes; return passes;
} }
static Trip* trips_parse(const MfClassicData* data) { static Transaction* transactions_parse(const MfClassicData* data) {
// Transaction history (Sectors 67) // Transaction history (Sectors 67)
// //
// 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0 1 2 3 4 5 6 7 8 9 A B C D E F
// +----.----.----.----.----.----.----+----.----.----.----.----.----.----+----.----+ // +----.----.----.----.----.----.----+----.----.----.----.----.----.----+----.----+
// 0x180 | trip0 | trip1 | crc | // 0x180 | transaction0 | transaction1 | crc |
// +----.----.----.----.----.----.----+----.----.----.----.----.----.----+----.----+ // +----.----.----.----.----.----.----+----.----.----.----.----.----.----+----.----+
// ... ... ... ... // ... ... ... ...
// +----.----.----.----.----.----.----+----.----.----.----.----.----.----+----.----+ // +----.----.----.----.----.----.----+----.----.----.----.----.----.----+----.----+
// 0x1D0 | trip8 | trip9 | crc | // 0x1D0 | transaction8 | transaction9 | crc |
// +----.----.----.----.----.----.----+----.----.----.----.----.----.----+----.----+ // +----.----.----.----.----.----.----+----.----.----.----.----.----.----+----.----+
// 0x1E0 | ... 00 00 ... | crc | // 0x1E0 | ... 00 00 ... | crc |
// +----.----.----.----.----.----.----.----.----.----.----.----.----.----+----.----+ // +----.----.----.----.----.----.----.----.----.----.----.----.----.----+----.----+
@@ -903,20 +908,20 @@ static Trip* trips_parse(const MfClassicData* data) {
// Transactions are not sorted, rather, appear to get overwritten // Transactions are not sorted, rather, appear to get overwritten
// sequentially. (eg, sorted modulo array rotation) // sequentially. (eg, sorted modulo array rotation)
Trip* trips = malloc(sizeof(Trip) * CHARLIE_N_TRIP_HISTORY); Transaction* transactions = malloc(sizeof(Transaction) * CHARLIE_N_TRANSACTION_HISTORY);
// Parse each trip field using some modular math magic to get the offsets: // Parse each transaction field using some modular math magic to get the offsets:
// move from sector 6 -> 7 after the first 6 trips // move from sector 6 -> 7 after the first 6 transactions
// move a block within a given sector every 2 trips, reset every 3 blocks (as sector has changed) // move a block within a given sector every 2 transactions, reset every 3 blocks (as sector has changed)
// alternate between a start byte of 0 and 7 with every iteration // alternate between a start byte of 0 and 7 with every iteration
for(size_t i = 0; i < CHARLIE_N_TRIP_HISTORY; i++) { for(size_t i = 0; i < CHARLIE_N_TRANSACTION_HISTORY; i++) {
trips[i] = trip_parse(data, 6 + (i / 6), (i / 2) % 3, (i % 2) * 7); transactions[i] = transaction_parse(data, 6 + (i / 6), (i / 2) % 3, (i % 2) * 7);
} }
// Iterate through the array to find the maximum (newest) date value // Iterate through the array to find the maximum (newest) date value
int max_idx = 0; int max_idx = 0;
for(int i = 1; i < CHARLIE_N_TRIP_HISTORY; i++) { for(int i = 1; i < CHARLIE_N_TRANSACTION_HISTORY; i++) {
if(dt_ge(trips[i].date, trips[max_idx].date)) { if(dt_ge(transactions[i].date, transactions[max_idx].date)) {
max_idx = i; max_idx = i;
} }
} }
@@ -924,24 +929,24 @@ static Trip* trips_parse(const MfClassicData* data) {
// Sort by rotating // Sort by rotating
for(int r = 0; r < (max_idx + 1); r++) { for(int r = 0; r < (max_idx + 1); r++) {
// Store the first element // Store the first element
Trip temp = trips[0]; Transaction temp = transactions[0];
// Shift elements to the left // Shift elements to the left
for(int i = 0; i < CHARLIE_N_TRIP_HISTORY - 1; i++) { for(int i = 0; i < CHARLIE_N_TRANSACTION_HISTORY - 1; i++) {
trips[i] = trips[i + 1]; transactions[i] = transactions[i + 1];
} }
// Move the first element to the last // Move the first element to the last
trips[CHARLIE_N_TRIP_HISTORY - 1] = temp; transactions[CHARLIE_N_TRANSACTION_HISTORY - 1] = temp;
} }
// Reverse order, such that newest is first, oldest last // Reverse order, such that newest is first, oldest last
for(int i = 0; i < CHARLIE_N_TRIP_HISTORY / 2; i++) { for(int i = 0; i < CHARLIE_N_TRANSACTION_HISTORY / 2; i++) {
// Swap elements at index i and size - i - 1 // Swap elements at index i and size - i - 1
Trip temp = trips[i]; Transaction temp = transactions[i];
trips[i] = trips[CHARLIE_N_TRIP_HISTORY - i - 1]; transactions[i] = transactions[CHARLIE_N_TRANSACTION_HISTORY - i - 1];
trips[CHARLIE_N_TRIP_HISTORY - i - 1] = temp; transactions[CHARLIE_N_TRANSACTION_HISTORY - i - 1] = temp;
} }
return trips; return transactions;
} }
/* /*
@@ -976,12 +981,12 @@ static DateTime expiry(DateTime iss) {
return exp; return exp;
} }
static bool expired(DateTime expiry, DateTime last_trip) { static bool expired(DateTime expiry, DateTime last_transaction) {
// if a card has sat unused for >2 years, expired (verify this claim?) // if a card has sat unused for >2 years, expired (verify this claim?)
// else expired if current date > expiry date // else expired if current date > expiry date
uint32_t ts_exp = datetime_datetime_to_timestamp(&expiry); uint32_t ts_exp = datetime_datetime_to_timestamp(&expiry);
uint32_t ts_last = datetime_datetime_to_timestamp(&last_trip); uint32_t ts_last = datetime_datetime_to_timestamp(&last_transaction);
uint32_t ts_now = time_now(); uint32_t ts_now = time_now();
return (ts_exp <= ts_now) | ((ts_now - ts_last) >= (2 * 365 * 24 * 60 * 60)); return (ts_exp <= ts_now) | ((ts_now - ts_last) >= (2 * 365 * 24 * 60 * 60));
@@ -1020,7 +1025,8 @@ void type_format_cat(FuriString* out, uint16_t type) {
void pass_format_cat(FuriString* out, Pass pass) { void pass_format_cat(FuriString* out, Pass pass) {
furi_string_cat_printf(out, "\n-Pre: %b", pass.pre); furi_string_cat_printf(out, "\n-Pre: %b", pass.pre);
// type_format_cat(out, pass.type); // type_format_cat(out, pass.type);
furi_string_cat_printf(out, "\n-Post: %b", pass.post); furi_string_cat_printf(out, "\n-Post: ");
type_format_cat(out, pass.post);
// locale_format_dt_cat(out, &pass.start); // locale_format_dt_cat(out, &pass.start);
furi_string_cat_printf(out, "\n-Date: "); furi_string_cat_printf(out, "\n-Date: ");
locale_format_dt_cat(out, &pass.date); locale_format_dt_cat(out, &pass.date);
@@ -1055,37 +1061,37 @@ void money_format_cat(FuriString* out, Money money) {
furi_string_cat_printf(out, "$%u.%02u", money.dollars, money.cents); furi_string_cat_printf(out, "$%u.%02u", money.dollars, money.cents);
} }
void trip_format_cat(FuriString* out, Trip trip) { void transaction_format_cat(FuriString* out, Transaction transaction) {
const char* sep = " "; const char* sep = " ";
const char* sta; const char* sta;
locale_format_dt_cat(out, &trip.date); locale_format_dt_cat(out, &transaction.date);
furi_string_cat_printf(out, "\n%s", !!(trip.g_flag & 0x1) ? "-" : "+"); furi_string_cat_printf(out, "\n%s", !!(transaction.g_flag & 0x1) ? "-" : "+");
money_format_cat(out, trip.fare); money_format_cat(out, transaction.fare);
if(!!(trip.g_flag & 0x1) && (trip.fare.dollars == FARE_BUS.dollars) && if(!!(transaction.g_flag & 0x1) && (transaction.fare.dollars == FARE_BUS.dollars) &&
(trip.fare.cents == FARE_BUS.cents)) { (transaction.fare.cents == FARE_BUS.cents)) {
// if not a refill, and the fare amount is equal to bus fare (any better approach? flag bits for modality?) // if not a refill, and the fare amount is equal to bus fare (any better approach? flag bits for modality?)
// format for bus — supposedly some correlation between gate ID & bus #, haven't investigated // format for bus — supposedly some correlation between gate ID & bus #, haven't investigated
furi_string_cat_printf(out, "%s#%u", sep, trip.gate); furi_string_cat_printf(out, "%s#%u", sep, transaction.gate);
} else if(get_map_item(trip.gate, charliecard_fare_gate_ids, kNumFareGateIds, &sta)) { } else if(get_map_item(transaction.gate, charliecard_fare_gate_ids, kNumFareGateIds, &sta)) {
// station found in fare gate ID map, append station name // station found in fare gate ID map, append station name
furi_string_cat_str(out, sep); furi_string_cat_str(out, sep);
furi_string_cat_str(out, sta); furi_string_cat_str(out, sta);
} else { } else {
// no found station in fare gate ID map & not a bus, just print ID w/o add'l info // no found station in fare gate ID map & not a bus, just print ID w/o add'l info
furi_string_cat_printf(out, "%s#%u", sep, trip.gate); furi_string_cat_printf(out, "%s#%u", sep, transaction.gate);
} }
// print flags for debugging purposes // print flags for debugging purposes
if(is_debug()) { if(is_debug()) {
furi_string_cat_printf(out, "%s%x%s%x", sep, trip.g_flag, sep, trip.f_flag); furi_string_cat_printf(out, "%s%x%s%x", sep, transaction.g_flag, sep, transaction.f_flag);
} }
} }
void trips_format_cat(FuriString* out, Trip* trips) { void transactions_format_cat(FuriString* out, Transaction* transactions) {
furi_string_cat_printf(out, "\nTransactions:"); furi_string_cat_printf(out, "\nTransactions:");
for(size_t i = 0; i < CHARLIE_N_TRIP_HISTORY; i++) { for(size_t i = 0; i < CHARLIE_N_TRANSACTION_HISTORY; i++) {
furi_string_cat_printf(out, "\n"); furi_string_cat_printf(out, "\n");
trip_format_cat(out, trips[i]); transaction_format_cat(out, transactions[i]);
furi_string_cat_printf(out, "\n"); furi_string_cat_printf(out, "\n");
} }
} }
@@ -1126,7 +1132,7 @@ static bool charliecard_parse(const NfcDevice* device, FuriString* parsed_data)
const BalanceSector balance_sector = const BalanceSector balance_sector =
balance_sector_parse(data, counter_sector.active_balance_sector); balance_sector_parse(data, counter_sector.active_balance_sector);
Pass* passes = passes_parse(data); Pass* passes = passes_parse(data);
Trip* trips = trips_parse(data); Transaction* transactions = transactions_parse(data);
// print/append card data // print/append card data
furi_string_cat_printf(parsed_data, "\e#CharlieCard"); furi_string_cat_printf(parsed_data, "\e#CharlieCard");
@@ -1152,8 +1158,8 @@ static bool charliecard_parse(const NfcDevice* device, FuriString* parsed_data)
passes_format_cat(parsed_data, passes); passes_format_cat(parsed_data, passes);
free(passes); free(passes);
trips_format_cat(parsed_data, trips); transactions_format_cat(parsed_data, transactions);
free(trips); free(transactions);
parsed = true; parsed = true;
} while(false); } while(false);