elsif($c->{iscall})
{
my $func = $s->{a};
- my $funcid = $progs->{globals}[$func]{v}{int};
last
- if $progs->{error_func}{$funcid};
+ if $progs->{builtins}{error}{$func};
$ip += 1;
}
elsif($c->{isjump})
if $type eq 'vector';
return "entity $g->{int}"
if $type eq 'entity';
- return ".$progs->{entityfieldnames}[$g->{int}]"
- if $type eq 'field' and defined $progs->{entityfieldnames}[$g->{int}];
+ return ".$progs->{entityfieldnames}[$g->{int}][0]"
+ if $type eq 'field' and defined $progs->{entityfieldnames}[$g->{int}][0];
return "$g->{int}i"
if $type eq 'int';
}
}
}
-
+
+ my %solid_seen = ();
+ run_nfa $progs, $func->{first_statement}, do { my $state = -1; \$state; },
+ sub
+ {
+ my $state = ${$_[0]};
+ return \$state;
+ },
+ sub
+ {
+ my ($ip, $state) = @_;
+ return $solid_seen{"$ip $$state"}++;
+ },
+ sub
+ {
+ my ($ip, $state, $s, $c) = @_;
+
+ if($s->{op} eq 'ADDRESS')
+ {
+ my $field_ptr_ofs = $s->{b};
+ my $def = $progs->{globaldef_byoffset}->($field_ptr_ofs);
+ use Data::Dumper;
+ if (($def->{globaltype} eq 'read_only' || $def->{globaltype} eq 'const') &&
+ grep { $_ eq 'solid' } @{$progs->{entityfieldnames}[$progs->{globals}[$field_ptr_ofs]{v}{int}]})
+ {
+ # Taking address of 'solid' for subsequent write!
+ # TODO check if this address is then actually used in STOREP.
+ $$state = $ip;
+ }
+ }
+
+ if($c->{iscall})
+ {
+ # TODO check if the entity passed is actually the one on which solid was set.
+ my $func = $s->{a};
+ if ($progs->{builtins}{setmodel}{$func} || $progs->{builtins}{setmodelindex}{$func} || $progs->{builtins}{setorigin}{$func} || $progs->{builtins}{setsize}{$func})
+ {
+ # All is clean.
+ $$state = -1;
+ }
+ }
+
+ if($c->{isreturn})
+ {
+ if ($$state >= 0) {
+ ++$warned{$$state}{''}{"Changing .solid without setmodel/setmodelindex/setorigin/setsize breaks area grid linking in Quake [write is here]"};
+ ++$warned{$ip}{''}{"Changing .solid without setmodel/setmodelindex/setorigin/setsize breaks area grid linking in Quake [return is here]"};
+ }
+ }
+
+ return 0;
+ };
+
disassemble_function($progs, $func, \%warned)
if keys %warned;
}
my $name = $p{getstring}->($g->{s_name});
die "Out of range ofs $g->{ofs} in fielddef $_ (name: \"$name\")"
if $g->{ofs} >= $p{header}{entityfields};
- #warn "Duplicate fielddef for ofs $g->{ofs} in fielddef $_ (name: \"$name\")"
- # if exists $p{entityfieldnames}[$g->{ofs}];
- $p{entityfieldnames}[$g->{ofs}] = $name;
+ push @{$p{entityfieldnames}[$g->{ofs}]}, $name;
}
print STDERR "Parsing statements...\n";
}
}
- print STDERR "Looking for error()...\n";
- $p{error_func} = {};
+ print STDERR "Looking for error(), setmodel(), setmodelindex(), setorigin(), setsize()...\n";
+ $p{builtins} = { error => {}, setmodel => {}, setmodelindex => {}, setorigin => {}, setsize => {} };
for(@{$p{globaldefs}})
{
+ my $name = $p{getstring}($_->{s_name});
next
- if $p{getstring}($_->{s_name}) ne 'error';
+ if not exists $p{builtins}{$name};
my $v = $p{globals}[$_->{ofs}]{v}{int};
next
if $v <= 0 || $v >= @{$p{functions}};
my $first = $p{functions}[$v]{first_statement};
next
if $first >= 0;
- print STDERR "Detected error() at offset $_->{ofs} (builtin #@{[-$first]})\n";
- $p{error_func}{$_->{ofs}} = 1;
+ print STDERR "Detected $name() at offset $_->{ofs} (builtin #@{[-$first]})\n";
+ $p{builtins}{$name}{$_->{ofs}} = 1;
}
print STDERR "Scanning functions...\n";