maps/Info/map_check

384 lines
9.1 KiB
Perl

#!/usr/bin/perl
#
# (C) Copyright Markus Weber, 1994. All rights reserved.
# Permission is granted to use, copy, and modify for non-commercial use.
#
# usage: check-consistency.pl [options]...
# Options:
# archdb=pathname-of-archetype-database *** not used ***
# default ./ARCHDB .{dir,pag}
# archetypes=pathname-of-archetypes-file
# default $cfdir/share/crossfire/archetypes
# cfdir=pathname-to-crossfire-installation
# default /opt/cf0901 (hardcoded)
# mapdir=pathname-of-map-directory
# default $cfdir/share/crossfire/maps
# start-map=map-path-of-starting map
# default (init in archetypes)
# %% make it a command line option
$debug = 1;
#
# ARGUMENT PROCESSING
#
# preset options
$cfdir = "/export/home/crossfire/cf-installroot";
# loop thru arg vector
while (@ARGV) {
$_ = @ARGV[0];
if (/^archdb=/) {
($junk,$archdb) = split(/=/,$ARGV[0]);
shift;
}
elsif (/^archetypes=/) {
($junk,$archetypes) = split(/=/,$ARGV[0]);
shift;
}
elsif (/^cfdir=/) {
($junk,$cfdir) = split(/=/,$ARGV[0]);
shift;
}
elsif (/^mapdir=/) {
($junk,$mapdir) = split(/=/,$ARGV[0]);
shift;
}
elsif (/^start-map=/) {
($junk,$start_map) = split(/=/,$ARGV[0]);
shift;
}
else {
print "Unknown option $ARGV[0]\n";
exit;
}
}
# post-process
$mapdir = "$cfdir/share/crossfire/maps" unless defined($mapdir);
$archetypes = "$cfdir/share/crossfire/archetypes" unless defined($archetypes);
print STDERR "DBG: archetypes=$archetypes\n" if $debug > 5;
print STDERR "DBG: archdb=$archdb\n" if $debug > 5;
print STDERR "DBG: mapdir=$mapdir\n" if $debug > 5;
#
# INIT ARCHETYPES DATABASE
#
print STDERR "DBG: initializing archetype database...\n" if $debug;
&init_archetypes_database; # unless $skip_db_init;
print STDERR "DBG: ...done\n" if $debug;
defined($start_map) || die "FATAL: no starting map";
print STDERR "DBG: start_map=$start_map\n" if $debug;
print STDERR "DBG: scanning for archetypes of special interest...\n" if $debug;
while ( ($arch,$type) = each(%ARCHDB) ) {
next if !defined($type); # skip if not special
$_ = $type; # see below
if ($type == 41 || $type == 66 || $type == 94) {
# EXITS: archetypes with exits to other maps
$EXITS{$arch} = 1;
}
# Bad Programming Style Alert. Don't try this at home!
elsif (/^1[78]$/ || /^2[679]$/ || /^3[012]$/ || /^9[123]$/) {
# CONNECT: "connected" archetypes,
# e.g. buttons, handles, gates, ...
$CONNECT{$arch} = 1;
}
if ($type == 85) {
$SB{$arch} = 1;
}
}
print STDERR "DBG: ...done.\n" if $debug;
#
# MAIN LOOP
#
# pathname of start_map is assumed to be absolute (e.g. /village/village
push(@MAPS,$start_map);
while ($map = pop(@MAPS)) {
# print STDERR "array stack size is $#MAPS\n";
next if $visited{$map}; # skip if been here before
$visited{$map} = 1; # flag it if not
# skip random maps
next if ($map =~ m#/!#);
print STDERR "DBG: visiting $map\n" if $debug;
#print "visiting $map\n" if $debug;
#
# side effect: check_map pushes any (legal) exits found on stack
#
&check_map($map);
}
print "Unused archetypes:\n";
foreach $key (sort(keys %ARCHDB)) {
print "$key\n" if (!defined($USED{$key}))
}
exit;
#
# ++++++++++++++++++++ END OF MAIN ++++++++++++++++++
#
#
# INIT ARCHETYPES DATABASE
#
# store (archname,type) pairs
#
sub init_archetypes_database {
local($arch_lines,$arches); # counters
local($arch,$type,$slaying); # values
local($junk);
print STDERR "DBG: opening archetypes: $archetypes\n" if $debug > 5;
open(ARCHETYPES,$archetypes) || die "can't open $archetypes";
$arch_lines = 0;
$arches = 0;
$type = 0;
while ( <ARCHETYPES> ) {
$arch_lines++;
if (/^Object\s/) {
($junk,$arch) = split;
if (!defined($arch)) {
print STDERR "$archetypes: bad Object, line $arch_lines\n";
}
}
elsif (/^type\s/) {
($junk,$type) = split;
if (!defined($type)) {
print STDERR "$archetypes: bad type, line $arch_lines\n";
}
}
elsif (/^slaying\s/ && $arch eq "map") {
($junk,$slaying) = split;
# don't care if defined or not (yet)
}
elsif (/^end$/) {
print STDERR "DBG: entered arch=$arch, optional type=$type\n" if $debug > 10;
next if (!defined($arch));
# don't care whether $type defined or not
$ARCHDB{$arch} = $type;
$arches++;
$type = 0;
}
elsif (/^end\s*$/) {
print STDERR "DBG: arch $arch is using end with space before newline\n";
next if (!defined($arch));
# don't care whether $type defined or not
$ARCHDB{$arch} = $type;
$arches++;
$type = 0;
}
}
#
# find start map
# print error message iff "map" arch not found or missing path
# assign start map (unless pre-defined on cmd line)
#
if (!defined($slaying)) {
print STDERR "***ERROR*** no map object or map path missing\n";
}
elsif (!defined($start_map)) {
$start_map = $slaying;
}
#print STDERR "DBG: start_map=$start_map\n";
close(ARCHETYPES);
print STDERR "DBG: closed $archetypes, $arch_lines lines, $arches arches\n"
if $debug > 5;
}
#
# CHECK MAP FOR ELEMENTARY CONSISTENCY
#
sub check_map {
local($map) = @_;
local($arch,$connected,$slaying,$exit,$x,$y, $rx, $ry);
local($lines,$fullmap);
local($junk);
$depth=0;
# build full pathname (nb: map path starts with /) and open map file
$fullmap = "$mapdir$map";
open(MAP,$fullmap) || die "can't open $fullmap";
print STDERR "DBG: opened $map\n" if $debug > 5;
$lines = 0;
while ( <MAP> ) {
if (/^tile_path_/) {
($junk,$slaying) = split;
$_ = "$map $slaying"; # easy matching :-)
s@^(/.*/)([^/]*)\s([^\./].*)$@\1\2 \1\3@;
s@^(/.*/)([^/]*)\s(\.\./.*)$@\1\2 \1\3@;
s@/[^/]*/\.\./@/@g;
($junk,$exit) = split;
next if $visited{$exit};
if ( (! -r "$mapdir$exit") && ( $exit ne "/!") ) {
print "ERROR: map $map, arch $arch, line $lines, no such exit $exit ($rx, $ry, to $x, $y)\n";
next;
}
push(@MAPS,$exit);
}
$lines++;
if (/^arch\s/) {
# Note we have to do some checks here - that is because
# if an object is inside an object, the value of $arch
# is clobbered.
($junk,$arch) = split;
# if ($SB{$arch}) {
# print "WARNING: spellbook found at map $map, line $lines, arch $arch\n";
# }
if (!defined($ARCHDB{$arch})) {
print "FATAL: map $map, line $lines, bad archetype: $arch ($rx, $ry)\n";
}
$USED{$arch}=1;
undef($slaying);
undef($x);
undef($y);
undef($rx);
undef($ry);
undef($connected);
$depth++;
}
elsif (/^connected\s/) {
($junk,$connected) = split;
}
elsif (/^slaying\s/) {
($junk,$slaying) = split;
}
elsif (/^hp\s/) {
($junk,$x) = split;
}
elsif (/^sp\s/) {
($junk,$y) = split;
}
elsif (/^x\s/) {
($junk, $rx) = split;
}
elsif (/^y\s/) {
($junk, $ry) = split;
}
elsif (/^anim$/) {
print "Map $fullmap has an anim command in it\n";
}
next if !/^end$/; # continue iff not end of arch
$depth--;
#
# CHECK 2: connect-arch actually connected?
# NB: if not, that's perfectly legal, but suspicious
#
# if ($CONNECT{$arch}) {
# if (!$connected) {
#print STDERR "WARNING: map $map, line $lines, arch $arch, not connected\n";
#print "WARNING: map $map, line $lines, arch $arch, not connected\n";
# }
# next;
# }
next if !$EXITS{$arch}; # continue if not an exit
#
# CHECK 3: exit-type arch, but no path given
# Presumably the path defaults to the local map,
# but in all probability this is an error
#
if (!defined($slaying)) {
if ($x || $y) {
#print STDERR "ERROR: map $map, line $lines, arch $arch, exit defaults\n";
#print "ERROR: map $map, line $lines, arch $arch, exit defaults\n";
}
else {
#print STDERR "INFO: map $map, line $lines, arch $arch, no exit defined\n";
#print "INFO: map $map, line $lines, arch $arch, no exit defined\n";
}
next;
}
#
# CHECK 4: verify that exit map exists
# if not, the game (hopefully!) won't crash, but
# chances are this _is_ an error
#
#
# normalize exit path (FullyQualifiedPathName :-)))
# (i.e. construct absolute pathname, rooted in CLibDir/maps)
# E.g.:
# current map: /village/somewhere
# EXIT PATH YIELDS
# /village/building /village/building
# townhouse /village/townhouse
# ../island /island
#
$_ = "$map $slaying"; # easy matching :-)
# /path/map exit --> /path/map /path/exit
s@^(/.*/)([^/]*)\s([^\./].*)$@\1\2 \1\3@;
# /path/map ../exit --> /path/map /path/../exit
s@^(/.*/)([^/]*)\s(\.\./.*)$@\1\2 \1\3@;
# /dir/../ --> / (all occurances)
s@/[^/]*/\.\./@/@g;
($junk,$exit) = split;
#print STDERR "DBG: exit $map $exit\n" if $debug > 5;
#print "exit $map $exit\n";
#
# shortcut: if the exit map was already checked, don't bother
# stacking it again.
# %% if a map is never pushed twice in the first place,
# the corresponding test in the main loop is probably
# in vain.
#
next if $visited{$exit};
#
# this is check 4, finally.
# if exit map can't be opened, complain and continue
#
if ( (! -r "$mapdir$exit") && ( $exit ne "/!") ) {
#print STDERR "ERROR: map $map, arch $arch, line $lines, no such exit $exit\n";
print "ERROR: map $map, arch $arch, line $lines, no such exit $exit ($rx, $ry, to $x, $y)\n";
next;
}
#
# the exit map looks good; push it and continue
push(@MAPS,$exit);
}
close(MAP);
if ($depth != 0) {
print "ERROR: map $map, mismatched arch/end, $depth\n";
}
}