#!/usr/bin/env perl use 5.010; use strict; use warnings; use experimental 'switch'; use lib 'lib'; # make runnable for simple cases use Data::Dumper; use Getopt::Long 2.33 qw(HelpMessage :config bundling); use Games::Wormy::TICalcLevels; use Games::Wormy::WormEdit; our $VERSION = '1.07'; GetOptions(\my %opt, 'format|f=s', # output type 'raw|r!', # compatibility for yaml format 'version=i', # force version 'levels|render:i', # image of level(s) 'output|o=s', # output file ) or HelpMessage(-exitval => 2); $opt{format} //= 'yaml' if $opt{raw}; $opt{format} //= 'pnm' if defined $opt{levels}; my @OBJTYPE = ('none', 'line', 'fat line', 'bar', 'circle'); my @ENDTYPE = ('none', 'message', 'small message'); sub objsummary { my ($objects) = @_; my @objtypes = map { $_->{type} } @$objects; my %count; $count{$_}++ for @objtypes; return (@objtypes > 1 && keys %count == 1 && 'all ') . join(', ', map { $OBJTYPE[$_] ? $OBJTYPE[$_] . ($count{$_} > 1 && 's') : $_ } sort keys %count ); } # read and parse all input data my $data; local $/; my $rawdata = readline; if (substr($rawdata, 0, 11) eq "**TI86**\032\012\000") { # compiled calculator file $data = Games::Wormy::TICalcLevels->read($rawdata, $opt{version}); } elsif (substr($rawdata, 0, 8) eq 'WormEdit') { # original wormedit source $data = Games::Wormy::WormEdit->read($rawdata, $opt{version}); } else { die "Unrecognised file type\n"; } my $format = $opt{format}; # override distinct from image fallback if ($opt{output}) {{ # derive format from file extension $format //= $1 if $opt{output} =~ /\.([^.]+)$/; if ($format ~~ [qw{ yaml json txt }]) { # redirect standard output to given file open my $output, '>', $opt{output} or die "Cannot output to '$opt{output}': $!"; select $output; } else { # images are written directly to file } }} else { $format //= 'txt'; } # output with user-preferred formatting given ($format) { when ('json') { require JSON; say JSON->new->encode($data); } when ('yaml') { # full data in yaml (human-readable) formatting require YAML; local $YAML::CompressSeries; $YAML::CompressSeries = 0; my $yml = "# Wormy levelset\n" . YAML::Dump($data); # inline format of short hashes $yml =~ s{ ^(\ *) - \n # array indicator ((?:\1\ \ [a-z0-9]{1,5}:\ *\d+\n)+) # simple hash declaration (?!\1\ ) # no further children }[ my ($indent, $value) = ($1, $2); chop $value; $value =~ s/^ +//gm; $value =~ s/\n/, /g; "$indent- {$value}\n"; ]egmx; print $yml; } when ('txt') { print $data->{name}; print " ($data->{description})" if defined $data->{description}; print "\n"; printf "File version: %s\n", "$data->{format} v$data->{version}"; printf "Defaults: %s\n", join('; ', $data->{sprite} ? 'sprite ' . scalar @{ $data->{sprite} } : (), defined $data->{hiname} ? 'hiscore by ' . $data->{hiname} : (), ); my $startnr = 0; for my $variant (qw/single peaworm multi race ctf/) { my $count = $data->{levelcount}->{$variant}; defined $count or next; print "\n"; printf '%s (%s)', ucfirst $variant, $count; $count or next; print ":"; for (0 .. $count - 1) { my $level = $data->{levels}->[$_ + $startnr]; printf("\n- %-22s%4s:%3s+%2s%3s %3sx%-3s%s", $level->{id} || $level->{name} || '#'.($_+1), @$level{qw/size bsize growth/}, $variant eq 'single' && "x$level->{peas}", @$level{qw/width height/}, join(';', map {" $_"} grep {$_} @{$level->{objects}} && sprintf('%2d object%s (%s)', scalar @{$level->{objects}}, @{$level->{objects}} != 1 && 's', objsummary($level->{objects}), ), $level->{sprite} && @{$level->{sprite}} && sprintf('sprite %d', scalar @{$level->{sprite}}, ), $level->{balls} && @{$level->{balls}} && sprintf('%d bounc%s', scalar @{$level->{balls}}, @{$level->{balls}} == 1 ? 'y' : 'ies', ), ), ); } $startnr += $count; print "\n"; printf("-- %-21s%4s: %s (%s)\n", '(ending)', defined $data->{finish}->{code} ? length $data->{finish}->{code} : '?', defined $data->{finish}->{type} ? $ENDTYPE[$data->{finish}->{type}] || 'unknown' : 'code', $data->{finish}->{message} // '?', ) if $variant eq 'single'; } print "\n"; } default { require Games::Wormy::Render; my @request; if ($opt{levels}) { # find all numeric values in argument @request = $opt{levels} =~ /(\d+)/g; } else { # default to all levels @request = 0 .. $data->{levelcount}->{total} - 1; } @request or die "no levels found or specified\n"; my $img = Games::Wormy::Render->composite( map { $data->{levels}->[$_] } @request ) or die "empty result for levels\n"; if ($format ~~ 'pbm') { $img = $img->to_paletted({make_colors => 'mono'}); $opt{format} = 'pnm'; } $img->write( $opt{output} ? (file => $opt{output}) : (fh => \*STDOUT), type => $opt{format}, ) or die $img->errstr; } } __END__ =head1 NAME parse-wormedit - Wormy level data parser =head1 SYNOPSIS parse-wormedit [--format=] [--level=] [--output=] =head1 DESCRIPTION Reads Wormy levels (either original WormEdit source or compiled TI-86 string) from STDIN or given file, and prints parsed data to STDOUT. If an I file name is given, its extension determines the format, otherwise explicitly given by the I option: =over 6 =item txt Plain text summary of levelpack contents. =item yaml All parsed data in YAML syntax. =item json Parsed data in JSON syntax. =item pnm, png, bmp, ... Image drawing of rendered levels. Unrecognised values are interpreted as file type and converted by Imager. =back =head1 AUTHOR Mischa POSLAWSKY =head1 LICENSE GPL version 3.