save the options my build machine actually uses
[xonotic/xonotic.git] / misc / tools / xonotic-map-compiler
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5 use POSIX;
6 use File::Temp;
7
8 # change these to match your system, or define them in ~/.xonotic-map-compiler
9 # (just copy paste this part to the file ~/.xonotic-map-compiler)
10
11         # Path to Xonotic (where the data directory is in)
12         our $XONOTICDIR   = '/home/rpolzer/Games/Xonotic';
13
14         # Path to your q3map2 program. You find it in your GtkRadiant/install
15         # directory.
16         our $Q3MAP2      = '/home/rpolzer/Games/Xonotic/netradiant/install/q3map2.x86';
17
18         # General flags for q3map2 (for example -threads 4)
19         our $Q3MAP2FLAGS = '';
20
21         # Default flags for the -bsp stage
22         our $BSPFLAGS    = '-meta -samplesize 8 -minsamplesize 4 -mv 1000000 -mi 6000000';
23
24         # Default flags for the -vis stage
25         our $VISFLAGS    = '';
26
27         # Default flags for the -light stage
28         our $LIGHTFLAGS  = '-deluxe -patchshadows -samples 3 -lightmapsize 512';
29
30         # Default flags for the -minimap stage
31         our $MINIMAPFLAGS = '';
32
33         # Default order of commands
34         our $ORDER = 'light,vis,minimap';
35
36 # end of user changable part
37
38 do "$ENV{HOME}/.xonotic-map-compiler";
39
40 sub Usage()
41 {
42         print <<EOF;
43 Usage:
44 $0 mapname [-bsp bspflags...] [-vis visflags...] [-light lightflags...]
45 EOF
46         exit 1;
47 }
48
49 my $options =
50 {
51         bsp => [split /\s+/, $BSPFLAGS],
52         vis => [split /\s+/, $VISFLAGS],
53         light => [split /\s+/, $LIGHTFLAGS],
54         minimap => [split /\s+/, $MINIMAPFLAGS],
55         order => [split /\s*,\s*/, $ORDER],
56         maps => [],
57         scale => 1
58 };
59
60 my $curmode = 'maps';
61
62 while(@ARGV)
63 {
64         $_ = shift @ARGV;
65         my $enterflags = undef;
66         if($_ eq '-bsp')
67         {
68                 $enterflags = 'bsp';
69         }
70         elsif($_ eq '-vis')
71         {
72                 $enterflags = 'vis';
73         }
74         elsif($_ eq '-light')
75         {
76                 $enterflags = 'light';
77         }
78         elsif($_ eq '-minimap')
79         {
80                 $enterflags = 'minimap';
81         }
82         elsif($_ eq '-map')
83         {
84                 $curmode = 'maps';
85         }
86         elsif($_ eq '-scale')
87         {
88                 $options->{scale} = (shift @ARGV) || 1;
89         }
90         elsif($_ eq '-novis')
91         {
92                 $options->{vis} = undef;
93         }
94         elsif($_ eq '-nolight')
95         {
96                 $options->{light} = undef;
97         }
98         elsif($_ eq '-nominimap')
99         {
100                 $options->{minimap} = undef;
101         }
102         elsif($_ eq '-noshaderlist')
103         {
104                 $options->{noshaderlist} = 1;
105         }
106         elsif($_ eq '-order')
107         {
108                 $options->{order} = [split /\s*,\s*/, shift @ARGV];
109         }
110         elsif($_ =~ /^-(-.*)/)
111         {
112                 if($curmode eq 'maps')
113                 {
114                         $curmode = 'bsp';
115                 }
116                 push @{$options->{$curmode}}, $1;
117         }
118         elsif($_ =~ /^-/ and $curmode eq 'maps')
119         {
120                 $curmode = 'bsp';
121                 push @{$options->{$curmode}}, $_;
122         }
123         else
124         {
125                 push @{$options->{$curmode}}, $_;
126         }
127         if(defined $enterflags)
128         {
129                 $curmode = $enterflags;
130                 if($ARGV[0] eq '+')
131                 {
132                         shift @ARGV;
133                 }
134                 else
135                 {
136                         $options->{$curmode} = [];
137                 }
138         }
139 }
140
141 my $linkdir = File::Temp::tempdir("xonotic-map-compiler.XXXXXX", TMPDIR => 1, CLEANUP => 1);
142
143 sub q3map2(@)
144 {
145         my @args = ($Q3MAP2, split(/\s+/, $Q3MAP2FLAGS), '-game', 'xonotic', '-fs_basepath', $XONOTICDIR, '-fs_basepath', $linkdir, '-v', @_);
146         print "\$ @args\n";
147         return !system @args;
148 }
149
150 (my $mapdir = getcwd()) =~ s!/[^/]*(?:$)!!;
151 $mapdir = "/" if $mapdir eq "";
152 symlink "$mapdir", "$linkdir/data";
153
154 my ($prescale, $postscale) = ($options->{scale} =~ /^([0-9.]+)(?::([0-9.]+))?$/);
155 $postscale = 1 if not defined $postscale;
156
157 for my $m(@{$options->{maps}})
158 {
159         $m =~ s/\.(?:map|bsp)$//;
160         if($prescale != 1)
161         {
162                 open my $checkfh, "<", "$m.map"
163                         or die "open $m.map: $!";
164                 my $keeplights = 0;
165                 while(<$checkfh>)
166                 {
167                         /^\s*"_keeplights"\s+"1"\s*$/
168                                 or next;
169                         $keeplights = 1;
170                 }
171                 close $checkfh;
172                 die "$m does not define _keeplights to 1"
173                         unless $keeplights;
174         }
175
176         my %shaders = map { m!/([^/.]*)\.shader(?:$)! ? ($1 => 1) : () } glob "../scripts/*.shader";
177
178         my $restore_shaderlist = sub { };
179         if(!$options->{noshaderlist})
180         {
181                 my $previous_shaderlist = undef;
182                 my $shaderlist = "";
183                 if(open my $fh, "<", "$XONOTICDIR/data/scripts/shaderlist.txt")
184                 {
185                         while(<$fh>)
186                         {
187                                 $shaderlist .= $_;
188                         }
189
190                         # we may have to restore the file on exit
191                         $previous_shaderlist = $shaderlist
192                                 if "$XONOTICDIR/data" eq $mapdir;
193                 }
194                 else
195                 {
196                         # possibly extract the shader list from a pk3?
197                         local $ENV{N} = $XONOTICDIR;
198                         $shaderlist = `cd "\$N" && for X in "\$N"/data/data*.pk3; do Y=\$X; done; unzip -p "\$Y" scripts/shaderlist.txt`;
199                 }
200
201                 my $shaderlist_new = "";
202                 for(split /\r?\n|\r/, $shaderlist)
203                 {
204                         delete $shaders{$_};
205                         $shaderlist_new .= "$_\n";
206                 }
207                 if(%shaders)
208                 {
209                         for(sort keys %shaders)
210                         {
211                                 $shaderlist_new .= "$_\n";
212                         }
213                 }
214                 else
215                 {
216                         $shaderlist_new = undef;
217                 }
218
219                 $restore_shaderlist = sub
220                 {
221                         if(defined $shaderlist_new)
222                         {
223                                 if(defined $previous_shaderlist)
224                                 {
225                                         open my $fh, ">", "$mapdir/scripts/shaderlist.txt";
226                                         print $fh $previous_shaderlist;
227                                         close $fh;
228                                 }
229                                 else
230                                 {
231                                         unlink "$mapdir/scripts/shaderlist.txt";
232                                 }
233                         }
234                 };
235
236                 if(defined $shaderlist_new)
237                 {
238                         mkdir "$mapdir/scripts";
239                         open my $fh, ">", "$mapdir/scripts/shaderlist.txt";
240                         print $fh $shaderlist_new;
241                         close $fh;
242                 }
243         }
244
245         local $SIG{INT} = sub
246         {
247                 print "SIGINT caught, cleaning up...\n";
248                 $restore_shaderlist->();
249                 exit 0;
250         };
251
252         eval
253         {
254                 unlink <$m/lm_*>; # delete old external lightmaps
255                 q3map2 '-bsp', @{$options->{bsp}},   "$m.map"
256                         or die "-bsp: $?";
257                 if($prescale != 1)
258                 {
259                         q3map2 '-scale', $prescale, "$m.bsp"
260                                 or die "-scale: $?";
261                         rename "${m}_s.bsp", "$m.bsp"
262                                 or die "rename ${m}_s.bsp $m.bsp: $!";
263                 }
264                 my @o = @{$options->{order}};
265                 push @o, qw/light vis minimap/;
266                 my %o = ();
267
268                 for(@o)
269                 {
270                         next if $o{$_}++;
271                         if($_ eq 'light')
272                         {
273                                 if(defined $options->{light})
274                                 {
275                                         q3map2 '-light',        @{$options->{light}}, "$m.map"
276                                                 or die "-light: $?";
277                                 }
278                         }
279                         if($_ eq 'vis')
280                         {
281                                 if(defined $options->{vis})
282                                 {
283                                         q3map2 '-vis',          @{$options->{vis}},   "$m.map"
284                                                 or die "-vis: $?";
285                                 }
286                         }
287                         if($_ eq 'minimap')
288                         {
289                                 if(defined $options->{minimap})
290                                 {
291                                         q3map2 '-minimap',      @{$options->{minimap}}, "$m.map"
292                                                 or die "-minimap: $?";
293                                 }
294                         }
295                 }
296
297                 if($postscale != 1)
298                 {
299                         q3map2 '-scale', $postscale, "$m.bsp"
300                                 or die "-scale: $?";
301                         rename "${m}_s.bsp", "$m.bsp"
302                                 or die "rename ${m}_s.bsp $m.bsp: $!";
303                 }
304
305                 unlink "$m.srf";
306                 unlink "$m.prt";
307
308                 $restore_shaderlist->();
309                 1;
310         }
311         or do
312         {
313                 $restore_shaderlist->();
314                 die $@;
315         };
316 }