#
# Perl module to expand Microsoft-compressed files
#
# Based on algorithm provided in the GPL'ed mscompress package by:
# Martin Hinner <
[email protected]>
# M. Winterhoff <
[email protected]>
#
# 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;