+sub unpack {
+ my ($self, $format, $input) = @_;
+ my @data = CORE::unpack $self->template($format), $input;
+ return $self->convert($format, \@data);
+}
+
+
+package main;
+
+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 = Shiar_Parse::WormyLevel->read($rawdata);
+}
+elsif (substr($rawdata, 0, 8) eq 'WormEdit') {
+ # original wormedit source
+ $data = Shiar_Parse::WormEdit->read($rawdata);
+}
+else {
+ die "Unrecognised file type\n";
+}
+
+# output with user-preferred formatting
+if ($opt{raw}) {
+ # 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;
+}
+else {
+ 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 multi race ctf/) {
+ my $count = $data->{levelcount}->{$variant};
+ print "\n";
+ printf '%s (%s)', ucfirst $variant, $count // 'invalid';
+ $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;
+ }
+ continue {
+ 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';
+ }
+}
+