#!/usr/bin/perl

$prompt = '> ';
$window = 23*16;
$width = 16;


undef $ENV{'LANG'};

$file = shift;
open(DATA,$file) || die "Can't open $file: $!\n";
# binmode(DATA);
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
  $atime,$mtime,$ctime,$blksize,$blocks)
  = stat($file);

printf "file size %12.8lx\n",$size;
&xdump;
print $prompt;
$| = 1;
while(<>) {
    $| = 0;
    if (/^x\s*([0-9a-fA-F]+)/ || /^x\s*0x([0-9a-fA-F]+)/) {
       $offset = hex($1);
       &xdump;
    } elsif (/^x/) {
       $offset -= $window;
       &xdump;
    } elsif (/^d\s*([0-9]+)/) {
       $offset = $1;
       &xdump;
    } elsif (/^([0-9.]+)\%/) {
      $offset = int($size * $1 / 100);
      $offset -= $offset & 0xf;
       &xdump;
    } elsif (/\=/) {
       eval "$_";
    } elsif (/^\-$/) {
       $offset -= $window * 2;
       &xdump;
    } elsif (/^\+$/) {
       &xdump;
    } elsif (/^\[0.]$/) {
       $offset -= $window;
       &xdump;
    } elsif (/^[+-].*/) {
       eval "\$offset = \$offset $&";
       &xdump;
    } elsif (/^push\s*([0-9a-fA-F]+)/ || /^push\s*0x([0-9a-fA-F]+)/) {
       $offset = hex($1);
       push(@mark,$offset-$window);
    } elsif (/^push/) {
       push(@mark,$offset-$window);
    } elsif (/^pop/) {
       $offset = pop(@mark);
    } elsif (/^p\s*(.*)/) {
       print "$1=";
       eval 'print '."$1" ; print " ";
       eval 'printf "0x%x\n",'."$1" ; 
    } elsif (/^o/) {
       printf "\$offset=0x%lx %4.2f%%\n",$offset,$offset/$size*100;
    } elsif (/^size/) {
       printf "size = %lx\n",$size;
    } elsif (/^write\s+([.\w]+)\s+(\d+)/) {
	$name= $1; $len = $2;
	print "reading $len ";
        open(OUTPUT,">$name");
        seek(DATA,$prev_offset,0) || return 0;
        $len = read(DATA,$data,$len);
	print "$len read\n";
        syswrite(OUTPUT,$data,$len) if ($len>0);
	close(OUTPUT);
    } elsif (/^file/) {
       printf "file name %s size = %lx ",$file,$size;
       printf "\$offset=0x%lx %4.2f%%\n",$offset,$offset/$size*100;
    } elsif (/^list/) {
       foreach $i ( @mark ) {
	  printf "%lx ",$i;
       }
       print "\n";
    } elsif (/^q/) {
       exit 1;
    } elsif (/^$/) {
       &xdump;
    } else {
       print
"d xxxx		dump from xxxx (in decimal position)
x xxxx		dump from xxxx (in hexdecimal position)
d               dump continue
dd%             percentage offset
o		current offset
size		current file size
file		current file
p expression	print expression (such as \$offset)
=		assignment
push xxxx	push offset
pop xxxx	pop offset
list		list marked offset
-		previous page
+		next page
+dddd		add offset (use 0x for hex)
-dddd		minus offset (use 0x for hex)
write name len 	write to file from previous offset in len
h		this help
q		quit
";
    }
    $| = 1; print $prompt;  
}

use NKF;

sub xdump {
    if($offset>$size) { printf "%12.8lx Too big\n",$offset; return 0;}
    seek(DATA,$offset,0) || return 0;
    $i = $window;
    $prev_offset = $offset;
    while($i>0 && ($leng = read(DATA,$data,$width)) >0 ) {
      # if($leng < $width) { $data .= ord(0) x ($width-$leng); }
      $array = unpack('H*',$data);
      $array =~ s/(........)/$1 /g;
      $data = nkf('-w',$data);
      $data =~ tr/\0-\37/./;
      # $data =~ tr/\0-\37\177-\377/./;
      # printf "%12.8lx	%8.8lx %8.8lx %8.8lx %8.8lx	%s\n",
      printf "%12.8lx\t%s\t%s\n",
	 $offset, $array , $data;
      $offset += $width; $i -= $width;
    }
    return $offset;
}
