# # Perl module to expand Microsoft-compressed files # # Based on algorithm provided in the GPL'ed mscompress package by: # Martin Hinner # M. Winterhoff <100326.2776@compuserve.com> # # The mscompress package is at: # ftp://ftp.penguin.cz/pub/users/mhi/mscompress/ package MSExpand; use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); use Carp; use IO::File; require Exporter; $VERSION = "0.01"; @ISA = qw(Exporter); @EXPORT = qw(ms_expand); @EXPORT_OK = qw(ms_expand); sub get_uint8 { croak("get_uint8(FILEHANDLE)") if @_ != 1; my($fh) = @_; my($buf, $count); $count = sysread($fh, $buf, 1); if (defined($count) and $count == 1) { my $value = unpack("C", $buf); return $value; } else { return undef; } } sub put_uint8 { croak("put_uint8(FILEHANDLE, VALUE)") if @_ != 2; my($fh, $value) = @_; return syswrite($fh, pack("C", $value & 0xFF)); } sub get_uint16_le { croak("get_uint16_le(FILEHANDLE)") if @_ != 1; my($fh) = @_; my($buf, $count); $count = sysread($fh, $buf, 2); if (defined($count) and $count == 2) { my $value = unpack("v", $buf); return $value; } else { return undef; } } sub get_uint32_le { croak("get_uint32_le(FILEHANDLE)") if @_ != 1; my($fh) = @_; my($buf, $count); $count = sysread($fh, $buf, 4); if (defined($count) and $count == 4) { my $value = unpack("V", $buf); return $value; } else { return undef; } } sub ms_expand { croak("ms_expand(SRC_FILENAME, DST_FILENAME)") if @_ != 2; my($src_fname, $dst_fname) = @_; # Manifest constants for the algorithm. my $N = 4096; my $F = 16; # Open the source file. my $src = new IO::File "<$src_fname"; return 0 if not defined $src; # Open the output file. my $dst = new IO::File ">$dst_fname"; return 0 if not defined $dst; # Read and check the magic values from the file. my($magic1, $magic2, $magic3, $reserved, $filesize); $magic1 = get_uint32_le($src); if ($magic1 == 0x44445A53) { $magic2 = get_uint32_le($src); $reserved = get_uint16_le($src); $filesize = get_uint32_le($src); if ($magic2 != 0x3327F088) { print STDERR "$src_fname: not a MS-compressed file\n"; return 0; } } elsif ($magic1 == 0x4A41574B) { $magic2 = get_uint32_le($src); $magic3 = get_uint32_le($src); $reserved = get_uint16_le($src); if ($magic2 != 0xD127F088 || $magic3 != 0x00120003) { print STDERR "$src_fname: not a MS-compressed file\n"; return 0; } } else { print STDERR "$src_fname: not a MS-compressed file\n"; return 0; } my($i, $j, $mask, @buffer); # Allocate a look-back buffer for the decompression algorithm. for ($i = 0; $i < $N; $i++) { push(@buffer, ord(' ')); } $i = $N - $F; while (1) { my $bits = get_uint8($src); last if not defined $bits; for ($mask = 0x01; $mask & 0xFF; $mask <<= 1) { if (($bits & $mask) == 0) { $j = get_uint8($src); last if not defined $j; my $len = get_uint8($src); $j += ($len & 0xF0) << 4; $len = ($len & 15) + 3; while ($len--) { $buffer[$i] = $buffer[$j]; if (put_uint8($dst, $buffer[$i]) != 1) { print STDERR "output error: $!\n"; return 0; } $j++; $j %= $N; $i++; $i %= $N; } } else { my $ch = get_uint8($src); last if not defined $ch; $buffer[$i] = $ch; if (put_uint8($dst, $buffer[$i]) != 1) { print STDERR "output error: $!\n"; return 0; } $i++; $i %= $N; } } } return 1; } 1;