Data::StarCraft::Replay package taken out of screp
authorShiar <shiar@shiar.org>
Tue, 27 Nov 2007 19:29:49 +0000 (20:29 +0100)
committerShiar <shiar@shiar.org>
Tue, 27 Nov 2007 19:29:49 +0000 (20:29 +0100)
Data-StarCraft/lib/Data/StarCraft/Replay.pm [new file with mode: 0644]
screp

diff --git a/Data-StarCraft/lib/Data/StarCraft/Replay.pm b/Data-StarCraft/lib/Data/StarCraft/Replay.pm
new file mode 100644 (file)
index 0000000..507f7d6
--- /dev/null
@@ -0,0 +1,402 @@
+package Data::StarCraft::Replay;
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+use constant {
+       CMD_REPEAT => 4,
+};
+
+my %build = (
+       0x19 => "morph",
+       0x1E => "build",
+       0x1F => "warp",
+       0x24 => "add-on",
+       0x2E => "evolve",
+       0x47 => "land",
+);
+my %unit = (
+       0x00 => "Marine",
+       0x01 => "Ghost",
+       0x02 => "Vulture",
+       0x03 => "Goliath",
+       #               undef,
+       0x05 => "Siege Tank",
+       #               undef,
+       0x07 => "SCV",
+       0x08 => "Wraith",
+       0x09 => "Science Vessel",
+       #               undef,
+       0x0B => "Dropship",
+       0x0C => "Battlecruiser",
+       #               undef,
+       0x0E => "Nuke",
+       #               (undef) x 0x11,
+       0x20 => "Firebat",
+       #               undef,
+       0x22 => "Medic",
+       #               undef,
+       #               undef,
+       0x25 => "Zergling",
+       0x26 => "Hydralisk",
+       0x27 => "Ultralisk",
+       #               undef,
+       0x29 => "Drone",
+       0x2A => "Overlord",
+       0x2B => "Mutalisk",
+       0x2C => "Guardian",
+       0x2D => "Queen",
+       0x2E => "Defiler",
+       0x2F => "Scourge",
+       #               undef,
+       #               undef,
+       0x32 => "Infested Terran",
+       #               (undef) x 7,
+       0x3A => "Valkyrie",
+       #               undef,
+       0x3C => "Corsair",
+       0x3D => "Dark Templar",
+       0x3E => "Devourer",
+       #               undef,
+       0x40 => "Probe",
+       0x41 => "Zealot",
+       0x42 => "Dragoon",
+       0x43 => "High Templar",
+       #               undef,
+       0x45 => "Shuttle",
+       0x46 => "Scout",
+       0x47 => "Arbiter",
+       0x48 => "Carrier",
+       #               (undef) x 0x0A,
+       0x53 => "Reaver",
+       0x54 => "Observer",
+       #               (undef) x 0x12,
+       0x67 => "Lurker",
+       #               undef,
+       #               undef,
+       0x6A => "Command Center",
+       0x6B => "ComSat",
+       0x6C => "Nuclear Silo",
+       0x6D => "Supply Depot",
+       0x6E => "Refinery", # refinery?
+       0x6F => "Barracks",
+       0x70 => "Academy", # Academy?
+       0x71 => "Factory",
+       0x72 => "Starport",
+       0x73 => "Control Tower",
+       0x74 => "Science Facility",
+       0x75 => "Covert Ops",
+       0x76 => "Physics Lab",
+       #               undef,
+       0x78 => "Machine Shop",
+       #               undef,
+       0x7A => "Engineering Bay",
+       0x7B => "Armory",
+       0x7C => "Missile Turret",
+       0x7D => "Bunker",
+       #               (undef) x 4,
+       0x82 => "Infested CC",
+       0x83 => "Hatchery",
+       0x84 => "Lair",
+       0x85 => "Hive",
+       0x86 => "Nydus Canal",
+       0x87 => "Hydralisk Den",
+       0x88 => "Defiler Mound",
+       0x89 => "Greater Spire",
+       0x8A => "Queens Nest",
+       0x8B => "Evolution Chamber",
+       0x8C => "Ultralisk Cavern",
+       0x8D => "Spire",
+       0x8E => "Spawning Pool",
+       0x8F => "Creep Colony",
+       0x90 => "Spore Colony",
+       #               undef,
+       0x92 => "Sunken Colony",
+       #               undef,
+       #               undef,
+       0x95 => "Extractor",
+       #               (undef) x 4,
+       0x9A => "Nexus",
+       0x9B => "Robotics Facility",
+       0x9C => "Pylon",
+       0x9D => "Assimilator",
+       #               undef,
+       0x9F => "Observatory",
+       0xA0 => "Gateway",
+       #               undef,
+       0xA2 => "Photon Cannon",
+       0xA3 => "Citadel of Adun",
+       0xA4 => "Cybernetics Core",
+       0xA5 => "Templar Archives",
+       0xA6 => "Forge",
+       0xA7 => "Stargate",
+       #               undef,
+       0xA9 => "Fleet Beacon",
+       0xAA => "Arbiter Tribunal",
+       0xAB => "Robotics Support Bay",
+       0xAC => "Shield Battery",
+       #               (undef) x 0x14,
+       0xC0 => "Larva",
+       0xC1 => "Rine/Bat",
+       0xC2 => "Dark Archon",
+       0xC3 => "Archon",
+       0xC4 => "Scarab",
+       0xC5 => "Interceptor",
+       0xC6 => "Interceptor/Scarab",
+);
+my @upgrade = (
+       "Terran Infantry Armor",
+       "Terran Vehicle Plating",
+       "Terran Ship Plating",
+       "Zerg Carapace",
+       "Zerg Flyer Carapace",
+       "Protoss Ground Armor",
+       "Protoss Air Armor",
+       "Terran Infantry Weapons",
+       "Terran Vehicle Weapons",
+       "Terran Ship Weapons",
+       "Zerg Melee Attacks",
+       "Zerg Missile Attacks",
+       "Zerg Flyer Attacks",
+       "Protoss Ground Weapons",
+       "Protoss Air Weapons",
+       "Protoss Plasma Shields",
+       # 0x10
+       "U-238 Shells (Marine Range)",
+       "Ion Thrusters (Vulture Speed)",
+       undef,
+       "Titan Reactor (Science Vessel Energy)",
+       "Ocular Implants (Ghost Sight)",
+       "Moebius Reactor (Ghost Energy)",
+       "Apollo Reactor (Wraith Energy)",
+       "Colossus Reactor (Battle Cruiser Energy)",
+       "Ventral Sacs (Overlord Transport)",
+       "Antennae (Overlord Sight)",
+       "Pneumatized Carapace (Overlord Speed)",
+       "Metabolic Boost (Zergling Speed)",
+       "Adrenal Glands (Zergling Attack)",
+       "Muscular Augments (Hydralisk Speed)",
+       "Grooved Spines (Hydralisk Range)",
+       "Gamete Meiosis (Queen Energy)",
+       # 0x20
+       "Defiler Energy",
+       "Singularity Charge (Dragoon Range)",
+       "Leg Enhancement (Zealot Speed)",
+       "Scarab Damage",
+       "Reaver Capacity",
+       "Gravitic Drive (Shuttle Speed)",
+       "Sensor Array (Observer Sight)",
+       "Gravitic Booster (Observer Speed)",
+       "Khaydarin Amulet (Templar Energy)",
+       "Apial Sensors (Scout Sight)",
+       "Gravitic Thrusters (Scout Speed)",
+       "Carrier Capacity",
+       "Khaydarin Core (Arbiter Energy)",
+       undef,
+       undef,
+       "Argus Jewel (Corsair Energy)",
+       # 0x30
+       undef,
+       "Argus Talisman (Dark Archon Energy)",
+       "Caduceus Reactor (Medic Energy)",
+       "Chitinous Plating (Ultralisk Armor)",
+       "Anabolic Synthesis (Ultralisk Speed)",
+       "Charon Boosters (Goliath Range)",
+);
+my @research = (
+       "Stim Pack",
+       "Lockdown",
+       "EMP Shockwave",
+       "Spider Mines",
+       undef,
+       "Siege Tank",
+       undef,
+       "Irradiate",
+       "Yamato Gun",
+       "Cloaking Field (wraith)",
+       "Personal Cloaking (ghost)",
+       "Burrow",
+       undef,
+       "Spawn Broodling",
+       undef,
+       "Plague",
+       # 0x10
+       "Consume",
+       "Ensnare",
+       undef,
+       "Psionic Storm",
+       "Hallucination",
+       "Recall",
+       "Stasis Field",
+       undef,
+       "Restoration",
+       "Disruption Web",
+       undef,
+       "Mind Control",
+       undef,
+       undef,
+       "Optical Flare",
+       "Maelstrom",
+       # 0x20
+       "Lurker Aspect",
+);
+my %action = (
+       0x00 => "Move",
+       0x02 => "Unallowed Move?",
+       0x06 => "Force move",
+       0x08 => "Attack",
+       0x09 => "Gather",
+       0x0E => "Attack Move",
+       0x13 => "Failed Casting (?)",
+       0x17 => "#23 (?)",
+       0x1B => "Infest CC",
+       0x22 => "Repair",
+       0x27 => "Clear Rally",
+       0x28 => "Set Rally",
+       0x4F => "Gather",
+       0x50 => "Gather",
+       0x70 => "Unload",
+       0x71 => "Yamato",
+       0x73 => "Lockdown",
+       0x77 => "Dark Swarm",
+       0x78 => "Parasite",
+       0x79 => "Spawn Broodling",
+       0x7A => "EMP",
+       0x7E => "Launch Nuke",
+       0x84 => "Lay Mine",
+       0x8B => "ComSat Scan",
+       0x8D => "Defense Matrix",
+       0x8E => "Psionic Storm",
+       0x8F => "Recall",
+       0x90 => "Plague",
+       0x91 => "Consume",
+       0x92 => "Ensnare",
+       0x93 => "Stasis",
+       0x94 => "Hallucination",
+       0x98 => "Patrol",
+       0xB1 => "Heal",
+       0xB4 => "Restore",
+       0xB5 => "Disruption Web",
+       0xB6 => "Mind Control",
+       0xB8 => "Feedback",
+       0xB9 => "Optic Flare",
+       0xBA => "Maelstrom",
+       0xC0 => "Irradiate",
+);
+
+my %cmdread = (
+       0x09 => ["select", 1, 2 | CMD_REPEAT],
+       0x0A => ["add", 1, 2 | CMD_REPEAT],
+       0x0B => ["deselect", 1, 2 | CMD_REPEAT],
+       0x0C => ["build", 1, \%build, 2, 2, 2, \%unit],
+       0x0D => ["vision", 2],
+       0x0E => ["ally", 2, 2],
+       0x13 => ["hotkey", 1, [qw"assign select"], 1],
+       0x14 => ["move", 2, 2, 2, 2, 1], # 1 = queued?
+       0x15 => ["action", 2, 2, 2, 2, 1, \%action, 1, [qw"normal queued"]],
+       0x18 => ["cancel"],
+       0x19 => ["cancel hatch"],
+       0x1A => ["stop", 1],
+#      0x1B => ["move-thing??"], # tim: after hotkey (unit, reaver??) select; soon after reselected and moved
+       0x1E => ["return cargo", 1],
+       0x1F => ["train", 2, \%unit],
+       0x20 => ["cancel train", 2], # == 254
+       0x21 => ["cloak", 1],
+       0x22 => ["decloak", 1],
+       0x23 => ["hatch", 2, \%unit],
+       0x25 => ["unsiege", 1],
+       0x26 => ["siege", 1],
+       0x27 => ["arm", 0], # scarab/interceptor
+       0x28 => ["unload all", 1],
+       0x29 => ["unload", 2],
+       0x2A => ["merge archon", 0],
+       0x2B => ["hold position", 1],
+       0x2C => ["burrow", 1],
+       0x2D => ["unburrow", 1],
+       0x2E => ["cancel nuke", 0],
+       0x2F => ["lift", 2, 2],
+       0x30 => ["research", 1, \@research],
+       0x31 => ["cancel research", 0],
+       0x32 => ["upgrade", 1, \@upgrade],
+#      0x33 => ["forge-thing??"], # right after forge select: probably unpowered, iirc cancel research
+       0x35 => ["morph", 2, \%unit],
+       0x36 => ["stim", 0],
+       0x57 => ["part", 1, {qw"1 quit  6 drop"}],
+       0x5A => ["merge dark archon", 0],
+);
+
+sub new {
+       my ($class) = @_;
+       bless [], $class;
+}
+
+sub _read {
+       my $self = shift;
+       my ($fh, $size, $seek) = @_;
+       seek *$fh, $seek, 0 if $seek;
+       read(*$fh, my $in, $size) eq $size or return undef;
+       return $in;
+}
+
+sub open {
+       my $self = shift;
+       my ($file) = @_;
+
+       while (not eof $file) {
+               local $_ = $self->_read($file, 5)
+                       and my ($time, $size) = unpack "VC", $_
+                       or die "Couldn't read time block head\n";
+               local $_ = $self->_read($file, $size)
+                       and my @block = unpack "C*", $_
+                       or die "Couldn't read time block data\n";
+               while (@block) {
+                       my $player = shift @block;
+                       my $cmd = shift @block;
+                       if (not defined $cmdread{$cmd}) {
+                               warn sprintf "command #%X not defined: %d bytes ignored\n",
+                                       $cmd, scalar @block;
+                               push @$self, [$time, $player, "??? $cmd"] if $SHOWWARN;
+                               last;
+                       }
+
+                       sub readbyte {
+                               my ($data, $byte) = @_;
+                               my $out = shift @$data;
+                               if (($byte & 3) == 2) {
+                                       @$data ? ($out += shift(@$data) << 8)
+                                               : warn "high byte not present\n";
+                               }
+                               return $out;
+                       }
+
+                       my @format = @{ $cmdread{$cmd} };
+                       my $desc = shift @format;
+                       my @data;
+                       for my $bit (@format) {
+                               if (ref $bit) {
+                                       if (ref $bit eq "ARRAY") {
+                                               $data[-1] = defined $bit->[$data[-1]] ? $bit->[$data[-1]]
+                                                       : "? ($data[-1])";
+                                       } else {
+                                               $data[-1] = defined $bit->{$data[-1]} ? $bit->{$data[-1]}
+                                                       : "? ($data[-1])";
+                                       }
+                                       next;
+                               }
+                               $bit & 3 or next;
+                               if ($bit & CMD_REPEAT) {
+                                       push @data, readbyte(\@block, $bit) for 1 .. shift @data;
+                               } else {
+                                       push @data, readbyte(\@block, $bit);
+                               }
+                       }
+                       $desc eq "move" and $data[2] == 0 and $desc = "rally";
+                       push @$self, [$time, $player, $desc, @data];
+               }
+       }
+       return $self;
+}
+
+1;
+
diff --git a/screp b/screp
index 5e754adaaa9e34826c4f31262a4b63be9e7d4cee..a97ea97234fceb7690a7ce9efe960550edc30e2b 100755 (executable)
--- a/screp
+++ b/screp
@@ -3,6 +3,8 @@ use strict;
 use warnings;
 use Data::Dumper;
 
+use Data::StarCraft::Replay;
+
 my $SHOWWARN = 0;
 my $ACTGIF = undef;
 my $APMSVG = undef;
@@ -16,408 +18,6 @@ GetOptions(
 
 use constant { APM_FIRSTFRAME => 80 / .042 };
 
-{
-
-package Data::StarCraft::Replay;
-
-use Data::Dumper;
-
-use constant {
-       CMD_REPEAT => 4,
-};
-
-my %build = (
-       0x19 => "morph",
-       0x1E => "build",
-       0x1F => "warp",
-       0x24 => "add-on",
-       0x2E => "evolve",
-       0x47 => "land",
-);
-my %unit = (
-       0x00 => "Marine",
-       0x01 => "Ghost",
-       0x02 => "Vulture",
-       0x03 => "Goliath",
-       #               undef,
-       0x05 => "Siege Tank",
-       #               undef,
-       0x07 => "SCV",
-       0x08 => "Wraith",
-       0x09 => "Science Vessel",
-       #               undef,
-       0x0B => "Dropship",
-       0x0C => "Battlecruiser",
-       #               undef,
-       0x0E => "Nuke",
-       #               (undef) x 0x11,
-       0x20 => "Firebat",
-       #               undef,
-       0x22 => "Medic",
-       #               undef,
-       #               undef,
-       0x25 => "Zergling",
-       0x26 => "Hydralisk",
-       0x27 => "Ultralisk",
-       #               undef,
-       0x29 => "Drone",
-       0x2A => "Overlord",
-       0x2B => "Mutalisk",
-       0x2C => "Guardian",
-       0x2D => "Queen",
-       0x2E => "Defiler",
-       0x2F => "Scourge",
-       #               undef,
-       #               undef,
-       0x32 => "Infested Terran",
-       #               (undef) x 7,
-       0x3A => "Valkyrie",
-       #               undef,
-       0x3C => "Corsair",
-       0x3D => "Dark Templar",
-       0x3E => "Devourer",
-       #               undef,
-       0x40 => "Probe",
-       0x41 => "Zealot",
-       0x42 => "Dragoon",
-       0x43 => "High Templar",
-       #               undef,
-       0x45 => "Shuttle",
-       0x46 => "Scout",
-       0x47 => "Arbiter",
-       0x48 => "Carrier",
-       #               (undef) x 0x0A,
-       0x53 => "Reaver",
-       0x54 => "Observer",
-       #               (undef) x 0x12,
-       0x67 => "Lurker",
-       #               undef,
-       #               undef,
-       0x6A => "Command Center",
-       0x6B => "ComSat",
-       0x6C => "Nuclear Silo",
-       0x6D => "Supply Depot",
-       0x6E => "Refinery", # refinery?
-       0x6F => "Barracks",
-       0x70 => "Academy", # Academy?
-       0x71 => "Factory",
-       0x72 => "Starport",
-       0x73 => "Control Tower",
-       0x74 => "Science Facility",
-       0x75 => "Covert Ops",
-       0x76 => "Physics Lab",
-       #               undef,
-       0x78 => "Machine Shop",
-       #               undef,
-       0x7A => "Engineering Bay",
-       0x7B => "Armory",
-       0x7C => "Missile Turret",
-       0x7D => "Bunker",
-       #               (undef) x 4,
-       0x82 => "Infested CC",
-       0x83 => "Hatchery",
-       0x84 => "Lair",
-       0x85 => "Hive",
-       0x86 => "Nydus Canal",
-       0x87 => "Hydralisk Den",
-       0x88 => "Defiler Mound",
-       0x89 => "Greater Spire",
-       0x8A => "Queens Nest",
-       0x8B => "Evolution Chamber",
-       0x8C => "Ultralisk Cavern",
-       0x8D => "Spire",
-       0x8E => "Spawning Pool",
-       0x8F => "Creep Colony",
-       0x90 => "Spore Colony",
-       #               undef,
-       0x92 => "Sunken Colony",
-       #               undef,
-       #               undef,
-       0x95 => "Extractor",
-       #               (undef) x 4,
-       0x9A => "Nexus",
-       0x9B => "Robotics Facility",
-       0x9C => "Pylon",
-       0x9D => "Assimilator",
-       #               undef,
-       0x9F => "Observatory",
-       0xA0 => "Gateway",
-       #               undef,
-       0xA2 => "Photon Cannon",
-       0xA3 => "Citadel of Adun",
-       0xA4 => "Cybernetics Core",
-       0xA5 => "Templar Archives",
-       0xA6 => "Forge",
-       0xA7 => "Stargate",
-       #               undef,
-       0xA9 => "Fleet Beacon",
-       0xAA => "Arbiter Tribunal",
-       0xAB => "Robotics Support Bay",
-       0xAC => "Shield Battery",
-       #               (undef) x 0x14,
-       0xC0 => "Larva",
-       0xC1 => "Rine/Bat",
-       0xC2 => "Dark Archon",
-       0xC3 => "Archon",
-       0xC4 => "Scarab",
-       0xC5 => "Interceptor",
-       0xC6 => "Interceptor/Scarab",
-);
-my @upgrade = (
-       "Terran Infantry Armor",
-       "Terran Vehicle Plating",
-       "Terran Ship Plating",
-       "Zerg Carapace",
-       "Zerg Flyer Carapace",
-       "Protoss Ground Armor",
-       "Protoss Air Armor",
-       "Terran Infantry Weapons",
-       "Terran Vehicle Weapons",
-       "Terran Ship Weapons",
-       "Zerg Melee Attacks",
-       "Zerg Missile Attacks",
-       "Zerg Flyer Attacks",
-       "Protoss Ground Weapons",
-       "Protoss Air Weapons",
-       "Protoss Plasma Shields",
-       # 0x10
-       "U-238 Shells (Marine Range)",
-       "Ion Thrusters (Vulture Speed)",
-       undef,
-       "Titan Reactor (Science Vessel Energy)",
-       "Ocular Implants (Ghost Sight)",
-       "Moebius Reactor (Ghost Energy)",
-       "Apollo Reactor (Wraith Energy)",
-       "Colossus Reactor (Battle Cruiser Energy)",
-       "Ventral Sacs (Overlord Transport)",
-       "Antennae (Overlord Sight)",
-       "Pneumatized Carapace (Overlord Speed)",
-       "Metabolic Boost (Zergling Speed)",
-       "Adrenal Glands (Zergling Attack)",
-       "Muscular Augments (Hydralisk Speed)",
-       "Grooved Spines (Hydralisk Range)",
-       "Gamete Meiosis (Queen Energy)",
-       # 0x20
-       "Defiler Energy",
-       "Singularity Charge (Dragoon Range)",
-       "Leg Enhancement (Zealot Speed)",
-       "Scarab Damage",
-       "Reaver Capacity",
-       "Gravitic Drive (Shuttle Speed)",
-       "Sensor Array (Observer Sight)",
-       "Gravitic Booster (Observer Speed)",
-       "Khaydarin Amulet (Templar Energy)",
-       "Apial Sensors (Scout Sight)",
-       "Gravitic Thrusters (Scout Speed)",
-       "Carrier Capacity",
-       "Khaydarin Core (Arbiter Energy)",
-       undef,
-       undef,
-       "Argus Jewel (Corsair Energy)",
-       # 0x30
-       undef,
-       "Argus Talisman (Dark Archon Energy)",
-       "Caduceus Reactor (Medic Energy)",
-       "Chitinous Plating (Ultralisk Armor)",
-       "Anabolic Synthesis (Ultralisk Speed)",
-       "Charon Boosters (Goliath Range)",
-);
-my @research = (
-       "Stim Pack",
-       "Lockdown",
-       "EMP Shockwave",
-       "Spider Mines",
-       undef,
-       "Siege Tank",
-       undef,
-       "Irradiate",
-       "Yamato Gun",
-       "Cloaking Field (wraith)",
-       "Personal Cloaking (ghost)",
-       "Burrow",
-       undef,
-       "Spawn Broodling",
-       undef,
-       "Plague",
-       # 0x10
-       "Consume",
-       "Ensnare",
-       undef,
-       "Psionic Storm",
-       "Hallucination",
-       "Recall",
-       "Stasis Field",
-       undef,
-       "Restoration",
-       "Disruption Web",
-       undef,
-       "Mind Control",
-       undef,
-       undef,
-       "Optical Flare",
-       "Maelstrom",
-       # 0x20
-       "Lurker Aspect",
-);
-my %action = (
-       0x00 => "Move",
-       0x02 => "Unallowed Move?",
-       0x06 => "Force move",
-       0x08 => "Attack",
-       0x09 => "Gather",
-       0x0E => "Attack Move",
-       0x13 => "Failed Casting (?)",
-       0x17 => "#23 (?)",
-       0x1B => "Infest CC",
-       0x22 => "Repair",
-       0x27 => "Clear Rally",
-       0x28 => "Set Rally",
-       0x4F => "Gather",
-       0x50 => "Gather",
-       0x70 => "Unload",
-       0x71 => "Yamato",
-       0x73 => "Lockdown",
-       0x77 => "Dark Swarm",
-       0x78 => "Parasite",
-       0x79 => "Spawn Broodling",
-       0x7A => "EMP",
-       0x7E => "Launch Nuke",
-       0x84 => "Lay Mine",
-       0x8B => "ComSat Scan",
-       0x8D => "Defense Matrix",
-       0x8E => "Psionic Storm",
-       0x8F => "Recall",
-       0x90 => "Plague",
-       0x91 => "Consume",
-       0x92 => "Ensnare",
-       0x93 => "Stasis",
-       0x94 => "Hallucination",
-       0x98 => "Patrol",
-       0xB1 => "Heal",
-       0xB4 => "Restore",
-       0xB5 => "Disruption Web",
-       0xB6 => "Mind Control",
-       0xB8 => "Feedback",
-       0xB9 => "Optic Flare",
-       0xBA => "Maelstrom",
-       0xC0 => "Irradiate",
-);
-
-my %cmdread = (
-       0x09 => ["select", 1, 2 | CMD_REPEAT],
-       0x0A => ["add", 1, 2 | CMD_REPEAT],
-       0x0B => ["deselect", 1, 2 | CMD_REPEAT],
-       0x0C => ["build", 1, \%build, 2, 2, 2, \%unit],
-       0x0D => ["vision", 2],
-       0x0E => ["ally", 2, 2],
-       0x13 => ["hotkey", 1, [qw"assign select"], 1],
-       0x14 => ["move", 2, 2, 2, 2, 1], # 1 = queued?
-       0x15 => ["action", 2, 2, 2, 2, 1, \%action, 1, [qw"normal queued"]],
-       0x18 => ["cancel"],
-       0x19 => ["cancel hatch"],
-       0x1A => ["stop", 1],
-#      0x1B => ["move-thing??"], # tim: after hotkey (unit, reaver??) select; soon after reselected and moved
-       0x1E => ["return cargo", 1],
-       0x1F => ["train", 2, \%unit],
-       0x20 => ["cancel train", 2], # == 254
-       0x21 => ["cloak", 1],
-       0x22 => ["decloak", 1],
-       0x23 => ["hatch", 2, \%unit],
-       0x25 => ["unsiege", 1],
-       0x26 => ["siege", 1],
-       0x27 => ["arm", 0], # scarab/interceptor
-       0x28 => ["unload all", 1],
-       0x29 => ["unload", 2],
-       0x2A => ["merge archon", 0],
-       0x2B => ["hold position", 1],
-       0x2C => ["burrow", 1],
-       0x2D => ["unburrow", 1],
-       0x2E => ["cancel nuke", 0],
-       0x2F => ["lift", 2, 2],
-       0x30 => ["research", 1, \@research],
-       0x31 => ["cancel research", 0],
-       0x32 => ["upgrade", 1, \@upgrade],
-#      0x33 => ["forge-thing??"], # right after forge select: probably unpowered, iirc cancel research
-       0x35 => ["morph", 2, \%unit],
-       0x36 => ["stim", 0],
-       0x57 => ["part", 1, {qw"1 quit  6 drop"}],
-       0x5A => ["merge dark archon", 0],
-);
-
-sub new {
-       my ($class) = @_;
-       bless [], $class;
-}
-
-sub _read {
-       my $self = shift;
-       my ($fh, $size, $seek) = @_;
-       seek *$fh, $seek, 0 if $seek;
-       read(*$fh, my $in, $size) eq $size or return undef;
-       return $in;
-}
-
-sub open {
-       my $self = shift;
-       my ($file) = @_;
-
-       while (not eof $file) {
-               local $_ = $self->_read($file, 5)
-                       and my ($time, $size) = unpack "VC", $_
-                       or die "Couldn't read time block head\n";
-               local $_ = $self->_read($file, $size)
-                       and my @block = unpack "C*", $_
-                       or die "Couldn't read time block data\n";
-               while (@block) {
-                       my $player = shift @block;
-                       my $cmd = shift @block;
-                       if (not defined $cmdread{$cmd}) {
-                               warn sprintf "command #%X not defined: %d bytes ignored\n",
-                                       $cmd, scalar @block;
-                               push @$self, [$time, $player, "??? $cmd"] if $SHOWWARN;
-                               last;
-                       }
-
-                       sub readbyte {
-                               my ($data, $byte) = @_;
-                               my $out = shift @$data;
-                               if (($byte & 3) == 2) {
-                                       @$data ? ($out += shift(@$data) << 8)
-                                               : warn "high byte not present\n";
-                               }
-                               return $out;
-                       }
-
-                       my @format = @{ $cmdread{$cmd} };
-                       my $desc = shift @format;
-                       my @data;
-                       for my $bit (@format) {
-                               if (ref $bit) {
-                                       if (ref $bit eq "ARRAY") {
-                                               $data[-1] = defined $bit->[$data[-1]] ? $bit->[$data[-1]]
-                                                       : "? ($data[-1])";
-                                       } else {
-                                               $data[-1] = defined $bit->{$data[-1]} ? $bit->{$data[-1]}
-                                                       : "? ($data[-1])";
-                                       }
-                                       next;
-                               }
-                               $bit & 3 or next;
-                               if ($bit & CMD_REPEAT) {
-                                       push @data, readbyte(\@block, $bit) for 1 .. shift @data;
-                               } else {
-                                       push @data, readbyte(\@block, $bit);
-                               }
-                       }
-                       $desc eq "move" and $data[2] == 0 and $desc = "rally";
-                       push @$self, [$time, $player, $desc, @data];
-               }
-       }
-       return $self;
-}
-
-}
-
 my @race = (qw(Z T P), (undef) x 3, '-');
 
 sub showtime {