Skip to content

Instantly share code, notes, and snippets.

@micolous
Last active September 12, 2023 14:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save micolous/5884e82a5ea7aff6c1df to your computer and use it in GitHub Desktop.
Save micolous/5884e82a5ea7aff6c1df to your computer and use it in GitHub Desktop.
Requires mfoc dump in mfc format (1 kilobyte)
import argparse, struct, base64, datetime
MODES = {
1: 'Value-add machine',
4: 'Bus',
5: 'Train',
18: 'Ferry',
}
STATIONS = {
4: 'Fortitude Valley', # place_forsta
5: 'Central', # place_censta
9: 'Domestic Airport', # place_domsta
20: 'Roma Street', # place_romsta
63: 'Nundah', # place_nunsta
88: 'Fairfield', # place_faista
194: 'New Farm Park', # 317588
614: 'Wickham St @ Warner St (St 229)', # 000229
2423: 'Beatrice Street - 22', # 002944
2425: 'Beatrice Terrace - 22', # 002946
4006: 'Bretts Wharf - 19', # 005031
19513: 'Bretts Wharf', # 319743
}
class Metadata(object):
def __init__(self, record):
self.raw = record
self.balance_index = ord(record[13])
if self.balance_index:
self.balance = struct.unpack('<h', record[2:4])[0]
else:
self.balance = None
def __cmp__(self, other):
return cmp(self.balance_index, other.balance_index)
def __str__(self):
return """MetadataRecord
%(raw)s
- balance index: %(balance_index)d
- balance: %(balance)s cents
""" % dict(
balance=self.balance,
raw=base64.b16encode(self.raw),
balance_index=self.balance_index)
class Transaction(object):
def __init__(self, record):
self.raw = record
#print 'TripRecord'# (%d cents)' % value
self.mode = ord(record[1])
#print ' - mode: %s (%d)' % (MODES.get(mode, None), mode)
self.ts = unpack_date(record[2:6])
#print ' - ts: %s' % (dt.isoformat(),)
if record[1] == '\x01': # value-add machine
self.credit = struct.unpack('<H', record[6:8])[0]
self.journey = None
#print ' - added credit: %d cents' % credit
else:
self.journey = (struct.unpack('<H', record[5:7])[0]) >> 5
self.credit = None
#print ' - journey: %d' % (journey,)
self.station, self.checksum = struct.unpack('<HH', record[12:16])
#print ' - station: %d (%s)' % (station, STATIONS.get(station))
#print ' - checksum: %04x' % checksum
def __cmp__(self, other):
return cmp(self.ts, other.ts)
def __str__(self):
return """TripRecord
%(raw)s
- ts: %(ts)s
- mode: %(mode)d (%(modes)s)
- credit: %(credit)s cents
- journey: %(journey)s
- station: %(station)d (%(stations)s)
""" % dict(
raw=base64.b16encode(self.raw),
mode=self.mode,
modes=MODES.get(self.mode),
credit=self.credit,
journey=self.journey,
station=self.station,
stations=STATIONS.get(self.station),
ts=self.ts.isoformat())
def unpack_date(d):
# 0001111 1100 00100 = 2015-12-04
# yyyyyyy mmmm ddddd
date, minute = struct.unpack('<HH', d)
minute &= 2047
year = (date >> 9) + 2000
month = (date >> 5) & 0b1111
day = date & 0b11111
return datetime.datetime(year, month, day, minute // 60, minute % 60, 0)
def gocard(input_f):
record = input_f.read(16)
# Read card UID
uid = struct.unpack('<L', record[:4])[0]
print 'Go Card number: 016%012dX' % uid
record = input_f.read(16)
transactions = []
metadatas = []
rnum = 0
while record:
rnum += 1
if rnum % 4 == 3:
# key record
pass
elif record[0] in '\x01\x31' and record[1] != '\0':
if record[0] == '\x31' or record[:2] == '\x01\x01':
transactions.append(Transaction(record))
elif record[0] == '\x01':
metadatas.append(Metadata(record))
record = input_f.read(16)
print 'Transactions:'
transactions.sort()
for t in transactions: print t
print 'Metadatas:'
metadatas.sort()
for m in metadatas: print m
input_f.close()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('input', nargs=1, type=argparse.FileType('rb'))
options = parser.parse_args()
gocard(options.input[0])
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment