182 lines
5.1 KiB
Perl
Executable file
182 lines
5.1 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Getopt::Std; $Getopt::Std::STANDARD_HELP_VERSION = 1;
|
|
|
|
sub VERSION_MESSAGE
|
|
{
|
|
print <<"EOF";
|
|
lmpwatch.pl: a clone of PrBoom-Plus's automated demo playback system.
|
|
EOF
|
|
}
|
|
|
|
sub HELP_MESSAGE
|
|
{
|
|
print <<"EOF";
|
|
Usage: $0 [OPTIONS] DEMO_FILES...
|
|
|
|
Options:
|
|
-d x name of doom executable (try "echo" to test the program)
|
|
-e x any extra options to pass to doom
|
|
-p x name of patterns file from lmpwatch.zip
|
|
-s x colon- or space-separated search path, may contain wildcards
|
|
-t test demo sync (not fully implemented, needs engine-side support)
|
|
-v print lots of debugging output
|
|
|
|
EOF
|
|
}
|
|
|
|
getopts('d:e:p:s:tv', \my %opts) or do {
|
|
HELP_MESSAGE();
|
|
die "$0: bad options\n"
|
|
};
|
|
if (!@ARGV) {
|
|
HELP_MESSAGE();
|
|
die "$0: no demos?\n";
|
|
}
|
|
|
|
my $verbose = defined($opts{'v'});
|
|
|
|
# name of doom executable
|
|
my $DOOM = (defined $opts{'d'}) ? $opts{'d'} : "prboom";
|
|
# any extra options to pass?
|
|
my $extra = (defined $opts{'e'}) ? $opts{'e'} : "";
|
|
|
|
# are we playing back demos or testing them for demosync?
|
|
my $testing = defined($opts{'t'});
|
|
# "batch mode" if you're testing a demo (turns off the renderer)
|
|
my $demo_opts = $testing ? "-nodraw -nosound -timedemo" : "-playdemo";
|
|
|
|
# paths to search for files, these will be subject to filename globbing
|
|
my @paths = do {
|
|
# expand colon- or space-separated string into list
|
|
my @s = defined($opts{'s'}) ? split(/[: ]+/, $opts{'s'}) : ();
|
|
|
|
# expand paths that contain wildcards
|
|
map { glob $_ } grep { defined $_ } (@s, $ENV{"DOOMWADDIR"},
|
|
"/usr/local/share/games/doom", "/usr/share/games/doom");
|
|
};
|
|
print "DIRS: @paths\n\n" if $verbose;
|
|
|
|
# name of the patterns file from lmpwatch.zip
|
|
my $patsfile = do {
|
|
my $p = (defined $opts{'p'}) ? $opts{'p'} : "patterns.txt";
|
|
path_expand($p, @paths);
|
|
} or die "$0: lmpwatch.zip patterns file is needed to use this program\n";
|
|
print "PATTERNS: $patsfile\n\n" if $verbose;
|
|
open my ($PATTERNS), $patsfile;
|
|
|
|
while (@ARGV) {
|
|
my $demo = path_expand(shift @ARGV, @paths) or next;
|
|
my ($comment, $iwad, @files) = parse_patterns($PATTERNS, $demo);
|
|
print "DEMO: $demo\nIWAD: $iwad\nFILES: @files\n" if $verbose;
|
|
|
|
# filename globbing and switch prepending for wad files
|
|
$iwad = path_expand($iwad, @paths) or next; # not strictly necessary
|
|
@files = map { path_expand($_, @paths) or next; } @files;
|
|
my @deh = grep { /\.(deh|bex)$/i } @files;
|
|
unshift @deh, "-deh" if @deh;
|
|
my @wad = grep { /\.wad$/i } @files;
|
|
unshift @wad, "-file" if @wad; # should be "-merge" for chocolate-doom?
|
|
|
|
my $command = sprintf("%s -iwad %s %s %s %s %s %s",
|
|
$DOOM, $iwad, "@wad", "@deh", $demo_opts, $demo, $extra);
|
|
print "RUNNING: $command\n" if $verbose;
|
|
|
|
if ($testing) {
|
|
my @finished = test_demo($command);
|
|
# unfortunately there is not yet an easy way to test if the
|
|
# list of maps the player exited is correct. lmpwatch.zip is
|
|
# only meant for demo playback, not testing.
|
|
# just print out the names of maps that were completed
|
|
print "FINISHED: " if $verbose;
|
|
print join(" ", (split(m!/!, $demo))[-1], @finished), "\n";
|
|
} else {
|
|
system($command);
|
|
}
|
|
print "\n" if $verbose;
|
|
}
|
|
|
|
close $PATTERNS;
|
|
|
|
exit 0;
|
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
# play back a demo, return list of maps the player finishes
|
|
# requires that the engine prints "FINISHED: <mapname>" when a map is beaten
|
|
sub test_demo
|
|
{
|
|
my ($command) = @_;
|
|
my @finished;
|
|
|
|
# run the game and capture its output
|
|
open my ($doom), "$command 2>&1 |";
|
|
# make a list of maps the player managed to exit
|
|
# this needs engine-side support
|
|
while (<$doom>) {
|
|
push @finished, $1 if m/^FINISHED: (.*)$/
|
|
}
|
|
close $doom;
|
|
|
|
return @finished;
|
|
}
|
|
|
|
# parse lmpwatch.zip's patterns file to determine wads required to play a demo
|
|
sub parse_patterns
|
|
{
|
|
my ($PATTERNS, $demofile) = @_;
|
|
my ($mask, $comment, $pattern, $iwad, @files);
|
|
|
|
my ($demo) = (split m!/!,$demofile)[-1];
|
|
|
|
seek $PATTERNS, 0, 0; # rewind file to start
|
|
while (<$PATTERNS>) {
|
|
chomp;
|
|
# read valid key/value pairs
|
|
my ($key, $value) = m/^(\w+)\s+\"(.+)\"/;
|
|
next if !defined($key) || !defined($value);
|
|
|
|
# set the pattern mask
|
|
$mask = $value, next if $key eq 'demo_patterns_mask';
|
|
|
|
# ignore keys that don't start with the pattern mask
|
|
next if !$mask || $key !~ m/^$mask/;
|
|
|
|
# decode the pattern
|
|
($comment, $pattern, my $files) = split(m!/!, $value);
|
|
# the first file is always the iwad, apparently
|
|
($iwad, @files) = split(m!\|!, $files);
|
|
next if !$iwad; # no iwad??
|
|
|
|
# stop at the first match (conflict avoidance)
|
|
last if $demo =~ m/$pattern/
|
|
}
|
|
|
|
return ($comment, $iwad, @files);
|
|
}
|
|
|
|
# from the list supplied, find the first directory a given filename is in
|
|
sub path_expand
|
|
{
|
|
my $file = shift;
|
|
|
|
# if it's already a valid filename, skip the searching
|
|
return $file if -f $file;
|
|
|
|
print " SEARCHING: $file\n" if $verbose;
|
|
for my $dir (@_) {
|
|
my $path = "$dir/$file";
|
|
print " TRYING: $path\n" if $verbose;
|
|
if (-f $path) {
|
|
print " FOUND: $file => $path\n" if $verbose;
|
|
return $path;
|
|
}
|
|
}
|
|
warn "$0: cannot find $file\n";
|
|
return undef;
|
|
}
|
|
|
|
# vim:set sts=2 sw=2 ts=8 et:
|