#!/usr/bin/perl -w
#############################################################################
#
# Zwrap v1.00
#
# Copyright (C) 2004 David Griffith <
[email protected]>
#
# This program will create, in effect, a self-extracting executable
# from a Z-machine binary or zcode file. This is intended to simplify
# giving zcode games to people using Unix machines who might not
# clearly understand what a zcode interpreter is and how to use it.
# This program creates a Perl script which includes the zcode file
# encoded in uuencode format along with code to extract it. When that
# script is executed, the game is extracted to /tmp and given as a
# command-line parameter to a zcode interpreter. When the interpreter
# exits, the zcode file is deleted.
#
#############################################################################
#############################################################################
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Although this program is GPLed, the programs it creates are not.
# That decision is the business of the user. In other words, you can
# distribute your zwrapped program however you like.
#
#############################################################################
use strict;
use POSIX;
use File::Basename;
use File::Copy;
use vars qw($opt_a $opt_o $opt_r $opt_s $opt_t);
use Getopt::Std;
# uuencode / uudecode code taken from:
#
http://search.cpan.org/author/ANDK/Convert-UU-0.52/lib/Convert/UU.pm
my ($zwrap_version, $zwrap_date, $zwrap_author, $zwrap_author_email);
my ($tempdir, $encoded_string, $zcode, $filename, $gzfilename, @zcode);
my ($uufilename, $filename_fix, $outfile, $pwd, $umask_save, $terp);
my ($author, $title, $release, $serial);
$zwrap_version = "1.00";
$zwrap_date = "2003";
$zwrap_author = "David Griffith";
$zwrap_author_email = "<dgriffi\@cs.csubak.edu>";
$author = "Unknown Author";
$release = "Unknown Release";
$serial = "Unknown Serial";
$title = "Unknown Title";
$tempdir = "/tmp";
$terp = "frotz";
getopts("a:o:r:s:t:");
if ($opt_a) {
$author = $opt_a;
}
if ($opt_o) {
$outfile = $opt_o;
}
if ($opt_r) {
$release = $opt_r;
}
if ($opt_s) {
$serial = $opt_s;
}
if ($opt_t) {
$title = $opt_t;
}
if ($ARGV[0]) {
$filename = $ARGV[0];
if (! -f $filename) {
die "I don't see that file.\n";
}
} else {
usage();
}
$filename_fix = basename($filename)."$$";
$gzfilename = "$tempdir/" . $filename_fix;
$umask_save = umask();
umask(077);
copy($filename, $gzfilename);
`gzip -q $gzfilename`;
$gzfilename = "$gzfilename.gz";
$uufilename = basename($filename).".gz";
open(GAMEFILE, "< $gzfilename");
@zcode = <GAMEFILE>;
close(GAMEFILE);
umask($umask_save);
foreach my $i (@zcode) {
$zcode = $zcode.$i;
}
unlink $gzfilename;
$encoded_string = uuencode($zcode, $uufilename);
# Protect single-quotes and backslashes from being mangled.
#
$encoded_string =~ s/\134/\134\134/g;
$encoded_string =~ s/\'/\\'/g;
# Now write out the wrapper script.
#
if (!$outfile) {
$outfile = basename("$filename");
$outfile =~ s/\..+//;
$outfile = "$outfile.pl";
}
open(OUTFILE, "> $outfile") || die "Unable to write $outfile\n";
print OUTFILE <<EOF;
#!/usr/bin/perl -w
#
#############################################################################
#
# Zwrap version $zwrap_version by $zwrap_author presents:
#
# \"$title\" by $author
# Release $release / Serial number $serial
# Zwrapped file: $filename
#
#############################################################################
#
# This script was created by zwrap $zwrap_version
# Copyright (C) $zwrap_date $zwrap_author $zwrap_author_email";
#
# Upon execution, this script will extract and decompress a zcode file
# and execute a zcode interpreter.
#
# Requirements:
# A resonably modern Unix operating system.
# Perl 5.0 or later.
# A Z-machine interpreter (currently only Frotz is supported).
#
#
# Although the script that created this script is GPLed, the copyright
# for this script belongs to whoever created it.
#
use strict;
sub uudecode {
die("Usage: uudecode( {string|filehandle|array ref})\\n")
unless(\@_ == 1);
my(\$in) = \@_;
my(\@result,\$file,\$mode);
\$mode = \$file = "";
if (
ref(\$in) eq 'IO::Handle' or
ref(\\\$in) eq "GLOB" or
ref(\$in) eq "GLOB" or
ref(\$in) eq 'FileHandle'
) {
local(\$\\) = "\\n";
binmode(\$in);
while (<\$in>) {
if (\$file eq "" and !\$mode){
(\$mode,\$file) = (\$1, \$2) if /^begin\\s+(\\d+)\\s+(.+)\$/ ;
next;
}
last if /^end/;
push \@result, uudecode_chunk(\$_);
}
} elsif (ref(\\\$in) eq "SCALAR") {
while (\$in =~ m/\\G(.*?(\\n|\\r|\\r\\n|\\n\\r))/gc) {
my \$line = \$1;
if (\$file eq "" and !\$mode){
(\$mode,\$file) = \$line =~ /^begin\\s+(\\d+)\\s+(.+)\$/ ;
next;
}
next if \$file eq "" and !\$mode;
last if \$line =~ /^end/;
push \@result, uudecode_chunk(\$line);
}
} elsif (ref(\$in) eq "ARRAY") {
my \$line;
foreach \$line (\@\$in) {
if (\$file eq "" and !\$mode){
(\$mode,\$file) = \$line =~ /^begin\\s+(\\d+)\\s+(.+)\$/ ;
next;
}
next if \$file eq "" and !\$mode;
last if \$line =~ /^end/;
push \@result, uudecode_chunk(\$line);
}
}
wantarray ? (join("",\@result),\$file,\$mode) : join("",\@result);
}
sub uudecode_chunk {
my(\$chunk) = \@_;
return "" if \$chunk =~ /^(?:--|CREATED)/;
my \$string = substr(\$chunk,0,int((((ord(\$chunk) - 32) & 077) + 2) / 3)*4+1);
my \$ret = unpack("u", \$string);
defined \$ret ? \$ret : "";
}
# It should be obvious how to extract the zcode file if you want to.
#
my \$filestring =\n\'$encoded_string\';
my (\$tempdir, \$zcode, \$filename, \$mode, \$terp);
\$tempdir = "$tempdir";
\$terp = "$terp";
(\$zcode, \$filename, \$mode) = uudecode(\$filestring);
\$filename = "\$tempdir\"."/zwrap_\".\"\$\$\".\"_\".\"\$filename";
umask 077;
open(OUTFILE, "> \$filename") || die "Unable to write \$filename.\\n";
print OUTFILE \$zcode;
close(OUTFILE);
system("gzip -d \$filename");
\$filename =~ s/.gz\$//;
system("\$terp \$filename");
system("reset -Q");
unlink(\$filename);
EOF
close(OUTFILE);
# Finished creating the wrapper script
#
sub usage {
die "usage: $0 [options] zcodefile
options: -a \"Joe Bloggs\" (author)
-t \"My Game\" (game title)
-r \"4\" (release number)
-s \"010101\" (serial number)\n";
}
sub uuencode {
die("Usage: uuencode( {string|filehandle} [,filename] [, mode] )")
unless(@_ >= 1 && @_ <= 3);
my ($in, $file, $mode) = @_;
$mode ||= "644";
$file ||= "uuencode.uu";
my ($chunk, @result, $r);
if (
ref($in) eq 'IO::Handle' or
ref(\$in) eq "GLOB" or
ref($in) eq "GLOB" or
ref($in) eq 'FileHandle'
) {
# local $^W = 0; # Why did I get use of undefined value here ?
binmode($in);
local $/;
$in = <$in>;
}
pos($in)=0;
while ($in =~ m/\G(.{1,45})/sgc) {
push @result, uuencode_chunk($1);
}
push @result, "`\n";
join "", "begin $mode $file\n", @result, "end\n";
}
sub uuencode_chunk {
my($string) = shift;
my $encoded_string = pack("u", $string); # unix uuencode
$encoded_string;
}