#!/usr/bin/perl

use Getopt::Std;
use strict;

getopts('v');

use vars qw($atend
	    $stuff
	    $opt_v
	    $curpos
	    $esc
	    %seqtable);

$atend = 0;

%seqtable = ( "@", 0,
	      "(R", "REMOTE",
	      "(", "VARIABLE",
	      "U", 1,
	      "\\", 2,
	      "\$", 2,
	      "r", 1,
	      ".", "SPECIAL",
	      "i", "SPECIAL1",
	      "\000", 2,
	      "\001", 22
	  );

$esc = "\033";

$curpos = 0;

sub get_stuff($) {
    my ($where) = @_;
    my ($end) = length $stuff;
    if ($curpos == 0 && $end == 0) {
	$stuff = <>;		# Need to do this once to "activate" ARGV
	$end = length $stuff;
    }
    my ($old_end) = length $stuff;
    my ($tmp);
    my ($bytes_to_read) = 4096;
    if ($where - $end > $bytes_to_read) {
	$bytes_to_read = $where - $end;
    }
    while ($end < $where) {
	my $foo = read ARGV, $tmp, $bytes_to_read;
#	print STDERR length $tmp, " ", length $stuff, " $curpos $where $bytes_to_read $foo $!", "\n";
	$stuff .= $tmp;
#	print STDERR length $tmp, " ", length $stuff, " $curpos $where $bytes_to_read $foo $!", "\n\n";
	$end = length $stuff;
	if ($old_end == $end) {
	    $atend = 1;
	    return 0;
	} else {
	    $bytes_to_read -= $end - $old_end;
	    $old_end = $end;
	}
    }
    return 1;
}

sub increment_curpos($) {
    my ($curpos_increment) = @_;
    substr($stuff, 0, $curpos_increment) = "";
    $curpos += $curpos_increment;
}

sub do_remote_command() {
    print "\n";
    printf "%08x  ", $curpos;
    print "1b  (  R ";
    increment_curpos(3);
    get_stuff(2);
    my $lchar = substr($stuff, 0, 1);
    my $nlchar = unpack("C", $lchar);
    my $hchar = substr($stuff, 1, 1);
    my $nhchar = unpack("C", $hchar);
    printf "%02x %02x ", $nlchar, $nhchar;
    my $skipchars = ($nhchar * 256) + $nlchar;
    increment_curpos(2);
    my $i;
    my $char;
    my $nchar;
    get_stuff($skipchars);
    for ($i = 0; $i < $skipchars; $i++) {
	$char = substr($stuff, $i, 1);
	$nchar = unpack("C", $char);
	if ($nchar >= 32 && $nchar < 127) {
	    print $char;
	} else {
	    printf "%02x ", $nchar;
	}
    }
    increment_curpos($skipchars);
    while (get_stuff(2) &&
	   substr($stuff, 0, 2) =~ /[A-Z0-9][A-Z0-9]/) {
	print "\n";
	printf "%08x    ", $curpos;
	my ($cmd) = substr($stuff, 0, 2);
	print $cmd;
	increment_curpos(2);
	get_stuff(2);
	$lchar = substr($stuff, 0, 1);
	$nlchar = unpack("C", $lchar);
	$hchar = substr($stuff, 1, 1);
	$nhchar = unpack("C", $hchar);
	if ($cmd eq "DF") {
	    $skipchars = 0;
	} else {
	    $skipchars = ($nhchar * 256) + $nlchar;
	}
	printf "%02x %02x ", $nlchar, $nhchar;
	increment_curpos(2);
	get_stuff($skipchars);
	for ($i = 0; $i < $skipchars; $i++) {
	    printf "%02x ", unpack("C", substr($stuff, $i, 1));
	}
	increment_curpos($skipchars);
    }
}

sub print_prefix_bytes($) {
    my ($bytes_to_print) = @_;
    print "\n";
    printf "%08x  ", $curpos;
    print "1b ";
    my $i;
    my $char;
    my $nchar;
    get_stuff($bytes_to_print);
    for ($i = 1; $i < $bytes_to_print; $i++) {
	$char = substr($stuff, $i, 1);
	$nchar = unpack("C", $char);
	if ($i < 2 && $nchar >= 32 && $nchar < 127) {
	    print " $char ";
	} else {
	    printf "%02x ", unpack("C", $char);
	}
    }
}

sub print_output_data($$$$$) {
    my ($comptype, $bitsperpixel, $dots, $rows, $dot_scale) = @_;
    my $counter;
    my $fchar;
    print " ($dots, $rows, $bitsperpixel) ";
    $dots *= 8;
    $dots /= $dot_scale;
    my $savedots = $dots;
    if ($comptype == 0) {
	if ($opt_v) {
	    get_stuff($dots);
	    printf "%*v02x ", " ", substr($stuff, 0, $dots);
	}
	increment_curpos($dots);
    } elsif ($comptype == 1) {
	while ($rows-- > 0) {
	    $dots = $savedots;
	    while ($dots > 0) {
		get_stuff(1);
		$counter = ord(substr($stuff, 0, 1));
		increment_curpos(1);
		if ($counter <= 127) {
		    $counter++;
		    get_stuff($counter);
		    if ($opt_v) {
			printf "%*v02x ", " ", substr($stuff, 0, $counter);
		    }
		    increment_curpos($counter);
		} else {
		    $counter = 257 - $counter;
		    get_stuff(1);
		    if ($opt_v) {
			$fchar = sprintf "%v02x ", substr($stuff, 0, 1);
			map { print $fchar } (0..$counter - 1);
		    }
		    increment_curpos(1);
		}
		$dots -= $counter * 8;
	    }
	    if ($rows > 0 && $opt_v) {
		print "\n        ";
	    }
	}
    } else {
	print "\nUnknown compression type $comptype!\n";
    }
}

sub do_special_command() {
    get_stuff(8);
    my $comptype = unpack("C", substr($stuff, 2, 1));
    my $dots = unpack("v", substr($stuff, 6, 2));
    my $rows = unpack("C", substr($stuff, 5, 1));
    print_prefix_bytes(8);
    increment_curpos(8);
    print_output_data($comptype, 1, $dots, $rows, 8);
}

sub do_special1_command() {
    get_stuff(9);
    my $comptype = unpack("C", substr($stuff, 3, 1));
    my $bitsperpixel = unpack("C", substr($stuff, 4, 1));
    my $dots = unpack("v", substr($stuff, 5, 2));
    my $rows = unpack("v", substr($stuff, 7, 2));
    print_prefix_bytes(9);
    increment_curpos(9);
    print_output_data($comptype, $bitsperpixel, $dots, $rows, 1);
}

while (! $atend) {
    my $found;
    my $key;
    my $skipchars;
    my $startoff;
    my $kchar;
    my $nkchar;
    my $lchar;
    my $nlchar;
    my $hchar;
    my $nhchar;
    my $i;
    my $char;
    my $nchar;
    my $bytes;
    get_stuff(2);
    if (substr($stuff, 0, 1) eq "$esc") {
	$found = 0;
	foreach $key (sort { length $b <=> length $a } keys %seqtable) {
	    get_stuff(1 + length $key);
	    if (substr($stuff, 1, length $key) eq $key) {
		$skipchars = $seqtable{$key};
		if ($skipchars eq "SPECIAL") {
		    do_special_command;
		    $found = 2;
		} elsif ($skipchars eq "SPECIAL1") {
		    do_special1_command;
		    $found = 2;
		} elsif ($skipchars eq "REMOTE") {
		    do_remote_command;
		    $found = 2;
		} else {
		    print "\n";
		    printf "%08x  ", $curpos;
		    print "1b ";
		    $startoff = 0;
		    if ($skipchars eq "VARIABLE") {
			get_stuff((length $key) + 3);
			$kchar = substr($stuff, (length $key) + 1, 1);
			$nkchar = unpack("C", $kchar);
			$lchar = substr($stuff, (length $key) + 2, 1);
			$nlchar = unpack("C", $lchar);
			$hchar = substr($stuff, (length $key) + 3, 1);
			$nhchar = unpack("C", $hchar);
			$skipchars = ($nhchar * 256) + $nlchar;
			$startoff = 3;
		    }
		    get_stuff($skipchars + (length $key) + $startoff + 1);
		    for ($i = 0;
			 $i < $skipchars + (length $key) + $startoff;
			 $i++) {
			$char = substr($stuff, $i + 1, 1);
			$nchar = unpack("C", $char);
			if ($i < 3 && $nchar >= 32 && $nchar < 127) {
			    print " $char ";
			} else {
			    printf "%02x ", unpack("C", $char);
			}
		    }
		    $found = 1;
		}
		$bytes = length($key) + 1 + $skipchars + $startoff;
		last;
	    }
	}
	if (! $found) {
	    print "\n";
	    printf "%08x  ", $curpos;
	    print "1b ";
	    increment_curpos(1);
	} elsif ($found == 1) {
	    increment_curpos($bytes);
	} else {
	}
    } else {
	get_stuff(1);
	$char = substr($stuff, 0, 1);
	$nchar = unpack("C", $char);
	if ($nchar >= 32 && $nchar < 127) {
	    print " *$char ";
	} else {
	    printf "*%02x ", unpack("C", $char);
	}
	increment_curpos(1);
    }
}
