#!/usr/bin/perl
#
# Extract ZIX files
#
# Written by Alan Mizrahi, alan at mizrahi dot com dot ve (http://www.mizrahi.com.ve/)
#
# Thanks to Kenneth Sorling, nevershaveyourduck at gmail dot com (http://www.kennethsorling.se/)
# for the file format
#
# WARNING: do NOT run any programs contained inside any .ZIX files, they are presumably
# loaded with crap that can harm your computer.  Media content (movies, sound) should be ok.
# Read http://en.wikipedia.org/wiki/Winzix for more information
#
# first (and hopefully last) version: 20080124 [AM]
#

use strict;
use warnings;
use File::Basename;
use Compress::Zlib;

die "Syntax: unzix.pl <file.zix>" if $#ARGV < 0;

my $in = $ARGV[0];
open IN, $in or die "Error opening $in: $!";
binmode IN;

my $buffer;
my $BUFSIZE = 8 * 1048576;

my $r = read IN, $buffer, 6;
die "Error reading $in: $!" if $r!=6;
die "Not zix 2.0 format" if unpack('a6', $buffer) ne 'WINZIX';
$r = read IN, $buffer, 2;
die "Error reading $in: $!" if $r!=2;
die "New version of format, please contact the author" if unpack('n', $buffer) != 3;

my $p = 8;
while (!eof(IN)) {
	$r = read IN, $buffer, 43;
	die "Error reading $in: $!" if $r != 43;
	my ($compSize, $size, $fnLen, $sum) = unpack('x6Nx4Nx6nxC16', $buffer);
	$r = read IN, $buffer, $fnLen;
	die "Error reading $in: $!" if $r != $fnLen;
	my $fname = unpack("a$fnLen", $buffer);
	($fname) = $fname =~/^\/*(.*)/; # convert to relative path
	print "Extracting $fname ($size bytes)\n";
	my $dir = '';
	for (split('/', dirname($fname))) {
		$dir.="$_/";
		if (! -d $dir) {
			mkdir $dir or die "Error creating dir $dir: $!";
		}
	}
	open OUT, ">$fname" or die "Error creating $fname: $!";
	binmode OUT;
	my $zlib = inflateInit() or die "Error creating inflation stream";
	
	my $rs = 0; # already read size
	while ($rs < $compSize) {
		my $bs = $BUFSIZE<($compSize-$rs)?$BUFSIZE:($compSize-$rs); # how much to read
		$r = read IN, $buffer, $bs;
		die "Error reading $in: $!" if $bs != $r;
		my ($outbuf, $status) = $zlib->inflate(\$buffer);
		die "Error uncompressing $in: zlib status: $status" if ($status != Z_OK && $status != Z_STREAM_END);
		syswrite OUT, $outbuf;
		$rs += $r;
		printf "%d/%d (%3d%%)\n", $rs, $compSize, ($rs * 100 /$compSize);
	}
	close OUT;
}

close IN;
exit 0;
