… | |
… | |
20 | use Crossfire; |
20 | use Crossfire; |
21 | use Coro; |
21 | use Coro; |
22 | use Coro::AIO; |
22 | use Coro::AIO; |
23 | use POSIX (); |
23 | use POSIX (); |
24 | use Digest::MD5; |
24 | use Digest::MD5; |
|
|
25 | use Storable; $Storable::canonical = 1; |
25 | |
26 | |
26 | sub usage { |
27 | sub usage { |
27 | warn <<EOF; |
28 | warn <<EOF; |
28 | Usage: cfutil [-v] [-q] [--force] [--cache] |
29 | Usage: cfutil [-v] [-q] [--force] [--cache] |
29 | [--install-arch path] |
30 | [--install-arch path] |
… | |
… | |
67 | } |
68 | } |
68 | |
69 | |
69 | sub inst_maps($) { |
70 | sub inst_maps($) { |
70 | my (undef, $path) = @_; |
71 | my (undef, $path) = @_; |
71 | |
72 | |
72 | print "installing '$path' to '$DATADIR/maps'\n\n"; |
73 | print "installing '$path' to '$DATADIR/maps'\n"; |
73 | |
74 | |
74 | if (!-f "$path/regions") { |
75 | if (!-f "$path/regions") { |
75 | warn "'$path' does not look like a maps directory ('regions' file is missing).\n"; |
76 | warn "'$path' does not look like a maps directory ('regions' file is missing).\n"; |
76 | exit 1 unless $FORCE; |
77 | exit 1 unless $FORCE; |
77 | } |
78 | } |
78 | |
79 | |
79 | system $RSYNC, "-av", "$path/.", "$DATADIR/maps/.", "--delete", "--exclude", "CVS", "--delete-excluded"; |
80 | system $RSYNC, "-av", "$path/.", "$DATADIR/maps/.", "--delete", "--exclude", "CVS", "--delete-excluded" |
|
|
81 | and die "map installation failed.\n"; |
|
|
82 | |
|
|
83 | print "maps installed successfully.\n"; |
80 | } |
84 | } |
81 | |
85 | |
82 | { |
86 | { |
83 | our %PNG32; |
87 | our %PNG32; |
84 | our %FACEINFO; |
88 | our %FACEINFO; |
85 | our @ARC; |
89 | our @ARC; |
|
|
90 | our $TRS; |
86 | our $NFILE; |
91 | our $NFILE; |
87 | our %ANIM; |
92 | our %ANIM; |
88 | |
93 | |
89 | our (@png, @trs, @arc); # files we are interested in |
94 | our (@png, @trs, @arc); # files we are interested in |
90 | |
95 | |
… | |
… | |
117 | my ($w, $h) = unpack "NN", $1; |
122 | my ($w, $h) = unpack "NN", $1; |
118 | |
123 | |
119 | (my $face = $path) =~ s/^.*\///; |
124 | (my $face = $path) =~ s/^.*\///; |
120 | my $T = 32; |
125 | my $T = 32; |
121 | |
126 | |
122 | unless ($face =~ s/\.base\.(...)\.png$/.$1/) { |
127 | unless ($face =~ s/\.32x32\.png$//) { |
123 | warn "$path: weird filename, skipping.\n"; |
128 | warn "$path: weird filename, skipping.\n"; |
124 | next; |
129 | next; |
125 | } |
130 | } |
126 | |
131 | |
127 | if ($w < $T || $h < $T) { |
132 | if ($w < $T || $h < $T) { |
… | |
… | |
202 | |
207 | |
203 | my $arc = read_arch "$dir/$file"; |
208 | my $arc = read_arch "$dir/$file"; |
204 | for my $o (values %$arc) { |
209 | for my $o (values %$arc) { |
205 | push @ARC, $o; |
210 | push @ARC, $o; |
206 | |
211 | |
|
|
212 | $o->{editor_folder} = $dir; |
|
|
213 | |
207 | my $visibility = delete $o->{visibility}; |
214 | my $visibility = delete $o->{visibility}; |
208 | my $magicmap = delete $o->{magicmap}; |
215 | my $magicmap = delete $o->{magicmap}; |
209 | |
216 | |
210 | # find upper left corner :/ |
217 | # find upper left corner :/ |
211 | # omg, this is sooo broken |
218 | # omg, this is sooo broken |
… | |
… | |
219 | my $x = $o->{x} - $dx; |
226 | my $x = $o->{x} - $dx; |
220 | my $y = $o->{y} - $dy; |
227 | my $y = $o->{y} - $dy; |
221 | |
228 | |
222 | my $ext = $x|$y ? "+$x+$y" : ""; |
229 | my $ext = $x|$y ? "+$x+$y" : ""; |
223 | |
230 | |
224 | $o->{face} .= $ext; |
231 | $o->{face} .= $ext unless /^blank.x11$|^empty.x11$/; |
225 | |
232 | |
226 | my $visibility = delete $o->{visibility} if exists $o->{visibility}; |
233 | my $visibility = delete $o->{visibility} if exists $o->{visibility}; |
227 | my $magicmap = delete $o->{magicmap} if exists $o->{magicmap}; |
234 | my $magicmap = delete $o->{magicmap} if exists $o->{magicmap}; |
228 | |
235 | |
229 | my $anim = delete $o->{anim}; |
236 | my $anim = delete $o->{anim}; |
230 | |
237 | |
231 | if ($anim) { |
238 | if ($anim) { |
232 | $o->{animation} = "$o->{_name}$ext"; |
239 | $o->{animation} = "$o->{_name}"; |
233 | |
240 | |
234 | for (@$anim) { |
241 | for (@$anim) { |
235 | $_ .= $ext unless /^facings\s/; |
242 | $_ .= $ext unless /^facings\s|^blank.x11$|^empty.x11$/; |
236 | } |
243 | } |
237 | |
244 | |
238 | $ANIM{"$o->{_name}$ext"} = |
245 | $ANIM{"$o->{_name}$ext"} = |
239 | join "", map "$_\n", |
246 | join "", map "$_\n", |
240 | "anim $o->{_name}$ext", |
247 | "anim $o->{_name}", |
241 | @$anim, |
248 | @$anim, |
242 | "mina"; |
249 | "mina"; |
243 | } |
250 | } |
244 | |
251 | |
245 | for my $face ($o->{face} || (), @{$anim || []}) { |
252 | for my $face ($o->{face} || (), @{$anim || []}) { |
246 | next if /^facings\s/; |
253 | next if $face =~ /^facings\s|^blank.x11$|^empty.x11$/; |
247 | |
254 | |
248 | my $info = $FACEINFO{$face} ||= {}; |
255 | my $info = $FACEINFO{$face} ||= {}; |
249 | |
256 | |
250 | $info->{visibility} = $visibility if defined $visibility; |
257 | $info->{visibility} = $visibility if defined $visibility; |
251 | $info->{magicmap} = $magicmap if defined $magicmap; |
258 | $info->{magicmap} = $magicmap if defined $magicmap; |
|
|
259 | } |
|
|
260 | |
|
|
261 | if (my $smooth = delete $o->{smoothface}) { |
|
|
262 | my ($face, $smooth) = split /\s+/, $smooth; |
|
|
263 | # skip empty_S.x11, it seems to server no purpose whatsoever |
|
|
264 | # but increases bandwidth demands and worse. |
|
|
265 | unless ($smooth eq "empty_S.x11") { |
|
|
266 | $FACEINFO{$face}{smooth} = $smooth; |
|
|
267 | } |
252 | } |
268 | } |
253 | } |
269 | } |
254 | } |
270 | } |
255 | } |
271 | } |
256 | } |
272 | } |
257 | |
273 | |
258 | sub process_trs { |
274 | sub process_trs { |
259 | while (@trs) { |
275 | while (@trs) { |
260 | my ($dir, $file) = @{pop @trs}; |
276 | my ($dir, $file) = @{pop @trs}; |
|
|
277 | my $path = "$dir/$file"; |
|
|
278 | |
|
|
279 | my $trs; |
|
|
280 | if (0 > aio_load $path, $trs) { |
|
|
281 | warn "$path: $!, skipping.\n"; |
|
|
282 | next; |
|
|
283 | } |
|
|
284 | |
|
|
285 | $TRS .= $trs; |
261 | } |
286 | } |
262 | } |
287 | } |
263 | |
288 | |
264 | sub find_files; |
289 | sub find_files; |
265 | sub find_files { |
290 | sub find_files { |
… | |
… | |
287 | } |
312 | } |
288 | |
313 | |
289 | sub inst_arch($) { |
314 | sub inst_arch($) { |
290 | my (undef, $path) = @_; |
315 | my (undef, $path) = @_; |
291 | |
316 | |
292 | print "installing '$path' to '$DATADIR'\n\n"; |
317 | print "installing '$path' to '$DATADIR'\n"; |
293 | |
318 | |
294 | if (!-d "$path/treasures") { |
319 | if (!-d "$path/treasures") { |
295 | warn "'$path' does not look like an arch directory ('treasures' directory is missing).\n"; |
320 | warn "'$path' does not look like an arch directory ('treasures' directory is missing).\n"; |
296 | exit 1 unless $FORCE; |
321 | exit 1 unless $FORCE; |
297 | } |
322 | } |
… | |
… | |
312 | } |
337 | } |
313 | |
338 | |
314 | { |
339 | { |
315 | open my $fh, ">:utf8", "$DATADIR/archetypes~" |
340 | open my $fh, ">:utf8", "$DATADIR/archetypes~" |
316 | or die "$DATADIR/archetypes~: $!"; |
341 | or die "$DATADIR/archetypes~: $!"; |
|
|
342 | substr $_->{editor_folder}, 0, 1 + length $path, "" for @ARC; |
317 | print $fh Crossfire::archlist_to_string \@ARC; |
343 | print $fh Crossfire::archlist_to_string \@ARC; |
|
|
344 | } |
|
|
345 | |
|
|
346 | { |
|
|
347 | open my $fh, ">:utf8", "$DATADIR/treasures~" |
|
|
348 | or die "$DATADIR/treasures~: $!"; |
|
|
349 | print $fh $TRS; |
318 | } |
350 | } |
319 | |
351 | |
320 | { |
352 | { |
321 | while (my ($k, $v) = each %FACEINFO) { |
353 | while (my ($k, $v) = each %FACEINFO) { |
322 | $v->{data32} ||= delete $PNG32{$k}; |
354 | $v->{data32} ||= delete $PNG32{$k}; |
323 | } |
355 | } |
324 | |
356 | |
325 | while (my ($k, $v) = each %FACEINFO) { |
357 | while (my ($k, $v) = each %FACEINFO) { |
326 | exists $v->{data32} or warn "$k: face has no png32. this will crash the server.\n"; |
358 | length $v->{data32} or warn "$k: face has no png32. this will not work (shoddy gcfclient will crash of course).\n"; |
327 | |
359 | |
328 | $v->{chksum32} = Digest::MD5::md5 $v->{data32}; |
360 | $v->{chksum32} = Digest::MD5::md5 $v->{data32}; |
329 | } |
361 | } |
330 | |
362 | |
331 | open my $fh, ">:perlio", "$DATADIR/faces~" |
363 | open my $fh, ">:perlio", "$DATADIR/faces~" |
332 | or die "$DATADIR/faces~: $!"; |
364 | or die "$DATADIR/faces~: $!"; |
333 | |
365 | |
|
|
366 | $FACEINFO{""} = { version => 1}; |
334 | print $fh Storable::nfreeze \%FACEINFO; |
367 | print $fh Storable::nfreeze \%FACEINFO; |
335 | |
|
|
336 | #use PApp::Util; warn PApp::Util::dumpval \%FACEINFO; |
|
|
337 | } |
368 | } |
338 | |
369 | |
339 | for (qw(archetypes faces animations)) { |
370 | for (qw(archetypes faces animations treasures)) { |
340 | chmod 0644, "$DATADIR/$_~"; |
371 | chmod 0644, "$DATADIR/$_~"; |
341 | rename "$DATADIR/$_~", "$DATADIR/$_"; |
372 | rename "$DATADIR/$_~", "$DATADIR/$_" |
|
|
373 | or die "$DATADIR/$_: $!"; |
342 | } |
374 | } |
343 | |
375 | |
344 | die "--install-arch not fully implemented\n"; |
376 | print "archetype data installed successfully.\n"; |
345 | } |
377 | } |
346 | } |
378 | } |
347 | |
379 | |
348 | Getopt::Long::Configure ("bundling", "no_ignore_case"); |
380 | Getopt::Long::Configure ("bundling", "no_ignore_case"); |
349 | GetOptions ( |
381 | GetOptions ( |