User:Barracuda/wiki-fgd.pl

From Valve Developer Community
Jump to: navigation, search

This is a quick, buggy and very dirty Perl script to convert a FGD file into wikitext. It creates a .wiki file for each entity it can find in the given FGD. Feel free to edit this page for improvements and bug fixes.

#!/usr/bin/perl -w

use strict;

die "Usage: wiki-fgd.pl <fgd> [game]\n" if @ARGV < 1;

my $fgd_file = $ARGV[0];
my $game = defined($ARGV[1]) ? $ARGV[1] : "base";
my $read_entry = 0;
my @entry;

my $wiki_dir = $fgd_file;
$wiki_dir =~ s/\..*//;
mkdir $wiki_dir unless -d $wiki_dir;

$/ = "\r\n";

open(FGD, "<$fgd_file") or die "Couldn't open '$fgd_file': $!\n";

while (<FGD>) {
	if (m/^@(?!include)/) {
		$read_entry = 1;
	}
	
	next if not $read_entry;
	
	# skip comments
	next if m!^\s*//!;
	
	push(@entry, $_);
	
	if (m/^\]/ || m/\[\]/) {
		$read_entry = 0;
		parse_entry(join('', @entry));
		@entry = ();
	}
}

close(FGD);

sub parse_entry {
	$_ = shift;

	my @keyvalues = ();
	my @flags = ();
	my @inputs = ();
	my @outputs = ();
	
	if (m/^@([a-zA-Z]+)Class( base\(([^)]*)\))?[^=]+= *(\w+)(\s+:\s+"([^[]+)")?/) {
		my ($type, $base, $name, $desc) = ($1, $3, $4, $6);
		
		#keyvalues
		while (m/(\w+)\((\w+)\)\s*:\s*"([^"]*)"\s*:\s*([0-9.-]+|"[^"]*")?(\s*:\s*"([^"]*)")?(\s*=\s*\[([^]]+)\])?/g) {
			my ($vname, $vtype, $name, $default, $desc, $choices) = ($1, $2, $3, $4, $6, $8);
			my $extra = '';
			
			if (defined($choices)) {
				my $tmp = $choices;
				
				# remove whitespaces
				$tmp =~ s/\s+//g;
				
				# check for yes/no -> boolean
				if ($tmp eq '0:"No"1:"Yes"') {
					$vtype = 'boolean';
				} else {
					while ($choices =~ m/([0-9.-]+)\s*:\s*"([^"]+)"/g) {
						my ($value, $desc) = ($1, $2);
						$extra .= "\n:* $value : $desc";
					}
				}
			}
			
			$name = escape_param($name);
			
			my $kv = "{{KV|$name|$vtype";
			
			if (defined($desc)) {
				$desc = escape_param($desc);
				$kv .= "|$desc";
			}
			
			$kv .= "}}$extra";
			
			push(@keyvalues, $kv);
		}
		
		#flags
		while (m/spawnflags\((\w+)\)\s*=\s*\[([^]]+)\]/g) {
			my ($name, $flag_list) = ($1, $2);
			
			while ($flag_list =~ m/(\d+)\s*:\s*"([^"]+)"/g) {
				my ($value, $desc) = ($1, $2);
				push(@flags, "* $value : $desc");
			}
		}
		
		#in-/outputs
		while (m/(input|output)\s+(\w+)\((\w+)\)( *: *"([^"]*)")?/g) {
			my ($iotype, $name, $ptype, $desc) = ($1, $2, $3, $5);
			
			$name = escape_param($name);
			
			my $io = "{{IO|$name";
			
			if (defined($desc) && $desc) {
				$desc = escape_param($desc);
				$io .= "|$desc";
			}
			
			if ($ptype ne "void") {
				$io .= "|param=$ptype";
			}
			
			$io .= "}}";
			
			if ($iotype eq "input") {
				push(@inputs, $io);
			} else {
				push(@outputs, $io);
			}
		}
		
		#base template
		if (defined($base)) {
			my @bases = split(/,/, $base);
			
			foreach (@bases){
				$_ = trim($_);
				
				if ($_ eq 'Targetname') {
					push(@keyvalues, "{{KV Targetname}}");
					push(@inputs, "{{I Targetname}}");
					push(@outputs, "{{O Targetname}}");
				} elsif ($_ eq 'Parentname') {
					push(@keyvalues, "{{KV Parentname}}");
					push(@inputs, "{{I Parentname}}");
				} elsif ($_ eq 'Angles') {
					push(@keyvalues, "{{KV Angles}}");
				} elsif ($_ eq 'EnableDisable') {
					push(@keyvalues, "{{KV EnableDisable}}");
					push(@inputs, "{{I EnableDisable}}");
				} elsif ($_ eq 'Toggle') {
					push(@inputs, "{{I Toggle}}");
				} elsif ($_ eq 'Trigger') {
					push(@keyvalues, "{{KV Trigger}}");
					push(@flags, "{{Fl Trigger}}");
					push(@inputs, "{{I Trigger}}");
					push(@outputs, "{{O Trigger}}");
				} elsif ($_ eq 'Origin') {
					push(@keyvalues, "{{KV Origin}}");
				} elsif ($_ eq 'Global') {
					push(@keyvalues, "{{KV Global}}");
				} elsif ($_ eq 'Inputfilter') {
					push(@keyvalues, "{{KV InputFilter}}");
				} elsif ($_ eq 'Shadow') {
					push(@keyvalues, "{{KV Shadow}}");
					push(@inputs, "{{I Shadow}}");
				} elsif ($_ eq 'Door') {
					push(@keyvalues, "{{KV Door}}");
					push(@flags, "{{Fl Door}}");
					push(@inputs, "{{I Door}}");
					push(@outputs, "{{O Door}}");
				} elsif (m/^(PropDynamicBase|prop_dynamic_base)$/) {
					push(@keyvalues, "{{KV PropDynamicBase}}");
					push(@flags, "{{Fl PropDynamicBase}}");
					push(@inputs, "{{I PropDynamicBase}}");
					push(@outputs, "{{O PropDynamicBase}}");
				} elsif ($_ eq 'BasePropPhysics') {
					push(@keyvalues, "{{KV BasePropPhysics}}");
					push(@flags, "{{Fl BasePropPhysics}}");
					push(@inputs, "{{I BasePropPhysics}}");
					push(@outputs, "{{O BasePropPhysics}}");
				} elsif ($_ eq 'BaseNPC') {
					push(@keyvalues, "{{KV BaseNPC}}");
					push(@flags, "{{Fl BaseNPC}}");
					push(@inputs, "{{I BaseNPC}}");
					push(@outputs, "{{O BaseNPC}}");
				} elsif ($_ eq 'RenderFields') {
					push(@keyvalues, "{{KV RenderFields}}");
					push(@inputs, "{{I RenderFields}}");
					push(@outputs, "{{O RenderFields}}");
				} elsif ($_ eq 'DXLevelChoice') {
					push(@keyvalues, "{{KV DXLevelChoice}}");
				} elsif ($_ eq 'BaseFilter') {
					push(@keyvalues, "{{KV BaseFilter}}");
					push(@inputs, "{{I BaseFilter}}");
					push(@outputs, "{{O BaseFilter}}");
				} elsif ($_ eq 'Reflection') {
					push(@keyvalues, "{{KV Reflection}}");
					push(@inputs, "{{I Reflection}}");
				} elsif ($_ eq 'TalkNPC') {
					push(@keyvalues, "{{KV TalkNPC}}");
					push(@inputs, "{{I TalkNPC}}");
				} elsif ($_ eq 'Weapon') {
					push(@inputs, "{{O Weapon}}");
				} elsif ($game eq 'tf2') {
					# TF2
					if ($_ eq 'ObjectABS') {
						push(@keyvalues, "{{KV TFObject}}");
						push(@inputs, "{{I TFObject}}");
						push(@outputs, "{{O TFObject}}");
					} elsif ($_ eq 'BaseObject') {
						push(@keyvalues, "{{KV TFBaseObject}}");
						push(@flags, "{{Fl TFBaseObject}}");
					} elsif (m/^(TeamNum|TFTeam)$/) {
						push(@keyvalues, "{{KV TFTeam}}");
						push(@flags, "{{I TFTeam}}");
					} elsif ($_ eq 'GameType') {
						push(@keyvalues, "{{KV TFGameType}}");
					} else {
						warn "Unknown base: $_ for $name";
					}
				} elsif ($game eq 'portal2') {
					# Portal 2
					if ($_ eq 'LinkedPortalDoor') {
						push(@keyvalues, "{{KV LinkedPortalDoor}}");
						push(@inputs, "{{I LinkedPortalDoor}}");
						push(@outputs, "{{O LinkedPortalDoor}}");
					} elsif ($_ eq 'BaseProjector') {
						push(@keyvalues, "{{KV BaseProjector}}");
						push(@inputs, "{{I BaseProjector}}");
					} else {
						warn "Unknown base: $_ for $name";
					}
				} else {
					warn "Unknown base: $_ for $name";
				}
			}
		}
		
		if ($type =~ /^(Point|NPC|Filter|Base)$/) {
			$type = "point";
		} elsif ($type eq "Solid") {
			$type = "brush";
		} else {
			warn "Unknown class type: $type";
			return;
		}
		
		if (defined($desc)) {
			$desc =~ s/"\s*\+\s*"//g;
			$desc =~ s/\\n/\n/g;
			$desc = escape_param($desc);
		}
		
		#printing
		my $wiki_file = "$wiki_dir/$name.wiki";
		open(WIKI, ">$wiki_file") or die "Couldn't open '$wiki_file': $!\n";
		
		if (!defined($desc)) {
			print WIKI "{{stub}}\n\n";
		}
		
		print WIKI "{{$game $type|$name}}\n\n";
		print WIKI "==Entity description==\n";
		
		if (defined($desc)) {
			print WIKI "$desc\n\n";
		} else {
			print WIKI "{{todo|add description}}\n\n"; 
		}
		
		print_section("Keyvalues", @keyvalues);
		print_section("Flags", @flags);
		print_section("Inputs", @inputs);
		print_section("Outputs", @outputs);
		
		close(WIKI);
		
		print "Created $wiki_file\n";
	} else {
		warn "Unrecognized entry";
	}
}

sub print_section {
	my ($name, @array) = @_;

	if (scalar(@array) > 0) {
		print WIKI "==$name==\n";
		print WIKI join("\n", @array);
		print WIKI "\n\n";
	}
}

sub escape_param {
	my $string = shift;
	$string =~ s/=/{{=}}/g;
	return $string;
}

sub trim
{
	my $string = shift;
	$string =~ s/^\s+//;
	$string =~ s/\s+$//;
	return $string;
}