use strict;
my $line_num = 0;
my @warnings = ();
my @errors = ();
my @all_artefacts = ();
my %used_names = ();
my %used_appears = ();
my %used_enums = ();
my %found_funcs = ();
my %field_type = (
AC => "num",
ACC => "num",
ANGRY => "num",
APPEAR => "str",
BERSERK => "bool",
BLINK => "bool",
BRAND => "enum",
CANTELEP => "bool",
CHAOTIC => "bool",
COLD => "num",
COLOUR => "enum",
CURSED => "num",
DAM => "num",
DEX => "num",
ELEC => "bool",
EV => "num",
EVIL => "bool",
FIRE => "num",
HOLY => "bool",
INT => "num",
INV => "bool",
LEV => "bool",
LIFE => "bool",
MAGIC => "num",
MAPPING => "bool",
METAB => "num",
MP => "num",
MUTATE => "num",
NAME => "str",
NOISES => "bool",
NOSPELL => "bool",
NOTELEP => "bool",
POISON => "bool",
RND_TELE => "bool",
SEEINV => "bool",
SPECIAL => "bool",
SPIRIT => "bool",
STEALTH => "num",
STR => "num",
VALUE => "num",
DESC => "str",
DESC_END => "str",
DESC_ID => "str",
TILE => "str",
TILE_EQ => "str",
TILERIM => "bool",
flags => "flags",
equip_func => "func",
unequip_func => "func",
world_reacts_func => "func",
fight_func_func => "func",
melee_effect_func => "func",
launch_func => "func",
evoke_func => "func",
plus => "num",
plus2 => "num",
base_type => "enum",
sub_type => "enum"
);
my %union_name = (
melee_effect => "fight_func",
launch => "fight_func"
);
my @field_list = keys(%field_type);
sub error
{
my ($artefact, $str) = @_;
my $msg = "";
my $name = $artefact->{NAME} || $artefact->{APPEAR} || "NAMELESS";
if ($artefact->{_FINISHING})
{
$msg .= "Artefact $name starting at line "
. $artefact->{_START_LINE} . ": ";
}
else
{
$msg = "Artefact $name at line $line_num: "
}
$msg .= $str;
$artefact->{_ERROR} = 1;
push(@errors, $msg);
}
sub is_number
{
my ($num) = @_;
return ($num =~ /^[+-]?\d+$/);
}
sub finish_art
{
my ($artefact) = @_;
$artefact->{_FINISHING} = 1;
my $must;
foreach $must ("NAME", "APPEAR", "OBJ", "COLOUR")
{
if (!defined($artefact->{$must}))
{
error($artefact, "Required field '$must' missing");
$artefact->{$must} = "_MISSING_";
}
}
$artefact->{base_type} ||= "";
$artefact->{sub_type} ||= "";
$artefact->{flags} = "";
if (!exists($artefact->{BRAND}))
{
my $type = $artefact->{base_type} || "";
if ($type eq "OBJ_WEAPONS")
{
$artefact->{BRAND} = "SPWPN_NORMAL";
}
elsif($type eq "OBJ_ARMOUR")
{
$artefact->{BRAND} = "SPARM_NORMAL";
}
else
{
$artefact->{BRAND} = "0";
}
}
my $enum = $artefact->{_ENUM};
my $funcs;
if ($found_funcs{$enum})
{
$funcs = $found_funcs{$enum};
delete($found_funcs{$enum});
}
else
{
$funcs = {};
}
foreach my $func_name ("equip", "unequip", "world_reacts", "fight_func",
"evoke")
{
my $val;
if ($funcs->{$func_name})
{
$val = "_${enum}_" . $funcs->{$func_name};
}
else
{
$val = "NULL";
}
$artefact->{"${func_name}_func"} = $val;
}
my $field;
foreach $field (@field_list)
{
if (!exists($artefact->{$field}))
{
my $type = $field_type{$field} || "";
if ($type eq "str")
{
$artefact->{$field} = "";
}
elsif($type eq "num" || $type eq "bool")
{
$artefact->{$field} = "0";
}
elsif($type eq "enum")
{
error($artefact, "No enumeration for field '$field'");
}
elsif($type eq "func")
{
$artefact->{$field} = "NULL";
}
else
{
error($artefact, "Unknown type '$type' for field '$field'");
}
}
elsif (!defined($artefact->{$field}))
{
error($artefact, "Field '$field' not defined");
}
}
my $flags = "";
my $flag;
foreach $flag ("SPECIAL", "HOLY", "EVIL", "CHAOTIC")
{
if ($artefact->{$flag})
{
$flags .= "UNRAND_FLAG_$flag | ";
}
}
if ($flags eq "")
{
$flags = "UNRAND_FLAG_NONE";
}
else
{
chop($flags);
chop($flags);
chop($flags);
}
$artefact->{flags} = $flags;
delete($artefact->{_FINISHING});
$artefact->{_FINISHED} = 1;
}
sub process_line
{
my ($artefact, $line) = @_;
if ($line =~ /^\s/)
{
my $prev_field = $artefact->{_PREV_FIELD} || "";
if ($field_type{$prev_field} eq "str")
{
$line =~ s/^\s*//;
$artefact->{$prev_field} .= " " . $line;
}
else
{
error($artefact, "line starts with whitespace");
}
return;
}
$artefact->{_START_LINE} ||= $line_num;
my ($field, $value) = ($line =~ /([^:]+):\s*(.*)/);
$field ||= "";
$value ||= "";
$field =~ s/^\s*|\s*$//g;
$value =~ s/^\s*|\s*$//g;
if ($field eq "")
{
error($artefact, "No field");
return;
}
if (defined($artefact->{$field}))
{
error($artefact, "Field '$field' already set");
return;
}
$artefact->{_PREV_FIELD} = $field;
$artefact->{$field} = $value;
if ($value eq "")
{
error($artefact, "Field '$field' has no value");
return;
}
if ($field eq "BOOL")
{
my @parts = split(/\s*,\s*/, $value);
my $part;
foreach $part (@parts)
{
my $up = uc($part);
if ($up eq "CURSED")
{
$artefact->{CURSED} = -1;
}
elsif (!exists($field_type{$up}))
{
error($artefact, "Unknown bool '$part'");
}
elsif ($field_type{$up} ne "bool")
{
error($artefact, "'$part' is not a boolean");
}
else
{
$artefact->{$up} = 1;
}
}
}
elsif ($field eq "OBJ")
{
my @parts = split(m!/!, $value);
if (@parts > 2)
{
error($artefact, "Too many parts to OBJ");
return;
}
elsif (@parts == 1)
{
error($artefact, "Too few parts to OBJ");
return;
}
if ($parts[0] !~ /^OBJ_/)
{
error($artefact, "OBJ base type must start with 'OBJ_'");
return;
}
$artefact->{base_type} = $parts[0];
$artefact->{sub_type} = $parts[1];
}
elsif($field eq "PLUS")
{
my @parts = split(m!/!, $value);
if (@parts > 2)
{
error($artefact, "Too many parts to PLUS");
return;
}
if (!is_number($parts[0]))
{
error($artefact, "'$parts[0]' in PLUS is not a number");
return;
}
if (@parts == 2 && !is_number($parts[1]))
{
error($artefact, "'$parts[1]' in PLUS is not a number");
return;
}
$artefact->{plus} = $parts[0];
$artefact->{plus2} = $parts[1] if (@parts == 2);
}
elsif ($field eq "ENUM")
{
}
else
{
if (!exists($field_type{$field}) || $field =~ /^[a-z]/)
{
error($artefact, "No such field as '$field'");
return;
}
if ($field_type{$field} eq "bool")
{
error($artefact, "'$field' should be expressed using BOOL");
return;
}
my $num = is_number($value);
my $type = $field_type{$field} || "";
if (($type eq "num" && !$num) || ($type eq "str" && $num))
{
error($artefact, "'$value' invalid value type for field '$field'.");
return;
}
}
my $enum = "";
if ($field eq "NAME")
{
if (exists($used_names{$value}))
{
error($artefact, "Name \"$value\" already used at line " .
$used_names{$value});
return;
}
$used_names{$value} = $line_num;
if (!exists($artefact->{_ENUM}))
{
$enum = $value;
$enum =~ s/'//g;
if ($enum =~ /"(.*)"/)
{
$enum = $1;
}
elsif ($enum =~ / of (?:the )?(.*)/)
{
$enum = $1;
}
$enum = uc($enum);
$enum =~ s/[ -]/_/g;
}
}
elsif ($field eq "APPEAR")
{
if (exists($used_appears{$value}))
{
error($artefact, "Name '$value' already used at line " .
$used_appears{$value});
return;
}
$used_appears{$value} = $line_num;
}
elsif ($field eq "ENUM")
{
if (exists($artefact->{NAME}))
{
error($artefact, "ENUM must be before NAME");
return;
}
$enum = "$value";
}
if ($enum ne "")
{
if (exists($used_enums{$enum}))
{
error($artefact, "Enum \"$enum\" already used by artefact " .
"\"$used_enums{$enum}\"");
return;
}
$used_enums{$enum} = $value;
$artefact->{_ENUM} = $enum;
}
}
my @art_order = (
"NAME", "APPEAR", "\n",
"base_type", "sub_type", "plus", "plus2", "COLOUR", "VALUE", "\n",
"flags",
"{", "BRAND", "AC", "EV", "STR", "INT", "DEX", "\n",
"FIRE", "COLD", "ELEC", "POISON", "LIFE", "MAGIC", "\n",
"SEEINV", "INV", "LEV", "BLINK", "CANTELEP", "BERSERK", "\n",
"MAPPING", "NOISES", "NOSPELL", "RND_TELE", "NOTELEP", "\n",
"ANGRY", "METAB", "MUTATE", "ACC", "DAM", "\n",
"CURSED", "STEALTH", "MP", "SPIRIT", "}",
"DESC", "\n",
"DESC_ID", "\n",
"DESC_END", "\n",
"equip_func", "unequip_func", "world_reacts_func", "{fight_func_func",
"evoke_func"
);
sub art_to_str
{
my ($artefact) = @_;
my $indent = 1;
my $str = "{\n ";
for (my $i = 0; $i < @art_order; $i++)
{
my $part = $art_order[$i];
if (length($part) == 1)
{
if ($part eq "{")
{
$str .= "\n" . (" " x ($indent * 4)) . "{";
$indent++;
$str .= "\n" . (" " x ($indent * 4));
}
elsif($part eq "}")
{
$indent--;
$str .= "\n" . (" " x ($indent * 4)) . "},";
$str .= "\n" . (" " x ($indent * 4));
}
else
{
$str .= "\n" . (" " x ($indent * 4));
}
next;
}
my $bracketed = 0;
if ($part =~ /^{(.*)/)
{
$part = $1;
$str .= "{ ";
$bracketed = 1;
}
if (!defined($field_type{$part}))
{
print STDERR "No field type for part '$part'\n";
next;
}
if ($field_type{$part} eq "str")
{
my $temp = $artefact->{$part};
$temp =~ s/"/\\"/g;
$str .= "\"$temp\"";
}
else
{
$str .= $artefact->{$part};
}
$str .= " }" if ($bracketed);
$str .= ", ";
}
$str .= "\n},\n\n";
return ($str);
}
sub write_data
{
unless (open(HEADER, ">art-data.h"))
{
die "Couldn't open 'art-data.h' for writing: $!\n";
}
print HEADER <<"ENDofTEXT";
/*
* File: art-data.h
* Summary: Definitions for unrandom artefacts.
*/
/*
* This file is automatically generated from art-data.txt via
* util/art-data.pl. Do not directly edit this file, but rather change
* art-data.txt.
*
* If the unrandart_entry struct is changed or a new artefact property is
* added to artefact_prop_type, then change art-data.pl so that the
* art-data.h file it produces matches up with the new structure.
*/
#ifdef ART_DATA_H
#error "art-data.h included twice!"
#endif
#ifndef ART_FUNC_H
#error "art-func.h must be included before art-data.h"
#endif
#define ART_DATA_H
ENDofTEXT
my $artefact;
my $art_num = 1;
foreach $artefact (@all_artefacts)
{
print HEADER "/* $art_num: UNRAND_$artefact->{_ENUM} */\n";
print HEADER art_to_str($artefact);
$art_num++;
}
close(HEADER);
}
sub write_enums
{
unless (open(ENUM_IN, "<artefact.h"))
{
die "Couldn't open 'artefact.h' for reading: $!";
}
my $changed = 0;
my $out = "";
while(<ENUM_IN>)
{
last if (/^#define NO_UNRANDARTS/);
$out .= $_;
}
if (!/^#define NO_UNRANDARTS (\d+)/)
{
die "Couldn't find NO_UNRANDARTS in artefact.h\n";
}
my $orig_num = $1;
my $new_num = scalar(@all_artefacts);
if ($orig_num == $new_num)
{
print "Number of unrandarts unchanged.\n";
$out .= $_;
}
else
{
print "Number of unrandarts changed from $orig_num to $new_num\n";
$out .= "#define NO_UNRANDARTS $new_num\n";
$changed = 1;
}
while(<ENUM_IN>)
{
$out .= $_;
last if (/^enum unrand_type/);
}
if (!/^enum unrand_type/)
{
die "Couldn't find 'enum unrand_type' in artefact.h\n";
}
$out .= <ENUM_IN>; # {
$out .= <ENUM_IN>; # UNRAND_START = 180,
my @enum_list = ();
while(<ENUM_IN>)
{
last if (/\bUNRAND_LAST\b/);
/^\s*(\w+)/;
push(@enum_list, $1);
}
<ENUM_IN>; # discard "};"
# Suck in rest of file
undef($/);
my $remainder = <ENUM_IN>;
close(ENUM_IN);
if (@enum_list != @all_artefacts)
{
print "Enumeration list changed.\n";
$changed = 1;
}
else
{
my $i;
for ($i = 0; $i < @enum_list; $i++)
{
if ($enum_list[$i] ne "UNRAND_$all_artefacts[$i]->{_ENUM}")
{
print "Enumeration list changed.\n";
$changed = 1;
last;
}
}
}
if (!$changed)
{
print "No changes made to artefact.h\n";
return;
}
print "Updating artefact.h...\n";
unless(open(ENUM_OUT, ">artefact.h"))
{
die "Couldn't open 'artefact.h' for writing: $!\n";
}
print ENUM_OUT $out;
my $i;
my $longest_enum = 0;
for ($i = 0; $i < @all_artefacts; $i++)
{
my $enum = $all_artefacts[$i]->{_ENUM};
my $len = length($enum);
$longest_enum = $len if ($len > $longest_enum);
}
my $enum;
for ($i = 0; $i < @all_artefacts; $i++)
{
$enum = $all_artefacts[$i]->{_ENUM};
print ENUM_OUT " UNRAND_$enum";
if ($i == 0)
{
print ENUM_OUT " = UNRAND_START,\n";
}
else
{
print ENUM_OUT ", ";
print ENUM_OUT " " x ($longest_enum - length($enum));
print ENUM_OUT "// $all_artefacts[$i]->{NAME}\n";
}
}
print ENUM_OUT " UNRAND_LAST = UNRAND_$enum\n";
print ENUM_OUT "};\n";
print ENUM_OUT $remainder;
close(ENUM_OUT);
}
sub write_tiles
{
my $tilefile = "dc-unrand.txt";
print "Updating $tilefile...\n";
die "Can't write to $tilefile\n" if (-e $tilefile && !-w $tilefile);
unless (open(TILES, ">$tilefile"))
{
die "Couldn't open '$tilefile' for writing: $!\n";
}
my %art_by_type = ();
foreach my $artefact (@all_artefacts)
{
next if ($artefact->{NAME} =~ /DUMMY/);
if ($artefact->{TILE} eq "")
{
print STDERR "No TILE defined for '$artefact->{NAME}'\n";
next;
}
# The path always has the form /item/$folder/artefact.
my $type = $artefact->{base_type} || "";
my $folder = "";
if ($type eq "OBJ_WEAPONS")
{
$folder = "weapon";
}
elsif ($type eq "OBJ_ARMOUR")
{
$folder = "armour";
}
elsif ($type eq "OBJ_JEWELLERY")
{
if ($artefact->{sub_type} =~ /RING_/)
{
$folder = "ring";
}
else
{
$folder = "amulet";
}
}
else
{
next;
}
my $definition = "$artefact->{TILE} UNRAND_$artefact->{_ENUM}";
my $needrim = ($artefact->{TILERIM} ? "1" : "0");
if (defined $art_by_type{$folder})
{
if (defined $art_by_type{$folder}{$needrim})
{
push @{$art_by_type{$folder}{$needrim}}, $definition;
}
else
{
$art_by_type{$folder}{$needrim} = [$definition];
}
}
else
{
$art_by_type{$folder} = {$needrim => [$definition]};
}
}
print TILES << "HEADER_END";
# This file is automatically generated from source/art-data.txt via
# util/art-data.pl. Do not directly edit this file, but rather change
# art-data.txt.
HEADER_END
# Output the tile definitions sorted by type (and thus path).
foreach my $type (keys %art_by_type)
{
print TILES "%sdir item/$type/artefact\n";
foreach my $needrim (sort keys %{$art_by_type{$type}})
{
print TILES "%rim 1\n" if ($needrim);
foreach my $def (@{$art_by_type{$type}{$needrim}})
{
print TILES "$def\n";
}
print TILES "%rim 0\n" if ($needrim);
}
print TILES "\n";
}
close(TILES);
my %parts;
foreach my $artefact (@all_artefacts)
{
next if ($artefact->{NAME} =~ /DUMMY/);
if (not(defined $artefact->{TILE_EQ}) || $artefact->{TILE_EQ} eq "")
{
if ($artefact->{base_type} ne "OBJ_JEWELLERY")
{
print STDERR "TILE_EQ not defined for '$artefact->{NAME}'\n";
}
next;
}
my $part = "BODY";
if ($artefact->{base_type} eq "OBJ_WEAPONS")
{
$part = "HAND1";
}
elsif ($artefact->{base_type} ne "OBJ_ARMOUR")
{
next;
}
elsif ($artefact->{sub_type} =~ /_SHIELD/
|| $artefact->{sub_type} =~ /_BUCKLER/)
{
$part = "HAND2";
}
elsif ($artefact->{sub_type} =~ /_CLOAK/)
{
$part = "CLOAK";
}
elsif ($artefact->{sub_type} =~ /_CAP/
|| $artefact->{sub_type} =~ /_HAT/)
{
$part = "HELM";
}
elsif ($artefact->{sub_type} =~ /_SHIELD/)
{
$part = "HAND2";
}
elsif ($artefact->{sub_type} =~ /_BOOTS/)
{
$part = "BOOTS";
}
elsif ($artefact->{sub_type} =~ /_GLOVES/)
{
$part = "ARM";
}
if (defined $parts{$part})
{
push @{$parts{$part}}, $artefact;
}
else
{
$parts{$part} = [$artefact];
}
}
$tilefile = "dc-player.txt";
unless (open(TILES, "<$tilefile"))
{
die "Couldn't open '$tilefile' for reading: $!\n";
}
my $curr_part = "";
my $content = <TILES>;
my @lines = split "\n", $content;
foreach my $line (@lines)
{
if ($line =~ /parts_ctg\s+(\S+)/)
{
$curr_part = $1;
next;
}
next if (not defined $parts{$curr_part});
if ($line =~ /^(\S+)\s+(\S+)/)
{
my $name = $1;
my $enum = $2;
foreach my $art (@{$parts{$curr_part}})
{
if ($art->{TILE_EQ} eq $name)
{
$art->{TILE_EQ_ENUM} = "TILEP_".$curr_part."_".$enum;
# Don't break from the loop in case several artefacts
# share the same tile.
}
}
}
}
close(TILES);
# Create tiledef-unrand.cc for the function unrandart_to_tile().
# Should we also create tiledef-unrand.h this way?
$tilefile = "tiledef-unrand.cc";
print "Updating $tilefile...\n";
die "Can't write to $tilefile\n" if (-e $tilefile && !-w $tilefile);
unless (open(TILES, ">$tilefile"))
{
die "Couldn't open '$tilefile' for writing: $!\n";
}
print TILES << "HEADER_END";
/*
* This file is automatically generated from source/art-data.txt via
* util/art-data.pl. Do not directly edit this file, but rather change
* art-data.txt.
*/
#include "AppHdr.h"
#include "tiledef-unrand.h"
#include "artefact.h"
#include "tiledef-main.h"
#include "tiledef-player.h"
int unrandart_to_tile(int unrand)
{
switch (unrand)
{
HEADER_END
my $longest_enum = 0;
foreach my $artefact (@all_artefacts)
{
my $enum = $artefact->{_ENUM};
my $len = length($enum);
$longest_enum = $len if ($len > $longest_enum);
}
$longest_enum += length("UNRAND_");
foreach my $artefact (@all_artefacts)
{
next if ($artefact->{NAME} =~ /DUMMY/);
next if ($artefact->{TILE} eq "");
my $enum = "UNRAND_$artefact->{_ENUM}";
print TILES (" " x 4) . "case $enum:"
. " " x ($longest_enum - length($enum) + 2) . "return TILE_$enum;\n";
}
print TILES (" " x 4) . "default: return -1;\n";
print TILES (" " x 4) . "}\n";
print TILES "}\n\n";
print TILES "int unrandart_to_doll_tile(int unrand)\n{\n";
print TILES (" " x 4) . "switch (unrand)\n";
print TILES (" " x 4) . "{\n";
foreach my $part (sort keys %parts)
{
print TILES (" " x 4) . "// $part\n";
foreach my $artefact (@{$parts{$part}})
{
if (not defined $artefact->{TILE_EQ_ENUM})
{
print STDERR "Tile '$artefact->{TILE_EQ}' for part '$part' not "
. "found in 'dc-player.txt'.\n";
next;
}
my $enum = "UNRAND_$artefact->{_ENUM}";
my $t_enum = $artefact->{TILE_EQ_ENUM};
print TILES (" " x 4) . "case $enum:"
. " " x ($longest_enum - length($enum) + 2) . "return $t_enum;\n";
}
}
print TILES (" " x 4) . "default: return -1;\n";
print TILES (" " x 4) . "}\n";
print TILES "}\n\n";
close(TILES);
}
my %valid_func = (
equip => 1,
unequip => 1,
world_reacts => 1,
melee_effect => 1,
evoke => 1
);
sub read_funcs
{
unless(open(INPUT, "<art-func.h"))
{
die "Couldn't open art-func.h for reading: $!\n";
}
while(<INPUT>)
{
if (/^static .* _([A-Z_]+)_(\S+)\s*\(/)
{
my $enum = $1;
my $func = $2;
if (!$valid_func{$func})
{
push(@warnings, "Unrecognized func '$func' for artefact " .
"'$enum'");
next;
}
$found_funcs{$enum} ||= {};
my $func_list = $found_funcs{$enum};
my $key = $union_name{$func} || $func;
$func_list->{$key} = $func;
}
}
close(INPUT);
}
sub read_data
{
unless(open(INPUT, "<art-data.txt"))
{
die "Couldn't open art-data.txt for reading: $!\n";
}
my $prev_line = "";
my $curr_art = {};
while (<INPUT>)
{
chomp;
$line_num++;
# Skip comment-only lines
next if (/^#/);
# Strip comments.
s/#.*//;
# Strip trailing whitspace; leading whitespace indicates the
# continuation of a string field.
s/\s*$//;
if ($_ =~ /^\s*$/)
{
if ($prev_line !~ /^\s*$/)
{
finish_art($curr_art);
push(@all_artefacts, $curr_art);
$curr_art = {};
}
}
else
{
process_line($curr_art, $_);
}
$prev_line = $_;
}
close(INPUT);
if (keys(%$curr_art) > 0)
{
finish_art($curr_art);
push(@all_artefacts, $curr_art);
}
}
###############################################################3
###############################################################3
###############################################################3
chdir("..") if (-e "../art-data.txt");
chdir("source") if (-e "source/art-data.txt");
die "Couldn't find art-data.txt\n" unless (-e "art-data.txt");
die "Couldn't find art-func.h\n" unless (-e "art-func.h");
die "Couldn't find artefact.h\n" unless (-e "artefact.h");
die "Can't read art-data.txt\n" unless (-r "art-data.txt");
die "Can't read art-func.h\n" unless (-r "art-func.h");
die "Can't read artefact.h\n" unless (-r "artefact.h");
die "Can't write to artefact.h\n" unless (-w "artefact.h");
die "Can't write to art-data.h\n" if (-e "art-data.h" && !-w "art-data.h");
read_funcs();
read_data();
if (keys(%found_funcs) > 0)
{
my $key;
foreach $key (keys(%found_funcs))
{
push(@warnings, "Funcs for unknown artefact enum $key: " .
join(", ", keys(%{$found_funcs{$key}})));
}
}
if (@warnings > 0)
{
print STDERR "Warning(s):\n";
my $warn;
foreach $warn (@warnings)
{
print STDERR "$warn\n";
}
print STDERR "\n";
}
if (@errors > 0)
{
print STDERR "Error(s) processing art-data.txt:\n\n";
my $err;
foreach $err (@errors)
{
print STDERR "$err\n";
}
exit (1);
}
write_data();
write_enums();
chdir("rltiles");
write_tiles();
exit (0);