source: ps/trunk/source/tools/entity/Entity.pm@ 25247

Last change on this file since 25247 was 25247, checked in by wraitii, 3 years ago

Implement mul_round in checkrefs.

Missed in rP22003. In use since rP25245.

  • Property svn:eol-style set to native
File size: 4.8 KB
Line 
1package Entity;
2
3use strict;
4use warnings;
5
6use XML::Parser;
7use Data::Dumper;
8use File::Find;
9
10my $vfsroot = '../../../binaries/data/mods';
11
12sub get_filename
13{
14 my ($vfspath, $mod) = @_;
15 my $fn = "$vfsroot/$mod/simulation/templates/special/filter/$vfspath.xml";
16 if (not -e $fn) {
17 $fn = "$vfsroot/$mod/simulation/templates/mixins/$vfspath.xml";
18 }
19 if (not -e $fn) {
20 $fn = "$vfsroot/$mod/simulation/templates/$vfspath.xml";
21 }
22 return $fn;
23}
24
25sub get_file
26{
27 my ($vfspath, $mod) = @_;
28 my $fn = get_filename($vfspath, $mod);
29 open my $f, $fn or die "Error loading $fn: $!";
30 local $/;
31 return <$f>;
32}
33
34sub trim
35{
36 my ($t) = @_;
37 return '' if not defined $t;
38 $t =~ /^\s*(.*?)\s*$/s;
39 return $1;
40}
41
42sub load_xml
43{
44 my ($vfspath, $file) = @_;
45 my $root = {};
46 my @stack = ($root);
47 my $p = new XML::Parser(Handlers => {
48 Start => sub {
49 my ($e, $n, %a) = @_;
50 my $t = {};
51 die "Duplicate child node '$n'" if exists $stack[-1]{$n};
52 $stack[-1]{$n} = $t;
53 for (keys %a) {
54 $t->{'@'.$_}{' content'} = trim($a{$_});
55 }
56 push @stack, $t;
57 },
58 End => sub {
59 my ($e, $n) = @_;
60 $stack[-1]{' content'} = trim($stack[-1]{' content'});
61 pop @stack;
62 },
63 Char => sub {
64 my ($e, $str) = @_;
65 $stack[-1]{' content'} .= $str;
66 },
67 });
68 eval {
69 $p->parse($file);
70 };
71 if ($@) {
72 die "Error parsing $vfspath: $@";
73 }
74 return $root;
75}
76
77sub apply_layer
78{
79 my ($base, $new) = @_;
80 if ($new->{'@datatype'} and $new->{'@datatype'}{' content'} eq 'tokens') {
81 my @old = split /\s+/, ($base->{' content'} || '');
82 my @new = split /\s+/, ($new->{' content'} || '');
83 my @t = @old;
84 for my $n (@new) {
85 if ($n =~ /^-(.*)/) {
86 @t = grep $_ ne $1, @t;
87 } else {
88 push @t, $n if not grep $_ eq $n, @t;
89 }
90 }
91 $base->{' content'} = join ' ', @t;
92 } elsif ($new->{'@op'}) {
93 my $op = $new->{'@op'}{' content'};
94 my $op1 = $base->{' content'};
95 my $op2 = $new->{' content'};
96 if ($op eq 'add') {
97 $base->{' content'} = $op1 + $op2;
98 }
99 elsif ($op eq 'mul') {
100 $base->{' content'} = $op1 * $op2;
101 }
102 elsif ($op eq 'mul_round') {
103 # This is incorrect (floors instead of rounding)
104 # but for schema purposes it ought be fine.
105 $base->{' content'} = int($op1 * $op2);
106 }
107 else {
108 die "Invalid operator '$op'";
109 }
110 } else {
111 $base->{' content'} = $new->{' content'};
112 }
113 for my $k (grep $_ ne ' content', keys %$new) {
114 if ($new->{$k}{'@disable'}) {
115 delete $base->{$k};
116 } else {
117 if ($new->{$k}{'@replace'}) {
118 delete $base->{$k};
119 }
120 $base->{$k} ||= {};
121 apply_layer($base->{$k}, $new->{$k});
122 delete $base->{$k}{'@replace'};
123 }
124 }
125}
126
127sub get_main_mod
128{
129 my ($vfspath, $mods) = @_;
130 my @mods_list = split(/\|/, $mods);
131 my $main_mod = $mods_list[0];
132 my $fn = "$vfsroot/$main_mod/simulation/templates/$vfspath.xml";
133 if (not -e $fn)
134 {
135 for my $dep (@mods_list)
136 {
137 $fn = "$vfsroot/$dep/simulation/templates/$vfspath.xml";
138 if (-e $fn)
139 {
140 $main_mod = $dep;
141 last;
142 }
143 }
144 }
145 return $main_mod;
146}
147
148sub load_inherited
149{
150 my ($vfspath, $mods, $base) = @_;
151 if ($vfspath =~ /\|/) {
152 my @paths = split(/\|/, $vfspath, 2);
153 $base = load_inherited($paths[1], $mods, $base);
154 $base = load_inherited($paths[0], $mods, $base);
155 return $base
156 }
157 my $main_mod = get_main_mod($vfspath, $mods);
158 my $layer = load_xml($vfspath, get_file($vfspath, $main_mod));
159
160 if ($layer->{Entity}{'@parent'}) {
161 my $parent = load_inherited($layer->{Entity}{'@parent'}{' content'}, $mods, $base);
162 apply_layer($parent->{Entity}, $layer->{Entity});
163 return $parent;
164 } else {
165 if (not $base) {
166 return $layer;
167 }
168 else {
169 apply_layer($base->{Entity}, $layer->{Entity});
170 return $base
171 }
172 }
173}
174
175sub find_entities
176{
177 my ($modName) = @_;
178 my @files;
179 my $find_process = sub {
180 return $File::Find::prune = 1 if $_ eq '.svn';
181 my $n = $File::Find::name;
182 return if /~$/;
183 return unless -f $_;
184 $n =~ s~\Q$vfsroot\E/$modName/simulation/templates/~~;
185 $n =~ s/\.xml$//;
186 push @files, $n;
187 };
188 find({ wanted => $find_process }, "$vfsroot/$modName/simulation/templates");
189
190 return @files;
191}
Note: See TracBrowser for help on using the repository browser.