#!/usr/bin/perl # This program is free software. You can redistribute it and/or modify it under # the same terms as perl itself. Copyright © 2011 M. Kristall use strict; use bytes; sub dumpentities { die ("$_[0]: $!\n") unless (open (BSP, '<', $_[0])); binmode (BSP); read (BSP, my $str, 4); die ("$_[0]: does not look like a BSP\n") if ($str ne 'IBSP'); seek (BSP, 8, 0); read (BSP, $str, 8); my ($pos, $len) = unpack ('V2', $str); seek (BSP, $pos, 0); read (BSP, $str, $len); close (BSP); # trailing null chop ($str) if (substr ($str, -1) eq "\0"); print $str; } sub updateentities { local $\; # just in case if ($_[1] eq '' || $_[1] eq '-') { *ENT = \*STDIN; } else { die ("$_[1]: $!\n") unless (open (ENT, '<', $_[1])); } binmode (ENT); read (ENT, my $newents, 10000000); close (ENT); # likes a trailing null $newents .= "\0" if (substr ($newents, -1) ne "\0"); my $len = length ($newents); my $offset; die ("$_[0]: $!\n") unless (open (RBSP, '<', $_[0])); binmode (RBSP); read (RBSP, my $str, 4); die ("$_[0]: does not look like a BSP\n") if ($str ne 'IBSP'); die ("can't create $_[0].new: $!\n") unless (open (WBSP, '>', "$_[0].new")); binmode (WBSP); print WBSP $str; # version read (RBSP, $str, 4); print WBSP $str; # direntries read (RBSP, $str, 4 * 2 * 17); my @direntries = unpack ('(V2)17', $str); $offset = $len - $direntries[1]; $direntries[1] = $len; # update offsets if ($offset) { for (my $i = 0; $i < @direntries; $i += 2) { $direntries[$i] += $offset if ($direntries[$i] > $direntries[0]); } } print WBSP pack ('(V2)17', @direntries); my $pos = 144; while ($pos + 4096 < $direntries[0]) { $pos += read (RBSP, $str, 4096); print WBSP $str; } read (RBSP, $str, $direntries[0] - $pos); print WBSP $str, $newents; seek (RBSP, $len - $offset, 1); while (read (RBSP, $str, 4096)) { print WBSP $str; } close (RBSP); close (WBSP); rename ($_[0], "$_[0].bak"); rename ("$_[0].new", $_[0]); } if (@ARGV == 1) { dumpentities (@ARGV); } elsif (@ARGV == 2) { updateentities (@ARGV); } else { die ("$0 bspfile [entitiesfile]\n"); }