#!/usr/bin/perl
# -samy kamkar, rfid@samy.pl

use strict;

die "usage: $0 <file with data> <binary to search for>\n" unless @ARGV == 2;

my ($file, $search) = @ARGV;
$search =~ s/\s//g;

# sure, these aren't perfect, but simplifies usability if you know what you're doing
# if in doubt, use binary

# binary, cool
if ($search =~ /^[01]+$/) { }
# decimal
elsif ($search =~ /^\d+$/)
{
    $search = unpack("B*", pack("N", $search));
    $search =~ s/^0*//;
}
# hex
elsif ($search =~ /^[\da-fA-F]+$/)
{
    $search = unpack("B*", pack("H*", $search));
    $search =~ s/^0*//;
}
# ascii
else
{
    $search = unpack("B*", $search);
    $search =~ s/^0*//;
}


# read file contents
open(F, "<$file") || die "Can't read $file: $!";
my $data = join("", <F>);
close(F);

# convert to binary
$data =~ s/\s//g;
# binary, great
if ($data =~ /^[01]+$/) { }
elsif ($data =~ /^[\da-fA-F]+$/)
{
    $data = unpack("B*", pack("H*", $data));
    $search =~ s/^0*//;
}
else
{
    die "Seriously. What sort of data is this file? Binary or hex only please.\n";
}


# search every method we know how
print "Testing normally...\n";
test_all($data, $search);

print "Testing with flipped bits...\n";
test_all($data, $search, 1);

# now try manchester demodulating
my @bits = split(//, $data);
my $man;
my $last = 0;
for (my $i = 1; $i < @bits; $i++)
{
    # if we changed, flip our bit
    if ($bits[$i-1] == 1)
    {
        $last ^= 1;
    }
    $man .= $last;
}

print "Testing with manchester demodulation...\n";
test_all($man, $search);

print "Testing with flipped manchester demodulation...\n";
test_all($man, $search, 1);


sub test_all
{
    my ($data, $search, $flip) = @_;

    if ($flip)
    {
        $data =~ s/(.)/$1 ^ 1/eg;
    }

    # first just see if our data is in the stream
    if ($data =~ /$search/)
    {
        print "Found $search in our stream ($data)\n";
    }

    # try removing parity every 4 and 8 bits
    foreach my $parity (4, 8)
    {
        # try removing a parity bit every $parity bits
        # test by cutting off a bit at a time in case we're in the wrong bit position
        my $tmp = $data;
        foreach (1 .. $parity)
        {
            my $test = $tmp;
            $test =~ s/(.{$parity})./$1/g;

            if ($test =~ /$search/)
            {
                print "Found $search with parity every " . ($parity + 1) . "th bit, round $_ out of $parity ($test)\n";
            }

            # chop of a bit to change our bit position next round
            $tmp =~ s/^.//;
        }
    }
}