+my $units = do $datafile;
+Abort("Cannot open unit data", 501, $_) for $@ || $! || ();
+my $patch = shift @{$units}
+ or Abort("Cannot open unit data: metadata not found", 501);
+
+say "<p>Unit properties as seen or measured in $scver{name}\n$patch.";
+say "Also see the $_ tables." for join(' and ',
+ (showlink('StarCraft 2: LotV', '/sc/lotv')) x ($Request ne 'lotv'),
+ (showlink( 'HotS', '/sc/hots')) x ($Request ne 'hots'),
+ (showlink('original SC: Brood War', '/sc/bw')) x ($Request ne 'bw'),
+);
+say "</p>\n";
+
+sub addupgrade {
+ my ($ref, $increase, $org) = @_;
+ if (ref $increase eq 'HASH') {
+ addupgrade(\${$ref}->{$_}, $increase->{$_}, $org->{$_}) for keys %{$increase};
+ }
+ elsif (ref $increase eq 'ARRAY') {
+ addupgrade(\${$ref}->[$_], $increase->[$_], $org->[$_]) for 0 .. $#{$increase};
+ }
+ ${$ref} //= $org;
+ ${$ref} += $increase if $increase =~ /^-?[0-9.]+/;
+}
+
+for my $unit (@{$units}) {
+ for my $upgrade (@{ $unit->{upgrade} }) {
+ while (my ($col, $increase) = each %{$upgrade}) {
+ defined $unit->{$col} or next;
+ addupgrade(\$unit->{upgraded}->{$col}, $increase, $unit->{$col});
+ }
+ }
+ for my $special (@{ $unit->{special} }) {
+ for my $upgrade (@{ $special->{upgrade} }) {
+ while (my ($col, $increase) = each %{$upgrade}) {
+ defined $special->{$col} or next;
+ addupgrade(\$special->{upgraded}->{$col}, $increase, $special->{$col});
+ }
+ }
+ }
+}