+if ($ACTGIF) {
+ open my $imgfile, '>', "test.gif" or die;
+ binmode $imgfile;
+ select $imgfile;
+
+ require GD;
+ my $ani = GD::Image->new($head->{width}, $head->{height});
+ my $bg = $ani->colorAllocate(0, 0, 0);
+ my @plot = (
+ $ani->colorAllocate(255, 0, 0),
+ $ani->colorAllocate(255, 255, 0),
+ $ani->colorAllocate(0, 255, 0),
+ $ani->colorAllocate(0, 255, 255),
+ $ani->colorAllocate(0, 0, 255),
+ $ani->colorAllocate(255, 0, 255),
+ );
+
+ print $ani->gifanimbegin;
+# print $ani->gifanimadd;
+ {
+ my $frame = GD::Image->new($ani->getBounds);
+ print $frame->gifanimadd;
+ my $length = 30 / .042;
+ my $last = 0;
+ for (@$map) {
+ my ($time, $player, $cmd, @data) = @$_;
+#$time < $length * 10 or last;
+ while ($time > $last + $length) {
+ $last += $length;
+ print $frame->gifanimadd(0, 0, 0, 32);
+# $frame = GD::Image->new($ani->getBounds);
+ }
+ if ($cmd eq "build") {
+ $frame->setPixel($data[1]>>5, $data[2]>>5, $plot[$player]);
+ }
+ elsif ($cmd eq "move" or $cmd eq "attack") {
+ $frame->setPixel($data[0]>>5, $data[1]>>5, $plot[$player]);
+# if $data[2] == 0xFFFF_FFFF;
+ }
+ }
+# add_frame_data($frame);
+ print $frame->gifanimadd;
+ }
+ print $ani->gifanimend;
+ select STDOUT;
+}
+
+if ($DBGAME or $DBNAME) {
+
+require Games::StarCraft::DB;
+my $Db = Games::StarCraft::DB->connect({RaiseError => 1})
+ or die "No database: $DBI::errstr\n";
+
+sub findaccount ($) {
+ my ($name) = @_;
+ my $query = $Db->query(q{
+ SELECT DISTINCT account FROM play
+ WHERE name = ? AND account IS NOT NULL
+ }, $name);
+ return $query->rows == 1 ? $query->list : undef;
+}
+
+if ($DBGAME) {{
+ print "\n";
+ my $game = $Db->query("SELECT * FROM game WHERE id=?", $DBGAME)->hash;
+ if (not $game) {
+ printf "Database game # %d not found\n", $DBGAME;
+ last;
+ }
+ if ($game->{map} ne $head->{map}) {
+ printf "Replay map (%s) does not match database map (%s)\n",
+ $head->{map}, $game->{map};
+ last;
+ }
+
+ $Db->begin;
+ $Db->insert("game", {
+ frames => $head->{frames},
+# map => $head->{map},
+# start => time2str('%Y-%m-%d %X', $head->{time}),
+ # endreplay => time2str('%Y-%m-%d %X', $repstats[9]), # mtime
+# durationguess => \"endreplay - start",
+ });
+ $Db->update("play", {
+ name => $_->{name}, #TODO: --force
+ race => $_->{race}, # --force
+ apm => $_->{gameactions} / ($_->{last} - APM_FIRSTFRAME) * 60 / .042,
+ team => $_->{team},
+ color => $_->{color},
+ }, {
+ game => $DBGAME,
+ slot => $_->{slot},
+ }) for values %stats;
+ $Db->commit;
+}}
+
+if ($DBNAME) {
+ print "\n";
+ my @repstats = stat $DBNAME or die "no rep: $!\n";
+ my ($name) = $DBNAME =~ m{.*/([^.]+)};
+
+ my %placetxt = (
+ bn => "bnet",
+ gr => "groningen",
+ md => "mdhq",
+ );
+ my ($placeid) = $name =~ /.*([a-z]{2})/;
+ my $place = defined $placetxt{$placeid} ? $placetxt{$placeid} : undef;
+
+ my $winslot;
+ if (@ARGV == 1 and $ARGV[0] =~ /^\d$/) {
+ $winslot = $ARGV[0];
+ }
+
+ $Db->begin;
+ $Db->insert("game", {
+ name => $name,
+ frames => $head->{frames},
+ map => $head->{map},
+ start => time2str('%Y-%m-%d %X', $head->{time}),
+ endreplay => time2str('%Y-%m-%d %X', $repstats[9]), # mtime
+# durationguess => \"endreplay - start",
+ place => $place,
+ });
+ my $gameid = $Db->last_insert_id((undef)x4, {sequence => "game_id_seq"});
+ $Db->update("game", {durationguess => \"endreplay - start"}, {id => $gameid});
+ $Db->insert("play", {
+ game => $gameid,
+ slot => $_->{slot},
+ name => $_->{name},
+ race => $_->{race},
+ apm => $_->{gameactions} / ($_->{last} - APM_FIRSTFRAME) * 60 / .042,
+ team => $_->{team},
+ color => $_->{color},
+ account => findaccount($_->{name}),
+ result => defined $winslot ? $_->{slot} == $winslot ? 1 : -1 : 0,
+ }) for values %stats;
+ $Db->commit;
+}
+
+}
+
+if ($APMSVG) {
+ my @seq; # player => time (s) => actions
+ $seq[$_->[1]][$_->[0] * .042]++ for @$map;
+ my $flatten = 120;
+ my @apm;
+ for my $player (0 .. $#seq) {
+ my $range = 0;
+ $range += $seq[$player][$_] || 0 for 0 .. $flatten - 1;
+ my $leadfill = $range / $flatten;
+ for my $frame (0 .. $#{$seq[$player]}) {
+ $range += $seq[$player][$frame] || 0;
+ $range -= $frame < $flatten ? $leadfill :
+ $seq[$player][$frame - $flatten] || 0;
+ $apm[$player][$frame] = $range / $flatten;
+ }
+ }
+
+ BEGIN { unshift @INC, '.' }
+ require SVG::TT::Graph::TimeSeries;
+ my $graph = SVG::TT::Graph::TimeSeries->new({
+ height => 1200,
+ width => 1600,
+ style_sheet => "apm.css",
+ show_data_values => 0,
+ show_data_points => 0,
+ x_label_format => '%k:%M',
+ key => 1,
+ timescale_divisions => "5 minutes",
+ # compress => 1,
+ });
+
+ for my $player (0 .. $#apm) {
+ $graph->add_data({
+ data => [map {
+ time2str('%Y-%m-%d %X', 946681200 + $_),
+ $apm[$player][$_] * 60
+ } 0 .. $#{$apm[$player]} ],
+ title => showplayer($player),
+ });
+ }
+
+ my ($name) = $APMSVG =~ /([^.]+)/;
+ my $title = "APM timeline" . ($name && " for $name");
+ my $lead = sprintf "\n<title>%s</title>", $title;
+
+ my $svg = $graph->burn();
+ s/^[ \t\r]+\n//gm, # remove lines with only whitespace (many useless ^M)
+ s/[ \t\r]+$//gm, # trailing whitespace
+ s/ {4}\r*/\t/g, # tabs for indenting
+ s/^(<svg width=")1600(" height=")1200("[^>]*>)/${1}100%${2}100%$3$lead/m,
+ for $svg; # cleanup xml
+
+ open my $apmfile, '>', "$APMSVG.svg";
+ print $apmfile $svg;
+}
+
+__END__
+
+=head1 NAME
+
+screp - StarCraft replay parser
+
+=head1 SYNOPSIS
+
+screp [options] < [replay data]
+
+ Options:
+ --verbose
+ --apm
+ --act
+ --dbname
+ --dbid
+
+=head1 OPTIONS
+
+=head1 AUTHOR
+
+Mischa POSLAWSKY <perl@shiar.org>
+
+=head1 STUFF
+