Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@micolous
Created January 29, 2017 13:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save micolous/c9f7ed9e4c593a7eed18371ec4266a4f to your computer and use it in GitHub Desktop.
Save micolous/c9f7ed9e4c593a7eed18371ec4266a4f to your computer and use it in GitHub Desktop.
Prototype reader for Greencard (TAS) card dumps.
#!/usr/bin/env python
# -*- mode: python; indent-tabs-mode: nil; tab-width: 2 -*-
"""
greencard_reader.py
Prototype reader for Greencard (TAS) card dumps.
See https://github.com/micolous/metrodroid/wiki/Greencard for format details.
Copyright 2017 Michael Farrell <micolous+git@gmail.com>
Note: This loads the entire file into memory blindly. Do not use on large file
sizes, as the memory usage of this file is extremely inefficient.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from __future__ import print_function
from argparse import ArgumentParser, FileType
from struct import unpack
from datetime import datetime, timedelta
from pytz import utc, timezone
EPOCH = utc.localize(datetime(1970,1,1,0,0))
HOBART_TZ = timezone('Australia/Hobart')
def parse_greencard(input_data):
if len(input_data) != 4096:
raise Exception, 'mfc data must be exactly 4096 bytes'
# TODO: read the other headers
# Read trips
off = 0x800
while off < 0xcf0:
if off % 0x100 >= 0xf0:
# This is a key, skip it
off += 0x10
continue
record_type = unpack('>L', input_data[off:off+0x4])[0]
if record_type & 0xffff00 != 0x011000:
# We wanted a record header.
off += 0x10
continue
is_credit = (record_type & 0xff000000) == 0x31000000
# Get the timestamp for the transaction
ts = unpack('>L', input_data[off+0xc:off+0x10])[0]
ts = (EPOCH + timedelta(seconds=ts)).astimezone(HOBART_TZ)
txn_no, unknown_14, route = unpack('>LL2x6s', input_data[off+0x10:off+0x20])
route = route.strip('\0')
if is_credit:
new_balance = unpack('>L', input_data[off+0x26:off+0x2a])[0]
amount = 0.
else:
amount, new_balance = unpack('>H4xL', input_data[off+0x22:off+0x2c])
print('@0x%03x : %s' % (off, ts.isoformat()))
print('record type = %08x (%s)' % (record_type, 'credit' if is_credit else 'trip'))
print(' txn num = %i' % txn_no)
print(' unknown 14 = %i' % unknown_14)
print(' bus route = Route %s' % route)
print(' value = $%0.2f (new balance $%0.2f)' % (amount/100., new_balance/100.))
print()
off += 0x50
def main():
parser = ArgumentParser()
parser.add_argument('input', type=FileType('rb'), nargs=1)
options = parser.parse_args()
parse_greencard(options.input[0].read())
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment