1#!/usr/bin/perl
2
3# Copyright 2000-2008 Robert Krawitz <rlk@alum.mit.edu>
4#
5#   This program is free software; you can redistribute it and/or modify it
6#   under the terms of the GNU General Public License as published by the Free
7#   Software Foundation; either version 2 of the License, or (at your option)
8#   any later version.
9#
10#   This program is distributed in the hope that it will be useful, but
11#   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12#   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13#   for more details.
14#
15#   You should have received a copy of the GNU General Public License
16#   along with this program; if not, write to the Free Software
17#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19use Getopt::Std;
20use strict;
21
22getopts('VvO:');
23
24use vars qw($atend
25	    $stuff
26	    $opt_v
27	    $opt_V
28	    $opt_O
29	    $curoffset
30	    $curpos
31	    $esc
32	    $initial_vertical_position
33	    $page_mgmt_unit
34	    $horizontal_position
35	    $horizontal_unit
36	    $vertical_unit
37	    $vertical_position
38	    $raster_x
39	    $raster_y
40	    $print_offsets
41	    %seqtable
42	    @seqkeys
43	    %chartable
44	    %xchartable
45	    %nchartable
46	    %rchartable
47	    %keylengths
48	    $total_length
49	    @offsets);
50
51$atend = 0;
52%chartable = ();
53%xchartable = ();
54%nchartable = ();
55%rchartable = ();
56%keylengths = ();
57
58%seqtable = ( "@", 0,
59	      "(R", "REMOTE",
60	      "(", "VARIABLE",
61	      "U", 1,
62	      "\\", 2,
63	      "\$", 2,
64	      "r", 1,
65	      "\031", 1,
66	      ".", "SPECIAL",
67	      "i", "SPECIAL1",
68	      "\000", 2,
69	      "\001", 22
70	  );
71
72map {
73    my ($xchar) = pack("C", $_);
74    if ($_ >= 32 && $_ < 127) {
75	$chartable{$xchar} = " $xchar";
76	$xchartable{$xchar} = " *$xchar";
77	$rchartable{$xchar} = $xchar;
78    } else {
79	$chartable{$xchar} = sprintf("%02x", $_);
80	$xchartable{$xchar} = sprintf("*%02x", $_);
81	$rchartable{$xchar} = sprintf("%02x ", $_);
82    }
83    $nchartable{$xchar} = $_;
84} (0..255);
85
86@seqkeys = (sort { length $b <=> length $a } keys %seqtable);
87
88map { $keylengths{$_} = length $_ } @seqkeys;
89
90$esc = "\033";
91
92$curpos = 0;
93$curoffset = 0;
94
95$page_mgmt_unit = 360;
96$horizontal_unit = 180;
97$vertical_unit = 360;
98
99$initial_vertical_position = 0;
100$vertical_position = 0;
101$horizontal_position = 0;
102$print_offsets = 0;
103$total_length = 0;
104
105sub fill_buffer($) {
106    my ($where) = @_;
107    return 1 if $total_length - $curoffset >= $where;
108    my ($end) = $total_length - $curoffset;
109    if ($curpos == 0 && $end == 0) {
110	$stuff = <>;		# Need to do this once to "activate" ARGV
111	$total_length = length $stuff;
112	$end = $total_length - $curoffset;
113    }
114    my ($old_end) = $end;
115    my ($tmp);
116    my ($bytes_to_read) = 16384;
117    if ($where - $end > $bytes_to_read) {
118	$bytes_to_read = $where - $end;
119    }
120    if ($curoffset >= 16384) {
121	substr($stuff, 0, $curoffset) = "";
122	$total_length -= $curoffset;
123	$curoffset = 0;
124    }
125    while ($end < $where) {
126	my $foo = read ARGV, $tmp, $bytes_to_read;
127	$stuff .= $tmp;
128	$end += $foo;
129	$total_length += $foo;
130	if ($old_end == $end) {
131	    $atend = 1;
132	    return 0;
133	} else {
134	    $bytes_to_read -= $end - $old_end;
135	    $old_end = $end;
136	}
137    }
138    return 1;
139}
140
141sub increment_curpos($) {
142    my ($curpos_increment) = @_;
143    $curpos += $curpos_increment;
144    $curoffset += $curpos_increment;
145}
146
147# This slows things down tremendously...
148sub xdata($;$) {
149    my ($start, $end) = @_;
150    if (defined $end) {
151	return substr($stuff, $start + $curoffset, $end);
152    } else {
153	return substr($stuff, $start + $curoffset);
154    }
155}
156
157sub do_remote_command() {
158    print "\n";
159    printf "%08x  ", $curpos;
160    print "1b  (  R ";
161    increment_curpos(3);
162    fill_buffer(2);
163    my $lchar = substr($stuff, $curoffset + 0, 1);
164    my $nlchar = $nchartable{$lchar};
165    my $hchar = substr($stuff, $curoffset + 1, 1);
166    my $nhchar = $nchartable{$hchar};
167    printf " %02x %02x ", $nlchar, $nhchar;
168    my $skipchars = ($nhchar * 256) + $nlchar;
169    increment_curpos(2);
170    fill_buffer($skipchars);
171    for (my $i = 0; $i < $skipchars; $i++) {
172	print $rchartable{substr($stuff, $curoffset + $i, 1)};
173    }
174    increment_curpos($skipchars);
175    while (fill_buffer(2) &&
176	   substr($stuff, $curoffset + 0, 2) =~ /[A-Z0-9][A-Z0-9]/) {
177	print "\n";
178	printf "%08x    ", $curpos;
179	my ($cmd) = substr($stuff, $curoffset + 0, 2);
180	print $cmd;
181	increment_curpos(2);
182	fill_buffer(2);
183	$lchar = substr($stuff, $curoffset + 0, 1);
184	$nlchar = $nchartable{$lchar};
185	$hchar = substr($stuff, $curoffset + 1, 1);
186	$nhchar = $nchartable{$hchar};
187	if ($cmd eq "DF") {
188	    $skipchars = 0;
189	} else {
190	    $skipchars = ($nhchar * 256) + $nlchar;
191	}
192	printf " %02x %02x ", $nlchar, $nhchar;
193	increment_curpos(2);
194	fill_buffer($skipchars);
195	printf "%*v02x ", " ", substr($stuff, $curoffset, $skipchars);
196	increment_curpos($skipchars);
197    }
198}
199
200sub print_prefix_bytes($$) {
201    my ($bytes_to_print) = @_;
202    print "\n";
203    printf "%08x  ", $curpos;
204    print "1b ";
205    fill_buffer($bytes_to_print);
206    my $char = substr($stuff, $curoffset + 1, 1);
207    print "$chartable{$char} ";
208    printf "%*v02x ", " ", substr($stuff, $curoffset + 2, $bytes_to_print - 2);
209    increment_curpos($bytes_to_print);
210}
211
212sub print_output_data($$$$$$) {
213    my ($comptype, $bitsperpixel, $dots, $rows, $dot_scale, $color) = @_;
214    my $counter;
215    my $fchar;
216    my $last_row = 0;
217    my $first_row = -1;
218    my $i;
219    my $vstuff;
220    $dots *= 8;
221    $dots /= $dot_scale;
222    my $real_dots = $dots / $bitsperpixel;
223    if ($opt_v) {
224	print " ($real_dots dots, $rows rows, $bitsperpixel bits";
225    }
226    my $savedots = $dots;
227    if ($comptype == 0) {
228	fill_buffer($dots / 8);
229	if ($opt_V) {
230	    printf "%*v02x ", " ", substr($stuff, $curoffset + 0, $dots / 8);
231	}
232	increment_curpos($dots / 8);
233    } elsif ($comptype == 1) {
234	foreach $i (0..$rows-1) {
235	    my ($found_something) = 0;
236	    $dots = $savedots;
237	    my ($tstuff) = "\n    $i    ";
238	    while ($dots > 0) {
239		fill_buffer(1);
240		$counter = ord(substr($stuff, $curoffset + 0, 1));
241		increment_curpos(1);
242		if ($counter <= 127) {
243		    $counter++;
244		    fill_buffer($counter);
245		    if ($opt_v || $opt_V) {
246			my $tmp = sprintf "%*v02x ", " ", substr($stuff, $curoffset + 0, $counter);
247			if (!($tmp =~ /^[0 ]+$/)) {
248			    $found_something = 1;
249			    $last_row = $i;
250			    if ($first_row == -1) {
251				$first_row = $i;
252			    }
253			}
254			if ($opt_V) {
255			    $tstuff .= $tmp;
256			}
257		    }
258		    increment_curpos($counter);
259		} else {
260		    $counter = 257 - $counter;
261		    fill_buffer(1);
262		    if ($opt_v || $opt_V) {
263			$fchar = sprintf "%v02x ", substr($stuff, $curoffset + 0, 1);
264			if ($fchar ne "00 ") {
265			    $found_something = 1;
266			    $last_row = $i;
267			    if ($first_row == -1) {
268				$first_row = $i;
269			    }
270			}
271		    }
272		    if ($opt_V) {
273			map { $tstuff .= $fchar } (0..$counter - 1);
274		    }
275		    increment_curpos(1);
276		}
277		$dots -= $counter * 8;
278	    }
279	    if ($opt_V && $found_something) {
280		$vstuff .= $tstuff;
281	    }
282	}
283    } else {
284	print "\nUnknown compression type $comptype!\n";
285    }
286    if ($opt_v) {
287	my ($offset) = $offsets[$color];
288	my ($first_position) = ($vertical_position / $vertical_unit)
289	    + ($first_row + $offset) * $raster_y;
290	my ($last_position) = ($vertical_position / $vertical_unit)
291	    + ($last_row + $offset) * $raster_y;
292	my ($final_position) = ($vertical_position / $vertical_unit)
293	    + ($rows + $offset) * $raster_y;
294	my ($final_horizontal) = $horizontal_position +
295	    ($real_dots * $page_mgmt_unit * $raster_x);
296	if ($print_offsets) {
297	    printf (" %d,%d+%d %.4f  %d,%d+%d %.4f  %.4f) ",
298		    $horizontal_position, $first_row, $offset, $first_position,
299		    $final_horizontal, $last_row, $offset, $last_position,
300		    $final_position);
301	} else {
302	    printf (" %d,%d %.4f  %d,%d %.4f  %.4f) ",
303		    $horizontal_position, $first_row, $first_position,
304		    $final_horizontal, $last_row, $last_position,
305		    $final_position);
306	}
307    }
308    if ($opt_V) {
309	print " $vstuff";
310    }
311}
312
313sub do_special_command() {
314    fill_buffer(8);
315    my $comptype = $nchartable{substr($stuff, $curoffset + 2, 1)};
316    my $color = 0;
317    my $dots = unpack("v", substr($stuff, $curoffset + 6, 2));
318    my $rows = $nchartable{substr($stuff, $curoffset + 5, 1)};
319    print_prefix_bytes(8, 2);
320    print_output_data($comptype, 1, $dots, $rows, 8, $color);
321    fill_buffer(1);
322    while (substr($stuff, $curoffset + 0, 1) eq "\r") {
323	fill_buffer(1);
324	increment_curpos(1);
325    }
326}
327
328sub do_special1_command() {
329    fill_buffer(9);
330    my $color = $nchartable{substr($stuff, $curoffset + 2, 1)};
331    my $comptype = $nchartable{substr($stuff, $curoffset + 3, 1)};
332    my $bitsperpixel = $nchartable{substr($stuff, $curoffset + 4, 1)};
333    my $dots = unpack("v", substr($stuff, $curoffset + 5, 2));
334    my $rows = unpack("v", substr($stuff, $curoffset + 7, 2));
335    print_prefix_bytes(9, 1);
336    print_output_data($comptype, $bitsperpixel, $dots, $rows, 1, $color);
337    fill_buffer(1);
338    while (substr($stuff, $curoffset + 0, 1) eq "\r") {
339	fill_buffer(1);
340	increment_curpos(1);
341    }
342}
343
344sub get_long($) {
345    my ($string) = @_;
346    my ($tmp) = unpack("V", $string);
347    if ($tmp >= (1 << 31)) {
348	return -(0xffffffff ^ $tmp) - 1;
349    } else {
350	return $tmp;
351    }
352}
353
354sub get_short($) {
355    my ($string) = @_;
356    my ($tmp) = unpack("v", $string);
357    if ($tmp >= (1 << 15)) {
358	return -(0xffff ^ $tmp) - 1;
359    } else {
360	return $tmp;
361    }
362}
363
364sub get_byte($) {
365    my ($string) = @_;
366    return $nchartable{$string};
367}
368
369if ($opt_O) {
370    my (@stuff) = split(/,/, $opt_O);
371    map {
372	my ($key, $val) = split(/=/, $_);
373	if ($val) {
374	    $print_offsets = 1;
375	}
376	@offsets[$key] = $val;
377    } @stuff;
378}
379
380while (! $atend) {
381    my $found;
382    my $key;
383    my $skipchars;
384    my $startoff;
385    my $kchar;
386    my $nkchar;
387    my $lchar;
388    my $nlchar;
389    my $hchar;
390    my $nhchar;
391    my $i;
392    my $char;
393    my $nchar;
394    my $bytes;
395    my ($maxklen) = $keylengths{$seqkeys[0]};
396    fill_buffer(1);
397    my $cchar = substr($stuff, $curoffset + 0, 1);
398    if ($cchar eq "$esc") {
399	$found = 0;
400	fill_buffer(2 + $maxklen);
401	foreach $key (@seqkeys) {
402	    my ($klen) = $keylengths{$key};
403	    if (substr($stuff, $curoffset + 1, $klen) eq $key) {
404		$skipchars = $seqtable{$key};
405		if ($skipchars eq "SPECIAL") {
406		    do_special_command();
407		    $found = 2;
408		} elsif ($skipchars eq "SPECIAL1") {
409		    do_special1_command();
410		    $found = 2;
411		} elsif ($skipchars eq "REMOTE") {
412		    do_remote_command();
413		    $found = 2;
414		} else {
415		    print "\n";
416		    printf "%08x  ", $curpos;
417		    print "1b ";
418		    $startoff = 0;
419		    my $print_stuff = 0;
420		    my $print_variable = 0;
421		    if ($skipchars eq "VARIABLE") {
422			fill_buffer(3);
423			$print_variable = 1;
424			$kchar = substr($stuff, $curoffset + $klen + 1, 1);
425			$nkchar = unpack("C", $kchar);
426			$lchar = substr($stuff, $curoffset + $klen + 2, 1);
427			$nlchar = unpack("C", $lchar);
428			$hchar = substr($stuff, $curoffset + $klen + 3, 1);
429			$nhchar = unpack("C", $hchar);
430			$skipchars = ($nhchar * 256) + $nlchar;
431			$startoff = 3;
432			$print_stuff = 1;
433		    }
434		    my ($blen) = $skipchars + $klen + $startoff;
435		    fill_buffer($blen + 1);
436		    $char = substr($stuff, $curoffset + 1, 1);
437		    print "$chartable{$char} ";
438		    if ($blen > 1) {
439			$char = substr($stuff, $curoffset + 2, 1);
440			print "$chartable{$char} ";
441			if ($blen > 2) {
442			    if ($print_variable && $char eq "d") {
443				printf ("%*v02x ", " ",
444					substr($stuff, $curoffset + 3, 2));
445			    } else {
446				printf ("%*v02x ", " ",
447					substr($stuff, $curoffset + 3, $blen - 2));
448			    }
449			}
450		    }
451		    if ($print_stuff) {
452			my $xchar = substr($stuff, $curoffset + 2, 1);
453			if ($xchar eq "c") {
454			    my ($top, $bottom);
455			    if ($skipchars == 8) {
456				$top = get_long(substr($stuff, $curoffset + 5, 4));
457				if ($opt_v) {
458				    $bottom = get_long(substr($stuff, $curoffset + 9, 4));
459				}
460			    } else {
461				$top = get_short(substr($stuff, $curoffset + 5, 2));
462				if ($opt_v) {
463				    $bottom = get_short(substr($stuff, $curoffset + 7, 2));
464				}
465			    }
466			    if ($opt_v) {
467				printf (" (page format %d %d %.2f %.2f)",
468					$top, $bottom, $top / $page_mgmt_unit,
469					$bottom / $page_mgmt_unit);
470			    }
471			    $initial_vertical_position =
472				$top * $vertical_unit / $page_mgmt_unit;
473			    $vertical_position = $initial_vertical_position;
474			} elsif ($xchar eq "S") {
475			    if ($opt_v) {
476				my ($width, $height);
477				if ($skipchars == 8) {
478				    $width = get_long(substr($stuff, $curoffset + 5, 4));
479				    $height = get_long(substr($stuff, $curoffset + 9, 4));
480				} else {
481				    $width = get_short(substr($stuff, $curoffset + 5, 2));
482				    $height = get_short(substr($stuff, $curoffset + 7, 2));
483				}
484				printf (" (paper size %d %d %.2f %.2f)",
485					$width, $height, $width / $page_mgmt_unit,
486					$height / $page_mgmt_unit);
487			    }
488			} elsif ($xchar eq "C") {
489			    if ($opt_v) {
490				my ($length);
491				if ($skipchars == 4) {
492				    $length = get_long(substr($stuff, $curoffset + 5, 4));
493				} else {
494				    $length = get_short(substr($stuff, $curoffset + 5, 2));
495				}
496				printf (" (page length %d %.2f)",
497					$length, $length / $page_mgmt_unit);
498			    }
499			} elsif ($xchar eq "D") {
500			    my $base = get_short(substr($stuff, $curoffset + 5, 2));
501			    my $y = $nchartable{substr($stuff, $curoffset + 7, 1)};
502			    my $x = $nchartable{substr($stuff, $curoffset + 8, 1)};
503			    $raster_x = $x / $base;
504			    $raster_y = $y / $base;
505			    if ($opt_v) {
506				printf (" (raster base %d, %d x %d)",
507					$base, $base / $x, $base / $y);
508			    }
509			} elsif ($xchar eq "U") {
510			    if ($skipchars == 5) {
511				my $page_mgmt = $nchartable{substr($stuff, $curoffset + 5, 1)};
512				my $vertical = $nchartable{substr($stuff, $curoffset + 6, 1)};
513				my $horiz = $nchartable{substr($stuff, $curoffset + 7, 1)};
514				my $scale = get_short(substr($stuff, $curoffset + 8, 2));
515				$page_mgmt_unit = $scale / $page_mgmt;
516				$horizontal_unit = $scale / $horiz;
517				$vertical_unit = $scale / $vertical;
518				if ($opt_v) {
519				    printf (" (units base %d  mgmt %d  vert %d  horiz %d)",
520					    $scale, $page_mgmt_unit,
521					    $vertical_unit, $horizontal_unit);
522				}
523			    } else {
524				my $page_mgmt = $nchartable{substr($stuff, $curoffset + 5, 1)};
525				if ($opt_v) {
526				    printf " (units base = %d/3600)", $page_mgmt;
527				}
528				$page_mgmt_unit = 3600 / $page_mgmt;
529				$horizontal_unit = 3600 / $page_mgmt;
530				$vertical_unit = 3600 / $page_mgmt;
531			    }
532			} elsif ($xchar eq "v") {
533			    my ($length);
534			    if ($skipchars == 4) {
535				$length = get_long(substr($stuff, $curoffset + 5, 4));
536			    } else {
537				$length = get_short(substr($stuff, $curoffset + 5, 2));
538			    }
539			    $vertical_position += $length;
540			    if ($opt_v) {
541				printf (" (skip vertical %d at %d %.4f)",
542					$length, $vertical_position,
543					$vertical_position / $vertical_unit);
544			    }
545			} elsif ($xchar eq "\$") {
546			    if ($skipchars == 4) {
547				$horizontal_position =
548				    get_long(substr($stuff, $curoffset + 5, 4));
549			    } else {
550				$horizontal_position =
551				    get_short(substr($stuff, $curoffset + 5, 2));
552			    }
553			    if ($opt_v) {
554				printf (" (horizontal position %d %.4f)",
555					$horizontal_position,
556					$horizontal_position / $horizontal_unit);
557			    }
558			} elsif ($xchar eq "d") {
559			    if ($opt_v) {
560				printf " (nop)";
561			    }
562			}
563		    }
564		    $found = 1;
565		}
566		$bytes = $klen + 1 + $skipchars + $startoff;
567		last;
568	    }
569	}
570	if (! $found) {
571	    print "\n";
572	    printf "%08x  ", $curpos;
573	    print "1b ";
574	    increment_curpos(1);
575	} elsif ($found == 1) {
576	    increment_curpos($bytes);
577	}
578    } elsif ($cchar eq "\0" || $cchar eq "\f") {
579	printf "\n%08x  ", $curpos;
580	print "$chartable{$cchar} ";
581	$vertical_position = $initial_vertical_position;
582	increment_curpos(1);
583    } else {
584	print "$xchartable{$cchar} " if ($cchar ne "\021");
585	increment_curpos(1);
586    }
587}
588
589print "\n" if $curpos > 1;
590
591