use strict; use warnings; my %vm = ( menu => {}, csprogs => {}, progs => {} ); my $skip = 0; my $parsing_builtins = undef; my $parsing_builtin = 0; my $parsing_fields = undef; my $parsing_globals = undef; my $parsing_vm = undef; for(<../*.h>, <../*.c>) { open my $fh, "<", $_ or die "<$_: $!"; while(<$fh>) { chomp; if(/^#if 0$/) { $skip = 1; } elsif(/^#else$/) { $skip = 0; } elsif(/^#endif$/) { $skip = 0; } elsif($skip) { } elsif(/^prvm_builtin_t vm_m_/) { $parsing_builtins = "menu"; $parsing_builtin = 0; } elsif(/^prvm_builtin_t vm_cl_/) { $parsing_builtins = "csprogs"; $parsing_builtin = 0; } elsif(/^prvm_builtin_t vm_sv_/) { $parsing_builtins = "progs"; $parsing_builtin = 0; } elsif(/^\}/) { $parsing_builtins = undef; $parsing_globals = undef; $parsing_fields = undef; $parsing_vm = undef; } elsif(/^typedef struct entvars_s$/) { $parsing_fields = "fields"; $parsing_vm = "progs"; } elsif(/^typedef struct cl_entvars_s$/) { $parsing_fields = "fields"; $parsing_vm = "csprogs"; } elsif(/^typedef struct prvm_prog_fieldoffsets_s$/) { $parsing_fields = "fields"; } elsif(/^typedef struct globalvars_s$/) { $parsing_globals = "globals"; $parsing_vm = "progs"; } elsif(/^typedef struct cl_globalvars_s$/) { $parsing_globals = "globals"; $parsing_vm = "csprogs"; } elsif(/^typedef struct m_globalvars_s$/) { $parsing_globals = "globals"; $parsing_vm = "menu"; } elsif(/^typedef struct prvm_prog_globaloffsets_s$/) { $parsing_globals = "globals"; } elsif($parsing_builtins) { s/\/\*.*?\*\// /g; if(/^\s*\/\//) { } elsif(/^NULL\b/) { $parsing_builtin += 1; } elsif(/^(\w+)\s*,?\s*\/\/\s+#(\d+)\s*(.*)/) { my $func = $1; my $builtin = int $2; my $descr = $3; my $extension = "DP_UNKNOWN"; if($descr =~ s/\s+\(([0-9A-Z_]*)\)//) { $extension = $1; } # 'void(vector ang) makevectors' if($descr eq "") { } elsif($descr eq "draw functions...") { } elsif($descr =~ /^\/\//) { } elsif($descr =~ /\) (\w+)/) { $func = $1; } elsif($descr =~ /(\w+)\s*\(/) { $func = $1; } elsif($descr =~ /^\w+$/) { $func = $descr; } else { warn "No function name found in $descr"; } warn "builtin sequence error: #$builtin (expected: $parsing_builtin)" if $builtin != $parsing_builtin; $parsing_builtin = $builtin + 1; $vm{$parsing_builtins}{builtins}[$builtin] = [0, $func, $extension]; } else { warn "Fails to parse: $_"; } } elsif($parsing_fields || $parsing_globals) { my $f = $parsing_fields || $parsing_globals; if(/^\s*\/\//) { } elsif(/^\s+(?:int|float|string_t|vec3_t|func_t)\s+(\w+);\s*(?:\/\/(.*))?/) { my $name = $1; my $descr = $2 || ""; my $extension = "DP_UNKNOWN"; $extension = $1 if $descr =~ /\b([0-9A-Z_]+)\b/; my $found = undef; $vm{menu}{$f}{$name} = ($found = [0, $extension]) if $descr =~ /common|menu/; $vm{progs}{$f}{$name} = ($found = [0, $extension]) if $descr =~ /common|ssqc/; $vm{csprogs}{$f}{$name} = ($found = [0, $extension]) if $descr =~ /common|csqc/; $vm{$parsing_vm}{$f}{$name} = ($found = [0, $extension]) if not defined $found and defined $parsing_vm; warn "$descr does not yield info about target VM" if not defined $found; } } elsif(/getglobal\w*\(\w+, "(\w+)"\)/) { # hack for weird DP source $vm{csprogs}{globals}{$1} = [0, "DP_CSQC_SPAWNPARTICLE"]; } } close $fh; } # now read in dpdefs for(( ["csprogsdefs.qc", "csprogs"], ["dpextensions.qc", "progs"], ["menudefs.qc", "menu"], ["progsdefs.qc", "progs"] )) { my ($file, $v) = @$_; open my $fh, "<", "$file" or die "<$file: $!"; while(<$fh>) { s/\/\/.*//; if(/^(?:float|entity|string|vector)\s+((?:\w+\s*,\s*)*\w+)\s*;/) { for(split /\s*,\s*/, $1) { print "// $v: Global $_ declared but not defined\n" if not $vm{$v}{globals}{$_}; $vm{$v}{globals}{$_}[0] = 1; # documented! } } elsif(/^\.(?:float|entity|string|vector|void)(?:.*\))?\s+((?:\w+\s*,\s*)*\w+)\s*;/) { for(split /\s*,\s*/, $1) { print "// $v: Field $_ declared but not defined\n" if not $vm{$v}{fields}{$_}; $vm{$v}{fields}{$_}[0] = 1; # documented! } } elsif(/#(\d+)/) { print "// $v: Builtin #$1 declared but not defined\n" if not $vm{$v}{builtins}[$1]; $vm{$v}{builtins}[$1][0] = 1; # documented! } else { } } close $fh; } # some dumb output for my $v(sort keys %vm) { print "/******************************************\n"; print " * $v\n"; print " ******************************************/\n"; my $b = $vm{$v}{builtins}; for(0..@$b) { next if not defined $b->[$_]; my ($documented, $func, $extension) = @{$b->[$_]}; print "float $func(...) = #$_; // $extension\n" unless $documented; } my $g = $vm{$v}{globals}; for(sort keys %$g) { my ($documented, $extension) = @{$g->{$_}}; print "float $_; // $extension\n" unless $documented; } my $f = $vm{$v}{fields}; for(sort keys %$f) { my ($documented, $extension) = @{$f->{$_}}; print ".float $_; // $extension\n" unless $documented; } } __END__ use Data::Dumper; $Data::Dumper::Sortkeys = 1; print Dumper \%vm;