Decoder

-----BEGIN decode.pl-----
#!/usr/bin/perl
#
# Display MetroCard Raw And Parsed Data
# Version 0.01
# Copyright (c) 2004-2005 Joseph Battaglia <redbird@2600.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
#
# Notes on input:
#  A line that begins with a '#' is not processed and not printed
#  A line that begins with a '%' is printed as a comment
#

$card_type = 0; #FIXME: this should be set by track 3, but since I only have
                # a single-track reader...

$typesfile = 'types.txt';
$lidfile = 'lids.txt';




# track 1-2 field names
@T12_FIELDS = (
  'Time',        'Sub-Type',    'Time',        'Date',
  'Times Used',  'Expiration',  'Transfer',    'Last ID',
  'Value',       'Purch ID',    'Unknown'
);

# track 3 field names
@T3_FIELDS = (
  'Type',        'Unknown',     'Expiration',  'Unkonwn',
  'Unknown',     'Unknown',     'Serial',      'Unused',
  'Unkonwn',     'Constant'
);

# generate regexps for track 1-2
@t12_lens = (2, 6, 6, 10, 6, 10, 1, 15, 16, 16); # lengths of track 1-2 fields
$t12_re1 = '11010111'; # start sentinel
$t12_re1 .= join('', map("([01]{$_})", @t12_lens)); # track 1-2 fields
$t12_re2 .= $t12_re1 . '(.*)' . $t12_re1; # regexp for dual records
# generate regexp for track 3
@t3_lens = (4, 4, 12, 4, 8, 8, 80, 16, 16); # lengths of track 3 fields
$t3_re = '000011000111'; # start sentinel
$t3_re .= join('', map("([01]{$_})", @t3_lens)); # track 3 fields
$t3_re .= '0010010100110010011010010110010101001100101001'; # end sentinel
$t3_re .= '0100110011010101001101001010100110100101011010';





# lookup card type/subtype in typesfile and return its name
sub lookup_type($$)
{
  ($in_type, $in_subtype) = @_; # read arguments
  open(FH, $typesfile) or die "Can't open $typesfile: $!"; # open typesfile
  while (<FH>) { # loop through each record
    ($type, $subtype, $name) = split(/:/); # split fields
    if (($type eq $in_type) and ($subtype eq $in_subtype)) { # look for match
      chomp($name); # remove newline
      return $name; # return name
    }
  }
  return 'UNKNOWN'; # could not find type/subtype; return 'UNKNOWN'
}


# lookup last id in lidfile and return its name
sub lookup_lid($)
{
  ($in_value) = @_; # read arguments
  open(FH, $lidfile) or die "Can't open $lidfile: $!"; # open lidfile
  while (<FH>) { # loop through each record
    ($value, $name) = split(/:/); # split fields
    if (($in_value eq $value)) { # look for match
      chomp($name); # remove newline
      return $name; # return name
    }
  }
  return 'UNKNOWN'; # could not find lid; return 'UNKNOWN'
}


# print header
sub print_header($)
{
  ($title) = @_; # read arguments
  print("$title\n"); # print title
  print("Field           Hex        Decimal    Parsed\n"); # print header
  print("--------------- ---------- ---------- ------\n");
}


# print field as hex and decimal and return decimal value
sub print_field($$$)
{
  ($track, $field, $value) = @_; # read arguments
  $tvar = 'T' . $track . '_FIELDS'; # create variable name
  $value = oct('0b' . $value); # convert to decimal
  printf('%2d: %-11s ', $field, $$tvar[$field - 1]);
  printf('%10X %10d ', $value, $value); # print base 10/16 values
  return $value; # return decimal value
}


# parse field
sub parse_field($$$)
{
  ($track, $field, $value) = @_; # read arguments
  $decvalue = oct('0b' . $value);

  if ($track == 12) { # track 1-2 fields

    if ($field == 1 or $field == 3) { # time
      if ($card_type == 0) {
        $time = $decvalue * 6;
        $hr = int($time / 60) - 1;
        $min = $time % 60;
        printf("%.2d:%.2d", $hr, $min);
      }
    }

    if ($field == 2) { # subtype
      print(lookup_type($card_type, $decvalue));
    }

    if ($field == 5) { # times used
      print($decvalue);
    }

    if ($field == 7) { # transfer
      print(($decvalue == 1) ? "YES" : "NO");
    }

    if ($field == 8) { # last id
      print(lookup_lid($decvalue));
    }

    if ($field == 9) { # value
      $dollars = int($decvalue / 100);
      $cents = $decvalue % 100;
      printf("\$%d.%.2d", $dollars, $cents);
    }
  }

  if ($track == 3) { # track 3 fields

    if ($field == 1) { # type
      print(lookup_type('M', $decvalue));
      $card_type = $decvalue;
    }

    if ($card_type == 0 and $field == 3) { # expiration
      $day = 30;
      if (!$decvalue % 2) {
        $decvalue++;
      }
      $decvalue /= 2;
      my $year = int ($decvalue / 12) + 1992;
      my $month = $decvalue % 12;
      if ($month > 1) {
        $month--;
      }
      else {
        $year--;
        $month = 12;
      }
      if ($month < 8) {
        if ($month % 2) {
          $day = "31";
        }
      }
      else {
        if (!($month % 2)) {
          $day = "31";
        }
      }
      if ($month == 2) {
        if (!($year % 4)) {
          $day = 29;
        }
        else {
          $day = 28;
        }
      }
      print("$month/$day/$year");
    }

    if ($field == 7) { # serial
      printf("%.10d", $decvalue);
    }
  }

  print("\n");
}


# main loop
while (<STDIN>) {

  if (/^%(.*)/) { # process printed comments
    print("### $1\n"); # print
    next; # process next input line
  }
  if (/^#/) { # process ignored comments
    next; # process next input line
  }

  # track 3
  if (/$t3_re/ or reverse =~ /$t3_re/) {
    print_header 'Track 3 Record';
    for ($i = 1; $i <= ($#t3_lens + 2); $i++) {
      $field = $i; # set field
      print_field(3, $field, $$i); # print each field
      parse_field(3, $field, $$i); # parse each field
    }
    print "\n";
  }

  # track 1-2 dual records
  if (/$t12_re2/ or reverse =~ /$t12_re2/) { # regexp
    print_header 'Track 1-2   Record 1'; # print header
    for ($i = 1; $i <= ($#t12_lens + 2); $i++) {
      $field = $i; # set field
      print_field(12, $field, $$i); # print/parse each field
      if ($field == 1 or $field == 3) { # time field is split in two
        parse_field(12, $field, $1 . $3); # combine time fields
      }
      else { # everything else is normal
        parse_field(12, $field, $$i); # parse each field
      }
    }
    print "\n";
    print_header 'Track 1-2   Record 2'; # print header
    for ($i = ($#t12_lens + 3); $i <= (($#t12_lens * 2) + 4); $i++) {
      $field = $i - ($#t12_lens + 2); # set field
      print_field(12, $field, $$i); # print/parse each field
      if ($field == 1 or $field == 3) { # time field is split in two
        $timevar1 = ($#t12_lens + 3);
        $timevar2 = ($#t12_lens + 5);
        parse_field(12, $field, $$timevar1 . $$timevar2); # combine time fields
      }
      else { # everything else is normal
        parse_field(12, $field, $$i); # parse each field
      }
    }
    print "\n";
    next; # process next input line
  }

  # track 1-2 single record
  if (/$t12_re1/ or reverse =~ /$t12_re1/) { # regexp
    print_header 'Track 1-2   (Read Error)'; # print header
    for ($i = 1; $i <= ($#t12_lens + 2); $i++) {
      $field = $i; # set field
      print_field(12, $field, $$i); # print each field
      if ($field == 1 or $field == 3) { # time field is split in two
        parse_field(12, $field, $1 . $3); # combine time fields
      }
      else { # everything else is normal
        parse_field(12, $field, $$i); # parse each field
      }
    }
    print "\n";
    next; # process next input line
  }
}
-----END decode.pl-----

-----BEGIN lids.txt-----
1513:14th St/Union Sq
1519:8th St/Broadway (A39)
1880:Lexington Ave (N601)
1942:ASTOR PLACE (R219)
2157:34th St/6th Ave (N506)
2204:42nd St/Grand Central
2278:9th Street PATH
-----END lids.txt-----

-----BEGIN mxms.txt-----
14TH ST. - UNION SQUARE :MVM:0530:A033  0400
14TH ST. - UNION SQUARE :MVM:0400:A033  0700
14TH ST. - UNION SQUARE :MVM:0481:A033  0701
14TH ST. - UNION SQUARE :MVM:1122:A034  0400
14TH ST. - UNION SQUARE :MVM:0216:A034  0700
14TH ST. - UNION SQUARE :MVM:0215:A034  0701
14TH ST. - UNION SQUARE :MVM:1370:A035  0700
14TH ST. - UNION SQUARE :MVM:0541:A037  0700
14TH ST. - UNION SQUARE :MVM:0265:A037  0701
8TH STREET & BROADWAY   :MEM:5462:A039  0400
8TH STREET & BROADWAY   :MEM:5662:A038  0401
95TH ST & FT. HAMILTON  :MVM:0982:C028  0700
14TH STREET & 8TH AVE   :MEM:5314:H001  0702
1ST AVE & 14TH STREET   :MVM:1358:H007  0700
1ST AVE & 14TH STREET   :MVM:1145:H007  0701
175 ST/FT. WASHINGTON AV:MVM:1632:N010  0400
175 ST/FT. WASHINGTON AV:MVM:1611:N010  0700
175 ST/FT. WASHINGTON AV:MEM:5274:N010  0701
W 4TH ST - WASHINGTON SQ:MVM:0321:N080  0700
W 4TH ST - WASHINGTON SQ:MVM:0109:N080  0701
FORDHAM ROAD            :MVM:0550:N218  0700
LEXINGTON AVE - 3RD AVE :MVM:0740:N305  0401
NASSAU AV & MANHATTAN AV:MVM:1738:N408A 0500
34TH STREET/SIXTH AVENUE:MVM:1428:N506  0702
34TH STREET/SIXTH AVENUE:MVM:0540:N507  0701
14TH STREET & 6TH AVENUE:MEM:5383:N513  0400
CHRISTOPHER STREET      :MVM:0637:R125  0700
CHRISTOPHER STREET      :MEM:5359:R125
CHRISTOPHER STREET      :MVM:0063:R125  0701
14TH STREET - 7TH AVENUE:MVM:0294:R127  0400
14TH STREET - 7TH AVENUE:MVM:1643:R127  0401
14TH STREET - 7TH AVENUE:MVM:0357:R127  0700
14TH STREET - 7TH AVENUE:MVM:0376:R127  0701
34TH STREET-PENN STATION:MVM:0553:R138  0701
WALL STREET & BROADWAY  :MVM:1123:R203  0400
WALL STREET & BROADWAY  :MVM:1038:R203  0700
ASTOR PLACE             :MVM:0654:R219  0400
ASTOR PLACE             :MVM:0586:R219  0700
ASTOR PLACE             :MVM:0545:R219  0701
ASTOR PLACE             :MVM:0744:R220  0700
ASTOR PLACE             :MVM:0318:R220  0701
ASTOR PLACE             :MEM:5389:R220
14TH ST. - UNION SQUARE :MVM:0576:R221  0400
14TH ST. - UNION SQUARE :MVM:0514:R221  0401
14TH ST. - UNION SQUARE :MVM:0475:R221  0700
14TH ST. - UNION SQUARE :MVM:0564:R221  0701
23RD STREET - PARK AVE  :MVM:0489:R227  0701
28TH STREET - PARK AVE  :MVM:1228:R229  0700
-----END mxms.txt-----

last edited 2008-07-23 01:29:54 by Xoder