User:Maven/entitybot
Jump to navigation
Jump to search
Here is code for a small app to convert FGD entries into markup for an entity entry in this wiki.
Notes
This code is in the public domain.
It uses C# 2.0, and comes without support. Please excuse its ugly kuldgeness. The correct way would have been to use Flex/Bison.
To use this, copy an entity entry from the FGD into the top box, click the convert button, and paste the resulting bottom-box text into the appropriate VDC edit page. You will need to fill in some of the Availability section.
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FGD2Wiki
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void fgd2wikiButton_Click( object sender, EventArgs e )
{
this.ConvertFGD2Wiki();
}
private enum EntityType
{
Error = 0,
PointBased,
BrushBased,
};
private class EntityInput
{
public string name;
public string type;
public string desc;
public string ToWiki()
{
StringBuilder sb = new StringBuilder();
sb.Append( "*'''" );
sb.Append( this.name );
if ( this.type != "void" )
{
sb.Append( " <" );
sb.Append( this.type );
sb.Append( ">" );
}
sb.AppendLine( "'''" );
sb.Append( ":" );
sb.AppendLine( this.desc );
return sb.ToString();
}
};
private class EntityOutput
{
public string name;
public string type;
public string desc;
public string ToWiki()
{
StringBuilder sb = new StringBuilder();
sb.Append( "*'''" );
sb.Append( this.name );
if ( this.type != "void" )
{
sb.Append( " <" );
sb.Append( this.type );
sb.Append( ">" );
}
sb.AppendLine( "'''" );
sb.Append( ":" );
sb.AppendLine( this.desc );
return sb.ToString();
}
};
private struct Pair<A,B>
{
public Pair( A a, B b ) { this.a = a; this.b = b; }
public A a;
public B b;
}
private class EntityKeyvalue
{
public string name;
public string type;
public string name2; // the SmartEdit name
public string desc;
public string def;
public List<Pair<int, string>> choices = new List<Pair<int, string>>();
public string ToWiki()
{
StringBuilder sb = new StringBuilder();
// Add the name
sb.Append( "*'''" );
sb.Append( this.name );
sb.AppendLine( "'''" );
// Add the type
sb.Append( ":<" );
switch ( this.type )
{
case "angle":
case "angles":
case "vecline":
case "byte":
case "color255":
sb.Append( "[[" );
sb.Append( this.type );
sb.Append( "]]" );
break;
case "boolean":
case "bool":
sb.Append( "[[Wikipedia:boolean|boolean]]" );
break;
default:
sb.Append( this.type );
break;
}
sb.Append( "> " );
// Add the SmartEdit name to the desc if the desc is shortish
if ( this.desc.Length < 12 )
{
sb.Append( this.name2 );
sb.Append( ". " );
}
// Add the description
sb.Append( this.desc );
// Add a period if there isn't one aleady
if ( this.desc.Length > 0 && !this.desc.EndsWith( "." ) )
sb.Append( "." );
/*
// Give the default value
if ( this.def != "" )
{
sb.Append( " Default: " );
sb.Append( this.def );
}
*/
sb.AppendLine();
// If this is a 'choices' variable, create the choice table
if ( this.type == "choices" )
{
sb.AppendLine( ":{|" );
sb.AppendLine( "! Literal value || Description" );
foreach ( Pair<int, string> pair in choices )
{
sb.AppendLine( "|-" );
sb.Append( "| " );
sb.Append( pair.a );
sb.Append( " || " );
sb.AppendLine( pair.b );
}
sb.AppendLine( "|}" );
}
return sb.ToString();
}
};
private class EntitySpawnflag
{
public int value;
public string desc;
public int def;
};
private string StripTextUntil( ref string s, char c )
{
int i = s.IndexOf( c );
if ( i < 0 )
{
string m = s.Trim();
s = "";
return m;
}
string n = s.Substring( 0, i );
s = s.Substring( i + 1 ).TrimStart();
return n.Trim();
}
private void ConvertFGD2Wiki()
{
// Get the (trimmed) input string
string input = this.fgdTextbox.Text.Trim();
string token;
//------------------
// Parse the header
//------------------
// Read the entity type: point-based or brush-based
EntityType eEntityType = EntityType.Error;
token = StripTextUntil( ref input, ' ' );
switch ( token )
{
case "@PointClass":
eEntityType = EntityType.PointBased;
break;
case "@SolidClass":
eEntityType = EntityType.BrushBased;
break;
case "@BaseClass":
this.wikiTextbox.Text = "@BaseClass not supported";
return;
default:
eEntityType = EntityType.Error;
break;
}
// Read everything until the equal sign
string[] basetypes = new string[0];
string sEntityParams = StripTextUntil( ref input, '=' );
while ( sEntityParams.Length > 0 )
{
// Get the param name
string sParamName = StripTextUntil( ref sEntityParams, '(' );
string sParamData = StripTextUntil( ref sEntityParams, ')' );
switch ( sParamName )
{
case "base":
{
// Split into individual items
basetypes = sParamData.Split( ',' );
// Remove whitespace
for ( int j = 0; j < basetypes.Length; ++j )
basetypes[j] = basetypes[j].Trim().ToLower();
// Place targetname and parentname go first, if they exist, followed by the other templates
List<string> llBaseTypes = new List<string>( basetypes ); // temporary linked list
// TODO: this way is cheesy and inefficient; fix it
if ( llBaseTypes.Contains( "parentname" ) )
{
// Move parentname to the front
llBaseTypes.Remove( "parentname" );
llBaseTypes.Insert( 0, "parentname" );
}
if ( llBaseTypes.Contains( "targetname" ) )
{
// Move targetname to the front
llBaseTypes.Remove( "targetname" );
llBaseTypes.Insert( 0, "targetname" );
}
basetypes = llBaseTypes.ToArray(); // convert back to array
break;
}
default:
/* ignore it */
break;
}
}
// TODO: not all entities have a description; handle that case
// Read the entity name
string sEntityName = StripTextUntil( ref input, ':' );
// Read the entity description
string sEntityDescriptionUnparsed = StripTextUntil( ref input, '[' );
string sEntityDescription = "";
while ( sEntityDescriptionUnparsed.Length > 0 )
{
// Ignore everything until a quote
StripTextUntil( ref sEntityDescriptionUnparsed, '"' );
// Take everything until the next quote
sEntityDescription = sEntityDescription + StripTextUntil( ref sEntityDescriptionUnparsed, '"' ) + " ";
// TODO: this will choke on escaped quotes
}
//---------------
// Read the body
//---------------
string sBody = StripTextUntil( ref input, '@' );
List<EntityInput> llInputs = new List<EntityInput>();
List<EntityOutput> llOutputs = new List<EntityOutput>();
List<EntityKeyvalue> llKeyvalues = new List<EntityKeyvalue>();
List<EntitySpawnflag> llSpawnflags = new List<EntitySpawnflag>();
while ( sBody.Length > 0 && !sBody.StartsWith( "]" ) )
{
// Check for line comment
while ( sBody.StartsWith( "//" ) )
{
// Eat everything until the next newline
StripTextUntil( ref sBody, '\n' );
}
// Get the item part of the next item:value pair
string sItem = StripTextUntil( ref sBody, ')' );
// Input
if ( sItem.StartsWith( "input " ) )
{
// Create a new input
EntityInput ei = new EntityInput();
// Discard the "input" part
StripTextUntil( ref sItem, ' ' );
// Read its information
ei.name = StripTextUntil( ref sItem, '(' );
ei.type = sItem;
// Read the desc
StripTextUntil( ref sBody, '"' );
ei.desc = StripTextUntil( ref sBody, '"' );
// Add it to the list of inputs
llInputs.Add( ei );
}
// Output
else if ( sItem.StartsWith( "output " ) )
{
// Create a new output
EntityOutput eo = new EntityOutput();
// Discard the "output" part
StripTextUntil( ref sItem, ' ' );
// Read its information
eo.name = StripTextUntil( ref sItem, '(' );
eo.type = StripTextUntil( ref sItem, ')' );
// Read the desc
StripTextUntil( ref sBody, '"' );
eo.desc = StripTextUntil( ref sBody, '"' );
// Add it to the list of outputs
llOutputs.Add( eo );
}
// Keyvalue
else
{
// Read the name
string sName = StripTextUntil( ref sItem, '(' );
// Is it the spawnflags field?
if ( sName == "spawnflags" )
{
StripTextUntil( ref sBody, '[' );
string sFlags = StripTextUntil( ref sBody, ']' );
while ( sFlags.Length > 0 )
{
// Create a new spawnflag
EntitySpawnflag fl = new EntitySpawnflag();
// Read its data
fl.value = int.Parse( StripTextUntil( ref sFlags, ':' ) );
StripTextUntil( ref sFlags, '"' );
fl.desc = StripTextUntil( ref sFlags, '"' );
StripTextUntil( ref sFlags, ':' );
fl.def = int.Parse( StripTextUntil( ref sFlags, '\n' ) );
// Add it to the list of spawnflags
llSpawnflags.Add( fl );
}
}
// Not spawnflag
else
{
// Create a new keyvalue
EntityKeyvalue kv = new EntityKeyvalue();
// Read its information
kv.name = sName;
kv.type = StripTextUntil( ref sItem, ')' );
// Read the SmartEdit name
StripTextUntil( ref sBody, '"' );
kv.name2 = StripTextUntil( ref sBody, '"' );
StripTextUntil( ref sBody, ':' );
// Is this a 'choices' keyvalue?
if ( kv.type == "choices" )
{
// Read the default value
kv.def = StripTextUntil( ref sBody, '=' );
// Parse the choices
StripTextUntil( ref sBody, '[' );
string sChoices = StripTextUntil( ref sBody, ']' );
while ( sChoices.Length > 0 )
{
// Create a new int,string pair
Pair<int,string> pair = new Pair<int,string>();
// Parse the choice info
pair.a = int.Parse( StripTextUntil( ref sChoices, ':' ) );
StripTextUntil( ref sChoices, '"' );
pair.b = StripTextUntil( ref sChoices, '"' );
// Add the pair
kv.choices.Add( pair );
}
// No desc (the SmartEdit name will be used)
kv.desc = "";
}
// Not a 'choices' keyvalue
else
{
// Read the rest of the line
String sLine = StripTextUntil( ref sBody, '\n' );
// Read the default value
if ( sLine.StartsWith( "\"" ) )
{
// This is the case where it's in quotes
StripTextUntil( ref sLine, '"' );
kv.def = StripTextUntil( ref sLine, '"' );
}
else
{
// This is the case where it isn't (it's an integer or blank)
kv.def = StripTextUntil( ref sLine, ':' );
}
// Read the desc
StripTextUntil( ref sLine, '"' );
kv.desc = StripTextUntil( ref sLine, '"' );
}
// Add it to the list of keyvalues
llKeyvalues.Add( kv );
}
}
}
//------------------------
// Create the output text
//------------------------
StringBuilder sb = new StringBuilder();
// Create the wrongtitle line
sb.Append( "{{wrongtitle|title=" );
sb.Append( sEntityName );
sb.AppendLine( "}}" );
sb.AppendLine();
// Create the Entity Description section
sb.AppendLine( "==Entity Description==" );
sb.AppendLine( sEntityDescription );
sb.AppendLine();
// Create the Availability section
sb.AppendLine( "==Availability==" );
sb.Append( "{{in game|" );
switch ( eEntityType )
{
case EntityType.PointBased:
sb.Append( "point" );
break;
case EntityType.BrushBased:
sb.Append( "brush" );
break;
default:
this.wikiTextbox.Text = "Unexpected entity type: " + eEntityType;
return;
}
sb.AppendLine( "}} ???" );
sb.AppendLine( "{{in code|class=???|file=???}}" );
sb.AppendLine();
// Create the Keyvalues section
sb.AppendLine( "==Keyvalues==" );
foreach ( string template in basetypes )
{
sb.Append( "*{{kv " );
sb.Append( template );
sb.AppendLine( "}}" );
}
foreach ( EntityKeyvalue kv in llKeyvalues )
{
sb.Append( kv.ToWiki() );
}
sb.AppendLine();
// Create the Flags section
StringBuilder sbFlags = new StringBuilder();
foreach ( string template in basetypes )
{
if ( IsEmptyFlag( template ) )
continue;
sbFlags.Append( "*{{fl " );
sbFlags.Append( template );
sbFlags.AppendLine( "}}" );
}
foreach ( EntitySpawnflag fl in llSpawnflags )
{
sbFlags.Append( "*" );
sbFlags.Append( fl.value );
sbFlags.Append( " : " );
sbFlags.AppendLine( fl.desc );
}
// If flag data is not empty, add the Flags section
if ( sbFlags.ToString().Length > 0 )
{
sb.AppendLine( "==Flags==" );
sb.Append( sbFlags.ToString() );
sb.AppendLine();
}
// Create the Inputs section
StringBuilder sbInputs = new StringBuilder();
foreach ( string template in basetypes )
{
if ( IsEmptyInput( template ) )
continue;
sbInputs.Append( "*{{i " );
sbInputs.Append( template );
sbInputs.AppendLine( "}}" );
}
foreach ( EntityInput ei in llInputs )
{
sbInputs.Append( ei.ToWiki() );
}
// If input data is not empty, add the Inputs section
if ( sbInputs.ToString().Length > 0 )
{
sb.AppendLine( "==Inputs==" );
sb.Append( sbInputs.ToString() );
sb.AppendLine();
}
// Create the Outputs sections
StringBuilder sbOutputs = new StringBuilder();
foreach ( string template in basetypes )
{
if ( IsEmptyOutput( template ) )
continue;
sbOutputs.Append( "*{{o " );
sbOutputs.Append( template );
sbOutputs.AppendLine( "}}" );
}
foreach ( EntityOutput eo in llOutputs )
{
sbOutputs.Append( eo.ToWiki() );
}
// If output data is not empty, add the Outputs section
if ( sbOutputs.ToString().Length > 0 )
{
sb.AppendLine( "==Outputs==" );
sb.Append( sbOutputs.ToString() );
sb.AppendLine();
}
// Add the Category:Entities line
sb.AppendLine( "[[Category:Entities]]" );
sb.AppendLine();
/*
// TEMP: dump the tokens to the output box
foreach ( string token in tokens )
sb.AppendLine( token );
*/
// Output the results
this.wikiTextbox.Text = sb.ToString();
}
// TODO: IsEmptyKeyvalues
private bool IsEmptyInput( string s )
{
// TODO: efficiency
switch ( s.ToLower() )
{
case "angles":
case "renderfxchoices":
return true;
default:
return false;
}
}
private bool IsEmptyOutput( string s )
{
// TODO: efficiency
switch ( s.ToLower() )
{
case "angles":
case "parentname":
case "renderfxchoices":
return true;
default:
return false;
}
}
private bool IsEmptyFlag( string s )
{
// TODO: efficiency
switch ( s.ToLower() )
{
case "angles":
case "parentname":
case "targetname":
case "renderfxchoices":
case "basevehicle":
return true;
default:
return false;
}
}
}
}
Form1.Designer.cs
namespace FGD2Wiki
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose( bool disposing )
{
if ( disposing && (components != null) )
{
components.Dispose();
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.fgdTextbox = new System.Windows.Forms.TextBox();
this.wikiTextbox = new System.Windows.Forms.TextBox();
this.panel1 = new System.Windows.Forms.Panel();
this.fgd2wikiButton = new System.Windows.Forms.Button();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point( 0, 0 );
this.splitContainer1.Name = "splitContainer1";
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add( this.fgdTextbox );
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add( this.wikiTextbox );
this.splitContainer1.Panel2.Controls.Add( this.panel1 );
this.splitContainer1.Size = new System.Drawing.Size( 522, 449 );
this.splitContainer1.SplitterDistance = 205;
this.splitContainer1.TabIndex = 0;
this.splitContainer1.Text = "splitContainer1";
//
// fgdTextbox
//
this.fgdTextbox.Dock = System.Windows.Forms.DockStyle.Fill;
this.fgdTextbox.Font = new System.Drawing.Font( "Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (0)) );
this.fgdTextbox.Location = new System.Drawing.Point( 0, 0 );
this.fgdTextbox.Multiline = true;
this.fgdTextbox.Name = "fgdTextbox";
this.fgdTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.fgdTextbox.Size = new System.Drawing.Size( 522, 205 );
this.fgdTextbox.TabIndex = 0;
//
// wikiTextbox
//
this.wikiTextbox.Dock = System.Windows.Forms.DockStyle.Fill;
this.wikiTextbox.Font = new System.Drawing.Font( "Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (0)) );
this.wikiTextbox.Location = new System.Drawing.Point( 0, 25 );
this.wikiTextbox.Multiline = true;
this.wikiTextbox.Name = "wikiTextbox";
this.wikiTextbox.ReadOnly = true;
this.wikiTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.wikiTextbox.Size = new System.Drawing.Size( 522, 215 );
this.wikiTextbox.TabIndex = 1;
//
// panel1
//
this.panel1.Controls.Add( this.fgd2wikiButton );
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
this.panel1.Location = new System.Drawing.Point( 0, 0 );
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size( 522, 25 );
this.panel1.TabIndex = 0;
//
// fgd2wikiButton
//
this.fgd2wikiButton.Dock = System.Windows.Forms.DockStyle.Left;
this.fgd2wikiButton.Location = new System.Drawing.Point( 0, 0 );
this.fgd2wikiButton.Name = "fgd2wikiButton";
this.fgd2wikiButton.Size = new System.Drawing.Size( 101, 25 );
this.fgd2wikiButton.TabIndex = 0;
this.fgd2wikiButton.Text = "&Convert";
this.fgd2wikiButton.Click += new System.EventHandler( this.fgd2wikiButton_Click );
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF( 6F, 13F );
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size( 522, 449 );
this.Controls.Add( this.splitContainer1 );
this.Name = "Form1";
this.Text = "FGD to Wiki";
this.splitContainer1.Panel1.ResumeLayout( false );
this.splitContainer1.Panel1.PerformLayout();
this.splitContainer1.Panel2.ResumeLayout( false );
this.splitContainer1.Panel2.PerformLayout();
this.splitContainer1.ResumeLayout( false );
this.panel1.ResumeLayout( false );
this.ResumeLayout( false );
}
#endregion
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Button fgd2wikiButton;
private System.Windows.Forms.TextBox fgdTextbox;
private System.Windows.Forms.TextBox wikiTextbox;
}
}
Program.cs
If it's not auto-generated for you, you'll need this too.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace FGD2Wiki
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run( new Form1() );
}
}
}